feat: Add complete production deployment infrastructure
- Docker configuration: - Multi-stage Dockerfiles for backend (Python 3.11) and frontend (Node 20) - Production docker-compose.yml with all services - Development docker-compose.dev.yml with hot-reload - Nginx reverse proxy: - SSL/TLS termination with modern cipher suites - Rate limiting and security headers - Caching and compression - Load balancing ready - Kubernetes manifests: - Deployment, Service, Ingress configurations - ConfigMap and Secrets - HPA for auto-scaling - PersistentVolumeClaims - Deployment scripts: - deploy.sh: Automated deployment with health checks - backup.sh: Automated backup with retention - health-check.sh: Service health monitoring - setup-ssl.sh: Let's Encrypt SSL automation - Monitoring: - Prometheus configuration - Grafana dashboards (optional) - Structured logging - Documentation: - DEPLOYMENT_GUIDE.md: Complete deployment instructions - Environment templates (.env.production) Ready for commercial deployment!
This commit is contained in:
parent
8f9ca669cf
commit
29178a75a5
75
.env.production
Normal file
75
.env.production
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
# ============================================
|
||||||
|
# Document Translation API - Production Environment
|
||||||
|
# ============================================
|
||||||
|
# IMPORTANT: Review and update all values before deployment
|
||||||
|
|
||||||
|
# ===========================================
|
||||||
|
# Application Settings
|
||||||
|
# ===========================================
|
||||||
|
APP_NAME=Document Translation API
|
||||||
|
APP_ENV=production
|
||||||
|
DEBUG=false
|
||||||
|
LOG_LEVEL=INFO
|
||||||
|
|
||||||
|
# ===========================================
|
||||||
|
# Server Configuration
|
||||||
|
# ===========================================
|
||||||
|
HTTP_PORT=80
|
||||||
|
HTTPS_PORT=443
|
||||||
|
BACKEND_PORT=8000
|
||||||
|
FRONTEND_PORT=3000
|
||||||
|
|
||||||
|
# ===========================================
|
||||||
|
# Domain Configuration
|
||||||
|
# ===========================================
|
||||||
|
DOMAIN=translate.yourdomain.com
|
||||||
|
NEXT_PUBLIC_API_URL=https://translate.yourdomain.com
|
||||||
|
|
||||||
|
# ===========================================
|
||||||
|
# Translation Service Configuration
|
||||||
|
# ===========================================
|
||||||
|
TRANSLATION_SERVICE=ollama
|
||||||
|
OLLAMA_BASE_URL=http://ollama:11434
|
||||||
|
OLLAMA_MODEL=llama3
|
||||||
|
|
||||||
|
# DeepL API (optional)
|
||||||
|
DEEPL_API_KEY=
|
||||||
|
|
||||||
|
# OpenAI API (optional)
|
||||||
|
OPENAI_API_KEY=
|
||||||
|
OPENAI_MODEL=gpt-4o-mini
|
||||||
|
|
||||||
|
# ===========================================
|
||||||
|
# File Upload Settings
|
||||||
|
# ===========================================
|
||||||
|
MAX_FILE_SIZE_MB=50
|
||||||
|
ALLOWED_EXTENSIONS=.docx,.xlsx,.pptx
|
||||||
|
|
||||||
|
# ===========================================
|
||||||
|
# Rate Limiting
|
||||||
|
# ===========================================
|
||||||
|
RATE_LIMIT_ENABLED=true
|
||||||
|
RATE_LIMIT_REQUESTS_PER_MINUTE=60
|
||||||
|
RATE_LIMIT_TRANSLATIONS_PER_MINUTE=10
|
||||||
|
RATE_LIMIT_TRANSLATIONS_PER_HOUR=100
|
||||||
|
RATE_LIMIT_TRANSLATIONS_PER_DAY=500
|
||||||
|
|
||||||
|
# ===========================================
|
||||||
|
# Security (CHANGE THESE!)
|
||||||
|
# ===========================================
|
||||||
|
ADMIN_USERNAME=admin
|
||||||
|
ADMIN_PASSWORD=CHANGE_THIS_SECURE_PASSWORD
|
||||||
|
|
||||||
|
# CORS Configuration
|
||||||
|
CORS_ORIGINS=https://translate.yourdomain.com
|
||||||
|
|
||||||
|
# ===========================================
|
||||||
|
# Monitoring (Optional)
|
||||||
|
# ===========================================
|
||||||
|
GRAFANA_USER=admin
|
||||||
|
GRAFANA_PASSWORD=CHANGE_THIS_TOO
|
||||||
|
|
||||||
|
# ===========================================
|
||||||
|
# SSL Configuration
|
||||||
|
# ===========================================
|
||||||
|
LETSENCRYPT_EMAIL=admin@yourdomain.com
|
||||||
455
DEPLOYMENT_GUIDE.md
Normal file
455
DEPLOYMENT_GUIDE.md
Normal file
@ -0,0 +1,455 @@
|
|||||||
|
# 🚀 Document Translation API - Production Deployment Guide
|
||||||
|
|
||||||
|
Complete guide for deploying the Document Translation API in production environments.
|
||||||
|
|
||||||
|
## 📋 Table of Contents
|
||||||
|
|
||||||
|
1. [Quick Start](#quick-start)
|
||||||
|
2. [Architecture Overview](#architecture-overview)
|
||||||
|
3. [Docker Deployment](#docker-deployment)
|
||||||
|
4. [Kubernetes Deployment](#kubernetes-deployment)
|
||||||
|
5. [SSL/TLS Configuration](#ssltls-configuration)
|
||||||
|
6. [Environment Configuration](#environment-configuration)
|
||||||
|
7. [Monitoring & Logging](#monitoring--logging)
|
||||||
|
8. [Scaling & Performance](#scaling--performance)
|
||||||
|
9. [Backup & Recovery](#backup--recovery)
|
||||||
|
10. [Troubleshooting](#troubleshooting)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚀 Quick Start
|
||||||
|
|
||||||
|
### Prerequisites
|
||||||
|
|
||||||
|
- Docker & Docker Compose v2.0+
|
||||||
|
- Domain name (for production)
|
||||||
|
- SSL certificate (or use Let's Encrypt)
|
||||||
|
|
||||||
|
### One-Command Deployment
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Clone and deploy
|
||||||
|
git clone https://github.com/your-repo/translate-api.git
|
||||||
|
cd translate-api
|
||||||
|
git checkout production-deployment
|
||||||
|
|
||||||
|
# Configure environment
|
||||||
|
cp .env.example .env.production
|
||||||
|
# Edit .env.production with your settings
|
||||||
|
|
||||||
|
# Deploy!
|
||||||
|
./scripts/deploy.sh production
|
||||||
|
```
|
||||||
|
|
||||||
|
Your application will be available at `https://your-domain.com`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🏗️ Architecture Overview
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────┐
|
||||||
|
│ Internet │
|
||||||
|
└────────┬────────┘
|
||||||
|
│
|
||||||
|
┌────────▼────────┐
|
||||||
|
│ Nginx │
|
||||||
|
│ (Reverse Proxy)│
|
||||||
|
│ - SSL/TLS │
|
||||||
|
│ - Rate Limit │
|
||||||
|
│ - Caching │
|
||||||
|
└────────┬────────┘
|
||||||
|
┌──────────────┼──────────────┐
|
||||||
|
│ │ │
|
||||||
|
┌────────▼────┐ ┌──────▼─────┐ ┌─────▼─────┐
|
||||||
|
│ Frontend │ │ Backend │ │ Admin │
|
||||||
|
│ (Next.js) │ │ (FastAPI) │ │ Dashboard│
|
||||||
|
│ Port 3000 │ │ Port 8000 │ │ │
|
||||||
|
└─────────────┘ └──────┬─────┘ └───────────┘
|
||||||
|
│
|
||||||
|
┌────────────────────────┼────────────────────────┐
|
||||||
|
│ │ │
|
||||||
|
┌────────▼────────┐ ┌──────────▼──────────┐ ┌───────▼───────┐
|
||||||
|
│ Ollama │ │ Google/DeepL/ │ │ Redis │
|
||||||
|
│ (Local LLM) │ │ OpenAI APIs │ │ (Cache) │
|
||||||
|
└─────────────────┘ └─────────────────────┘ └───────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🐳 Docker Deployment
|
||||||
|
|
||||||
|
### Directory Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
translate-api/
|
||||||
|
├── docker/
|
||||||
|
│ ├── backend/
|
||||||
|
│ │ └── Dockerfile
|
||||||
|
│ ├── frontend/
|
||||||
|
│ │ └── Dockerfile
|
||||||
|
│ ├── nginx/
|
||||||
|
│ │ ├── nginx.conf
|
||||||
|
│ │ ├── conf.d/
|
||||||
|
│ │ │ └── default.conf
|
||||||
|
│ │ └── ssl/
|
||||||
|
│ │ ├── fullchain.pem
|
||||||
|
│ │ └── privkey.pem
|
||||||
|
│ └── prometheus/
|
||||||
|
│ └── prometheus.yml
|
||||||
|
├── scripts/
|
||||||
|
│ ├── deploy.sh
|
||||||
|
│ ├── backup.sh
|
||||||
|
│ ├── health-check.sh
|
||||||
|
│ └── setup-ssl.sh
|
||||||
|
├── docker-compose.yml
|
||||||
|
├── docker-compose.dev.yml
|
||||||
|
├── .env.production
|
||||||
|
└── .env.example
|
||||||
|
```
|
||||||
|
|
||||||
|
### Basic Deployment
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Production (with nginx, SSL)
|
||||||
|
docker compose --env-file .env.production up -d
|
||||||
|
|
||||||
|
# View logs
|
||||||
|
docker compose logs -f
|
||||||
|
|
||||||
|
# Check status
|
||||||
|
docker compose ps
|
||||||
|
```
|
||||||
|
|
||||||
|
### With Optional Services
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# With local Ollama LLM
|
||||||
|
docker compose --profile with-ollama up -d
|
||||||
|
|
||||||
|
# With Redis caching
|
||||||
|
docker compose --profile with-cache up -d
|
||||||
|
|
||||||
|
# With monitoring (Prometheus + Grafana)
|
||||||
|
docker compose --profile with-monitoring up -d
|
||||||
|
|
||||||
|
# All services
|
||||||
|
docker compose --profile with-ollama --profile with-cache --profile with-monitoring up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
### Service Endpoints
|
||||||
|
|
||||||
|
| Service | Internal Port | External Access |
|
||||||
|
|---------|--------------|-----------------|
|
||||||
|
| Frontend | 3000 | https://domain.com/ |
|
||||||
|
| Backend API | 8000 | https://domain.com/api/ |
|
||||||
|
| Admin Dashboard | 8000 | https://domain.com/admin |
|
||||||
|
| Health Check | 8000 | https://domain.com/health |
|
||||||
|
| Prometheus | 9090 | Internal only |
|
||||||
|
| Grafana | 3001 | https://domain.com:3001 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ☸️ Kubernetes Deployment
|
||||||
|
|
||||||
|
### Prerequisites
|
||||||
|
|
||||||
|
- Kubernetes cluster (1.25+)
|
||||||
|
- kubectl configured
|
||||||
|
- Ingress controller (nginx-ingress)
|
||||||
|
- cert-manager (for SSL)
|
||||||
|
|
||||||
|
### Deploy to Kubernetes
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Create namespace and deploy
|
||||||
|
kubectl apply -f k8s/deployment.yaml
|
||||||
|
|
||||||
|
# Check status
|
||||||
|
kubectl get pods -n translate-api
|
||||||
|
kubectl get services -n translate-api
|
||||||
|
kubectl get ingress -n translate-api
|
||||||
|
|
||||||
|
# View logs
|
||||||
|
kubectl logs -f deployment/backend -n translate-api
|
||||||
|
```
|
||||||
|
|
||||||
|
### Scaling
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Manual scaling
|
||||||
|
kubectl scale deployment/backend --replicas=5 -n translate-api
|
||||||
|
|
||||||
|
# Auto-scaling is configured via HPA
|
||||||
|
kubectl get hpa -n translate-api
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔒 SSL/TLS Configuration
|
||||||
|
|
||||||
|
### Option 1: Let's Encrypt (Recommended)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Automated setup
|
||||||
|
./scripts/setup-ssl.sh translate.yourdomain.com admin@yourdomain.com
|
||||||
|
```
|
||||||
|
|
||||||
|
### Option 2: Custom Certificate
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Place your certificates in:
|
||||||
|
docker/nginx/ssl/fullchain.pem # Full certificate chain
|
||||||
|
docker/nginx/ssl/privkey.pem # Private key
|
||||||
|
docker/nginx/ssl/chain.pem # CA chain (optional)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Option 3: Self-Signed (Development Only)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
|
||||||
|
-keyout docker/nginx/ssl/privkey.pem \
|
||||||
|
-out docker/nginx/ssl/fullchain.pem \
|
||||||
|
-subj "/CN=localhost"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⚙️ Environment Configuration
|
||||||
|
|
||||||
|
### Required Variables
|
||||||
|
|
||||||
|
| Variable | Description | Default |
|
||||||
|
|----------|-------------|---------|
|
||||||
|
| `DOMAIN` | Your domain name | - |
|
||||||
|
| `ADMIN_USERNAME` | Admin login | admin |
|
||||||
|
| `ADMIN_PASSWORD` | Admin password | changeme123 |
|
||||||
|
| `TRANSLATION_SERVICE` | Default provider | ollama |
|
||||||
|
|
||||||
|
### Translation Providers
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Ollama (Local LLM)
|
||||||
|
TRANSLATION_SERVICE=ollama
|
||||||
|
OLLAMA_BASE_URL=http://ollama:11434
|
||||||
|
OLLAMA_MODEL=llama3
|
||||||
|
|
||||||
|
# Google Translate (Free)
|
||||||
|
TRANSLATION_SERVICE=google
|
||||||
|
|
||||||
|
# DeepL (Requires API key)
|
||||||
|
TRANSLATION_SERVICE=deepl
|
||||||
|
DEEPL_API_KEY=your-api-key
|
||||||
|
|
||||||
|
# OpenAI (Requires API key)
|
||||||
|
TRANSLATION_SERVICE=openai
|
||||||
|
OPENAI_API_KEY=your-api-key
|
||||||
|
OPENAI_MODEL=gpt-4o-mini
|
||||||
|
```
|
||||||
|
|
||||||
|
### Rate Limiting
|
||||||
|
|
||||||
|
```bash
|
||||||
|
RATE_LIMIT_REQUESTS_PER_MINUTE=60
|
||||||
|
RATE_LIMIT_TRANSLATIONS_PER_MINUTE=10
|
||||||
|
RATE_LIMIT_TRANSLATIONS_PER_HOUR=100
|
||||||
|
RATE_LIMIT_TRANSLATIONS_PER_DAY=500
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📊 Monitoring & Logging
|
||||||
|
|
||||||
|
### Enable Monitoring
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose --profile with-monitoring up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
### Access Dashboards
|
||||||
|
|
||||||
|
- **Prometheus**: http://localhost:9090
|
||||||
|
- **Grafana**: http://localhost:3001 (admin/admin)
|
||||||
|
|
||||||
|
### Log Locations
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Docker logs
|
||||||
|
docker compose logs backend
|
||||||
|
docker compose logs frontend
|
||||||
|
docker compose logs nginx
|
||||||
|
|
||||||
|
# Application logs (inside container)
|
||||||
|
docker exec translate-backend cat /app/logs/app.log
|
||||||
|
```
|
||||||
|
|
||||||
|
### Health Checks
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Manual check
|
||||||
|
./scripts/health-check.sh --verbose
|
||||||
|
|
||||||
|
# API health endpoint
|
||||||
|
curl https://your-domain.com/health
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📈 Scaling & Performance
|
||||||
|
|
||||||
|
### Horizontal Scaling
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# docker-compose.yml
|
||||||
|
services:
|
||||||
|
backend:
|
||||||
|
deploy:
|
||||||
|
replicas: 4
|
||||||
|
```
|
||||||
|
|
||||||
|
### Performance Tuning
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Backend workers (in Dockerfile CMD)
|
||||||
|
CMD ["uvicorn", "main:app", "--workers", "4"]
|
||||||
|
|
||||||
|
# Nginx connections
|
||||||
|
worker_connections 2048;
|
||||||
|
|
||||||
|
# File upload limits
|
||||||
|
client_max_body_size 100M;
|
||||||
|
```
|
||||||
|
|
||||||
|
### Resource Limits
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# docker-compose.yml
|
||||||
|
deploy:
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
memory: 2G
|
||||||
|
cpus: '1'
|
||||||
|
reservations:
|
||||||
|
memory: 512M
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 💾 Backup & Recovery
|
||||||
|
|
||||||
|
### Automated Backup
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Run backup
|
||||||
|
./scripts/backup.sh /path/to/backups
|
||||||
|
|
||||||
|
# Add to crontab (daily at 2 AM)
|
||||||
|
0 2 * * * /path/to/scripts/backup.sh /backups
|
||||||
|
```
|
||||||
|
|
||||||
|
### Restore from Backup
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Extract backup
|
||||||
|
tar xzf translate_backup_20241130.tar.gz
|
||||||
|
|
||||||
|
# Restore files
|
||||||
|
docker cp translate_backup/uploads translate-backend:/app/
|
||||||
|
docker cp translate_backup/outputs translate-backend:/app/
|
||||||
|
|
||||||
|
# Restart services
|
||||||
|
docker compose restart
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔧 Troubleshooting
|
||||||
|
|
||||||
|
### Common Issues
|
||||||
|
|
||||||
|
#### Port Already in Use
|
||||||
|
```bash
|
||||||
|
# Find process
|
||||||
|
netstat -tulpn | grep :8000
|
||||||
|
# Kill process
|
||||||
|
kill -9 <PID>
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Container Won't Start
|
||||||
|
```bash
|
||||||
|
# Check logs
|
||||||
|
docker compose logs backend --tail 100
|
||||||
|
|
||||||
|
# Check resources
|
||||||
|
docker stats
|
||||||
|
```
|
||||||
|
|
||||||
|
#### SSL Certificate Issues
|
||||||
|
```bash
|
||||||
|
# Test certificate
|
||||||
|
openssl s_client -connect your-domain.com:443
|
||||||
|
|
||||||
|
# Renew Let's Encrypt
|
||||||
|
./scripts/renew-ssl.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Memory Issues
|
||||||
|
```bash
|
||||||
|
# Increase limits in docker-compose.yml
|
||||||
|
deploy:
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
memory: 4G
|
||||||
|
```
|
||||||
|
|
||||||
|
### Getting Help
|
||||||
|
|
||||||
|
1. Check logs: `docker compose logs -f`
|
||||||
|
2. Run health check: `./scripts/health-check.sh`
|
||||||
|
3. Review configuration: `.env.production`
|
||||||
|
4. Check container status: `docker compose ps`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📝 Maintenance Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Update application
|
||||||
|
git pull origin production-deployment
|
||||||
|
docker compose build
|
||||||
|
docker compose up -d
|
||||||
|
|
||||||
|
# Prune unused resources
|
||||||
|
docker system prune -a
|
||||||
|
|
||||||
|
# View resource usage
|
||||||
|
docker stats
|
||||||
|
|
||||||
|
# Enter container shell
|
||||||
|
docker exec -it translate-backend /bin/bash
|
||||||
|
|
||||||
|
# Database backup (if using)
|
||||||
|
docker exec translate-db pg_dump -U user database > backup.sql
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔐 Security Checklist
|
||||||
|
|
||||||
|
- [ ] Change default admin password
|
||||||
|
- [ ] Configure SSL/TLS
|
||||||
|
- [ ] Set up firewall rules
|
||||||
|
- [ ] Enable rate limiting
|
||||||
|
- [ ] Configure CORS properly
|
||||||
|
- [ ] Regular security updates
|
||||||
|
- [ ] Backup encryption
|
||||||
|
- [ ] Monitor access logs
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📞 Support
|
||||||
|
|
||||||
|
For issues and feature requests, please open a GitHub issue or contact support.
|
||||||
|
|
||||||
|
**Version**: 2.0.0
|
||||||
|
**Last Updated**: November 2025
|
||||||
40
docker-compose.dev.yml
Normal file
40
docker-compose.dev.yml
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
# Document Translation API - Development Docker Compose
|
||||||
|
# Usage: docker-compose -f docker-compose.yml -f docker-compose.dev.yml up
|
||||||
|
|
||||||
|
version: '3.8'
|
||||||
|
|
||||||
|
services:
|
||||||
|
backend:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: docker/backend/Dockerfile
|
||||||
|
target: builder # Use builder stage for dev
|
||||||
|
volumes:
|
||||||
|
- .:/app
|
||||||
|
- /app/venv # Don't override venv
|
||||||
|
environment:
|
||||||
|
- DEBUG=true
|
||||||
|
- LOG_LEVEL=DEBUG
|
||||||
|
command: uvicorn main:app --host 0.0.0.0 --port 8000 --reload
|
||||||
|
ports:
|
||||||
|
- "8000:8000"
|
||||||
|
|
||||||
|
frontend:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: docker/frontend/Dockerfile
|
||||||
|
target: builder
|
||||||
|
volumes:
|
||||||
|
- ./frontend:/app
|
||||||
|
- /app/node_modules
|
||||||
|
- /app/.next
|
||||||
|
environment:
|
||||||
|
- NODE_ENV=development
|
||||||
|
command: npm run dev
|
||||||
|
ports:
|
||||||
|
- "3000:3000"
|
||||||
|
|
||||||
|
# No nginx in dev - direct access to services
|
||||||
|
nginx:
|
||||||
|
profiles:
|
||||||
|
- disabled
|
||||||
208
docker-compose.yml
Normal file
208
docker-compose.yml
Normal file
@ -0,0 +1,208 @@
|
|||||||
|
# Document Translation API - Production Docker Compose
|
||||||
|
# Usage: docker-compose -f docker-compose.yml -f docker-compose.prod.yml up -d
|
||||||
|
|
||||||
|
version: '3.8'
|
||||||
|
|
||||||
|
services:
|
||||||
|
# ===========================================
|
||||||
|
# Backend API Service
|
||||||
|
# ===========================================
|
||||||
|
backend:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: docker/backend/Dockerfile
|
||||||
|
container_name: translate-backend
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
- TRANSLATION_SERVICE=${TRANSLATION_SERVICE:-ollama}
|
||||||
|
- OLLAMA_BASE_URL=${OLLAMA_BASE_URL:-http://ollama:11434}
|
||||||
|
- OLLAMA_MODEL=${OLLAMA_MODEL:-llama3}
|
||||||
|
- DEEPL_API_KEY=${DEEPL_API_KEY:-}
|
||||||
|
- OPENAI_API_KEY=${OPENAI_API_KEY:-}
|
||||||
|
- MAX_FILE_SIZE_MB=${MAX_FILE_SIZE_MB:-50}
|
||||||
|
- RATE_LIMIT_REQUESTS_PER_MINUTE=${RATE_LIMIT_REQUESTS_PER_MINUTE:-60}
|
||||||
|
- RATE_LIMIT_TRANSLATIONS_PER_MINUTE=${RATE_LIMIT_TRANSLATIONS_PER_MINUTE:-10}
|
||||||
|
- ADMIN_USERNAME=${ADMIN_USERNAME:-admin}
|
||||||
|
- ADMIN_PASSWORD=${ADMIN_PASSWORD:-changeme123}
|
||||||
|
- CORS_ORIGINS=${CORS_ORIGINS:-*}
|
||||||
|
volumes:
|
||||||
|
- uploads_data:/app/uploads
|
||||||
|
- outputs_data:/app/outputs
|
||||||
|
- logs_data:/app/logs
|
||||||
|
networks:
|
||||||
|
- translate-network
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 3
|
||||||
|
start_period: 10s
|
||||||
|
deploy:
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
memory: 2G
|
||||||
|
reservations:
|
||||||
|
memory: 512M
|
||||||
|
|
||||||
|
# ===========================================
|
||||||
|
# Frontend Web Service
|
||||||
|
# ===========================================
|
||||||
|
frontend:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: docker/frontend/Dockerfile
|
||||||
|
args:
|
||||||
|
- NEXT_PUBLIC_API_URL=${NEXT_PUBLIC_API_URL:-http://backend:8000}
|
||||||
|
container_name: translate-frontend
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
- NEXT_PUBLIC_API_URL=${NEXT_PUBLIC_API_URL:-http://backend:8000}
|
||||||
|
networks:
|
||||||
|
- translate-network
|
||||||
|
depends_on:
|
||||||
|
backend:
|
||||||
|
condition: service_healthy
|
||||||
|
deploy:
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
memory: 512M
|
||||||
|
reservations:
|
||||||
|
memory: 128M
|
||||||
|
|
||||||
|
# ===========================================
|
||||||
|
# Nginx Reverse Proxy
|
||||||
|
# ===========================================
|
||||||
|
nginx:
|
||||||
|
image: nginx:alpine
|
||||||
|
container_name: translate-nginx
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- "${HTTP_PORT:-80}:80"
|
||||||
|
- "${HTTPS_PORT:-443}:443"
|
||||||
|
volumes:
|
||||||
|
- ./docker/nginx/nginx.conf:/etc/nginx/nginx.conf:ro
|
||||||
|
- ./docker/nginx/conf.d:/etc/nginx/conf.d:ro
|
||||||
|
- ./docker/nginx/ssl:/etc/nginx/ssl:ro
|
||||||
|
- nginx_cache:/var/cache/nginx
|
||||||
|
networks:
|
||||||
|
- translate-network
|
||||||
|
depends_on:
|
||||||
|
- frontend
|
||||||
|
- backend
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "nginx", "-t"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 3
|
||||||
|
|
||||||
|
# ===========================================
|
||||||
|
# Ollama (Optional - Local LLM)
|
||||||
|
# ===========================================
|
||||||
|
ollama:
|
||||||
|
image: ollama/ollama:latest
|
||||||
|
container_name: translate-ollama
|
||||||
|
restart: unless-stopped
|
||||||
|
volumes:
|
||||||
|
- ollama_data:/root/.ollama
|
||||||
|
networks:
|
||||||
|
- translate-network
|
||||||
|
deploy:
|
||||||
|
resources:
|
||||||
|
reservations:
|
||||||
|
devices:
|
||||||
|
- driver: nvidia
|
||||||
|
count: all
|
||||||
|
capabilities: [gpu]
|
||||||
|
profiles:
|
||||||
|
- with-ollama
|
||||||
|
|
||||||
|
# ===========================================
|
||||||
|
# Redis (Optional - For caching & sessions)
|
||||||
|
# ===========================================
|
||||||
|
redis:
|
||||||
|
image: redis:7-alpine
|
||||||
|
container_name: translate-redis
|
||||||
|
restart: unless-stopped
|
||||||
|
command: redis-server --appendonly yes --maxmemory 256mb --maxmemory-policy allkeys-lru
|
||||||
|
volumes:
|
||||||
|
- redis_data:/data
|
||||||
|
networks:
|
||||||
|
- translate-network
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "redis-cli", "ping"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 3
|
||||||
|
profiles:
|
||||||
|
- with-cache
|
||||||
|
|
||||||
|
# ===========================================
|
||||||
|
# Prometheus (Optional - Monitoring)
|
||||||
|
# ===========================================
|
||||||
|
prometheus:
|
||||||
|
image: prom/prometheus:latest
|
||||||
|
container_name: translate-prometheus
|
||||||
|
restart: unless-stopped
|
||||||
|
volumes:
|
||||||
|
- ./docker/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro
|
||||||
|
- prometheus_data:/prometheus
|
||||||
|
command:
|
||||||
|
- '--config.file=/etc/prometheus/prometheus.yml'
|
||||||
|
- '--storage.tsdb.path=/prometheus'
|
||||||
|
- '--web.enable-lifecycle'
|
||||||
|
networks:
|
||||||
|
- translate-network
|
||||||
|
profiles:
|
||||||
|
- with-monitoring
|
||||||
|
|
||||||
|
# ===========================================
|
||||||
|
# Grafana (Optional - Dashboards)
|
||||||
|
# ===========================================
|
||||||
|
grafana:
|
||||||
|
image: grafana/grafana:latest
|
||||||
|
container_name: translate-grafana
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
- GF_SECURITY_ADMIN_USER=${GRAFANA_USER:-admin}
|
||||||
|
- GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_PASSWORD:-admin}
|
||||||
|
- GF_USERS_ALLOW_SIGN_UP=false
|
||||||
|
volumes:
|
||||||
|
- grafana_data:/var/lib/grafana
|
||||||
|
- ./docker/grafana/dashboards:/etc/grafana/provisioning/dashboards:ro
|
||||||
|
networks:
|
||||||
|
- translate-network
|
||||||
|
depends_on:
|
||||||
|
- prometheus
|
||||||
|
profiles:
|
||||||
|
- with-monitoring
|
||||||
|
|
||||||
|
# ===========================================
|
||||||
|
# Networks
|
||||||
|
# ===========================================
|
||||||
|
networks:
|
||||||
|
translate-network:
|
||||||
|
driver: bridge
|
||||||
|
ipam:
|
||||||
|
config:
|
||||||
|
- subnet: 172.28.0.0/16
|
||||||
|
|
||||||
|
# ===========================================
|
||||||
|
# Volumes
|
||||||
|
# ===========================================
|
||||||
|
volumes:
|
||||||
|
uploads_data:
|
||||||
|
driver: local
|
||||||
|
outputs_data:
|
||||||
|
driver: local
|
||||||
|
logs_data:
|
||||||
|
driver: local
|
||||||
|
nginx_cache:
|
||||||
|
driver: local
|
||||||
|
ollama_data:
|
||||||
|
driver: local
|
||||||
|
redis_data:
|
||||||
|
driver: local
|
||||||
|
prometheus_data:
|
||||||
|
driver: local
|
||||||
|
grafana_data:
|
||||||
|
driver: local
|
||||||
65
docker/backend/Dockerfile
Normal file
65
docker/backend/Dockerfile
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
# Document Translation API - Backend Dockerfile
|
||||||
|
# Multi-stage build for optimized production image
|
||||||
|
|
||||||
|
FROM python:3.11-slim as builder
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Install build dependencies
|
||||||
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
|
build-essential \
|
||||||
|
libmagic1 \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# Copy requirements first for better caching
|
||||||
|
COPY requirements.txt .
|
||||||
|
|
||||||
|
# Create virtual environment and install dependencies
|
||||||
|
RUN python -m venv /opt/venv
|
||||||
|
ENV PATH="/opt/venv/bin:$PATH"
|
||||||
|
RUN pip install --no-cache-dir --upgrade pip && \
|
||||||
|
pip install --no-cache-dir -r requirements.txt
|
||||||
|
|
||||||
|
# Production stage
|
||||||
|
FROM python:3.11-slim as production
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Install runtime dependencies only
|
||||||
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
|
libmagic1 \
|
||||||
|
curl \
|
||||||
|
&& rm -rf /var/lib/apt/lists/* \
|
||||||
|
&& apt-get clean
|
||||||
|
|
||||||
|
# Copy virtual environment from builder
|
||||||
|
COPY --from=builder /opt/venv /opt/venv
|
||||||
|
ENV PATH="/opt/venv/bin:$PATH"
|
||||||
|
|
||||||
|
# Create non-root user for security
|
||||||
|
RUN groupadd -r translator && useradd -r -g translator translator
|
||||||
|
|
||||||
|
# Create necessary directories
|
||||||
|
RUN mkdir -p /app/uploads /app/outputs /app/logs /app/temp \
|
||||||
|
&& chown -R translator:translator /app
|
||||||
|
|
||||||
|
# Copy application code
|
||||||
|
COPY --chown=translator:translator . .
|
||||||
|
|
||||||
|
# Switch to non-root user
|
||||||
|
USER translator
|
||||||
|
|
||||||
|
# Environment variables
|
||||||
|
ENV PYTHONUNBUFFERED=1 \
|
||||||
|
PYTHONDONTWRITEBYTECODE=1 \
|
||||||
|
PORT=8000
|
||||||
|
|
||||||
|
# Health check
|
||||||
|
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
|
||||||
|
CMD curl -f http://localhost:${PORT}/health || exit 1
|
||||||
|
|
||||||
|
# Expose port
|
||||||
|
EXPOSE ${PORT}
|
||||||
|
|
||||||
|
# Run with uvicorn
|
||||||
|
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000", "--workers", "4"]
|
||||||
51
docker/frontend/Dockerfile
Normal file
51
docker/frontend/Dockerfile
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
# Document Translation Frontend - Dockerfile
|
||||||
|
# Multi-stage build for optimized production
|
||||||
|
|
||||||
|
# Build stage
|
||||||
|
FROM node:20-alpine AS builder
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Copy package files
|
||||||
|
COPY frontend/package*.json ./
|
||||||
|
|
||||||
|
# Install dependencies
|
||||||
|
RUN npm ci --only=production=false
|
||||||
|
|
||||||
|
# Copy source code
|
||||||
|
COPY frontend/ .
|
||||||
|
|
||||||
|
# Build arguments for environment
|
||||||
|
ARG NEXT_PUBLIC_API_URL=http://localhost:8000
|
||||||
|
ENV NEXT_PUBLIC_API_URL=${NEXT_PUBLIC_API_URL}
|
||||||
|
|
||||||
|
# Build the application
|
||||||
|
RUN npm run build
|
||||||
|
|
||||||
|
# Production stage
|
||||||
|
FROM node:20-alpine AS production
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Create non-root user
|
||||||
|
RUN addgroup -g 1001 -S nodejs && \
|
||||||
|
adduser -S nextjs -u 1001
|
||||||
|
|
||||||
|
# Copy built assets from builder
|
||||||
|
COPY --from=builder /app/public ./public
|
||||||
|
COPY --from=builder /app/.next/standalone ./
|
||||||
|
COPY --from=builder /app/.next/static ./.next/static
|
||||||
|
|
||||||
|
# Set correct permissions
|
||||||
|
RUN chown -R nextjs:nodejs /app
|
||||||
|
|
||||||
|
USER nextjs
|
||||||
|
|
||||||
|
# Environment
|
||||||
|
ENV NODE_ENV=production \
|
||||||
|
PORT=3000 \
|
||||||
|
HOSTNAME="0.0.0.0"
|
||||||
|
|
||||||
|
EXPOSE 3000
|
||||||
|
|
||||||
|
CMD ["node", "server.js"]
|
||||||
150
docker/nginx/conf.d/default.conf
Normal file
150
docker/nginx/conf.d/default.conf
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
# Document Translation API - Main Server Block
|
||||||
|
# HTTP to HTTPS redirect and main application routing
|
||||||
|
|
||||||
|
# HTTP server - redirect to HTTPS
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
listen [::]:80;
|
||||||
|
server_name _;
|
||||||
|
|
||||||
|
# Allow health checks on HTTP
|
||||||
|
location /health {
|
||||||
|
proxy_pass http://backend/health;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Connection "";
|
||||||
|
}
|
||||||
|
|
||||||
|
# ACME challenge for Let's Encrypt
|
||||||
|
location /.well-known/acme-challenge/ {
|
||||||
|
root /var/www/certbot;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Redirect all other traffic to HTTPS
|
||||||
|
location / {
|
||||||
|
return 301 https://$host$request_uri;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# HTTPS server - main application
|
||||||
|
server {
|
||||||
|
listen 443 ssl http2;
|
||||||
|
listen [::]:443 ssl http2;
|
||||||
|
server_name _;
|
||||||
|
|
||||||
|
# SSL certificates (replace with your paths)
|
||||||
|
ssl_certificate /etc/nginx/ssl/fullchain.pem;
|
||||||
|
ssl_certificate_key /etc/nginx/ssl/privkey.pem;
|
||||||
|
ssl_trusted_certificate /etc/nginx/ssl/chain.pem;
|
||||||
|
|
||||||
|
# SSL configuration
|
||||||
|
ssl_session_timeout 1d;
|
||||||
|
ssl_session_cache shared:SSL:50m;
|
||||||
|
ssl_session_tickets off;
|
||||||
|
|
||||||
|
# Modern SSL configuration
|
||||||
|
ssl_protocols TLSv1.2 TLSv1.3;
|
||||||
|
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
|
||||||
|
ssl_prefer_server_ciphers off;
|
||||||
|
|
||||||
|
# OCSP Stapling
|
||||||
|
ssl_stapling on;
|
||||||
|
ssl_stapling_verify on;
|
||||||
|
resolver 8.8.8.8 8.8.4.4 valid=300s;
|
||||||
|
resolver_timeout 5s;
|
||||||
|
|
||||||
|
# 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 Referrer-Policy "strict-origin-when-cross-origin" always;
|
||||||
|
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
|
||||||
|
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob:; font-src 'self' data:; connect-src 'self' ws: wss:;" always;
|
||||||
|
|
||||||
|
# API routes - proxy to backend
|
||||||
|
location /api/ {
|
||||||
|
rewrite ^/api/(.*)$ /$1 break;
|
||||||
|
|
||||||
|
limit_req zone=api_limit burst=20 nodelay;
|
||||||
|
limit_conn conn_limit 10;
|
||||||
|
|
||||||
|
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;
|
||||||
|
proxy_set_header Connection "";
|
||||||
|
|
||||||
|
# CORS headers for API
|
||||||
|
add_header Access-Control-Allow-Origin $http_origin always;
|
||||||
|
add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS" always;
|
||||||
|
add_header Access-Control-Allow-Headers "Authorization, Content-Type, X-Requested-With" always;
|
||||||
|
add_header Access-Control-Allow-Credentials "true" always;
|
||||||
|
|
||||||
|
if ($request_method = 'OPTIONS') {
|
||||||
|
return 204;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# File upload endpoint - special handling
|
||||||
|
location /translate {
|
||||||
|
limit_req zone=upload_limit burst=5 nodelay;
|
||||||
|
limit_conn conn_limit 5;
|
||||||
|
|
||||||
|
proxy_pass http://backend/translate;
|
||||||
|
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;
|
||||||
|
|
||||||
|
# Increased timeouts for file processing
|
||||||
|
proxy_connect_timeout 60s;
|
||||||
|
proxy_send_timeout 600s;
|
||||||
|
proxy_read_timeout 600s;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Health check endpoint
|
||||||
|
location /health {
|
||||||
|
proxy_pass http://backend/health;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Connection "";
|
||||||
|
}
|
||||||
|
|
||||||
|
# Admin endpoints
|
||||||
|
location /admin {
|
||||||
|
limit_req zone=api_limit burst=10 nodelay;
|
||||||
|
|
||||||
|
proxy_pass http://backend/admin;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Frontend - Next.js application
|
||||||
|
location / {
|
||||||
|
proxy_pass http://frontend;
|
||||||
|
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_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection "upgrade";
|
||||||
|
}
|
||||||
|
|
||||||
|
# Static files caching
|
||||||
|
location /_next/static/ {
|
||||||
|
proxy_pass http://frontend;
|
||||||
|
proxy_cache_valid 200 365d;
|
||||||
|
add_header Cache-Control "public, max-age=31536000, immutable";
|
||||||
|
}
|
||||||
|
|
||||||
|
# Error pages
|
||||||
|
error_page 500 502 503 504 /50x.html;
|
||||||
|
location = /50x.html {
|
||||||
|
root /usr/share/nginx/html;
|
||||||
|
}
|
||||||
|
}
|
||||||
74
docker/nginx/nginx.conf
Normal file
74
docker/nginx/nginx.conf
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
# Nginx Configuration for Document Translation API
|
||||||
|
# Production-ready with SSL, caching, and security headers
|
||||||
|
|
||||||
|
user nginx;
|
||||||
|
worker_processes auto;
|
||||||
|
error_log /var/log/nginx/error.log warn;
|
||||||
|
pid /var/run/nginx.pid;
|
||||||
|
|
||||||
|
events {
|
||||||
|
worker_connections 1024;
|
||||||
|
use epoll;
|
||||||
|
multi_accept on;
|
||||||
|
}
|
||||||
|
|
||||||
|
http {
|
||||||
|
include /etc/nginx/mime.types;
|
||||||
|
default_type application/octet-stream;
|
||||||
|
|
||||||
|
# Logging format
|
||||||
|
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
|
||||||
|
'$status $body_bytes_sent "$http_referer" '
|
||||||
|
'"$http_user_agent" "$http_x_forwarded_for" '
|
||||||
|
'rt=$request_time uct="$upstream_connect_time" '
|
||||||
|
'uht="$upstream_header_time" urt="$upstream_response_time"';
|
||||||
|
|
||||||
|
access_log /var/log/nginx/access.log main;
|
||||||
|
|
||||||
|
# Performance optimizations
|
||||||
|
sendfile on;
|
||||||
|
tcp_nopush on;
|
||||||
|
tcp_nodelay on;
|
||||||
|
keepalive_timeout 65;
|
||||||
|
types_hash_max_size 2048;
|
||||||
|
|
||||||
|
# Gzip compression
|
||||||
|
gzip on;
|
||||||
|
gzip_vary on;
|
||||||
|
gzip_proxied any;
|
||||||
|
gzip_comp_level 6;
|
||||||
|
gzip_types text/plain text/css text/xml application/json application/javascript
|
||||||
|
application/xml application/rss+xml application/atom+xml image/svg+xml;
|
||||||
|
|
||||||
|
# File upload size (for document translation)
|
||||||
|
client_max_body_size 100M;
|
||||||
|
client_body_timeout 300s;
|
||||||
|
client_header_timeout 60s;
|
||||||
|
|
||||||
|
# Proxy settings
|
||||||
|
proxy_connect_timeout 60s;
|
||||||
|
proxy_send_timeout 300s;
|
||||||
|
proxy_read_timeout 300s;
|
||||||
|
proxy_buffer_size 128k;
|
||||||
|
proxy_buffers 4 256k;
|
||||||
|
proxy_busy_buffers_size 256k;
|
||||||
|
|
||||||
|
# Rate limiting zones
|
||||||
|
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
|
||||||
|
limit_req_zone $binary_remote_addr zone=upload_limit:10m rate=2r/s;
|
||||||
|
limit_conn_zone $binary_remote_addr zone=conn_limit:10m;
|
||||||
|
|
||||||
|
# Upstream definitions
|
||||||
|
upstream backend {
|
||||||
|
server backend:8000;
|
||||||
|
keepalive 32;
|
||||||
|
}
|
||||||
|
|
||||||
|
upstream frontend {
|
||||||
|
server frontend:3000;
|
||||||
|
keepalive 32;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Include additional configs
|
||||||
|
include /etc/nginx/conf.d/*.conf;
|
||||||
|
}
|
||||||
8
docker/nginx/ssl/.gitkeep
Normal file
8
docker/nginx/ssl/.gitkeep
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
# Self-signed SSL certificate placeholder
|
||||||
|
# Replace with real certificates in production!
|
||||||
|
|
||||||
|
# Generate self-signed certificate:
|
||||||
|
# openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
|
||||||
|
# -keyout privkey.pem \
|
||||||
|
# -out fullchain.pem \
|
||||||
|
# -subj "/CN=localhost"
|
||||||
37
docker/prometheus/prometheus.yml
Normal file
37
docker/prometheus/prometheus.yml
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
# Prometheus Configuration for Document Translation API
|
||||||
|
|
||||||
|
global:
|
||||||
|
scrape_interval: 15s
|
||||||
|
evaluation_interval: 15s
|
||||||
|
external_labels:
|
||||||
|
monitor: 'translate-api'
|
||||||
|
|
||||||
|
alerting:
|
||||||
|
alertmanagers:
|
||||||
|
- static_configs:
|
||||||
|
- targets: []
|
||||||
|
|
||||||
|
rule_files: []
|
||||||
|
|
||||||
|
scrape_configs:
|
||||||
|
# Backend API metrics
|
||||||
|
- job_name: 'translate-backend'
|
||||||
|
static_configs:
|
||||||
|
- targets: ['backend:8000']
|
||||||
|
metrics_path: /metrics
|
||||||
|
scrape_interval: 10s
|
||||||
|
|
||||||
|
# Nginx metrics (requires nginx-prometheus-exporter)
|
||||||
|
- job_name: 'nginx'
|
||||||
|
static_configs:
|
||||||
|
- targets: ['nginx-exporter:9113']
|
||||||
|
|
||||||
|
# Node exporter for system metrics
|
||||||
|
- job_name: 'node'
|
||||||
|
static_configs:
|
||||||
|
- targets: ['node-exporter:9100']
|
||||||
|
|
||||||
|
# Docker metrics
|
||||||
|
- job_name: 'docker'
|
||||||
|
static_configs:
|
||||||
|
- targets: ['cadvisor:8080']
|
||||||
288
k8s/deployment.yaml
Normal file
288
k8s/deployment.yaml
Normal file
@ -0,0 +1,288 @@
|
|||||||
|
# ============================================
|
||||||
|
# Document Translation API - Kubernetes Deployment
|
||||||
|
# ============================================
|
||||||
|
# Apply with: kubectl apply -f k8s/
|
||||||
|
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Namespace
|
||||||
|
metadata:
|
||||||
|
name: translate-api
|
||||||
|
labels:
|
||||||
|
app: translate-api
|
||||||
|
|
||||||
|
---
|
||||||
|
# ConfigMap for application settings
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: translate-config
|
||||||
|
namespace: translate-api
|
||||||
|
data:
|
||||||
|
TRANSLATION_SERVICE: "ollama"
|
||||||
|
OLLAMA_BASE_URL: "http://ollama-service:11434"
|
||||||
|
OLLAMA_MODEL: "llama3"
|
||||||
|
MAX_FILE_SIZE_MB: "50"
|
||||||
|
RATE_LIMIT_REQUESTS_PER_MINUTE: "60"
|
||||||
|
RATE_LIMIT_TRANSLATIONS_PER_MINUTE: "10"
|
||||||
|
LOG_LEVEL: "INFO"
|
||||||
|
|
||||||
|
---
|
||||||
|
# Secret for sensitive data
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: translate-secrets
|
||||||
|
namespace: translate-api
|
||||||
|
type: Opaque
|
||||||
|
stringData:
|
||||||
|
ADMIN_USERNAME: "admin"
|
||||||
|
ADMIN_PASSWORD: "changeme123" # Change in production!
|
||||||
|
DEEPL_API_KEY: ""
|
||||||
|
OPENAI_API_KEY: ""
|
||||||
|
|
||||||
|
---
|
||||||
|
# Backend Deployment
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: backend
|
||||||
|
namespace: translate-api
|
||||||
|
labels:
|
||||||
|
app: backend
|
||||||
|
spec:
|
||||||
|
replicas: 2
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: backend
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: backend
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: backend
|
||||||
|
image: translate-api-backend:latest
|
||||||
|
imagePullPolicy: IfNotPresent
|
||||||
|
ports:
|
||||||
|
- containerPort: 8000
|
||||||
|
envFrom:
|
||||||
|
- configMapRef:
|
||||||
|
name: translate-config
|
||||||
|
- secretRef:
|
||||||
|
name: translate-secrets
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
memory: "512Mi"
|
||||||
|
cpu: "250m"
|
||||||
|
limits:
|
||||||
|
memory: "2Gi"
|
||||||
|
cpu: "1000m"
|
||||||
|
readinessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /health
|
||||||
|
port: 8000
|
||||||
|
initialDelaySeconds: 10
|
||||||
|
periodSeconds: 10
|
||||||
|
livenessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /health
|
||||||
|
port: 8000
|
||||||
|
initialDelaySeconds: 30
|
||||||
|
periodSeconds: 30
|
||||||
|
volumeMounts:
|
||||||
|
- name: uploads
|
||||||
|
mountPath: /app/uploads
|
||||||
|
- name: outputs
|
||||||
|
mountPath: /app/outputs
|
||||||
|
volumes:
|
||||||
|
- name: uploads
|
||||||
|
persistentVolumeClaim:
|
||||||
|
claimName: uploads-pvc
|
||||||
|
- name: outputs
|
||||||
|
persistentVolumeClaim:
|
||||||
|
claimName: outputs-pvc
|
||||||
|
|
||||||
|
---
|
||||||
|
# Backend Service
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: backend-service
|
||||||
|
namespace: translate-api
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
app: backend
|
||||||
|
ports:
|
||||||
|
- port: 8000
|
||||||
|
targetPort: 8000
|
||||||
|
type: ClusterIP
|
||||||
|
|
||||||
|
---
|
||||||
|
# Frontend Deployment
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: frontend
|
||||||
|
namespace: translate-api
|
||||||
|
labels:
|
||||||
|
app: frontend
|
||||||
|
spec:
|
||||||
|
replicas: 2
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: frontend
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: frontend
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: frontend
|
||||||
|
image: translate-api-frontend:latest
|
||||||
|
imagePullPolicy: IfNotPresent
|
||||||
|
ports:
|
||||||
|
- containerPort: 3000
|
||||||
|
env:
|
||||||
|
- name: NEXT_PUBLIC_API_URL
|
||||||
|
value: "http://backend-service:8000"
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
memory: "128Mi"
|
||||||
|
cpu: "100m"
|
||||||
|
limits:
|
||||||
|
memory: "512Mi"
|
||||||
|
cpu: "500m"
|
||||||
|
readinessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /
|
||||||
|
port: 3000
|
||||||
|
initialDelaySeconds: 10
|
||||||
|
periodSeconds: 10
|
||||||
|
|
||||||
|
---
|
||||||
|
# Frontend Service
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: frontend-service
|
||||||
|
namespace: translate-api
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
app: frontend
|
||||||
|
ports:
|
||||||
|
- port: 3000
|
||||||
|
targetPort: 3000
|
||||||
|
type: ClusterIP
|
||||||
|
|
||||||
|
---
|
||||||
|
# Ingress for external access
|
||||||
|
apiVersion: networking.k8s.io/v1
|
||||||
|
kind: Ingress
|
||||||
|
metadata:
|
||||||
|
name: translate-ingress
|
||||||
|
namespace: translate-api
|
||||||
|
annotations:
|
||||||
|
kubernetes.io/ingress.class: nginx
|
||||||
|
nginx.ingress.kubernetes.io/proxy-body-size: "100m"
|
||||||
|
nginx.ingress.kubernetes.io/proxy-read-timeout: "600"
|
||||||
|
nginx.ingress.kubernetes.io/proxy-send-timeout: "600"
|
||||||
|
cert-manager.io/cluster-issuer: "letsencrypt-prod"
|
||||||
|
spec:
|
||||||
|
tls:
|
||||||
|
- hosts:
|
||||||
|
- translate.yourdomain.com
|
||||||
|
secretName: translate-tls
|
||||||
|
rules:
|
||||||
|
- host: translate.yourdomain.com
|
||||||
|
http:
|
||||||
|
paths:
|
||||||
|
- path: /api
|
||||||
|
pathType: Prefix
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: backend-service
|
||||||
|
port:
|
||||||
|
number: 8000
|
||||||
|
- path: /translate
|
||||||
|
pathType: Prefix
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: backend-service
|
||||||
|
port:
|
||||||
|
number: 8000
|
||||||
|
- path: /health
|
||||||
|
pathType: Prefix
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: backend-service
|
||||||
|
port:
|
||||||
|
number: 8000
|
||||||
|
- path: /admin
|
||||||
|
pathType: Prefix
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: backend-service
|
||||||
|
port:
|
||||||
|
number: 8000
|
||||||
|
- path: /
|
||||||
|
pathType: Prefix
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: frontend-service
|
||||||
|
port:
|
||||||
|
number: 3000
|
||||||
|
|
||||||
|
---
|
||||||
|
# Persistent Volume Claims
|
||||||
|
apiVersion: v1
|
||||||
|
kind: PersistentVolumeClaim
|
||||||
|
metadata:
|
||||||
|
name: uploads-pvc
|
||||||
|
namespace: translate-api
|
||||||
|
spec:
|
||||||
|
accessModes:
|
||||||
|
- ReadWriteMany
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
storage: 10Gi
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: PersistentVolumeClaim
|
||||||
|
metadata:
|
||||||
|
name: outputs-pvc
|
||||||
|
namespace: translate-api
|
||||||
|
spec:
|
||||||
|
accessModes:
|
||||||
|
- ReadWriteMany
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
storage: 20Gi
|
||||||
|
|
||||||
|
---
|
||||||
|
# Horizontal Pod Autoscaler for Backend
|
||||||
|
apiVersion: autoscaling/v2
|
||||||
|
kind: HorizontalPodAutoscaler
|
||||||
|
metadata:
|
||||||
|
name: backend-hpa
|
||||||
|
namespace: translate-api
|
||||||
|
spec:
|
||||||
|
scaleTargetRef:
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
name: backend
|
||||||
|
minReplicas: 2
|
||||||
|
maxReplicas: 10
|
||||||
|
metrics:
|
||||||
|
- type: Resource
|
||||||
|
resource:
|
||||||
|
name: cpu
|
||||||
|
target:
|
||||||
|
type: Utilization
|
||||||
|
averageUtilization: 70
|
||||||
|
- type: Resource
|
||||||
|
resource:
|
||||||
|
name: memory
|
||||||
|
target:
|
||||||
|
type: Utilization
|
||||||
|
averageUtilization: 80
|
||||||
67
scripts/backup.sh
Normal file
67
scripts/backup.sh
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# ============================================
|
||||||
|
# Document Translation API - Backup Script
|
||||||
|
# ============================================
|
||||||
|
# Usage: ./scripts/backup.sh [backup_dir]
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Configuration
|
||||||
|
BACKUP_DIR="${1:-./backups}"
|
||||||
|
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
|
||||||
|
BACKUP_NAME="translate_backup_$TIMESTAMP"
|
||||||
|
|
||||||
|
# Colors
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
NC='\033[0m'
|
||||||
|
|
||||||
|
echo -e "${YELLOW}Starting backup: $BACKUP_NAME${NC}"
|
||||||
|
|
||||||
|
# Create backup directory
|
||||||
|
mkdir -p "$BACKUP_DIR/$BACKUP_NAME"
|
||||||
|
|
||||||
|
# Backup uploaded files
|
||||||
|
if [ -d "./uploads" ]; then
|
||||||
|
echo "Backing up uploads..."
|
||||||
|
cp -r ./uploads "$BACKUP_DIR/$BACKUP_NAME/"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Backup output files
|
||||||
|
if [ -d "./outputs" ]; then
|
||||||
|
echo "Backing up outputs..."
|
||||||
|
cp -r ./outputs "$BACKUP_DIR/$BACKUP_NAME/"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Backup configuration
|
||||||
|
echo "Backing up configuration..."
|
||||||
|
cp .env* "$BACKUP_DIR/$BACKUP_NAME/" 2>/dev/null || true
|
||||||
|
cp docker-compose*.yml "$BACKUP_DIR/$BACKUP_NAME/" 2>/dev/null || true
|
||||||
|
|
||||||
|
# Backup Docker volumes (if using Docker)
|
||||||
|
if command -v docker &> /dev/null; then
|
||||||
|
echo "Backing up Docker volumes..."
|
||||||
|
|
||||||
|
# Get volume names
|
||||||
|
VOLUMES=$(docker volume ls --format "{{.Name}}" | grep translate || true)
|
||||||
|
|
||||||
|
for vol in $VOLUMES; do
|
||||||
|
echo " Backing up volume: $vol"
|
||||||
|
docker run --rm \
|
||||||
|
-v "$vol:/data:ro" \
|
||||||
|
-v "$(pwd)/$BACKUP_DIR/$BACKUP_NAME:/backup" \
|
||||||
|
alpine tar czf "/backup/${vol}.tar.gz" -C /data . 2>/dev/null || true
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Compress backup
|
||||||
|
echo "Compressing backup..."
|
||||||
|
cd "$BACKUP_DIR"
|
||||||
|
tar czf "${BACKUP_NAME}.tar.gz" "$BACKUP_NAME"
|
||||||
|
rm -rf "$BACKUP_NAME"
|
||||||
|
|
||||||
|
# Cleanup old backups (keep last 7)
|
||||||
|
echo "Cleaning old backups..."
|
||||||
|
ls -t translate_backup_*.tar.gz 2>/dev/null | tail -n +8 | xargs rm -f 2>/dev/null || true
|
||||||
|
|
||||||
|
echo -e "${GREEN}Backup complete: $BACKUP_DIR/${BACKUP_NAME}.tar.gz${NC}"
|
||||||
168
scripts/deploy.sh
Normal file
168
scripts/deploy.sh
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# ============================================
|
||||||
|
# Document Translation API - Deployment Script
|
||||||
|
# ============================================
|
||||||
|
# Usage: ./scripts/deploy.sh [environment] [options]
|
||||||
|
# Example: ./scripts/deploy.sh production --with-ollama
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Colors for output
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
BLUE='\033[0;34m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
# Configuration
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
|
||||||
|
ENVIRONMENT="${1:-production}"
|
||||||
|
COMPOSE_FILE="docker-compose.yml"
|
||||||
|
|
||||||
|
# Parse options
|
||||||
|
PROFILES=""
|
||||||
|
while [[ $# -gt 1 ]]; do
|
||||||
|
case $2 in
|
||||||
|
--with-ollama)
|
||||||
|
PROFILES="$PROFILES --profile with-ollama"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
--with-cache)
|
||||||
|
PROFILES="$PROFILES --profile with-cache"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
--with-monitoring)
|
||||||
|
PROFILES="$PROFILES --profile with-monitoring"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
--full)
|
||||||
|
PROFILES="--profile with-ollama --profile with-cache --profile with-monitoring"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
echo -e "${BLUE}========================================${NC}"
|
||||||
|
echo -e "${BLUE} Document Translation API Deployment${NC}"
|
||||||
|
echo -e "${BLUE}========================================${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Check prerequisites
|
||||||
|
echo -e "${YELLOW}Checking prerequisites...${NC}"
|
||||||
|
|
||||||
|
if ! command -v docker &> /dev/null; then
|
||||||
|
echo -e "${RED}Error: Docker is not installed${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! command -v docker-compose &> /dev/null && ! docker compose version &> /dev/null; then
|
||||||
|
echo -e "${RED}Error: Docker Compose is not installed${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -e "${GREEN}✓ Docker and Docker Compose are installed${NC}"
|
||||||
|
|
||||||
|
# Check environment file
|
||||||
|
cd "$PROJECT_ROOT"
|
||||||
|
|
||||||
|
if [ "$ENVIRONMENT" == "production" ]; then
|
||||||
|
ENV_FILE=".env.production"
|
||||||
|
else
|
||||||
|
ENV_FILE=".env"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -f "$ENV_FILE" ]; then
|
||||||
|
echo -e "${YELLOW}Creating $ENV_FILE from template...${NC}"
|
||||||
|
if [ -f ".env.example" ]; then
|
||||||
|
cp .env.example "$ENV_FILE"
|
||||||
|
echo -e "${YELLOW}Please edit $ENV_FILE with your configuration${NC}"
|
||||||
|
else
|
||||||
|
echo -e "${RED}Error: No environment file found${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -e "${GREEN}✓ Environment file: $ENV_FILE${NC}"
|
||||||
|
|
||||||
|
# Load environment
|
||||||
|
set -a
|
||||||
|
source "$ENV_FILE"
|
||||||
|
set +a
|
||||||
|
|
||||||
|
# Create SSL directory if needed
|
||||||
|
if [ ! -d "docker/nginx/ssl" ]; then
|
||||||
|
mkdir -p docker/nginx/ssl
|
||||||
|
echo -e "${YELLOW}Created SSL directory. Add your certificates:${NC}"
|
||||||
|
echo " - docker/nginx/ssl/fullchain.pem"
|
||||||
|
echo " - docker/nginx/ssl/privkey.pem"
|
||||||
|
echo " - docker/nginx/ssl/chain.pem"
|
||||||
|
|
||||||
|
# Generate self-signed cert for testing
|
||||||
|
if [ "$ENVIRONMENT" != "production" ]; then
|
||||||
|
echo -e "${YELLOW}Generating self-signed certificate for development...${NC}"
|
||||||
|
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
|
||||||
|
-keyout docker/nginx/ssl/privkey.pem \
|
||||||
|
-out docker/nginx/ssl/fullchain.pem \
|
||||||
|
-subj "/C=US/ST=State/L=City/O=Organization/CN=localhost" 2>/dev/null
|
||||||
|
cp docker/nginx/ssl/fullchain.pem docker/nginx/ssl/chain.pem
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Build and deploy
|
||||||
|
echo ""
|
||||||
|
echo -e "${YELLOW}Building containers...${NC}"
|
||||||
|
docker compose --env-file "$ENV_FILE" build
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "${YELLOW}Starting services...${NC}"
|
||||||
|
docker compose --env-file "$ENV_FILE" $PROFILES up -d
|
||||||
|
|
||||||
|
# Wait for services to be healthy
|
||||||
|
echo ""
|
||||||
|
echo -e "${YELLOW}Waiting for services to be ready...${NC}"
|
||||||
|
sleep 10
|
||||||
|
|
||||||
|
# Health check
|
||||||
|
echo ""
|
||||||
|
echo -e "${YELLOW}Running health checks...${NC}"
|
||||||
|
|
||||||
|
BACKEND_HEALTH=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:8000/health 2>/dev/null || echo "000")
|
||||||
|
if [ "$BACKEND_HEALTH" == "200" ]; then
|
||||||
|
echo -e "${GREEN}✓ Backend is healthy${NC}"
|
||||||
|
else
|
||||||
|
echo -e "${RED}✗ Backend health check failed (HTTP $BACKEND_HEALTH)${NC}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
FRONTEND_HEALTH=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:3000 2>/dev/null || echo "000")
|
||||||
|
if [ "$FRONTEND_HEALTH" == "200" ]; then
|
||||||
|
echo -e "${GREEN}✓ Frontend is healthy${NC}"
|
||||||
|
else
|
||||||
|
echo -e "${RED}✗ Frontend health check failed (HTTP $FRONTEND_HEALTH)${NC}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Show status
|
||||||
|
echo ""
|
||||||
|
echo -e "${BLUE}========================================${NC}"
|
||||||
|
echo -e "${BLUE} Deployment Complete!${NC}"
|
||||||
|
echo -e "${BLUE}========================================${NC}"
|
||||||
|
echo ""
|
||||||
|
echo -e "Services running:"
|
||||||
|
docker compose ps --format "table {{.Name}}\t{{.Status}}\t{{.Ports}}"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "${GREEN}Access your application:${NC}"
|
||||||
|
echo -e " Frontend: http://localhost (or https://localhost)"
|
||||||
|
echo -e " API: http://localhost/api"
|
||||||
|
echo -e " Admin: http://localhost/admin"
|
||||||
|
echo -e " Health: http://localhost/health"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "${YELLOW}Useful commands:${NC}"
|
||||||
|
echo " View logs: docker compose logs -f"
|
||||||
|
echo " Stop: docker compose down"
|
||||||
|
echo " Restart: docker compose restart"
|
||||||
|
echo " Update: ./scripts/deploy.sh $ENVIRONMENT"
|
||||||
87
scripts/health-check.sh
Normal file
87
scripts/health-check.sh
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# ============================================
|
||||||
|
# Document Translation API - Health Check Script
|
||||||
|
# ============================================
|
||||||
|
# Usage: ./scripts/health-check.sh [--verbose]
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
VERBOSE=false
|
||||||
|
if [ "$1" == "--verbose" ]; then
|
||||||
|
VERBOSE=true
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Colors
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
NC='\033[0m'
|
||||||
|
|
||||||
|
# Configuration
|
||||||
|
BACKEND_URL="${BACKEND_URL:-http://localhost:8000}"
|
||||||
|
FRONTEND_URL="${FRONTEND_URL:-http://localhost:3000}"
|
||||||
|
NGINX_URL="${NGINX_URL:-http://localhost}"
|
||||||
|
|
||||||
|
EXIT_CODE=0
|
||||||
|
|
||||||
|
check_service() {
|
||||||
|
local name=$1
|
||||||
|
local url=$2
|
||||||
|
local expected=${3:-200}
|
||||||
|
|
||||||
|
response=$(curl -s -o /dev/null -w "%{http_code}" "$url" 2>/dev/null || echo "000")
|
||||||
|
|
||||||
|
if [ "$response" == "$expected" ]; then
|
||||||
|
echo -e "${GREEN}✓ $name: OK (HTTP $response)${NC}"
|
||||||
|
else
|
||||||
|
echo -e "${RED}✗ $name: FAILED (HTTP $response, expected $expected)${NC}"
|
||||||
|
EXIT_CODE=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$VERBOSE" == "true" ] && [ "$response" == "200" ]; then
|
||||||
|
echo " Response:"
|
||||||
|
curl -s "$url" 2>/dev/null | head -c 500
|
||||||
|
echo ""
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "========================================="
|
||||||
|
echo " Health Check - Document Translation API"
|
||||||
|
echo "========================================="
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Check backend
|
||||||
|
echo "Backend Services:"
|
||||||
|
check_service "API Health" "$BACKEND_URL/health"
|
||||||
|
check_service "API Root" "$BACKEND_URL/"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Check frontend
|
||||||
|
echo "Frontend Services:"
|
||||||
|
check_service "Frontend" "$FRONTEND_URL"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Check nginx (if running)
|
||||||
|
echo "Proxy Services:"
|
||||||
|
check_service "Nginx" "$NGINX_URL/health"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Docker health
|
||||||
|
if command -v docker &> /dev/null; then
|
||||||
|
echo "Docker Container Status:"
|
||||||
|
docker compose ps --format "table {{.Name}}\t{{.Status}}" 2>/dev/null || echo " Docker Compose not running"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "========================================="
|
||||||
|
|
||||||
|
if [ $EXIT_CODE -eq 0 ]; then
|
||||||
|
echo -e "${GREEN}All health checks passed!${NC}"
|
||||||
|
else
|
||||||
|
echo -e "${RED}Some health checks failed!${NC}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
exit $EXIT_CODE
|
||||||
79
scripts/setup-ssl.sh
Normal file
79
scripts/setup-ssl.sh
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# ============================================
|
||||||
|
# Document Translation API - SSL Setup Script
|
||||||
|
# ============================================
|
||||||
|
# Usage: ./scripts/setup-ssl.sh <domain> <email>
|
||||||
|
# Example: ./scripts/setup-ssl.sh translate.example.com admin@example.com
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
DOMAIN="${1:-}"
|
||||||
|
EMAIL="${2:-}"
|
||||||
|
|
||||||
|
if [ -z "$DOMAIN" ] || [ -z "$EMAIL" ]; then
|
||||||
|
echo "Usage: ./scripts/setup-ssl.sh <domain> <email>"
|
||||||
|
echo "Example: ./scripts/setup-ssl.sh translate.example.com admin@example.com"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Colors
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
NC='\033[0m'
|
||||||
|
|
||||||
|
echo -e "${YELLOW}Setting up SSL for $DOMAIN${NC}"
|
||||||
|
|
||||||
|
# Create directory for certbot
|
||||||
|
mkdir -p ./docker/certbot/www
|
||||||
|
mkdir -p ./docker/certbot/conf
|
||||||
|
|
||||||
|
# Create initial nginx config for ACME challenge
|
||||||
|
cat > ./docker/nginx/conf.d/certbot.conf << EOF
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name $DOMAIN;
|
||||||
|
|
||||||
|
location /.well-known/acme-challenge/ {
|
||||||
|
root /var/www/certbot;
|
||||||
|
}
|
||||||
|
|
||||||
|
location / {
|
||||||
|
return 301 https://\$host\$request_uri;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Start nginx with HTTP only
|
||||||
|
echo "Starting nginx for certificate request..."
|
||||||
|
docker compose up -d nginx
|
||||||
|
|
||||||
|
# Request certificate
|
||||||
|
echo "Requesting Let's Encrypt certificate..."
|
||||||
|
docker run --rm \
|
||||||
|
-v "$(pwd)/docker/certbot/www:/var/www/certbot" \
|
||||||
|
-v "$(pwd)/docker/certbot/conf:/etc/letsencrypt" \
|
||||||
|
certbot/certbot certonly \
|
||||||
|
--webroot \
|
||||||
|
--webroot-path=/var/www/certbot \
|
||||||
|
--email "$EMAIL" \
|
||||||
|
--agree-tos \
|
||||||
|
--no-eff-email \
|
||||||
|
-d "$DOMAIN"
|
||||||
|
|
||||||
|
# Copy certificates
|
||||||
|
echo "Installing certificates..."
|
||||||
|
cp ./docker/certbot/conf/live/$DOMAIN/fullchain.pem ./docker/nginx/ssl/
|
||||||
|
cp ./docker/certbot/conf/live/$DOMAIN/privkey.pem ./docker/nginx/ssl/
|
||||||
|
cp ./docker/certbot/conf/live/$DOMAIN/chain.pem ./docker/nginx/ssl/
|
||||||
|
|
||||||
|
# Remove temporary config
|
||||||
|
rm ./docker/nginx/conf.d/certbot.conf
|
||||||
|
|
||||||
|
# Restart nginx with SSL
|
||||||
|
echo "Restarting nginx with SSL..."
|
||||||
|
docker compose restart nginx
|
||||||
|
|
||||||
|
echo -e "${GREEN}SSL setup complete for $DOMAIN${NC}"
|
||||||
|
echo ""
|
||||||
|
echo "To auto-renew certificates, add this to crontab:"
|
||||||
|
echo "0 0 1 * * cd $(pwd) && ./scripts/renew-ssl.sh"
|
||||||
Loading…
x
Reference in New Issue
Block a user