From ec2ab0e9f4ebf9b7cb909175e335c6e0301e8df2 Mon Sep 17 00:00:00 2001 From: spliceboti <44727389-spliceboti@users.noreply.replit.com> Date: Thu, 10 Jul 2025 00:35:23 +0000 Subject: [PATCH] Enable containerization for simplified deployment and scaling of the app Adds Dockerfile, docker-compose files, and deployment scripts for containerizing the React/Express app. Replit-Commit-Author: Agent Replit-Commit-Session-Id: 3a22ac80-cd1d-4441-9e36-f24fc2f4c3de Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/3478f7c3-db8c-4fca-9165-3adbdf1b5829/e8da43e7-d99c-4328-9fdc-485bdeecffc1.jpg --- .dockerignore | 20 +++++ .env.example | 19 +++++ Dockerfile | 62 +++++++++++++++ README.md | 159 ++++++++++++++++++++++++++++++++++++++ deploy.sh | 164 ++++++++++++++++++++++++++++++++++++++++ docker-compose.prod.yml | 78 +++++++++++++++++++ docker-compose.yml | 35 +++++++++ init.sql | 143 +++++++++++++++++++++++++++++++++++ nginx.conf | 116 ++++++++++++++++++++++++++++ replit.md | 11 +++ 10 files changed, 807 insertions(+) create mode 100644 .dockerignore create mode 100644 .env.example create mode 100644 Dockerfile create mode 100644 README.md create mode 100755 deploy.sh create mode 100644 docker-compose.prod.yml create mode 100644 docker-compose.yml create mode 100644 init.sql create mode 100644 nginx.conf diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..f1ad676 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,20 @@ +node_modules +npm-debug.log +.git +.gitignore +README.md +.env +.env.local +.env.production.local +.env.development.local +.env.test.local +dist +coverage +.nyc_output +.vscode +.idea +*.log +*.tgz +*.tar.gz +.DS_Store +Thumbs.db \ No newline at end of file diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..cc9feae --- /dev/null +++ b/.env.example @@ -0,0 +1,19 @@ +# Database Configuration +DATABASE_URL=postgresql://username:password@localhost:5432/dj_management + +# Authentication +SESSION_SECRET=your-super-secret-session-key-here-minimum-32-characters-long +REPLIT_DOMAINS=localhost,yourdomain.com +REPL_ID=your-repl-id +ISSUER_URL=https://replit.com/oidc + +# Application +NODE_ENV=development +PORT=5000 + +# PostgreSQL Database (for Docker) +PGHOST=localhost +PGPORT=5432 +PGUSER=postgres +PGPASSWORD=postgres +PGDATABASE=dj_management \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..3091c34 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,62 @@ +# Multi-stage build for optimized production image +FROM node:18-alpine AS builder + +# Set working directory +WORKDIR /app + +# Copy package files +COPY package*.json ./ + +# Install all dependencies (including devDependencies for build) +RUN npm ci + +# Copy source code +COPY . . + +# Build the application +RUN npm run build + +# Production stage +FROM node:18-alpine AS production + +# Install dumb-init for proper signal handling +RUN apk add --no-cache dumb-init + +# Create non-root user +RUN addgroup -g 1001 -S nodejs +RUN adduser -S nodejs -u 1001 + +# Set working directory +WORKDIR /app + +# Copy package files +COPY package*.json ./ + +# Install only production dependencies +RUN npm ci --only=production && npm cache clean --force + +# Copy built application from builder stage +COPY --from=builder /app/dist ./dist +COPY --from=builder /app/dist/public ./dist/public + +# Create uploads directory for file uploads +RUN mkdir -p uploads && chown nodejs:nodejs uploads + +# Change ownership of app directory +RUN chown -R nodejs:nodejs /app + +# Switch to non-root user +USER nodejs + +# Expose port +EXPOSE 5000 + +# Set environment variables +ENV NODE_ENV=production + +# Health check +HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ + CMD node -e "const http = require('http'); const options = { host: 'localhost', port: 5000, path: '/', timeout: 2000 }; const req = http.request(options, (res) => { process.exit(res.statusCode === 200 ? 0 : 1); }); req.on('error', () => process.exit(1)); req.end();" + +# Start the application with dumb-init +CMD ["dumb-init", "npm", "start"] \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..3df7905 --- /dev/null +++ b/README.md @@ -0,0 +1,159 @@ +# DJ Management System + +A comprehensive web-based DJ management and event scheduling system built with React, Express.js, and PostgreSQL. + +## Features + +- **User Management**: DJ profiles with social media links, bio, and contact information +- **Event Management**: Create, schedule, and manage DJ events with multiple event types +- **Availability System**: Calendar-based availability management for DJs +- **Admin Dashboard**: Complete administrative controls for user and event management +- **Assignment Tool**: Automated DJ assignment with rotation algorithms +- **WordPress Integration**: API endpoints for displaying events on WordPress sites +- **Authentication**: Secure authentication using Replit Auth (OpenID Connect) + +## Quick Start with Docker + +### Prerequisites +- Docker and Docker Compose installed on your system + +### Environment Setup + +1. Create a `.env` file in the root directory: +```bash +# Database Configuration +DATABASE_URL=postgresql://postgres:postgres@db:5432/dj_management + +# Authentication +SESSION_SECRET=your-super-secret-session-key-here-minimum-32-chars +REPLIT_DOMAINS=localhost,yourdomain.com +REPL_ID=your-repl-id +ISSUER_URL=https://replit.com/oidc + +# Application +NODE_ENV=production +PORT=5000 +``` + +### Running with Docker Compose + +```bash +# Build and start the application +docker-compose up --build + +# Run in detached mode +docker-compose up -d --build + +# Stop the application +docker-compose down + +# View logs +docker-compose logs -f app +``` + +The application will be available at `http://localhost:5000` + +### Building Docker Image Manually + +```bash +# Build the Docker image +docker build -t dj-management . + +# Run the container +docker run -p 5000:5000 \ + -e DATABASE_URL="your-database-url" \ + -e SESSION_SECRET="your-session-secret" \ + -e REPLIT_DOMAINS="localhost" \ + dj-management +``` + +## Development Setup + +### Prerequisites +- Node.js 18+ +- PostgreSQL database +- npm or yarn + +### Local Development + +1. Install dependencies: +```bash +npm install +``` + +2. Set up environment variables: +```bash +cp .env.example .env +# Edit .env with your configuration +``` + +3. Set up the database: +```bash +npm run db:push +``` + +4. Start the development server: +```bash +npm run dev +``` + +## API Endpoints + +### Authentication +- `GET /api/login` - Initiate login flow +- `GET /api/logout` - Logout user +- `GET /api/auth/user` - Get current user + +### Events +- `GET /api/events` - Get all events +- `POST /api/events` - Create new event +- `GET /api/events/upcoming` - Get upcoming events +- `GET /api/events/public` - Public events (for WordPress integration) + +### Admin +- `GET /api/users` - Get all users (admin only) +- `GET /api/stats/admin` - Admin dashboard statistics +- `POST /api/invitations` - Send DJ invitation + +## WordPress Integration + +The system provides public API endpoints for WordPress integration: + +```javascript +// Fetch upcoming events for WordPress widget +fetch('https://your-domain.com/api/events/public') + .then(response => response.json()) + .then(events => { + // Display events in WordPress + }); +``` + +## Production Deployment + +### Docker Deployment + +1. Update the `docker-compose.yml` with your production settings +2. Set up proper SSL/TLS certificates +3. Configure your reverse proxy (nginx/Apache) +4. Set up proper backup for PostgreSQL data + +### Environment Variables + +Required environment variables for production: +- `DATABASE_URL`: PostgreSQL connection string +- `SESSION_SECRET`: Secure session encryption key +- `REPLIT_DOMAINS`: Comma-separated list of allowed domains +- `REPL_ID`: Your Replit application ID +- `NODE_ENV`: Set to "production" + +## Architecture + +- **Frontend**: React 18 with TypeScript, Vite, TailwindCSS +- **Backend**: Express.js with TypeScript, Drizzle ORM +- **Database**: PostgreSQL with session storage +- **Authentication**: OpenID Connect (Replit Auth) +- **UI Components**: Radix UI primitives with shadcn/ui + +## License + +MIT License - see LICENSE file for details \ No newline at end of file diff --git a/deploy.sh b/deploy.sh new file mode 100755 index 0000000..c49d350 --- /dev/null +++ b/deploy.sh @@ -0,0 +1,164 @@ +#!/bin/bash + +# DJ Management System Deployment Script +# This script helps deploy the application using Docker + +set -e + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +echo -e "${GREEN}๐ŸŽต DJ Management System Deployment Script${NC}" +echo "=================================================" + +# Check if Docker is installed +if ! command -v docker &> /dev/null; then + echo -e "${RED}โŒ Docker is not installed. Please install Docker first.${NC}" + exit 1 +fi + +# Check if Docker Compose is installed +if ! command -v docker-compose &> /dev/null; then + echo -e "${RED}โŒ Docker Compose is not installed. Please install Docker Compose first.${NC}" + exit 1 +fi + +# Function to create .env file if it doesn't exist +create_env_file() { + if [ ! -f .env ]; then + echo -e "${YELLOW}๐Ÿ“ Creating .env file from template...${NC}" + cp .env.example .env + echo -e "${YELLOW}โš ๏ธ Please edit .env file with your configuration before proceeding.${NC}" + read -p "Press Enter to continue after editing .env file..." + fi +} + +# Function to generate a random session secret +generate_session_secret() { + if [ -f .env ]; then + if grep -q "your-super-secret-session-key" .env; then + echo -e "${YELLOW}๐Ÿ” Generating secure session secret...${NC}" + SESSION_SECRET=$(openssl rand -base64 32) + sed -i "s/your-super-secret-session-key-here-minimum-32-characters-long/$SESSION_SECRET/" .env + fi + fi +} + +# Function to build and start the application +deploy_application() { + echo -e "${GREEN}๐Ÿš€ Building and starting the DJ Management System...${NC}" + + # Build the Docker image + docker-compose build --no-cache + + # Start the services + docker-compose up -d + + # Wait for services to be ready + echo -e "${YELLOW}โณ Waiting for services to start...${NC}" + sleep 10 + + # Check if services are running + if docker-compose ps | grep -q "Up"; then + echo -e "${GREEN}โœ… DJ Management System is running!${NC}" + echo "" + echo "๐ŸŒ Application URL: http://localhost:5000" + echo "๐Ÿ“Š Database: PostgreSQL on port 5432" + echo "" + echo "๐Ÿ“‹ To view logs: docker-compose logs -f" + echo "๐Ÿ›‘ To stop: docker-compose down" + echo "๐Ÿ”„ To restart: docker-compose restart" + else + echo -e "${RED}โŒ Failed to start services. Check logs with: docker-compose logs${NC}" + exit 1 + fi +} + +# Function to deploy production version +deploy_production() { + echo -e "${GREEN}๐Ÿญ Deploying production version...${NC}" + + # Use production docker-compose file + docker-compose -f docker-compose.prod.yml build --no-cache + docker-compose -f docker-compose.prod.yml up -d + + echo -e "${GREEN}โœ… Production deployment complete!${NC}" +} + +# Function to show usage +show_usage() { + echo "Usage: $0 [OPTION]" + echo "Options:" + echo " dev Deploy development version (default)" + echo " prod Deploy production version" + echo " stop Stop all services" + echo " restart Restart all services" + echo " logs Show application logs" + echo " clean Clean up Docker containers and images" + echo " help Show this help message" +} + +# Function to stop services +stop_services() { + echo -e "${YELLOW}๐Ÿ›‘ Stopping DJ Management System...${NC}" + docker-compose down + echo -e "${GREEN}โœ… Services stopped.${NC}" +} + +# Function to restart services +restart_services() { + echo -e "${YELLOW}๐Ÿ”„ Restarting DJ Management System...${NC}" + docker-compose restart + echo -e "${GREEN}โœ… Services restarted.${NC}" +} + +# Function to show logs +show_logs() { + echo -e "${GREEN}๐Ÿ“‹ Showing application logs...${NC}" + docker-compose logs -f app +} + +# Function to clean up Docker resources +clean_up() { + echo -e "${YELLOW}๐Ÿงน Cleaning up Docker resources...${NC}" + docker-compose down --volumes --remove-orphans + docker system prune -f + echo -e "${GREEN}โœ… Cleanup complete.${NC}" +} + +# Main script logic +case "${1:-dev}" in + dev) + create_env_file + generate_session_secret + deploy_application + ;; + prod) + create_env_file + generate_session_secret + deploy_production + ;; + stop) + stop_services + ;; + restart) + restart_services + ;; + logs) + show_logs + ;; + clean) + clean_up + ;; + help) + show_usage + ;; + *) + echo -e "${RED}โŒ Invalid option: $1${NC}" + show_usage + exit 1 + ;; +esac \ No newline at end of file diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml new file mode 100644 index 0000000..be85621 --- /dev/null +++ b/docker-compose.prod.yml @@ -0,0 +1,78 @@ +version: '3.8' + +services: + app: + build: + context: . + target: production + ports: + - "5000:5000" + environment: + - NODE_ENV=production + - DATABASE_URL=${DATABASE_URL} + - SESSION_SECRET=${SESSION_SECRET} + - REPLIT_DOMAINS=${REPLIT_DOMAINS} + - REPL_ID=${REPL_ID} + - ISSUER_URL=${ISSUER_URL:-https://replit.com/oidc} + depends_on: + db: + condition: service_healthy + volumes: + - uploads_data:/app/uploads + restart: unless-stopped + healthcheck: + test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:5000/"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 40s + + db: + image: postgres:15-alpine + environment: + - POSTGRES_DB=${PGDATABASE:-dj_management} + - POSTGRES_USER=${PGUSER:-postgres} + - POSTGRES_PASSWORD=${PGPASSWORD} + ports: + - "5432:5432" + volumes: + - postgres_data:/var/lib/postgresql/data + - ./init.sql:/docker-entrypoint-initdb.d/init.sql + restart: unless-stopped + healthcheck: + test: ["CMD-SHELL", "pg_isready -U ${PGUSER:-postgres} -d ${PGDATABASE:-dj_management}"] + interval: 10s + timeout: 5s + retries: 5 + + # Optional: Redis for session storage (alternative to PostgreSQL sessions) + redis: + image: redis:7-alpine + ports: + - "6379:6379" + volumes: + - redis_data:/data + restart: unless-stopped + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 10s + timeout: 3s + retries: 5 + + # Optional: Nginx reverse proxy + nginx: + image: nginx:alpine + ports: + - "80:80" + - "443:443" + volumes: + - ./nginx.conf:/etc/nginx/nginx.conf + - ./ssl:/etc/nginx/ssl + depends_on: + - app + restart: unless-stopped + +volumes: + postgres_data: + redis_data: + uploads_data: \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..1b8b775 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,35 @@ +version: '3.8' + +services: + app: + build: . + ports: + - "5000:5000" + environment: + - NODE_ENV=production + - DATABASE_URL=postgresql://postgres:postgres@db:5432/dj_management + - SESSION_SECRET=your-super-secret-session-key-here + - REPLIT_DOMAINS=localhost + - REPL_ID=your-repl-id + - ISSUER_URL=https://replit.com/oidc + depends_on: + - db + volumes: + - ./uploads:/app/uploads + restart: unless-stopped + + db: + image: postgres:15-alpine + environment: + - POSTGRES_DB=dj_management + - POSTGRES_USER=postgres + - POSTGRES_PASSWORD=postgres + ports: + - "5432:5432" + volumes: + - postgres_data:/var/lib/postgresql/data + - ./init.sql:/docker-entrypoint-initdb.d/init.sql + restart: unless-stopped + +volumes: + postgres_data: \ No newline at end of file diff --git a/init.sql b/init.sql new file mode 100644 index 0000000..4bc029d --- /dev/null +++ b/init.sql @@ -0,0 +1,143 @@ +-- Database initialization script for Docker container +-- This script sets up the initial database schema + +-- Create sessions table for auth +CREATE TABLE IF NOT EXISTS sessions ( + sid VARCHAR PRIMARY KEY, + sess JSONB NOT NULL, + expire TIMESTAMP NOT NULL +); + +-- Create index for sessions +CREATE INDEX IF NOT EXISTS IDX_session_expire ON sessions(expire); + +-- Create enum for user roles +CREATE TYPE user_role AS ENUM ('dj', 'admin'); + +-- Create users table +CREATE TABLE IF NOT EXISTS users ( + id VARCHAR PRIMARY KEY, + email VARCHAR UNIQUE, + first_name VARCHAR, + last_name VARCHAR, + profile_image_url VARCHAR, + display_name VARCHAR, + bio TEXT, + phone VARCHAR, + location VARCHAR, + website VARCHAR, + role user_role DEFAULT 'dj', + is_active BOOLEAN DEFAULT true, + max_monthly_events INTEGER DEFAULT 4, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); + +-- Create event types table +CREATE TABLE IF NOT EXISTS event_types ( + id SERIAL PRIMARY KEY, + name VARCHAR NOT NULL, + description TEXT, + color VARCHAR DEFAULT '#3B82F6', + is_active BOOLEAN DEFAULT true, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); + +-- Create schedule templates table +CREATE TABLE IF NOT EXISTS schedule_templates ( + id SERIAL PRIMARY KEY, + name VARCHAR NOT NULL, + description TEXT, + is_active BOOLEAN DEFAULT true, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); + +-- Create schedule template slots table +CREATE TABLE IF NOT EXISTS schedule_template_slots ( + id SERIAL PRIMARY KEY, + template_id INTEGER REFERENCES schedule_templates(id) ON DELETE CASCADE, + day_of_week INTEGER NOT NULL CHECK (day_of_week >= 0 AND day_of_week <= 6), + start_time TIME NOT NULL, + end_time TIME NOT NULL, + event_type_id INTEGER REFERENCES event_types(id), + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); + +-- Create events table +CREATE TABLE IF NOT EXISTS events ( + id SERIAL PRIMARY KEY, + title VARCHAR NOT NULL, + description TEXT, + date DATE NOT NULL, + start_time TIME NOT NULL, + end_time TIME NOT NULL, + location VARCHAR, + event_type_id INTEGER REFERENCES event_types(id), + dj_id VARCHAR REFERENCES users(id), + is_admin_assigned BOOLEAN DEFAULT false, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); + +-- Create social links table +CREATE TABLE IF NOT EXISTS social_links ( + id SERIAL PRIMARY KEY, + dj_id VARCHAR REFERENCES users(id) ON DELETE CASCADE, + platform VARCHAR NOT NULL, + url VARCHAR NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); + +-- Create availability table +CREATE TABLE IF NOT EXISTS availability ( + id SERIAL PRIMARY KEY, + dj_id VARCHAR REFERENCES users(id) ON DELETE CASCADE, + date DATE NOT NULL, + is_available BOOLEAN DEFAULT true, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + UNIQUE(dj_id, date) +); + +-- Create slot eligibility table +CREATE TABLE IF NOT EXISTS slot_eligibility ( + id SERIAL PRIMARY KEY, + dj_id VARCHAR REFERENCES users(id) ON DELETE CASCADE, + event_type_id INTEGER REFERENCES event_types(id), + is_eligible BOOLEAN DEFAULT true, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); + +-- Create removal requests table +CREATE TABLE IF NOT EXISTS removal_requests ( + id SERIAL PRIMARY KEY, + event_id INTEGER REFERENCES events(id) ON DELETE CASCADE, + dj_id VARCHAR REFERENCES users(id), + reason TEXT NOT NULL, + status VARCHAR DEFAULT 'pending' CHECK (status IN ('pending', 'approved', 'denied')), + reviewed_by VARCHAR REFERENCES users(id), + reviewed_at TIMESTAMP, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); + +-- Create invitations table +CREATE TABLE IF NOT EXISTS invitations ( + id SERIAL PRIMARY KEY, + email VARCHAR NOT NULL, + token VARCHAR UNIQUE NOT NULL, + expires_at TIMESTAMP NOT NULL, + is_used BOOLEAN DEFAULT false, + created_by VARCHAR REFERENCES users(id), + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); + +-- Insert some default event types +INSERT INTO event_types (name, description, color) VALUES + ('Radio Show', 'Regular radio broadcasting', '#3B82F6'), + ('Club Night', 'Club and nightclub events', '#10B981'), + ('Wedding', 'Wedding and celebration events', '#F59E0B'), + ('Corporate Event', 'Business and corporate functions', '#6366F1'), + ('Festival', 'Music festivals and outdoor events', '#EF4444'), + ('Special Event', 'Special occasions and one-time events', '#8B5CF6') +ON CONFLICT DO NOTHING; \ No newline at end of file diff --git a/nginx.conf b/nginx.conf new file mode 100644 index 0000000..1e481a9 --- /dev/null +++ b/nginx.conf @@ -0,0 +1,116 @@ +events { + worker_connections 1024; +} + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + # Logging + access_log /var/log/nginx/access.log; + error_log /var/log/nginx/error.log; + + # Gzip compression + gzip on; + gzip_vary on; + gzip_min_length 1024; + gzip_proxied any; + gzip_comp_level 6; + gzip_types + text/plain + text/css + text/xml + text/javascript + application/json + application/javascript + application/xml+rss + application/atom+xml + image/svg+xml; + + # Rate limiting + limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s; + limit_req_zone $binary_remote_addr zone=login:10m rate=5r/m; + + # Upstream for the Node.js app + upstream app { + server app:5000; + } + + # HTTP server (redirect to HTTPS) + server { + listen 80; + server_name _; + return 301 https://$server_name$request_uri; + } + + # HTTPS server + server { + listen 443 ssl http2; + server_name _; + + # SSL configuration + ssl_certificate /etc/nginx/ssl/cert.pem; + ssl_certificate_key /etc/nginx/ssl/key.pem; + ssl_protocols TLSv1.2 TLSv1.3; + ssl_ciphers HIGH:!aNULL:!MD5; + ssl_prefer_server_ciphers on; + + # Security headers + add_header X-Frame-Options "SAMEORIGIN" always; + add_header X-Content-Type-Options "nosniff" always; + add_header X-XSS-Protection "1; mode=block" always; + add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; + add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:;" always; + + # Client max body size for file uploads + client_max_body_size 10M; + + # Rate limiting for API endpoints + location /api/ { + limit_req zone=api burst=20 nodelay; + proxy_pass http://app; + 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; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_cache_bypass $http_upgrade; + } + + # Rate limiting for login endpoints + location /api/login { + limit_req zone=login burst=5 nodelay; + proxy_pass http://app; + 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; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_cache_bypass $http_upgrade; + } + + # Static files with caching + location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { + expires 1y; + add_header Cache-Control "public, immutable"; + proxy_pass http://app; + } + + # Default location + location / { + proxy_pass http://app; + 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; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_cache_bypass $http_upgrade; + } + } +} \ No newline at end of file diff --git a/replit.md b/replit.md index 34f939d..8390889 100644 --- a/replit.md +++ b/replit.md @@ -114,17 +114,28 @@ Preferred communication style: Simple, everyday language. - **Database**: Drizzle migrations manage schema changes - **Environment**: Supports both development and production modes +### Docker Containerization +- **Multi-stage Build**: Optimized Docker image with builder and production stages +- **Security**: Non-root user, proper signal handling with dumb-init +- **Health Checks**: Built-in health monitoring for container orchestration +- **Development**: `docker-compose.yml` for local development with PostgreSQL +- **Production**: `docker-compose.prod.yml` with Redis, Nginx, and SSL support +- **Deployment Script**: `deploy.sh` for automated deployment management + ### Environment Configuration - **DATABASE_URL**: PostgreSQL connection string (required) - **SESSION_SECRET**: Session encryption key - **REPLIT_DOMAINS**: Allowed domains for Replit Auth - **NODE_ENV**: Environment mode (development/production) +- **REPL_ID**: Replit application identifier for authentication ### Hosting Requirements - **Node.js**: Version 18+ with ES modules support - **PostgreSQL**: Compatible with Neon serverless or traditional PostgreSQL - **SSL/TLS**: Required for secure authentication flows - **File Storage**: Local storage for uploaded profile images (can be extended to cloud storage) +- **Docker**: Container runtime for production deployment +- **Nginx**: Optional reverse proxy for production (included in Docker setup) ### Database Architecture - **Schema Management**: Drizzle ORM with TypeScript schema definitions