Compare commits
4 Commits
e806512a89
...
feature/cr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c6f18013b3 | ||
|
|
f03ac38c4f | ||
|
|
5a250204b5 | ||
|
|
c983c744ac |
84
deploy/README.md
Normal file
84
deploy/README.md
Normal file
@@ -0,0 +1,84 @@
|
||||
# Despliegue — testing.crm.aduanasoft.com (entorno de pruebas)
|
||||
|
||||
Guía para publicar el CRM Agente de Carga en el servidor de pruebas. El estándar
|
||||
Aduanasoft es desplegar vía **Jenkins (CI/CD)**; este runbook manual es para el
|
||||
levantamiento inicial o cuando el pipeline aún no está conectado.
|
||||
|
||||
> **Servidor:** `deploy@216.250.125.140:3232` · **DNS:** `testing.crm.aduanasoft.com` → `216.250.125.140`
|
||||
|
||||
## 0. Reglas de seguridad (no negociables)
|
||||
- `ENVIRONMENT=production` y `DEV_LOCAL_AUTH=false`. En `development` el RBAC
|
||||
auto-bootstrapea **super_admin** a cualquiera y se activa el login local — inaceptable
|
||||
en un dominio público.
|
||||
- Los secretos (DB, S3, `SECRET_KEY`, Keycloak) los captura **el operador en el servidor**,
|
||||
nunca se versionan ni se comparten por chat.
|
||||
- Las **migraciones** las ejecuta el operador/CI, no de forma automática. No correr
|
||||
migraciones contra producción.
|
||||
|
||||
## 1. Acceso por llave (una vez)
|
||||
Autoriza la llave pública del operador en el servidor (desde una máquina que ya entre):
|
||||
```bash
|
||||
ssh -p 3232 deploy@216.250.125.140 \
|
||||
"mkdir -p ~/.ssh && chmod 700 ~/.ssh && echo '<TU_LLAVE_PUBLICA>' >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"
|
||||
```
|
||||
|
||||
## 2. Código y entorno
|
||||
```bash
|
||||
ssh -p 3232 deploy@216.250.125.140
|
||||
git clone https://git.aduanasoft.com/ADUANASOFT/CRM_AGENTES_CARGA.git
|
||||
cd CRM_AGENTES_CARGA
|
||||
git checkout feature/crm-cumplimiento-pdf # o la rama/tag liberado
|
||||
cp deploy/env.testing.example .env
|
||||
$EDITOR .env # rellenar TODOS los CHANGE_ME
|
||||
```
|
||||
|
||||
## 3. Build de producción (importante)
|
||||
El `docker-compose.yml` del repo está orientado a desarrollo (vite dev + `--reload`).
|
||||
Para un sitio público hay que servir la **build** de producción:
|
||||
- **Frontend:** `npm ci && npm run build` (adapter-node) y arrancar con `node build`
|
||||
escuchando en `5173`, con `ORIGIN=https://testing.crm.aduanasoft.com`.
|
||||
- **Backend:** `uvicorn main:app --host 0.0.0.0 --port 8000` **sin** `--reload`.
|
||||
- Publica los puertos SOLO en loopback (override):
|
||||
```yaml
|
||||
# docker-compose.testing.yml (ejemplo de override)
|
||||
services:
|
||||
backend:
|
||||
ports: ["127.0.0.1:8000:8000"]
|
||||
command: ["uvicorn","main:app","--host","0.0.0.0","--port","8000"]
|
||||
frontend:
|
||||
ports: ["127.0.0.1:5173:5173"]
|
||||
command: ["node","build"]
|
||||
```
|
||||
```bash
|
||||
docker compose -f docker-compose.yml -f docker-compose.testing.yml up -d --build
|
||||
```
|
||||
|
||||
## 4. Migraciones y datos base
|
||||
```bash
|
||||
docker compose exec backend alembic upgrade head
|
||||
# Catálogo de permisos + roles por carril (Ventas/Operaciones/Facturación/Consulta):
|
||||
docker compose exec backend python -c "from api.v1.modules.core.permissions.service import PermissionService; from core.database import CoreSessionLocal; PermissionService(CoreSessionLocal()).sync_permissions()"
|
||||
```
|
||||
> En producción NO se usa el auto-bootstrap de dev: asigna los roles a los usuarios
|
||||
> reales desde el módulo de Roles y permisos.
|
||||
|
||||
## 5. Nginx + TLS
|
||||
```bash
|
||||
sudo cp deploy/nginx/testing.crm.aduanasoft.com.conf /etc/nginx/sites-available/
|
||||
sudo ln -s /etc/nginx/sites-available/testing.crm.aduanasoft.com.conf /etc/nginx/sites-enabled/
|
||||
sudo mkdir -p /var/www/certbot
|
||||
sudo certbot certonly --webroot -w /var/www/certbot -d testing.crm.aduanasoft.com
|
||||
sudo nginx -t && sudo systemctl reload nginx
|
||||
```
|
||||
|
||||
## 6. Smoke test
|
||||
```bash
|
||||
curl -fsS https://testing.crm.aduanasoft.com/api/health && echo OK
|
||||
# Abre la app y valida login (Keycloak), listar clientes, y un flujo end-to-end.
|
||||
```
|
||||
|
||||
## Notas
|
||||
- App y API van en el **mismo origen** (`/` y `/api/`) para no requerir CORS entre hosts.
|
||||
- MinIO: si el navegador debe abrir URLs prefirmadas, `S3_ENDPOINT_URL` debe resolver a un
|
||||
host accesible públicamente (o publicar MinIO detrás de nginx en otro subdominio). Revisar
|
||||
según la política de red del entorno de pruebas.
|
||||
17
deploy/docker-compose.preview.yml
Normal file
17
deploy/docker-compose.preview.yml
Normal file
@@ -0,0 +1,17 @@
|
||||
# Preview PRIVADO en el servidor (login dev), acceso solo por túnel SSH.
|
||||
# Publica los puertos ÚNICAMENTE en 127.0.0.1 para que NADA quede expuesto a internet.
|
||||
# Postgres y MinIO no se publican al host (solo red interna de Docker).
|
||||
#
|
||||
# Uso:
|
||||
# docker compose -f docker-compose.yml -f deploy/docker-compose.preview.yml up -d
|
||||
services:
|
||||
backend:
|
||||
ports: !override
|
||||
- "127.0.0.1:8000:8000"
|
||||
frontend:
|
||||
ports: !override
|
||||
- "127.0.0.1:5173:5173"
|
||||
postgres:
|
||||
ports: !override []
|
||||
minio:
|
||||
ports: !override []
|
||||
33
deploy/docker-compose.testing.yml
Normal file
33
deploy/docker-compose.testing.yml
Normal file
@@ -0,0 +1,33 @@
|
||||
# Despliegue en testing.crm.aduanasoft.com — AUTH REAL vía Hub (SSO relay).
|
||||
# Frontend: build de PRODUCCIÓN (Dockerfile.prod → node build). Backend: uvicorn sin --reload.
|
||||
# Puertos SOLO en loopback (nginx del host hace TLS + proxy). Postgres/MinIO no se publican.
|
||||
#
|
||||
# docker compose -f docker-compose.yml -f deploy/docker-compose.testing.yml up -d --build
|
||||
services:
|
||||
backend:
|
||||
ports: !override
|
||||
- "127.0.0.1:8000:8000"
|
||||
command: ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000", "--log-level", "info"]
|
||||
|
||||
frontend:
|
||||
build:
|
||||
context: ./frontend
|
||||
dockerfile: Dockerfile.prod
|
||||
args:
|
||||
VITE_API_URL: ${VITE_API_URL}
|
||||
VITE_HUB_URL: ${VITE_HUB_URL}
|
||||
VITE_KEYCLOAK_URL: ${VITE_KEYCLOAK_URL}
|
||||
VITE_KEYCLOAK_REALM: ${KEYCLOAK_REALM:-master}
|
||||
VITE_KEYCLOAK_CLIENT_ID: ${KEYCLOAK_CLIENT_ID:-aduanasoft}
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
volumes: !override []
|
||||
command: !override ["pnpm", "start"]
|
||||
ports: !override
|
||||
- "127.0.0.1:5173:5173"
|
||||
|
||||
postgres:
|
||||
ports: !override []
|
||||
|
||||
minio:
|
||||
ports: !override []
|
||||
48
deploy/env.testing.example
Normal file
48
deploy/env.testing.example
Normal file
@@ -0,0 +1,48 @@
|
||||
# ==========================================================================
|
||||
# testing.crm.aduanasoft.com — AUTH REAL vía Hub (SSO relay). Copia como `.env`
|
||||
# EN EL SERVIDOR y rellena los <...>. NO subas el .env con secretos al repo.
|
||||
#
|
||||
# Flujo: el Hub (App Launcher) redirige a /auth/sso?relay=<uuid>; el CRM
|
||||
# intercambia el relay en POST {HUB_URL}/api/v1/auth/sso-exchange y entra.
|
||||
# ==========================================================================
|
||||
|
||||
# ---- SEGURIDAD ----
|
||||
ENVIRONMENT=production
|
||||
DEV_LOCAL_AUTH=false
|
||||
SECRET_KEY=<genera: openssl rand -hex 32>
|
||||
|
||||
# ---- Workspace / Hub (DEBE ser el MISMO Hub que generó el relay) ----
|
||||
# Confirmar el host real de producción (workspace.aduanasoft.com o hub.aduanasoft.com):
|
||||
WORKSPACE_URL=https://<hub-produccion>
|
||||
HUB_URL=https://<hub-produccion>
|
||||
INTERNAL_HUB_URL=https://<hub-produccion>
|
||||
VITE_HUB_URL=https://<hub-produccion>
|
||||
|
||||
# ---- Keycloak (arquitectura single-realm / single-client) ----
|
||||
KEYCLOAK_URL=https://<keycloak-produccion>/kcauth
|
||||
VITE_KEYCLOAK_URL=https://<keycloak-produccion>/kcauth
|
||||
KEYCLOAK_REALM=master
|
||||
KEYCLOAK_CLIENT_ID=aduanasoft
|
||||
KEYCLOAK_CLIENT_SECRET=<secret del producto provisionado — lo pones tú>
|
||||
|
||||
# ---- Dominio del CRM (mismo origen app + API vía nginx) ----
|
||||
ORIGIN=https://testing.crm.aduanasoft.com
|
||||
APP_PUBLIC_URL=https://testing.crm.aduanasoft.com
|
||||
VITE_API_URL=https://testing.crm.aduanasoft.com/api/
|
||||
INTERNAL_API_URL=http://backend:8000/api/
|
||||
CORS_ORIGINS=https://testing.crm.aduanasoft.com
|
||||
|
||||
# ---- Base de datos (PostgreSQL) ----
|
||||
CORE_DB_HOST=postgres
|
||||
CORE_DB_PORT=5432
|
||||
CORE_DB_NAME=crm_core
|
||||
CORE_DB_USER=<usuario>
|
||||
POSTGRES_APP_PASSWORD=<password fuerte>
|
||||
|
||||
# ---- MinIO / S3 ----
|
||||
S3_ENDPOINT_URL=http://minio:9000
|
||||
S3_ACCESS_KEY=<access>
|
||||
S3_SECRET_KEY=<secret>
|
||||
S3_BUCKET=crm
|
||||
S3_REGION=us-east-1
|
||||
S3_USE_SSL=false
|
||||
81
deploy/nginx/testing.crm.aduanasoft.com.conf
Normal file
81
deploy/nginx/testing.crm.aduanasoft.com.conf
Normal file
@@ -0,0 +1,81 @@
|
||||
# nginx — testing.crm.aduanasoft.com
|
||||
# CRM Agente de Carga (entorno de PRUEBAS). App (SvelteKit adapter-node :5173) + API (FastAPI :8000)
|
||||
# servidas en el MISMO origen para evitar CORS. TLS con Let's Encrypt (certbot).
|
||||
#
|
||||
# Instalar en el servidor:
|
||||
# sudo cp testing.crm.aduanasoft.com.conf /etc/nginx/sites-available/
|
||||
# sudo ln -s /etc/nginx/sites-available/testing.crm.aduanasoft.com.conf /etc/nginx/sites-enabled/
|
||||
# sudo certbot certonly --webroot -w /var/www/certbot -d testing.crm.aduanasoft.com
|
||||
# sudo nginx -t && sudo systemctl reload nginx
|
||||
#
|
||||
# IMPORTANTE (seguridad): publica los puertos de la app SOLO en loopback del servidor
|
||||
# (127.0.0.1:5173 y 127.0.0.1:8000) para que nginx sea el único acceso público.
|
||||
|
||||
# ---- HTTP: reto ACME + redirección a HTTPS ----
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
server_name testing.crm.aduanasoft.com;
|
||||
|
||||
# Renovación de certificados (webroot)
|
||||
location /.well-known/acme-challenge/ {
|
||||
root /var/www/certbot;
|
||||
}
|
||||
|
||||
location / {
|
||||
return 301 https://$host$request_uri;
|
||||
}
|
||||
}
|
||||
|
||||
# ---- HTTPS ----
|
||||
server {
|
||||
listen 443 ssl;
|
||||
listen [::]:443 ssl;
|
||||
http2 on;
|
||||
server_name testing.crm.aduanasoft.com;
|
||||
|
||||
ssl_certificate /etc/letsencrypt/live/testing.crm.aduanasoft.com/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/testing.crm.aduanasoft.com/privkey.pem;
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_prefer_server_ciphers off;
|
||||
ssl_session_cache shared:SSL:10m;
|
||||
ssl_session_timeout 1d;
|
||||
|
||||
# Encabezados de seguridad
|
||||
add_header Strict-Transport-Security "max-age=63072000" always;
|
||||
add_header X-Content-Type-Options "nosniff" always;
|
||||
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
||||
|
||||
# Subida de documentos (máx. 25 MB en la app) + margen
|
||||
client_max_body_size 30m;
|
||||
|
||||
gzip on;
|
||||
gzip_types text/plain text/css application/javascript application/json image/svg+xml;
|
||||
gzip_min_length 1024;
|
||||
|
||||
# ---- API (FastAPI) — mismo origen: /api/... -> backend :8000 (conserva el path) ----
|
||||
location /api/ {
|
||||
proxy_pass http://127.0.0.1:8000;
|
||||
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 https;
|
||||
proxy_read_timeout 120s;
|
||||
}
|
||||
|
||||
# ---- App (SvelteKit adapter-node) — todo lo demás -> frontend :5173 ----
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:5173;
|
||||
proxy_http_version 1.1;
|
||||
# WebSocket / upgrade (SSR streaming y HMR si corriera en modo dev)
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
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 https;
|
||||
proxy_read_timeout 120s;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user