ORdenar archivos de PRod

This commit is contained in:
2025-11-18 17:35:32 -03:00
parent 443de4ec0e
commit bc4721737c
7 changed files with 427 additions and 2 deletions

17
.env.prod.local Normal file
View File

@@ -0,0 +1,17 @@
# Variables de Entorno para Prueba de Producción Local
# Database
POSTGRES_DB=checklist_db
POSTGRES_USER=checklist_user
POSTGRES_PASSWORD=checklist_pass_2024_prod
# Backend
SECRET_KEY=production-secret-key-super-segura-minimo-32-caracteres-123456
OPENAI_API_KEY=
ENVIRONMENT=production
# CORS - Para prueba local
ALLOWED_ORIGINS=http://localhost,http://localhost:80,http://127.0.0.1
# Frontend - Para prueba local (apunta al backend en puerto 8000)
VITE_API_URL=http://localhost:8000

17
.env.production Normal file
View File

@@ -0,0 +1,17 @@
# Variables de Entorno para Producción - Dockploy
# Database
POSTGRES_DB=checklist_db
POSTGRES_USER=checklist_user
POSTGRES_PASSWORD=CAMBIAR-PASSWORD-SUPER-SEGURA-AQUI
# Backend
SECRET_KEY=CAMBIAR-CLAVE-SECRETA-MINIMO-32-CARACTERES-SUPER-SEGURA
OPENAI_API_KEY=tu-openai-api-key-aqui
ENVIRONMENT=production
# CORS - Separar múltiples dominios con comas
ALLOWED_ORIGINS=https://tudominio.com,https://www.tudominio.com,https://app.tudominio.com
# Frontend
VITE_API_URL=https://api.tudominio.com

256
DEPLOY_DOCKPLOY.md Normal file
View File

@@ -0,0 +1,256 @@
# Guía de Deployment en Dockploy
## Arquitectura de Conexión
### Desarrollo (Docker Compose Local)
```
Frontend (localhost:5173) → Backend (localhost:8000) → PostgreSQL (localhost:5432)
```
### Producción (Dockploy)
```
Frontend (tudominio.com) → Backend (api.tudominio.com) → PostgreSQL (interno)
```
---
## 📋 Pasos para Deploy en Dockploy
### 1. Preparar Variables de Entorno
**Backend (.env)**
```bash
DATABASE_URL=postgresql://user:pass@postgres:5432/checklist_db
SECRET_KEY=tu-clave-secreta-minimo-32-caracteres-super-segura
OPENAI_API_KEY=tu-api-key-de-openai
ENVIRONMENT=production
ALLOWED_ORIGINS=https://tudominio.com,https://www.tudominio.com
```
**Frontend (.env)**
```bash
VITE_API_URL=https://api.tudominio.com
```
### 2. Actualizar CORS en Backend
Editar `backend/app/main.py`:
```python
import os
# CORS
allowed_origins = os.getenv("ALLOWED_ORIGINS", "http://localhost:5173").split(",")
app.add_middleware(
CORSMiddleware,
allow_origins=allowed_origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
```
### 3. Crear Dockerfile de Producción para Frontend
Crear `frontend/Dockerfile.prod`:
```dockerfile
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
ARG VITE_API_URL
ENV VITE_API_URL=$VITE_API_URL
RUN npm run build
# Servidor Nginx para servir archivos estáticos
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;"]
```
Crear `frontend/nginx.conf`:
```nginx
server {
listen 80;
server_name _;
root /usr/share/nginx/html;
index index.html;
location / {
try_files $uri $uri/ /index.html;
}
# Configuración de cache para assets
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
}
```
### 4. Estructura Recomendada en Dockploy
**Opción A: Servicios Separados (Recomendado)**
- **Servicio 1**: PostgreSQL (Base de datos interna)
- **Servicio 2**: Backend API (api.tudominio.com)
- **Servicio 3**: Frontend (tudominio.com)
**Opción B: Docker Compose en Dockploy**
Usar `docker-compose.prod.yml`:
```yaml
version: '3.8'
services:
postgres:
image: postgres:15-alpine
environment:
POSTGRES_DB: ${POSTGRES_DB}
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
volumes:
- postgres_data:/var/lib/postgresql/data
networks:
- app-network
backend:
build: ./backend
environment:
DATABASE_URL: postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB}
SECRET_KEY: ${SECRET_KEY}
OPENAI_API_KEY: ${OPENAI_API_KEY}
ENVIRONMENT: production
ALLOWED_ORIGINS: ${ALLOWED_ORIGINS}
ports:
- "8000:8000"
depends_on:
- postgres
networks:
- app-network
frontend:
build:
context: ./frontend
dockerfile: Dockerfile.prod
args:
VITE_API_URL: ${VITE_API_URL}
ports:
- "80:80"
depends_on:
- backend
networks:
- app-network
volumes:
postgres_data:
networks:
app-network:
driver: bridge
```
### 5. Configuración de Dominios en Dockploy
1. **Backend API**:
- Dominio: `api.tudominio.com`
- Puerto interno: `8000`
- Habilitar SSL/TLS
2. **Frontend**:
- Dominio: `tudominio.com`
- Puerto interno: `80`
- Habilitar SSL/TLS
3. **PostgreSQL**:
- Solo acceso interno (no exponer públicamente)
### 6. Scripts de Migración
Crear `backend/migrate.sh`:
```bash
#!/bin/bash
# Ejecutar migraciones en producción
python -c "from app.core.database import Base, engine; Base.metadata.create_all(bind=engine)"
```
### 7. Health Checks
El backend ya tiene el endpoint de health. Dockploy puede monitorearlo:
```
GET https://api.tudominio.com/docs
```
---
## 🔐 Seguridad en Producción
1. **Variables de Entorno**:
- Usar secretos de Dockploy, NO hardcodear
- Cambiar `SECRET_KEY` a algo seguro (min 32 caracteres)
- No exponer `DATABASE_URL` públicamente
2. **HTTPS**:
- Dockploy maneja SSL automáticamente con Let's Encrypt
- Asegurar que CORS solo acepte HTTPS en producción
3. **Database**:
- NO exponer puerto 5432 públicamente
- Usar credenciales fuertes
- Configurar backups automáticos
---
## 📝 Checklist Pre-Deploy
- [ ] Cambiar `SECRET_KEY` en variables de entorno
- [ ] Configurar `VITE_API_URL` con dominio de producción
- [ ] Actualizar CORS con dominios de producción
- [ ] Crear Dockerfile.prod para frontend
- [ ] Configurar nginx.conf
- [ ] Probar build local: `docker-compose -f docker-compose.prod.yml up`
- [ ] Configurar dominios en Dockploy
- [ ] Habilitar SSL/TLS en ambos servicios
- [ ] Crear usuario admin inicial en producción
- [ ] Configurar backups de PostgreSQL
---
## 🎯 Comandos Útiles
**Build local de producción:**
```bash
docker-compose -f docker-compose.prod.yml build
docker-compose -f docker-compose.prod.yml up -d
```
**Ver logs:**
```bash
docker-compose -f docker-compose.prod.yml logs -f backend
docker-compose -f docker-compose.prod.yml logs -f frontend
```
**Crear usuario admin en producción:**
```bash
docker-compose -f docker-compose.prod.yml exec backend python -c "
from app.core.database import SessionLocal
from app.core.security import get_password_hash
from app.models import User
db = SessionLocal()
admin = User(
username='admin',
email='admin@tudominio.com',
full_name='Administrador',
hashed_password=get_password_hash('tu-password-segura'),
role='admin',
is_active=True
)
db.add(admin)
db.commit()
print('Admin creado exitosamente')
"
```

View File

@@ -16,10 +16,11 @@ Base.metadata.create_all(bind=engine)
app = FastAPI(title="Checklist Inteligente API", version="1.0.0") app = FastAPI(title="Checklist Inteligente API", version="1.0.0")
# CORS # CORS - Configuración dinámica para desarrollo y producción
allowed_origins = os.getenv("ALLOWED_ORIGINS", "http://localhost:5173,http://localhost:3000").split(",")
app.add_middleware( app.add_middleware(
CORSMiddleware, CORSMiddleware,
allow_origins=["http://localhost:5173", "http://localhost:3000"], allow_origins=allowed_origins,
allow_credentials=True, allow_credentials=True,
allow_methods=["*"], allow_methods=["*"],
allow_headers=["*"], allow_headers=["*"],

64
docker-compose.prod.yml Normal file
View File

@@ -0,0 +1,64 @@
version: '3.8'
services:
postgres:
image: postgres:15-alpine
container_name: checklist-db-prod
environment:
POSTGRES_DB: ${POSTGRES_DB:-checklist_db}
POSTGRES_USER: ${POSTGRES_USER:-checklist_user}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
volumes:
- postgres_data:/var/lib/postgresql/data
networks:
- app-network
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-checklist_user}"]
interval: 10s
timeout: 5s
retries: 5
backend:
build:
context: ./backend
dockerfile: Dockerfile
container_name: checklist-backend-prod
environment:
DATABASE_URL: postgresql://${POSTGRES_USER:-checklist_user}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB:-checklist_db}
SECRET_KEY: ${SECRET_KEY}
OPENAI_API_KEY: ${OPENAI_API_KEY}
ENVIRONMENT: production
ALLOWED_ORIGINS: ${ALLOWED_ORIGINS}
ports:
- "8000:8000"
volumes:
- ./uploads:/app/uploads
depends_on:
postgres:
condition: service_healthy
networks:
- app-network
restart: unless-stopped
frontend:
build:
context: ./frontend
dockerfile: Dockerfile.prod
args:
VITE_API_URL: ${VITE_API_URL}
container_name: checklist-frontend-prod
ports:
- "80:80"
depends_on:
- backend
networks:
- app-network
restart: unless-stopped
volumes:
postgres_data:
networks:
app-network:
driver: bridge

34
frontend/Dockerfile.prod Normal file
View File

@@ -0,0 +1,34 @@
FROM node:18-alpine AS builder
WORKDIR /app
# Copiar package files
COPY package*.json ./
# Instalar todas las dependencias (necesitamos las dev para el build)
RUN npm install
# Copiar código
COPY . .
# Build argument para la URL de la API
ARG VITE_API_URL
ENV VITE_API_URL=$VITE_API_URL
# Construir la aplicación
RUN npm run build
# Etapa de producción con Nginx
FROM nginx:alpine
# Copiar archivos construidos
COPY --from=builder /app/dist /usr/share/nginx/html
# Copiar configuración de nginx
COPY nginx.conf /etc/nginx/conf.d/default.conf
# Exponer puerto 80
EXPOSE 80
# Comando para iniciar nginx
CMD ["nginx", "-g", "daemon off;"]

36
frontend/nginx.conf Normal file
View File

@@ -0,0 +1,36 @@
server {
listen 80;
server_name _;
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 text/xml text/javascript application/x-javascript application/xml+rss application/json application/javascript;
# SPA routing - todas las rutas van a index.html
location / {
try_files $uri $uri/ /index.html;
}
# Cache para assets estáticos
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# No cache para index.html
location = /index.html {
add_header Cache-Control "no-cache, no-store, must-revalidate";
add_header Pragma "no-cache";
add_header Expires 0;
}
# 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;
}