feat: add production Docker setup with multi-stage builds
- Add production Dockerfiles for frontend and backend * Frontend: multi-stage build with nginx serving static files * Backend: multi-stage build with Prisma generation - Create production nginx configuration (nginx/conf.d.prod/) * Routes to frontend-prod:80 and backend-prod:3000 * Supports WebSocket connections for Socket.IO - Update docker-compose.yml to use production config * Add env_file support for backend-prod * Mount production nginx config directory - Add .env.production.example template for deployment
This commit is contained in:
@@ -1,69 +1,24 @@
|
||||
# Production Environment Configuration
|
||||
# NEVER commit this file with real values!
|
||||
# Use environment variables or secrets manager in production
|
||||
|
||||
# Server
|
||||
NODE_ENV=production
|
||||
PORT=3000
|
||||
|
||||
# CORS - Your production domains
|
||||
CORS_ORIGIN=https://spotlight.cam,https://www.spotlight.cam
|
||||
# CORS
|
||||
CORS_ORIGIN=http://localhost
|
||||
|
||||
# Database - Use managed database or strong credentials
|
||||
# NEVER use default passwords in production!
|
||||
DATABASE_URL=postgresql://prod_user:STRONG_PASSWORD_HERE@db:5432/spotlightcam_prod
|
||||
# Database (production)
|
||||
DATABASE_URL=postgresql://spotlightcam:spotlightcam123@db-prod:5432/spotlightcam?schema=public
|
||||
|
||||
# JWT - CRITICAL: Generate strong secrets
|
||||
# Generate with: openssl rand -base64 64
|
||||
JWT_SECRET=CHANGE_THIS_TO_RANDOM_64_CHAR_STRING
|
||||
# JWT (CHANGE THESE IN PRODUCTION!)
|
||||
JWT_SECRET=production-secret-key-CHANGE-THIS-IN-REAL-PRODUCTION
|
||||
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=AK.........
|
||||
AWS_SECRET_ACCESS_KEY=change-it
|
||||
# AWS SES (REPLACE WITH YOUR CREDENTIALS)
|
||||
AWS_REGION=eu-central-1
|
||||
AWS_ACCESS_KEY_ID=your-aws-access-key-id
|
||||
AWS_SECRET_ACCESS_KEY=your-aws-secret-access-key
|
||||
SES_FROM_EMAIL=noreply@spotlight.cam
|
||||
SES_FROM_NAME=spotlight.cam
|
||||
|
||||
# Email Settings
|
||||
FRONTEND_URL=https://spotlight.cam
|
||||
FRONTEND_URL=http://localhost
|
||||
VERIFICATION_TOKEN_EXPIRY=24h
|
||||
|
||||
# Security Settings - Production (strict)
|
||||
RATE_LIMIT_ENABLED=true
|
||||
RATE_LIMIT_WINDOW_MS=900000
|
||||
RATE_LIMIT_MAX=100
|
||||
RATE_LIMIT_AUTH_MAX=5
|
||||
RATE_LIMIT_EMAIL_MAX=3
|
||||
ENABLE_CSRF=true
|
||||
BODY_SIZE_LIMIT=10kb
|
||||
LOG_LEVEL=warn
|
||||
|
||||
# Password Policy - Enforced in production
|
||||
PASSWORD_MIN_LENGTH=8
|
||||
PASSWORD_REQUIRE_UPPERCASE=true
|
||||
PASSWORD_REQUIRE_LOWERCASE=true
|
||||
PASSWORD_REQUIRE_NUMBER=true
|
||||
PASSWORD_REQUIRE_SPECIAL=false
|
||||
|
||||
# Account Lockout - Enabled in production
|
||||
ENABLE_ACCOUNT_LOCKOUT=true
|
||||
MAX_LOGIN_ATTEMPTS=5
|
||||
LOCKOUT_DURATION_MINUTES=15
|
||||
|
||||
# Database Connection Pool
|
||||
DB_POOL_MIN=2
|
||||
DB_POOL_MAX=10
|
||||
|
||||
# Monitoring (optional)
|
||||
SENTRY_DSN=
|
||||
NEW_RELIC_LICENSE_KEY=
|
||||
|
||||
# IMPORTANT SECURITY NOTES:
|
||||
# 1. Generate JWT_SECRET with: openssl rand -base64 64
|
||||
# 2. Use AWS IAM roles instead of access keys when possible
|
||||
# 3. Use environment variables or secrets manager (AWS Secrets Manager, HashiCorp Vault)
|
||||
# 4. Never commit .env files to version control
|
||||
# 5. Rotate all secrets regularly (every 90 days)
|
||||
# 6. Use strong database passwords (20+ characters, mixed case, numbers, symbols)
|
||||
|
||||
57
backend/Dockerfile.prod
Normal file
57
backend/Dockerfile.prod
Normal file
@@ -0,0 +1,57 @@
|
||||
# Multi-stage build for production
|
||||
|
||||
# Stage 1: Build
|
||||
FROM node:20-alpine AS builder
|
||||
|
||||
# Install OpenSSL for Prisma
|
||||
RUN apk add --no-cache openssl
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Copy package files
|
||||
COPY package*.json ./
|
||||
|
||||
# Install all dependencies (including devDependencies for Prisma generation)
|
||||
RUN npm ci
|
||||
|
||||
# Copy application files
|
||||
COPY . .
|
||||
|
||||
# Generate Prisma Client
|
||||
RUN npx prisma generate
|
||||
|
||||
# Stage 2: Production
|
||||
FROM node:20-alpine
|
||||
|
||||
# Install OpenSSL for Prisma
|
||||
RUN apk add --no-cache openssl
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Copy package files
|
||||
COPY package*.json ./
|
||||
|
||||
# Install only production dependencies
|
||||
RUN npm ci --only=production
|
||||
|
||||
# Copy application files and generated Prisma Client from builder
|
||||
COPY --from=builder /app/src ./src
|
||||
COPY --from=builder /app/prisma ./prisma
|
||||
COPY --from=builder /app/node_modules/.prisma ./node_modules/.prisma
|
||||
COPY --from=builder /app/node_modules/@prisma ./node_modules/@prisma
|
||||
|
||||
# Copy entrypoint script
|
||||
COPY docker-entrypoint.sh /usr/local/bin/
|
||||
RUN chmod +x /usr/local/bin/docker-entrypoint.sh
|
||||
|
||||
# Set environment to production
|
||||
ENV NODE_ENV=production
|
||||
|
||||
# Expose port
|
||||
EXPOSE 3000
|
||||
|
||||
# Set entrypoint
|
||||
ENTRYPOINT ["docker-entrypoint.sh"]
|
||||
|
||||
# Start the application
|
||||
CMD ["node", "src/server.js"]
|
||||
@@ -30,7 +30,7 @@ services:
|
||||
- backend-prod
|
||||
volumes:
|
||||
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
|
||||
- ./nginx/conf.d:/etc/nginx/conf.d:ro
|
||||
- ./nginx/conf.d.prod:/etc/nginx/conf.d:ro
|
||||
- ./ssl:/etc/nginx/ssl:ro
|
||||
ports:
|
||||
- "80:80"
|
||||
@@ -122,6 +122,8 @@ services:
|
||||
args:
|
||||
- NODE_ENV=production
|
||||
container_name: spotlightcam-backend-prod
|
||||
env_file:
|
||||
- ./backend/.env.production
|
||||
expose:
|
||||
- "3000"
|
||||
environment:
|
||||
|
||||
36
frontend/Dockerfile.prod
Normal file
36
frontend/Dockerfile.prod
Normal file
@@ -0,0 +1,36 @@
|
||||
# Multi-stage build for production
|
||||
|
||||
# Stage 1: Build
|
||||
FROM node:20-alpine AS builder
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Copy package files
|
||||
COPY package*.json ./
|
||||
|
||||
# Install dependencies
|
||||
RUN npm ci --only=production --ignore-scripts
|
||||
|
||||
# Copy all dependencies for build (including devDependencies)
|
||||
RUN npm install
|
||||
|
||||
# Copy project files
|
||||
COPY . .
|
||||
|
||||
# Build the application
|
||||
RUN npm run build
|
||||
|
||||
# Stage 2: Production
|
||||
FROM nginx:alpine
|
||||
|
||||
# Copy built assets from builder stage
|
||||
COPY --from=builder /app/dist /usr/share/nginx/html
|
||||
|
||||
# Copy nginx configuration (if needed)
|
||||
# COPY nginx.conf /etc/nginx/conf.d/default.conf
|
||||
|
||||
# Expose port
|
||||
EXPOSE 80
|
||||
|
||||
# Start nginx
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
64
nginx/conf.d.prod/default.conf
Normal file
64
nginx/conf.d.prod/default.conf
Normal file
@@ -0,0 +1,64 @@
|
||||
upstream frontend {
|
||||
server frontend-prod:80;
|
||||
}
|
||||
|
||||
upstream backend {
|
||||
server backend-prod:3000;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
server_name localhost;
|
||||
|
||||
client_max_body_size 500M;
|
||||
|
||||
# Frontend - Static files served by nginx
|
||||
location / {
|
||||
proxy_pass http://frontend;
|
||||
proxy_http_version 1.1;
|
||||
|
||||
# Standard proxy headers
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
|
||||
# Timeouts
|
||||
proxy_connect_timeout 60s;
|
||||
proxy_send_timeout 60s;
|
||||
proxy_read_timeout 60s;
|
||||
}
|
||||
|
||||
# Backend API
|
||||
location /api {
|
||||
proxy_pass http://backend;
|
||||
proxy_http_version 1.1;
|
||||
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
|
||||
# Timeouts
|
||||
proxy_connect_timeout 60s;
|
||||
proxy_send_timeout 60s;
|
||||
proxy_read_timeout 60s;
|
||||
}
|
||||
|
||||
# WebSocket for Socket.IO
|
||||
location /socket.io {
|
||||
proxy_pass http://backend;
|
||||
proxy_http_version 1.1;
|
||||
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
|
||||
# Timeouts for WebSocket
|
||||
proxy_connect_timeout 7d;
|
||||
proxy_send_timeout 7d;
|
||||
proxy_read_timeout 7d;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user