Files
thrillwiki_django_no_react/architecture/deployment-guide.md
pacnpal d504d41de2 feat: complete monorepo structure with frontend and shared resources
- Add complete backend/ directory with full Django application
- Add frontend/ directory with Vite + TypeScript setup ready for Next.js
- Add comprehensive shared/ directory with:
  - Complete documentation and memory-bank archives
  - Media files and avatars (letters, park/ride images)
  - Deployment scripts and automation tools
  - Shared types and utilities
- Add architecture/ directory with migration guides
- Configure pnpm workspace for monorepo development
- Update .gitignore to exclude .django_tailwind_cli/ build artifacts
- Preserve all historical documentation in shared/docs/memory-bank/
- Set up proper structure for full-stack development with shared resources
2025-08-23 18:40:07 -04:00

13 KiB

ThrillWiki Monorepo Deployment Guide

This document outlines deployment strategies, build processes, and infrastructure considerations for the ThrillWiki Django + Vue.js monorepo.

Build Process Overview

graph TB
    A[Source Code] --> B[Backend Build]
    A --> C[Frontend Build]
    B --> D[Django Static Collection]
    C --> E[Vue.js Production Build]
    D --> F[Backend Container]
    E --> G[Frontend Assets]
    F --> H[Production Deployment]
    G --> H

Development Environment

Prerequisites

  • Python 3.11+ with UV package manager
  • Node.js 18+ with pnpm
  • PostgreSQL (production) / SQLite (development)
  • Redis (for caching and sessions)

Local Development Setup

# Clone repository
git clone <repository-url>
cd thrillwiki-monorepo

# Install root dependencies
pnpm install

# Backend setup
cd backend
uv sync
uv run manage.py migrate
uv run manage.py collectstatic

# Frontend setup
cd ../frontend
pnpm install

# Start development servers
cd ..
pnpm run dev  # Starts both backend and frontend

Build Strategies

Multi-stage Dockerfile for Backend

# backend/Dockerfile
FROM python:3.11-slim as builder

WORKDIR /app
COPY pyproject.toml uv.lock ./
RUN pip install uv
RUN uv sync --no-dev

FROM python:3.11-slim as runtime

WORKDIR /app
COPY --from=builder /app/.venv /app/.venv
ENV PATH="/app/.venv/bin:$PATH"

COPY . .
RUN python manage.py collectstatic --noinput

EXPOSE 8000
CMD ["gunicorn", "config.wsgi:application", "--bind", "0.0.0.0:8000"]

Dockerfile for Frontend

# frontend/Dockerfile
FROM node:18-alpine as builder

WORKDIR /app
COPY package.json pnpm-lock.yaml ./
RUN npm install -g pnpm
RUN pnpm install --frozen-lockfile

COPY . .
RUN pnpm run build

FROM nginx:alpine as runtime
COPY --from=builder /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/nginx.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

Docker Compose for Development

# docker-compose.dev.yml
version: '3.8'

services:
  db:
    image: postgres:15
    environment:
      POSTGRES_DB: thrillwiki
      POSTGRES_USER: thrillwiki
      POSTGRES_PASSWORD: password
    volumes:
      - postgres_data:/var/lib/postgresql/data
    ports:
      - "5432:5432"

  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"

  backend:
    build:
      context: ./backend
      dockerfile: Dockerfile.dev
    ports:
      - "8000:8000"
    volumes:
      - ./backend:/app
      - ./shared/media:/app/media
    environment:
      - DEBUG=1
      - DATABASE_URL=postgresql://thrillwiki:password@db:5432/thrillwiki
      - REDIS_URL=redis://redis:6379/0
    depends_on:
      - db
      - redis

  frontend:
    build:
      context: ./frontend
      dockerfile: Dockerfile.dev
    ports:
      - "3000:3000"
    volumes:
      - ./frontend:/app
      - /app/node_modules
    environment:
      - VITE_API_URL=http://localhost:8000

volumes:
  postgres_data:

Docker Compose for Production

# docker-compose.prod.yml
version: '3.8'

services:
  db:
    image: postgres:15
    environment:
      POSTGRES_DB: ${POSTGRES_DB}
      POSTGRES_USER: ${POSTGRES_USER}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
    volumes:
      - postgres_data:/var/lib/postgresql/data
    restart: unless-stopped

  redis:
    image: redis:7-alpine
    restart: unless-stopped

  backend:
    build:
      context: ./backend
      dockerfile: Dockerfile
    environment:
      - DEBUG=0
      - DATABASE_URL=${DATABASE_URL}
      - REDIS_URL=${REDIS_URL}
      - SECRET_KEY=${SECRET_KEY}
      - ALLOWED_HOSTS=${ALLOWED_HOSTS}
    volumes:
      - ./shared/media:/app/media
      - static_files:/app/staticfiles
    depends_on:
      - db
      - redis
    restart: unless-stopped

  frontend:
    build:
      context: ./frontend
      dockerfile: Dockerfile
    restart: unless-stopped

  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf
      - ./nginx/ssl:/etc/nginx/ssl
      - static_files:/usr/share/nginx/html/static
      - ./shared/media:/usr/share/nginx/html/media
    depends_on:
      - backend
      - frontend
    restart: unless-stopped

volumes:
  postgres_data:
  static_files:

2. Static Site Generation (Alternative)

For sites with mostly static content, consider pre-rendering:

# Frontend build with pre-rendering
cd frontend
pnpm run build:prerender

# Serve static files with minimal backend

CI/CD Pipeline

GitHub Actions Workflow

# .github/workflows/deploy.yml
name: Deploy ThrillWiki

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest
    
    services:
      postgres:
        image: postgres:15
        env:
          POSTGRES_PASSWORD: postgres
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5

    steps:
    - uses: actions/checkout@v4
    
    - name: Set up Python
      uses: actions/setup-python@v4
      with:
        python-version: '3.11'
    
    - name: Install UV
      run: pip install uv
    
    - name: Backend Tests
      run: |
        cd backend
        uv sync
        uv run manage.py test
        uv run flake8 .
        uv run black --check .
    
    - name: Set up Node.js
      uses: actions/setup-node@v4
      with:
        node-version: '18'
    
    - name: Install pnpm
      run: npm install -g pnpm
    
    - name: Frontend Tests
      run: |
        cd frontend
        pnpm install --frozen-lockfile
        pnpm run test
        pnpm run lint
        pnpm run type-check

  build:
    needs: test
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'
    
    steps:
    - uses: actions/checkout@v4
    
    - name: Build and push Docker images
      run: |
        docker build -t thrillwiki-backend ./backend
        docker build -t thrillwiki-frontend ./frontend
        # Push to registry
    
    - name: Deploy to production
      run: |
        # Deploy using your preferred method
        # (AWS ECS, GCP Cloud Run, Azure Container Instances, etc.)

Platform-Specific Deployments

1. Vercel Deployment (Frontend + API)

// vercel.json
{
  "version": 2,
  "builds": [
    {
      "src": "frontend/package.json",
      "use": "@vercel/static-build",
      "config": {
        "distDir": "dist"
      }
    },
    {
      "src": "backend/config/wsgi.py",
      "use": "@vercel/python"
    }
  ],
  "routes": [
    {
      "src": "/api/(.*)",
      "dest": "backend/config/wsgi.py"
    },
    {
      "src": "/(.*)",
      "dest": "frontend/dist/$1"
    }
  ]
}

2. Railway Deployment

# railway.toml
[environments.production]

[environments.production.services.backend]
dockerfile = "backend/Dockerfile"
variables = { DEBUG = "0" }

[environments.production.services.frontend]
dockerfile = "frontend/Dockerfile"

[environments.production.services.postgres]
image = "postgres:15"
variables = { POSTGRES_DB = "thrillwiki" }

3. DigitalOcean App Platform

# .do/app.yaml
name: thrillwiki
services:
- name: backend
  source_dir: backend
  github:
    repo: your-username/thrillwiki-monorepo
    branch: main
  run_command: gunicorn config.wsgi:application
  environment_slug: python
  instance_count: 1
  instance_size_slug: basic-xxs
  envs:
  - key: DEBUG
    value: "0"

- name: frontend
  source_dir: frontend
  github:
    repo: your-username/thrillwiki-monorepo
    branch: main
  build_command: pnpm run build
  run_command: pnpm run preview
  environment_slug: node-js
  instance_count: 1
  instance_size_slug: basic-xxs

databases:
- name: thrillwiki-db
  engine: PG
  version: "15"

Environment Configuration

Environment Variables

Backend (.env)

# Django Settings
DEBUG=0
SECRET_KEY=your-secret-key-here
ALLOWED_HOSTS=yourdomain.com,www.yourdomain.com

# Database
DATABASE_URL=postgresql://user:password@host:port/database

# Redis
REDIS_URL=redis://host:port/0

# File Storage
MEDIA_ROOT=/app/media
STATIC_ROOT=/app/staticfiles

# Email
EMAIL_BACKEND=django.core.mail.backends.smtp.EmailBackend
EMAIL_HOST=smtp.yourmailprovider.com
EMAIL_PORT=587
EMAIL_USE_TLS=True
EMAIL_HOST_USER=your-email@yourdomain.com
EMAIL_HOST_PASSWORD=your-email-password

# Third-party Services
SENTRY_DSN=your-sentry-dsn
AWS_ACCESS_KEY_ID=your-aws-key
AWS_SECRET_ACCESS_KEY=your-aws-secret

Frontend (.env.production)

VITE_API_URL=https://api.yourdomain.com
VITE_APP_TITLE=ThrillWiki
VITE_SENTRY_DSN=your-frontend-sentry-dsn
VITE_GOOGLE_ANALYTICS_ID=your-ga-id

Performance Optimization

Backend Optimizations

# backend/config/settings/production.py

# Database optimization
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'CONN_MAX_AGE': 60,
        'OPTIONS': {
            'MAX_CONNS': 20,
        }
    }
}

# Caching
CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.redis.RedisCache',
        'LOCATION': 'redis://127.0.0.1:6379/1',
        'OPTIONS': {
            'CLIENT_CLASS': 'django_redis.client.DefaultClient',
        },
        'KEY_PREFIX': 'thrillwiki'
    }
}

# Static files with CDN
AWS_S3_CUSTOM_DOMAIN = 'cdn.yourdomain.com'
STATICFILES_STORAGE = 'storages.backends.s3boto3.StaticS3Boto3Storage'
DEFAULT_FILE_STORAGE = 'storages.backends.s3boto3.MediaS3Boto3Storage'

Frontend Optimizations

// frontend/vite.config.ts
export default defineConfig({
  build: {
    rollupOptions: {
      output: {
        manualChunks: {
          vendor: ['vue', 'vue-router', 'pinia'],
          ui: ['@headlessui/vue', '@heroicons/vue']
        }
      }
    },
    sourcemap: false,
    minify: 'terser',
    terserOptions: {
      compress: {
        drop_console: true,
        drop_debugger: true
      }
    }
  }
})

Monitoring and Logging

Application Monitoring

# backend/config/settings/production.py
import sentry_sdk
from sentry_sdk.integrations.django import DjangoIntegration

sentry_sdk.init(
    dsn="your-sentry-dsn",
    integrations=[DjangoIntegration()],
    traces_sample_rate=0.1,
    send_default_pii=True
)

# Logging configuration
LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        'file': {
            'level': 'INFO',
            'class': 'logging.FileHandler',
            'filename': '/var/log/django/thrillwiki.log',
        },
    },
    'root': {
        'handlers': ['file'],
    },
}

Infrastructure Monitoring

  • Use Prometheus + Grafana for metrics
  • Implement health check endpoints
  • Set up log aggregation (ELK stack or similar)
  • Monitor database performance
  • Track API response times

Security Considerations

Production Security Checklist

  • HTTPS enforced with SSL certificates
  • Security headers configured (HSTS, CSP, etc.)
  • Database credentials secured
  • Secret keys rotated regularly
  • CORS properly configured
  • Rate limiting implemented
  • File upload validation
  • SQL injection protection
  • XSS protection enabled
  • CSRF protection active

Security Headers

# backend/config/settings/production.py
SECURE_SSL_REDIRECT = True
SECURE_HSTS_SECONDS = 31536000
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_HSTS_PRELOAD = True
SECURE_CONTENT_TYPE_NOSNIFF = True
SECURE_BROWSER_XSS_FILTER = True
X_FRAME_OPTIONS = 'DENY'

# CORS for API
CORS_ALLOWED_ORIGINS = [
    "https://yourdomain.com",
    "https://www.yourdomain.com",
]

Backup and Recovery

Database Backup Strategy

# Automated backup script
#!/bin/bash
pg_dump $DATABASE_URL | gzip > backup_$(date +%Y%m%d_%H%M%S).sql.gz
aws s3 cp backup_*.sql.gz s3://your-backup-bucket/database/

Media Files Backup

# Sync media files to S3
aws s3 sync ./shared/media/ s3://your-media-bucket/media/ --delete

Scaling Strategies

Horizontal Scaling

  • Load balancer configuration
  • Database read replicas
  • CDN for static assets
  • Redis clustering
  • Auto-scaling groups

Vertical Scaling

  • Database connection pooling
  • Application server optimization
  • Memory usage optimization
  • CPU-intensive task optimization

Troubleshooting Guide

Common Issues

  1. Build failures: Check dependencies and environment variables
  2. Database connection errors: Verify connection strings and firewall rules
  3. Static file 404s: Ensure collectstatic runs and paths are correct
  4. CORS errors: Check CORS configuration and allowed origins
  5. Memory issues: Monitor application memory usage and optimize queries

Debug Commands

# Backend debugging
cd backend
uv run manage.py check --deploy
uv run manage.py shell
uv run manage.py dbshell

# Frontend debugging
cd frontend
pnpm run build --debug
pnpm run preview

This deployment guide provides a comprehensive approach to deploying the ThrillWiki monorepo across various platforms while maintaining security, performance, and scalability.