diff --git a/.env.example b/.env.example index 00c9816..a8c9992 100644 --- a/.env.example +++ b/.env.example @@ -22,6 +22,8 @@ DATABASE_URL=postgresql+asyncpg://paperdynasty:your-password@your-db-server:5432 DISCORD_CLIENT_ID=your-discord-client-id DISCORD_CLIENT_SECRET=your-discord-client-secret DISCORD_REDIRECT_URI=http://localhost:3000/auth/callback +DISCORD_SERVER_REDIRECT_URI=http://localhost:8000/api/auth/discord/callback/server +FRONTEND_URL=http://localhost:3000 # ============================================================================ # League REST APIs @@ -39,9 +41,28 @@ PD_API_KEY=your-pd-api-key # ============================================================================ CORS_ORIGINS=http://localhost:3000,http://localhost:3001 +# ============================================================================ +# Access Control +# ============================================================================ +# Comma-separated Discord user IDs allowed access +# Leave empty or use * for all users (dev only!) +ALLOWED_DISCORD_IDS= + +# ============================================================================ +# WebSocket Settings +# ============================================================================ +WS_HEARTBEAT_INTERVAL=30 +WS_CONNECTION_TIMEOUT=60 + # ============================================================================ # Redis (optional - for caching) # ============================================================================ # When using Docker Compose, Redis is automatically available at redis://redis:6379 # When running locally, use redis://localhost:6379 -# REDIS_URL=redis://localhost:6379 \ No newline at end of file +# REDIS_URL=redis://localhost:6379 + +# ============================================================================ +# Frontend Dev Server +# ============================================================================ +# Comma-separated hostnames for Vite dev server external access +NUXT_ALLOWED_HOSTS=localhost,127.0.0.1 \ No newline at end of file diff --git a/CLAUDE.md b/CLAUDE.md index ad1d5a6..3c90bcf 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -81,9 +81,52 @@ strat-gameplay-webapp/ │ └── tests/ ├── frontend-sba/ # SBA League Nuxt app ├── frontend-pd/ # PD League Nuxt app -└── shared-components/ # Shared Vue components (optional) +├── docker-compose.yml # Dev orchestration (all services) +├── docker-compose.prod.yml # Production overrides +└── .env.example # Root environment template ``` +## Environment Configuration + +### Multi-Domain Support +The project is fully environment-driven for deployment to different domains. All URLs and credentials are externalized. + +### Environment Files +| File | Purpose | +|------|---------| +| `.env.example` | Root template - copy to `.env` | +| `backend/.env.example` | Backend-specific template | +| `frontend-sba/.env.example` | SBA frontend template | +| `frontend-pd/.env.example` | PD frontend template | + +### Key Environment Variables + +**Backend** (in `backend/.env`): +- `DATABASE_URL` - PostgreSQL connection string +- `DISCORD_CLIENT_ID/SECRET` - OAuth credentials +- `DISCORD_SERVER_REDIRECT_URI` - Server-side OAuth callback +- `FRONTEND_URL` - Frontend base URL for redirects +- `CORS_ORIGINS` - Allowed origins (comma-separated) +- `ALLOWED_DISCORD_IDS` - User whitelist (comma-separated, empty = all) + +**Frontend** (in `frontend-*/env`): +- `NUXT_PUBLIC_API_URL` - Backend API URL +- `NUXT_PUBLIC_WS_URL` - WebSocket URL +- `NUXT_PUBLIC_DISCORD_CLIENT_ID` - OAuth client ID (public) +- `NUXT_PUBLIC_DISCORD_REDIRECT_URI` - OAuth callback URL +- `NUXT_ALLOWED_HOSTS` - Vite dev server allowed hosts (comma-separated) + +### Docker Deployment +```bash +# Development +docker compose up + +# Production +docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d +``` + +See `README.md` "Multi-Domain Deployment" section for full checklist. + ## Development Guidelines ### Code Quality diff --git a/README.md b/README.md index c9b8260..384e9d2 100644 --- a/README.md +++ b/README.md @@ -208,6 +208,47 @@ Copy `.env.example` to `.env` and configure: - `REDIS_URL` - Redis connection (auto-configured in Docker) - `CORS_ORIGINS` - Allowed origins (defaults to localhost:3000,3001) +--- + +## Multi-Domain Deployment + +This project supports deployment to multiple domains. Key environment variables to configure: + +### Backend Variables +| Variable | Description | Example | +|----------|-------------|---------| +| `FRONTEND_URL` | Frontend base URL for OAuth redirects | `https://yourdomain.com` | +| `DISCORD_REDIRECT_URI` | Legacy Discord callback | `https://yourdomain.com/auth/callback` | +| `DISCORD_SERVER_REDIRECT_URI` | Server-side Discord callback | `https://yourdomain.com/api/auth/discord/callback/server` | +| `CORS_ORIGINS` | Allowed CORS origins (comma-separated) | `https://yourdomain.com,https://api.yourdomain.com` | +| `ALLOWED_DISCORD_IDS` | Whitelist of Discord user IDs | `123456,789012` or empty for all | + +### Frontend Variables +| Variable | Description | Example | +|----------|-------------|---------| +| `NUXT_PUBLIC_API_URL` | Backend API base URL | `https://yourdomain.com` | +| `NUXT_PUBLIC_WS_URL` | WebSocket base URL | `https://yourdomain.com` | +| `NUXT_PUBLIC_DISCORD_REDIRECT_URI` | OAuth callback URL | `https://yourdomain.com/auth/callback` | +| `NUXT_ALLOWED_HOSTS` | Vite dev server allowed hosts (comma-separated) | `yourdomain.com,localhost` | + +### Production Deployment + +```bash +# Using Docker Compose with production overrides +docker-compose -f docker-compose.yml -f docker-compose.prod.yml up -d +``` + +### Domain Deployment Checklist + +When deploying to a new domain: +1. [ ] Update Discord OAuth redirect URIs in [Discord Developer Portal](https://discord.com/developers/applications) +2. [ ] Copy `.env.example` files to `.env` and configure all URLs +3. [ ] Update `CORS_ORIGINS` to include new domain +4. [ ] Add domain to `NUXT_ALLOWED_HOSTS` (if using dev server externally) +5. [ ] Configure reverse proxy (Nginx/NPM) to route traffic + +--- + ## Available Services When running, the following services are available: diff --git a/backend/CLAUDE.md b/backend/CLAUDE.md index 09c2347..329abe2 100644 --- a/backend/CLAUDE.md +++ b/backend/CLAUDE.md @@ -75,14 +75,36 @@ now = pendulum.now('UTC') - **Database**: paperdynasty_dev - **Connection**: `postgresql+asyncpg://...` -### Required .env Variables +### Environment Variables +Copy `.env.example` to `.env` and configure. Key variables: + ```bash -DATABASE_URL=postgresql+asyncpg://... -SECRET_KEY=your-secret-key -DISCORD_CLIENT_ID=... -DISCORD_CLIENT_SECRET=... +# Required +DATABASE_URL=postgresql+asyncpg://user:pass@host:5432/db +SECRET_KEY=your-secret-key-min-32-chars +DISCORD_CLIENT_ID=your-discord-client-id +DISCORD_CLIENT_SECRET=your-discord-client-secret + +# OAuth URLs (update for your domain) +DISCORD_REDIRECT_URI=http://localhost:3000/auth/callback +DISCORD_SERVER_REDIRECT_URI=http://localhost:8000/api/auth/discord/callback/server +FRONTEND_URL=http://localhost:3000 + +# Access Control +ALLOWED_DISCORD_IDS= # Comma-separated user IDs, empty = all users + +# CORS (JSON array format) +CORS_ORIGINS=["http://localhost:3000","http://localhost:3001"] + +# League APIs +SBA_API_URL=https://api.sba.manticorum.com +SBA_API_KEY=your-sba-api-key +PD_API_URL=https://pd-api.example.com +PD_API_KEY=your-pd-api-key ``` +**Multi-Domain Deployment**: When deploying to a new domain, update `FRONTEND_URL`, `DISCORD_*_REDIRECT_URI`, and `CORS_ORIGINS`. See root `README.md` for full checklist. + ### Python Environment - **Version**: Python 3.13.3 - **Package Manager**: UV (fast, reliable) diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml new file mode 100644 index 0000000..c87af15 --- /dev/null +++ b/docker-compose.prod.yml @@ -0,0 +1,28 @@ +# Production overrides for Paper Dynasty Game Engine +# Usage: docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d + +services: + backend: + build: + target: production + environment: + - APP_ENV=production + - DEBUG=false + restart: always + + frontend-sba: + build: + target: production + environment: + - NODE_ENV=production + restart: always + + frontend-pd: + build: + target: production + environment: + - NODE_ENV=production + restart: always + + redis: + restart: always diff --git a/docker-compose.yml b/docker-compose.yml index 7b10d68..12a6752 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,8 +2,6 @@ # Use this for integration testing, demos, or when you want everything containerized # For daily dev, see README.md for local development workflow -version: '3.8' - services: # Redis cache (shared dependency) redis: @@ -51,6 +49,17 @@ services: # CORS - CORS_ORIGINS=http://localhost:3000,http://localhost:3001 + + # Server-side OAuth + - FRONTEND_URL=${FRONTEND_URL:-http://localhost:3000} + - DISCORD_SERVER_REDIRECT_URI=${DISCORD_SERVER_REDIRECT_URI:-http://localhost:8000/api/auth/discord/callback/server} + + # Access Control + - ALLOWED_DISCORD_IDS=${ALLOWED_DISCORD_IDS:-} + + # WebSocket Settings + - WS_HEARTBEAT_INTERVAL=${WS_HEARTBEAT_INTERVAL:-30} + - WS_CONNECTION_TIMEOUT=${WS_CONNECTION_TIMEOUT:-60} depends_on: redis: condition: service_healthy @@ -79,6 +88,7 @@ services: - NUXT_PUBLIC_WS_URL=http://localhost:8000 - NUXT_PUBLIC_DISCORD_CLIENT_ID=${DISCORD_CLIENT_ID} - NUXT_PUBLIC_DISCORD_REDIRECT_URI=http://localhost:3000/auth/callback + - NUXT_ALLOWED_HOSTS=${NUXT_ALLOWED_HOSTS:-localhost,127.0.0.1} depends_on: backend: condition: service_healthy @@ -103,6 +113,7 @@ services: - NUXT_PUBLIC_WS_URL=http://localhost:8000 - NUXT_PUBLIC_DISCORD_CLIENT_ID=${DISCORD_CLIENT_ID} - NUXT_PUBLIC_DISCORD_REDIRECT_URI=http://localhost:3001/auth/callback + - NUXT_ALLOWED_HOSTS=${NUXT_ALLOWED_HOSTS:-localhost,127.0.0.1} depends_on: backend: condition: service_healthy diff --git a/frontend-pd/.env.example b/frontend-pd/.env.example new file mode 100644 index 0000000..a690c66 --- /dev/null +++ b/frontend-pd/.env.example @@ -0,0 +1,14 @@ +# PD League Frontend Configuration +# Copy to .env and update with your values + +# API Configuration +NUXT_PUBLIC_API_URL=http://localhost:8000 +NUXT_PUBLIC_WS_URL=http://localhost:8000 + +# Discord OAuth (public client ID only - safe to expose) +NUXT_PUBLIC_DISCORD_CLIENT_ID=your-discord-client-id +NUXT_PUBLIC_DISCORD_REDIRECT_URI=http://localhost:3001/auth/callback + +# Vite Dev Server - Allowed external hosts (comma-separated) +# Add your domain here for external access during development +NUXT_ALLOWED_HOSTS=localhost,127.0.0.1 diff --git a/frontend-pd/CLAUDE.md b/frontend-pd/CLAUDE.md index d7173d2..01672b2 100644 --- a/frontend-pd/CLAUDE.md +++ b/frontend-pd/CLAUDE.md @@ -257,16 +257,24 @@ export const useGameStore = defineStore('game', () => { ## Configuration ### Environment Variables -Create `.env` file with: +Copy `.env.example` to `.env` and configure: + ```bash -NUXT_PUBLIC_LEAGUE_ID=pd -NUXT_PUBLIC_LEAGUE_NAME=Paper Dynasty +# API endpoints NUXT_PUBLIC_API_URL=http://localhost:8000 NUXT_PUBLIC_WS_URL=http://localhost:8000 + +# Discord OAuth (public client ID - safe to expose) NUXT_PUBLIC_DISCORD_CLIENT_ID=your-client-id NUXT_PUBLIC_DISCORD_REDIRECT_URI=http://localhost:3001/auth/callback + +# Vite dev server allowed hosts (comma-separated) +# Add your domain for external access +NUXT_ALLOWED_HOSTS=localhost,127.0.0.1 ``` +**Note**: `NUXT_ALLOWED_HOSTS` is read in `nuxt.config.ts` to configure Vite's `allowedHosts`. Required when accessing dev server through a reverse proxy or external domain. + ### Nuxt Config ```typescript // nuxt.config.ts diff --git a/frontend-pd/nuxt.config.ts b/frontend-pd/nuxt.config.ts index ad069b3..3600613 100644 --- a/frontend-pd/nuxt.config.ts +++ b/frontend-pd/nuxt.config.ts @@ -16,6 +16,14 @@ export default defineNuxtConfig({ devtools: { enabled: true }, + // Vite config for external hostname access + // Configure NUXT_ALLOWED_HOSTS env var for your domains (comma-separated) + vite: { + server: { + allowedHosts: process.env.NUXT_ALLOWED_HOSTS?.split(',') || ['localhost', '127.0.0.1'] + } + }, + typescript: { strict: true, typeCheck: true diff --git a/frontend-sba/.env.example b/frontend-sba/.env.example new file mode 100644 index 0000000..9722fc6 --- /dev/null +++ b/frontend-sba/.env.example @@ -0,0 +1,14 @@ +# SBA League Frontend Configuration +# Copy to .env and update with your values + +# API Configuration +NUXT_PUBLIC_API_URL=http://localhost:8000 +NUXT_PUBLIC_WS_URL=http://localhost:8000 + +# Discord OAuth (public client ID only - safe to expose) +NUXT_PUBLIC_DISCORD_CLIENT_ID=your-discord-client-id +NUXT_PUBLIC_DISCORD_REDIRECT_URI=http://localhost:3000/auth/callback + +# Vite Dev Server - Allowed external hosts (comma-separated) +# Add your domain here for external access during development +NUXT_ALLOWED_HOSTS=localhost,127.0.0.1 diff --git a/frontend-sba/CLAUDE.md b/frontend-sba/CLAUDE.md index 9a7032d..634f77f 100644 --- a/frontend-sba/CLAUDE.md +++ b/frontend-sba/CLAUDE.md @@ -58,14 +58,25 @@ npm run lint # Lint code ## Configuration -### Environment Variables (.env) +### Environment Variables +Copy `.env.example` to `.env` and configure: + ```bash -NUXT_PUBLIC_LEAGUE_ID=sba +# API endpoints NUXT_PUBLIC_API_URL=http://localhost:8000 NUXT_PUBLIC_WS_URL=http://localhost:8000 + +# Discord OAuth (public client ID - safe to expose) NUXT_PUBLIC_DISCORD_CLIENT_ID=your-client-id +NUXT_PUBLIC_DISCORD_REDIRECT_URI=http://localhost:3000/auth/callback + +# Vite dev server allowed hosts (comma-separated) +# Add your domain for external access +NUXT_ALLOWED_HOSTS=localhost,127.0.0.1 ``` +**Note**: `NUXT_ALLOWED_HOSTS` is read in `nuxt.config.ts` to configure Vite's `allowedHosts`. Required when accessing dev server through a reverse proxy or external domain. + ### League Theme - Primary: #1e40af (SBA Blue) - Secondary: #dc2626 (SBA Red) diff --git a/frontend-sba/nuxt.config.ts b/frontend-sba/nuxt.config.ts index 61225fd..6c48db6 100644 --- a/frontend-sba/nuxt.config.ts +++ b/frontend-sba/nuxt.config.ts @@ -31,9 +31,10 @@ export default defineNuxtConfig({ }, // Vite config for external hostname access + // Configure NUXT_ALLOWED_HOSTS env var for your domains (comma-separated) vite: { server: { - allowedHosts: ['gameplay-demo.manticorum.com', 'localhost', '127.0.0.1'] + allowedHosts: process.env.NUXT_ALLOWED_HOSTS?.split(',') || ['localhost', '127.0.0.1'] } },