ORdenar archivos de PRod
This commit is contained in:
17
.env.prod.local
Normal file
17
.env.prod.local
Normal 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
17
.env.production
Normal 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
256
DEPLOY_DOCKPLOY.md
Normal 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')
|
||||||
|
"
|
||||||
|
```
|
||||||
@@ -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
64
docker-compose.prod.yml
Normal 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
34
frontend/Dockerfile.prod
Normal 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
36
frontend/nginx.conf
Normal 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;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user