diff --git a/.env.example b/.env.example index 6323e1b..a5d0f74 100644 --- a/.env.example +++ b/.env.example @@ -1,9 +1,24 @@ -DISCORD_TOKEN="" # Your bot token -CLIENT_ID="" # Your bot's application ID +# Local Environment Variables (Secrets) +# Copy this file to .env and fill in your actual values +# .env is gitignored and should NEVER be committed -SERVER_ID= # Discord Server ID where the bot will operate -MODERATORS_ROLE_IDS= # Comma separated list of role IDs that are Moderators(Mods, Admins, etc) +# Discord Bot Token & Application ID (REQUIRED) +# Get this from: https://discord.com/developers/applications +DISCORD_TOKEN=your-bot-token-here +CLIENT_ID=your-bot-application-id -REPEL_LOG_CHANNEL_ID= # Channel ID where the bot will log repel actions -REPEL_ROLE_ID= # Role ID assigned to users who can use the repel command -GUIDES_CHANNEL_ID="" # The ID of the channel where guides will be posted +# Override any public config values for local testing + +# Discord Server ID (your dev server) +SERVER_ID=your-server-id + +# Channel IDs (from your dev server) +GUIDES_CHANNEL_ID=your-guide-channel-id +REPEL_LOG_CHANNEL_ID=your-repel-log-channel-id + +# Role IDs (from your dev server) +REPEL_ROLE_ID=your-repel-role-id +MODERATORS_ROLE_IDS=your-moderator-role-id + +# Other +GUIDES_TRACKER_PATH=guides-tracker.json \ No newline at end of file diff --git a/.env.production b/.env.production new file mode 100644 index 0000000..fb3ab4c --- /dev/null +++ b/.env.production @@ -0,0 +1,19 @@ +# Production Environment Configuration +# Public values - safe to commit to repository +# These are Discord IDs that are publicly visible anyway + +# Discord Server ID (your dev server) +SERVER_ID=434487340535382016 + +# Channel IDs (from your dev server) +GUIDES_CHANNEL_ID=1429492053825290371 +REPEL_LOG_CHANNEL_ID=1403558160144531589 + +# Role IDs (from your dev server) +REPEL_ROLE_ID=1002411741776461844 +MODERATORS_ROLE_IDS=849481536654803004 + +# Other +GUIDES_TRACKER_PATH=/app/data/guides-tracker.json + +# Note: DISCORD_TOKEN & CLIENT_ID should be in .env.local (not committed) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 58e6a74..a92a697 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -38,21 +38,19 @@ jobs: export NODE_VERSION=$(cat .nvmrc | sed 's/v//') echo "Using Node version: $NODE_VERSION" - # Create .env.local file with secrets - cat > .env.local << EOF + # Create .env file with secrets + # Public config comes from .env.production (committed to repo) + # NODE_ENV=production is set in docker-compose.yml + cat > .env << EOF DISCORD_TOKEN=${{ secrets.DISCORD_TOKEN }} CLIENT_ID=${{ secrets.CLIENT_ID }} - GUIDES_CHANNEL_ID=${{ secrets.GUIDES_CHANNEL_ID }} - SERVER_ID=${{ secrets.SERVER_ID }} - REPEL_LOG_CHANNEL_ID=${{ secrets.REPEL_LOG_CHANNEL_ID }} - REPEL_ROLE_ID=${{ secrets.REPEL_ROLE_ID }} - MODERATORS_ROLE_IDS=${{ secrets.MODERATORS_ROLE_IDS }} EOF # Stop any existing containers docker compose down || true # Build and start production container with profile + # NODE_ENV=production is explicitly set in docker-compose.yml bot-prod service docker compose --profile prod up -d --build # Check status diff --git a/.gitignore b/.gitignore index e6f1a18..67b2b83 100644 --- a/.gitignore +++ b/.gitignore @@ -12,10 +12,15 @@ yarn-debug.log* yarn-error.log* # Env files -!.env.example .env -.env.local -.env.*.local +.env.* + +# Public config (committed to repo) +!.env.production +!.env.example # guides tracker -guides-tracker.json \ No newline at end of file +guides-tracker.json + +# Docker +docker-compose.yml \ No newline at end of file diff --git a/DOCKER.md b/DOCKER.md index d800001..2011742 100644 --- a/DOCKER.md +++ b/DOCKER.md @@ -6,7 +6,7 @@ This document explains how to run the webdev-bot using Docker. - Docker installed (version 20.10 or higher) - Docker Compose installed (version 2.0 or higher) -- `.env.local` file with required environment variables +- `.env` file with required environment variables ## Node Version Management @@ -14,7 +14,7 @@ The Docker setup uses `.nvmrc` as the single source of truth for the Node.js ver ## Environment Variables -Before running the bot, create a `.env.local` file in the project root with the following variables: +Before running the bot, create a `.env` file in the project root with the following variables: ```env DISCORD_TOKEN=your_discord_bot_token @@ -104,7 +104,7 @@ Run the production container manually (after building with the NODE_VERSION arg) ```bash docker run -d \ --name webdev-bot \ - --env-file .env.local \ + --env-file .env \ --restart unless-stopped \ webdev-bot:latest ``` @@ -114,7 +114,7 @@ Run the development container manually (after building with the NODE_VERSION arg ```bash docker run -it \ --name webdev-bot-dev \ - --env-file .env.local \ + --env-file .env \ -v $(pwd)/src:/app/src:ro \ webdev-bot:dev ``` @@ -183,7 +183,7 @@ docker compose build --no-cache ## Best Practices -1. **Never commit `.env.local`** - Keep your secrets secure +1. **Never commit `.env`** - Keep your secrets secure 2. **Use production profile for deployment** - Smaller, more secure images 3. **Keep development profile for local testing** - Faster iteration with hot reload 4. **Node version is managed in `.nvmrc`** - Update `.nvmrc` to change Node version for Docker diff --git a/Dockerfile b/Dockerfile index 60f7635..6830afc 100644 --- a/Dockerfile +++ b/Dockerfile @@ -41,6 +41,9 @@ COPY --from=deps /app/node_modules ./node_modules COPY --from=build /app/dist ./dist COPY package.json ./ +# Copy environment config file (public, non-secret) +COPY .env.production ./ + # Create data directory and set permissions for node user RUN mkdir -p /app/data && chown -R node:node /app/data diff --git a/README.md b/README.md index 7ee3074..560c23b 100644 --- a/README.md +++ b/README.md @@ -52,10 +52,10 @@ A comprehensive Discord bot designed specifically for the Web Dev Discord server pnpm install ``` -3. Create a `.env.local` file based on `.env.example` and fill in the required environment variables: +3. Create a `.env` file based on `.env.example` and fill in the required environment variables: ```bash - cp .env.example .env.local - # Edit .env.local to add your Discord bot token and other configurations + cp .env.example .env + # Edit .env to add your Discord bot token and other configurations ``` 4. Build and start the bot: diff --git a/docker-compose.yml b/docker-compose.yml index 0978750..bd5b017 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -9,10 +9,12 @@ services: container_name: webdev-bot-prod restart: unless-stopped env_file: - - .env.local + - .env environment: - - GUIDES_TRACKER_PATH=/app/data/guides-tracker.json + - NODE_ENV=production volumes: + # Mount environment config file + - ./.env.production:/app/.env.production:ro # Persist guides tracker data - guides-data:/app/data profiles: @@ -28,9 +30,9 @@ services: container_name: webdev-bot-dev restart: unless-stopped env_file: - - .env.local + - .env environment: - - GUIDES_TRACKER_PATH=/app/data/guides-tracker.json + - NODE_ENV=development volumes: # Mount source code for hot reload - ./src:/app/src:ro @@ -40,6 +42,8 @@ services: # Mount package files (in case dependencies change) - ./package.json:/app/package.json:ro - ./pnpm-lock.yaml:/app/pnpm-lock.yaml:ro + # Mount environment config files + - ./.env.production:/app/.env.production:ro # Persist guides tracker data - guides-data:/app/data profiles: diff --git a/docs/GUIDE_SYNC.md b/docs/GUIDE_SYNC.md index f4b203e..ec873dd 100644 --- a/docs/GUIDE_SYNC.md +++ b/docs/GUIDE_SYNC.md @@ -4,7 +4,7 @@ The bot automatically synchronizes guide markdown files from `src/commands/guide ## Setup -Add to your `.env.local` file: +Add to your `.env` file: ``` GUIDES_CHANNEL_ID=1234567890123456789 ``` diff --git a/src/env.ts b/src/env.ts index cc90dea..b595138 100644 --- a/src/env.ts +++ b/src/env.ts @@ -8,7 +8,7 @@ function requireEnv(key: string): string { const value = process.env[key]; if (!value) { console.error(`❌ Required environment variable ${key} is not set`); - console.error('Please check your .env.local file or CI/CD configuration'); + console.error('Please check your .env file or CI/CD configuration'); process.exit(1); } return value; @@ -33,13 +33,6 @@ export const config = { channelId: requireEnv('GUIDES_CHANNEL_ID'), trackerPath: optionalEnv('GUIDES_TRACKER_PATH'), }, - // Add more config sections as needed: - // database: { - // url: requireEnv('DATABASE_URL'), - // }, - // api: { - // openaiKey: optionalEnv('OPENAI_API_KEY'), - // }, }; export type Config = typeof config; diff --git a/src/loadEnvFile.ts b/src/loadEnvFile.ts index d402947..793ec4f 100644 --- a/src/loadEnvFile.ts +++ b/src/loadEnvFile.ts @@ -4,6 +4,7 @@ import { join } from 'node:path'; // Simple .env loader without external dependencies function loadEnvFile(filePath: string) { if (!existsSync(filePath)) { + console.warn(`Warning: File ${filePath} not found`); return; } @@ -24,12 +25,24 @@ function loadEnvFile(filePath: string) { } } } + console.log(`✅ Loaded: ${filePath}`); } catch (error) { console.warn(`Warning: Could not load ${filePath}`); - console.warn('Make sure a valid .env.local file exists.'); console.warn(error); } } -// Load local environment file if it exists -loadEnvFile(join(process.cwd(), '.env.local')); +// Determine environment (defaults to development) +const nodeEnv = process.env.NODE_ENV || 'development'; +console.log(`🌍 Environment: ${nodeEnv}`); + +// Load environment-specific config first (public values, production only) +if (nodeEnv === 'production') { + const envFile = join(process.cwd(), '.env.production'); + loadEnvFile(envFile); +} + +// Load .env file with secrets and local config (overrides public config if any) +// Required for DISCORD_TOKEN and other secrets +const localEnvFile = join(process.cwd(), '.env'); +loadEnvFile(localEnvFile);