Nivel nube
Arquitectura de infraestructura
Qué corre dónde en producción, cómo se conectan las piezas y cómo llega el
código desde GitHub hasta los usuarios. Todo el cómputo está en Google Cloud (proyecto
hermetica, región us-central1) salvo el frontend, que se sirve
desde Cloudflare.
1. Topología en producción
Diagrama de lo que está vivo y quién habla con quién. Las flechas son llamadas en tiempo de ejecución.
flowchart TB
U["Usuarios Hermética
navegador · PWA instalada"]
subgraph CF["Cloudflare"]
CFW["Worker: lu-hermetica-comercial-webapp
assets estáticos del build de Vite
hermetica.lumini.dev"]
end
subgraph GCP["Google Cloud · proyecto hermetica · us-central1"]
RUN["Cloud Run: lu-hermetica-comercial-backend
Django + DRF · contenedor Docker
1 vCPU · 1 GiB · min 1 / max 20 instancias"]
DB[("Cloud SQL
PostgreSQL 17.9
hermetica-comercial-db")]
GCS[("Cloud Storage
hermetica_bucket
fotos, adjuntos, evidencias")]
SCH["Cloud Scheduler
tareas vencidas del Portal Gerencial"]
end
subgraph ONP["Red Hermética · on-premise"]
NIS[("Datawarehouse Nisira
datawarehouse.hermetica.pe:8050
ERP · fuente de OVT y OPR")]
end
subgraph EXT["Servicios externos"]
SMTP["SMTP Microsoft 365
despacho@hermetica.pe"]
AI["Anthropic · OpenAI · ElevenLabs
Chat Nisira y voz"]
end
CRON["Cron de importación
cada 30 min · 06:00-21:30
hoy corre en una laptop"]
U -->|HTTPS| CFW
CFW -->|"llamadas XHR a /api/*"| RUN
U -.->|"URLs públicas de imágenes"| GCS
RUN --> DB
RUN --> GCS
RUN -->|"REST con token"| NIS
RUN --> SMTP
RUN --> AI
CRON -->|"GET /api/dispatch/import/"| RUN
SCH -->|"POST con X-Cron-Secret"| RUN
classDef cfx fill:#fdf0e3,stroke:#e08b2f,color:#7a4a10
classDef gcpx fill:#e8f1fb,stroke:#4285f4,color:#0b3d78
classDef store fill:#eaf6ef,stroke:#34a853,color:#0d5228
classDef onp fill:#f1eefb,stroke:#7b5ecf,color:#3d2a75
classDef extx fill:#f3f4f6,stroke:#9aa3af,color:#374151
classDef cronx fill:#fdecec,stroke:#d9534f,color:#7f1d1d
classDef usr fill:#ffffff,stroke:#111827,color:#111827
class CFW cfx
class RUN,SCH gcpx
class DB,GCS store
class NIS onp
class SMTP,AI extx
class CRON cronx
class U usr
Regla de oro de esta arquitectura: el frontend no tiene credenciales de nada. No conoce la base de datos, no llama a Nisira, no manda correos. Su única dependencia es la URL de la API. Por eso el frontend se puede mover de proveedor en minutos, y el backend no.
2. Ciclo de vida de un despliegue
Ambos repositorios despliegan automáticamente al hacer merge a main. No
hay paso manual de publicación.
flowchart LR
subgraph FE["Frontend"]
direction TB
G1["GitHub
lu-hermetica-comercial-webapp"]
B1["Cloudflare Workers Builds
pnpm install --frozen-lockfile
vite build → dist/"]
D1["Worker en producción
hermetica.lumini.dev"]
G1 -->|"merge a main"| B1 --> D1
end
subgraph BE["Backend"]
direction TB
G2["GitHub
lu-hermetica-comercial-backend"]
B2["Cloud Build
cloudbuild.yaml"]
IMG["Artifact Registry
lu-hermetica-repo"]
MIG["manage.py migrate --noinput"]
DBX[("Cloud SQL")]
D2["gcloud run deploy
nueva revisión de Cloud Run"]
G2 -->|"merge a main"| B2
B2 -->|"docker build + push"| IMG
B2 --> MIG --> DBX
B2 --> D2
IMG -.->|"imagen :COMMIT_SHA"| D2
end
classDef ok fill:#e8f1fb,stroke:#4285f4,color:#0b3d78
classDef gh fill:#f3f4f6,stroke:#6b7280,color:#111827
class B1,B2,D1,D2,MIG ok
class G1,G2 gh
Etapa del cloudbuild.yaml | Qué hace |
|---|---|
| 1 · Artifact Registry | Crea el repositorio lu-hermetica-repo si no existe (idempotente). |
| 2 · Bucket GCS | Crea hermetica_bucket con lectura pública y CORS si no existe. |
| 3 · Docker build | Construye la imagen y la etiqueta con :$COMMIT_SHA y :latest. |
| 4 · Docker push | Sube ambas etiquetas a Artifact Registry. |
| 5 · Migraciones | Corre python manage.py migrate --noinput contra Cloud SQL. Si falla, la build muere aquí y producción queda intacta. |
| 6 · Deploy | gcloud run deploy con --update-env-vars (no --set-env-vars) para no borrar las variables gestionadas fuera del repo. |
3. Origen de los datos: integración con Nisira
El sistema no reemplaza al ERP. Nisira sigue siendo el sistema de registro de órdenes de venta (OVT) y órdenes de producción (OPR); el sistema comercial las importa periódicamente y agrega encima la capa de despacho, calendario, servicios, MRP, PAOM y cotizaciones.
flowchart LR
N[("Datawarehouse Nisira
puerto 8050")]
T["POST /token
obtiene bearer token"]
A["GET /api/clientes
GET /api/dordencompracliente
GET /api/ordenproduccion
GET /api/productos"]
C["import_group + import_dispatch
comandos de Django"]
P[("PostgreSQL
SalesOrder · ProductionOrder
NisiraProductionOrder · Group")]
UI["Pantallas de Despachos,
Calendario, MRP, PAOM"]
N --> T --> A --> C --> P --> UI
classDef onp fill:#f1eefb,stroke:#7b5ecf,color:#3d2a75
classDef store fill:#eaf6ef,stroke:#34a853,color:#0d5228
classDef proc fill:#e8f1fb,stroke:#4285f4,color:#0b3d78
class N onp
class P store
class C,T,A proc
Dependencia operativa a tener en cuenta. El datawarehouse Nisira está en la red de
Hermética y se accede por IP/DNS interno sobre HTTP plano. Cualquier migración del backend a
otro proveedor de nube debe garantizar que el nuevo host siga alcanzando
datawarehouse.hermetica.pe:8050, o la importación deja de funcionar y las
pantallas de despacho se congelan en el último dato importado.
4. Recursos exactos en producción
| Recurso | Servicio | Configuración |
|---|---|---|
lu-hermetica-comercial-backend | Cloud Run | us-central1 · puerto 8080 · 1 vCPU + CPU boost · 1 GiB · concurrencia 4 ·
min 1 / max 20 instancias · timeout 300 s · --allow-unauthenticated |
hermetica-comercial-db | Cloud SQL | PostgreSQL 17.9 · IP pública con allowlist de IPs · sslmode=require ·
usuario hermetica-sql |
hermetica_bucket | Cloud Storage | us-central1 · objetos con lectura pública (publicRead) · CORS abierto ·
sin firma en URLs |
lu-hermetica-repo | Artifact Registry | us-central1 · formato Docker · una imagen por commit |
| Cron de tareas vencidas | Cloud Scheduler | → POST /api/portal/cron/overdue/ con cabecera X-Cron-Secret |
lu-hermetica-comercial-webapp | Cloudflare Workers | assets desde ./dist · dominio hermetica.lumini.dev ·
compatibility_date 2025-12-18 |
5. Observaciones de seguridad heredadas
Puntos que el equipo receptor debería revisar y endurecer al tomar el sistema. Se documentan explícitamente para que no se descubran por accidente más adelante.
Revisar Permiso DRF por defecto
DEFAULT_PERMISSION_CLASSES es AllowAny. Cada viewset declara su
propio GroupBasedPermission; los que no lo declaran quedan abiertos. Un endpoint
nuevo nace público salvo que se le ponga permiso a mano.
Revisar Endpoint de importación abierto
GET /api/dispatch/import/ no pide autenticación: por eso el cron puede
dispararlo con un curl pelado. Conviene protegerlo con el mismo esquema
X-Cron-Secret que ya usa el cron del Portal Gerencial.
Revisar Secretos en el repositorio
Contraseña de base de datos, credenciales de Nisira y del SMTP están escritas en
cloudbuild.yaml, settings.py y los comandos de importación.
Deberían migrarse a Secret Manager.
Revisar Bucket público
hermetica_bucket sirve todos los objetos con lectura pública y sin firma. Las
evidencias y adjuntos son accesibles por URL para cualquiera que la tenga.