4 Commits

Author SHA1 Message Date
Aduanasoft
c6f18013b3 chore(deploy): override de testing (build de producción del frontend + loopback)
Frontend construido con Dockerfile.prod (adapter-node, VITE_* horneadas), backend
uvicorn sin reload, puertos solo en 127.0.0.1 (nginx del host hace TLS/proxy),
postgres/minio sin publicar. Para testing.crm.aduanasoft.com con auth real vía Hub.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-16 09:50:21 -06:00
Aduanasoft
f03ac38c4f docs(deploy): env de testing con auth real vía Hub (SSO relay, realm master, client aduanasoft)
Corrige la receta de integración según el código del Hub: single-realm (master) +
single-client (aduanasoft), HUB_URL para el sso-exchange del relay, y URLs del
dominio del CRM. Secretos en blanco (los pone el operador en el server).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-16 08:55:28 -06:00
Aduanasoft
5a250204b5 chore(deploy): override de preview privado (puertos solo en loopback)
Para levantar el stack en el servidor de pruebas con acceso por túnel SSH sin
exponer nada a internet: backend/frontend publicados solo en 127.0.0.1 y
postgres/minio sin publicar al host.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 17:37:17 -06:00
Aduanasoft
c983c744ac chore(deploy): nginx + entorno + runbook para testing.crm.aduanasoft.com
- deploy/nginx/testing.crm.aduanasoft.com.conf: reverse proxy TLS, app (:5173) y
  API (:8000) en el mismo origen, headers de seguridad, límite 30 MB de subida.
- deploy/env.testing.example: plantilla de entorno SIN secretos, con banderas de
  seguridad (ENVIRONMENT=production, DEV_LOCAL_AUTH=false) y URLs del dominio.
- deploy/README.md: runbook (acceso por llave, build de producción, migraciones,
  nginx+certbot, smoke test). Los secretos y las migraciones los ejecuta el operador.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 17:19:21 -06:00
5 changed files with 263 additions and 0 deletions

84
deploy/README.md Normal file
View 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.

View 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 []

View 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 []

View 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

View 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;
}
}