diff --git a/backend/requirements.txt b/backend/requirements.txt index 94274878..39f753eb 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -20,4 +20,5 @@ openpyxl==3.1.2 python-docx==1.1.2 scikit-learn==1.5.0 numpy==1.26.4 -pandas==2.2.2 \ No newline at end of file +pandas==2.2.2 +requests==2.32.3 diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml new file mode 100644 index 00000000..d656063d --- /dev/null +++ b/docker-compose.prod.yml @@ -0,0 +1,132 @@ +version: "3.8" + +services: + db: + image: postgres:16-alpine + container_name: pdg-postgres + environment: + POSTGRES_USER: pdg + POSTGRES_PASSWORD: pdg_secret_2024 + POSTGRES_DB: prop_data_guard + volumes: + - pg_data:/var/lib/postgresql/data + expose: + - "5432" + healthcheck: + test: ["CMD-SHELL", "pg_isready -U pdg -d prop_data_guard"] + interval: 5s + timeout: 5s + retries: 5 + restart: unless-stopped + + redis: + image: redis:7-alpine + container_name: pdg-redis + expose: + - "6379" + volumes: + - redis_data:/data + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 5s + timeout: 5s + retries: 5 + restart: unless-stopped + + minio: + image: minio/minio:RELEASE.2024-05-10T01-41-38Z + container_name: pdg-minio + environment: + MINIO_ROOT_USER: pdgminio + MINIO_ROOT_PASSWORD: pdgminio_secret_2024 + command: server /data --console-address ":9001" + volumes: + - minio_data:/data + expose: + - "9000" + - "9001" + restart: unless-stopped + + backend: + build: ./backend + container_name: pdg-backend + environment: + - DATABASE_URL=postgresql+psycopg2://pdg:pdg_secret_2024@db:5432/prop_data_guard + - REDIS_URL=redis://redis:6379/0 + - MINIO_ENDPOINT=minio:9000 + - MINIO_ACCESS_KEY=pdgminio + - MINIO_SECRET_KEY=pdgminio_secret_2024 + - SECRET_KEY=${SECRET_KEY:-prop-data-guard-production-secret-key} + - DB_ENCRYPTION_KEY=${DB_ENCRYPTION_KEY:-} + - ACCESS_TOKEN_EXPIRE_MINUTES=30 + - REFRESH_TOKEN_EXPIRE_DAYS=7 + expose: + - "8000" + depends_on: + db: + condition: service_healthy + redis: + condition: service_healthy + command: > + sh -c "alembic upgrade head && uvicorn app.main:app --host 0.0.0.0 --port 8000" + restart: unless-stopped + + frontend: + build: + context: ./frontend + dockerfile: Dockerfile.prod + container_name: pdg-frontend + ports: + - "80:80" + depends_on: + - backend + restart: unless-stopped + + celery_worker: + build: ./backend + container_name: pdg-celery-worker + environment: + - DATABASE_URL=postgresql+psycopg2://pdg:pdg_secret_2024@db:5432/prop_data_guard + - REDIS_URL=redis://redis:6379/0 + - SECRET_KEY=${SECRET_KEY:-prop-data-guard-production-secret-key} + - DB_ENCRYPTION_KEY=${DB_ENCRYPTION_KEY:-} + depends_on: + - db + - redis + command: > + sh -c "celery -A app.tasks.worker worker --loglevel=info --concurrency=2" + restart: unless-stopped + + celery_beat: + build: ./backend + container_name: pdg-celery-beat + environment: + - DATABASE_URL=postgresql+psycopg2://pdg:pdg_secret_2024@db:5432/prop_data_guard + - REDIS_URL=redis://redis:6379/0 + - SECRET_KEY=${SECRET_KEY:-prop-data-guard-production-secret-key} + - DB_ENCRYPTION_KEY=${DB_ENCRYPTION_KEY:-} + depends_on: + - db + - redis + command: > + sh -c "celery -A app.tasks.worker beat --loglevel=info" + restart: unless-stopped + + flower: + build: ./backend + container_name: pdg-flower + environment: + - REDIS_URL=redis://redis:6379/0 + ports: + - "5555:5555" + depends_on: + - redis + - celery_worker + command: > + sh -c "celery -A app.tasks.worker flower --port=5555" + restart: unless-stopped + +volumes: + pg_data: + redis_data: + minio_data: diff --git a/frontend/Dockerfile.prod b/frontend/Dockerfile.prod new file mode 100644 index 00000000..09e623d8 --- /dev/null +++ b/frontend/Dockerfile.prod @@ -0,0 +1,21 @@ +# Build stage +FROM node:20-alpine AS builder + +WORKDIR /app +RUN npm install -g npm@10 + +COPY package.json . +RUN npm install + +COPY . . +RUN npm run build + +# Production stage +FROM nginx:alpine + +COPY --from=builder /app/dist /usr/share/nginx/html +COPY nginx.conf /etc/nginx/conf.d/default.conf + +EXPOSE 80 + +CMD ["nginx", "-g", "daemon off;"] diff --git a/frontend/nginx.conf b/frontend/nginx.conf new file mode 100644 index 00000000..ba1172af --- /dev/null +++ b/frontend/nginx.conf @@ -0,0 +1,41 @@ +server { + listen 80; + server_name localhost; + root /usr/share/nginx/html; + index index.html; + + # Gzip compression + gzip on; + gzip_vary on; + gzip_min_length 1024; + gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript; + + # Frontend static files + location / { + try_files $uri $uri/ /index.html; + } + + # API proxy to backend + location /api/ { + proxy_pass http://backend:8000/api/; + 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; + proxy_connect_timeout 30s; + proxy_send_timeout 30s; + proxy_read_timeout 30s; + } + + # Health check endpoint + location /health { + proxy_pass http://backend:8000/health; + } + + # Cache static assets + location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { + expires 30d; + add_header Cache-Control "public, immutable"; + } +}