diff --git a/backend/.env.production.example b/backend/.env.production.example index 349ea4c..9e45afd 100644 --- a/backend/.env.production.example +++ b/backend/.env.production.example @@ -21,8 +21,8 @@ JWT_EXPIRES_IN=24h # AWS SES - Production credentials # BEST PRACTICE: Use IAM roles instead of access keys AWS_REGION=us-east-1 -AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE -AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY +AWS_ACCESS_KEY_ID=AKIASOH3DHHDA557Z5N7 +AWS_SECRET_ACCESS_KEY=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 SES_FROM_EMAIL=noreply@spotlight.cam SES_FROM_NAME=spotlight.cam diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml deleted file mode 100644 index ecb2325..0000000 --- a/docker-compose.dev.yml +++ /dev/null @@ -1,39 +0,0 @@ -# Development environment overrides -# Usage: docker compose -f docker-compose.yml -f docker-compose.dev.yml up - -services: - nginx: - ports: - - "8080:80" - restart: unless-stopped - - frontend: - environment: - - NODE_ENV=development - - VITE_HOST=0.0.0.0 - volumes: - - ./frontend:/app - - /app/node_modules - command: npm run dev - stdin_open: true - tty: true - - backend: - environment: - - NODE_ENV=development - # Security: Relaxed for development - - RATE_LIMIT_ENABLED=false - - RATE_LIMIT_AUTH_MAX=100 - - RATE_LIMIT_EMAIL_MAX=20 - - ENABLE_CSRF=false - - BODY_SIZE_LIMIT=50mb - - LOG_LEVEL=debug - volumes: - - ./backend:/app - - /app/node_modules - command: npm run dev - - db: - ports: - - "5432:5432" # Expose for local tools (pgAdmin, etc.) - restart: unless-stopped diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml deleted file mode 100644 index 39b2b23..0000000 --- a/docker-compose.prod.yml +++ /dev/null @@ -1,100 +0,0 @@ -# Production environment configuration -# Usage: docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d - -services: - nginx: - ports: - - "80:80" - - "443:443" - volumes: - - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro - - ./nginx/conf.d:/etc/nginx/conf.d:ro - - ./ssl:/etc/nginx/ssl:ro # SSL certificates - restart: always - logging: - driver: "json-file" - options: - max-size: "10m" - max-file: "3" - - frontend: - build: - context: ./frontend - dockerfile: Dockerfile.prod - args: - - NODE_ENV=production - environment: - - NODE_ENV=production - volumes: [] # No volumes in production (baked into image) - command: ["nginx", "-g", "daemon off;"] - restart: always - logging: - driver: "json-file" - options: - max-size: "10m" - max-file: "3" - - backend: - build: - context: ./backend - dockerfile: Dockerfile.prod - args: - - NODE_ENV=production - environment: - - NODE_ENV=production - # Security: Strict for production - - RATE_LIMIT_ENABLED=true - - RATE_LIMIT_AUTH_MAX=5 - - RATE_LIMIT_EMAIL_MAX=3 - - ENABLE_CSRF=true - - BODY_SIZE_LIMIT=10kb - - LOG_LEVEL=warn - # Secrets should come from environment or secrets manager - # Do not hardcode in docker-compose.prod.yml - volumes: [] # No volumes in production - command: ["node", "src/server.js"] - restart: always - logging: - driver: "json-file" - options: - max-size: "10m" - max-file: "3" - deploy: - resources: - limits: - cpus: '1' - memory: 512M - reservations: - cpus: '0.5' - memory: 256M - - db: - # In production, consider using managed database (AWS RDS, etc.) - # This is for self-hosted production - environment: - - POSTGRES_USER=${POSTGRES_USER} - - POSTGRES_PASSWORD=${POSTGRES_PASSWORD} - - POSTGRES_DB=${POSTGRES_DB} - volumes: - - postgres_data:/var/lib/postgresql/data - - ./backups:/backups # For database backups - # Don't expose port in production (only internal) - # ports: [] - restart: always - logging: - driver: "json-file" - options: - max-size: "10m" - max-file: "3" - deploy: - resources: - limits: - cpus: '2' - memory: 2G - reservations: - cpus: '1' - memory: 1G - -volumes: - postgres_data: - driver: local diff --git a/docker-compose.yml b/docker-compose.yml index effa3dc..75bfc24 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,17 +1,51 @@ +# Docker Compose for spotlight.cam +# Usage: +# Development: docker compose --profile dev up +# Production: docker compose --profile prod up +# Both: docker compose --profile dev --profile prod up + services: + # ============================================ + # Nginx - Reverse Proxy & Static File Server + # ============================================ nginx: image: nginx:alpine container_name: spotlightcam-nginx - ports: - - "8080:80" - volumes: - - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro - - ./nginx/conf.d:/etc/nginx/conf.d:ro depends_on: - frontend - backend + volumes: + - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro + - ./nginx/conf.d:/etc/nginx/conf.d:ro restart: unless-stopped + profiles: ["dev"] + ports: + - "8080:80" + nginx-prod: + image: nginx:alpine + container_name: spotlightcam-nginx-prod + depends_on: + - frontend-prod + - backend-prod + volumes: + - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro + - ./nginx/conf.d:/etc/nginx/conf.d:ro + - ./ssl:/etc/nginx/ssl:ro + ports: + - "80:80" + - "443:443" + restart: always + logging: + driver: "json-file" + options: + max-size: "10m" + max-file: "3" + profiles: ["prod"] + + # ============================================ + # Frontend - React/Vite Application + # ============================================ frontend: build: context: ./frontend @@ -28,7 +62,32 @@ services: stdin_open: true tty: true command: npm run dev + restart: unless-stopped + profiles: ["dev"] + frontend-prod: + build: + context: ./frontend + dockerfile: Dockerfile.prod + args: + - NODE_ENV=production + container_name: spotlightcam-frontend-prod + expose: + - "5173" + environment: + - NODE_ENV=production + command: ["nginx", "-g", "daemon off;"] + restart: always + logging: + driver: "json-file" + options: + max-size: "10m" + max-file: "3" + profiles: ["prod"] + + # ============================================ + # Backend - Node.js/Express API + # ============================================ backend: build: context: ./backend @@ -42,12 +101,60 @@ services: environment: - NODE_ENV=development - PORT=3000 - - CORS_ORIGIN=http://localhost:8080 - - DATABASE_URL=postgresql://spotlightcam:spotlightcam123@db:5432/spotlightcam + # Security: Relaxed for development + - RATE_LIMIT_ENABLED=false + - RATE_LIMIT_AUTH_MAX=100 + - RATE_LIMIT_EMAIL_MAX=20 + - ENABLE_CSRF=false + - BODY_SIZE_LIMIT=50mb + - LOG_LEVEL=debug depends_on: - db + command: npm run dev restart: unless-stopped + profiles: ["dev"] + backend-prod: + build: + context: ./backend + dockerfile: Dockerfile.prod + args: + - NODE_ENV=production + container_name: spotlightcam-backend-prod + expose: + - "3000" + environment: + - NODE_ENV=production + - PORT=3000 + # Security: Strict for production (override with .env file) + - RATE_LIMIT_ENABLED=true + - RATE_LIMIT_AUTH_MAX=5 + - RATE_LIMIT_EMAIL_MAX=3 + - ENABLE_CSRF=true + - BODY_SIZE_LIMIT=10kb + - LOG_LEVEL=warn + depends_on: + - db-prod + command: ["node", "src/server.js"] + restart: always + logging: + driver: "json-file" + options: + max-size: "10m" + max-file: "3" + deploy: + resources: + limits: + cpus: '1' + memory: 512M + reservations: + cpus: '0.5' + memory: 256M + profiles: ["prod"] + + # ============================================ + # Database - PostgreSQL + # ============================================ db: image: postgres:15-alpine container_name: spotlightcam-db @@ -58,8 +165,39 @@ services: volumes: - postgres_data:/var/lib/postgresql/data ports: - - "5432:5432" + - "5432:5432" # Exposed for dev tools restart: unless-stopped + profiles: ["dev"] + + db-prod: + image: postgres:15-alpine + container_name: spotlightcam-db-prod + environment: + - POSTGRES_USER=${POSTGRES_USER:-spotlightcam} + - POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-spotlightcam123} + - POSTGRES_DB=${POSTGRES_DB:-spotlightcam} + volumes: + - postgres_data_prod:/var/lib/postgresql/data + - ./backups:/backups + # No exposed ports in production (internal only) + restart: always + logging: + driver: "json-file" + options: + max-size: "10m" + max-file: "3" + deploy: + resources: + limits: + cpus: '2' + memory: 2G + reservations: + cpus: '1' + memory: 1G + profiles: ["prod"] volumes: postgres_data: + driver: local + postgres_data_prod: + driver: local diff --git a/docs/DEPLOYMENT.md b/docs/DEPLOYMENT.md index f680454..90b195b 100644 --- a/docs/DEPLOYMENT.md +++ b/docs/DEPLOYMENT.md @@ -23,7 +23,8 @@ cp backend/.env.example backend/.env 3. **Start development environment** ```bash -docker compose -f docker-compose.yml -f docker-compose.dev.yml up -d +docker compose --profile dev up -d +# Or simply: docker compose up (dev is default) ``` 4. **Run database migrations** @@ -80,17 +81,17 @@ Edit `backend/.env.production`: 4. **Build production images** ```bash -docker compose -f docker-compose.yml -f docker-compose.prod.yml build +docker compose --profile prod build ``` 5. **Start production services** ```bash -docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d +docker compose --profile prod up -d ``` 6. **Run migrations** ```bash -docker compose -f docker-compose.yml -f docker-compose.prod.yml exec backend npx prisma migrate deploy +docker compose --profile prod exec backend-prod npx prisma migrate deploy ``` --- @@ -219,9 +220,10 @@ cat backups/backup_YYYYMMDD_HHMMSS.sql | docker exec -i spotlightcam-db psql -U ### View logs +**Development:** ```bash # All services -docker compose logs -f +docker compose --profile dev logs -f # Specific service docker compose logs -f backend @@ -231,6 +233,19 @@ docker compose logs -f nginx docker compose logs --tail 100 backend ``` +**Production:** +```bash +# All services +docker compose --profile prod logs -f + +# Specific service (note -prod suffix) +docker compose logs -f backend-prod +docker compose logs -f nginx-prod + +# Last 100 lines +docker compose --profile prod logs --tail 100 backend-prod +``` + ### Production log management Logs are configured with rotation: @@ -238,11 +253,6 @@ Logs are configured with rotation: - Max files: 3 - Located in Docker's logging directory -**View logs:** -```bash -docker compose -f docker-compose.yml -f docker-compose.prod.yml logs --tail 100 -f -``` - --- ## Security Checklist @@ -391,25 +401,39 @@ docker compose -f docker-compose.yml -f docker-compose.prod.yml exec backend npx ```bash # Start development -docker compose -f docker-compose.yml -f docker-compose.dev.yml up -d +docker compose --profile dev up -d +# Or simply: docker compose up -d # Start production -docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d +docker compose --profile prod up -d -# Stop all -docker compose down +# Stop all (specific profile) +docker compose --profile dev down +docker compose --profile prod down -# View logs +# View logs (development) docker compose logs -f backend -# Shell into container +# View logs (production) +docker compose logs -f backend-prod + +# Shell into container (development) docker compose exec backend sh -# Run migrations +# Shell into container (production) +docker compose exec backend-prod sh + +# Run migrations (development) docker compose exec backend npx prisma migrate deploy -# Backup database +# Run migrations (production) +docker compose exec backend-prod npx prisma migrate deploy + +# Backup database (development) docker exec spotlightcam-db pg_dump -U spotlightcam spotlightcam > backup.sql + +# Backup database (production) +docker exec spotlightcam-db-prod pg_dump -U spotlightcam spotlightcam > backup.sql ``` ---