UI Integration
UI Integration
Regole per chiamare i microservizi backend node-* del namespace ditta da un MFE Vue 3 (vue-*) o da qualunque consumer browser-side. Le regole sono derivate dagli ingress reali del cluster.
In sintesi
- Base URL:
https://{tenant}.pzeta.it/<prefix>/...(es.https://ditta.pzeta.it/storage/upload) - URL sempre relativi dal codice UI: mai host assoluto né
http://node-X-ditta.ditta.svc.cluster.local:3000(DNS interno cluster, non risolvibile dal browser) - Sessione: cookie
jwt_tokenHttpOnly settato dopo login aPOST /user-auth/auth/login - Auth flow ingress: ogni richiesta passa per nginx →
auth_request→node-user-auth/nginx/auth→ al successo gli headerX-User-*vengono iniettati nel forward al backend - 401 → redirect a
/user-auth/auth/login(gestito centralmente dal layer di app) - Token refresh trasparente:
node-user-authpuò rinnovare il cookie via header upstream alla risposta
Mappa path-prefix → microservizio
Tutti i path sotto https://ditta.pzeta.it/<prefix> sono pubblici (passano per nginx). I path interni (DNS cluster) sono raggiungibili solo M2M e non vanno mai usati dal frontend.
| Path pubblico | Microservizio | Ruolo | Auth nginx |
|---|---|---|---|
/user-auth/* | node-user-auth | Login, logout, refresh, validazione sessione | off (è l'auth backend stesso) |
/user-profiling/* | node-user-profiling | Ruoli, gruppi, permessi RBAC/ABAC | on |
/apiV1/* | node-postgrest-sidecar | Accesso dati PostgREST (rewrite /api/*) | on |
/orchestrator/* | node-orchestrator | Workflow, executions, registry, webhooks | on |
/scheduler/* | node-scheduler | Job schedulati | on |
/notification/* | node-notification | Notifiche multi-canale | on |
/storage/* | node-storage | Upload/download file, link temporanei | on |
/print/* | node-print | Stampa documenti PDF/etichette | on |
/renderer/* | node-renderer | Rendering template Handlebars | on |
/excel-export/* | node-excel-export | Export Excel/CSV | on |
/excel-import/* | node-excel-import | Import Excel/CSV | on |
/xmlvalid/* | node-xmlvalidation | Validazione XML/XSD | on |
/listen-admin/* | node-listen | Control plane mapping Postgres → NATS | on |
Il prefisso
/listen-adminè dedicato all'amministrazione del bridgenode-listen(CRUD mapping canali); il listener Postgres → NATS continua a girare in background e non è chiamato direttamente dal frontend.
Auth flow nginx
Sequenza completa di una richiesta autenticata (caso happy path):
Caso 401 (cookie scaduto/invalido):
Il backend del microservizio non viene mai contattato quando l'auth nginx fallisce: questo garantisce che X-User-* ricevuti dal backend siano sempre fidati (provengono dal sub-request nginx → node-user-auth).
Header propagati dal nginx al backend
| Header iniettato | Valore | Tipico uso backend |
|---|---|---|
X-User-Id | UUID utente | RLS PostgREST, audit log, ownership check |
X-User-Email | email utente | logging, notifiche |
X-User-Roles | ruoli (CSV) | RBAC route-level |
X-User-Scopes | scope OAuth-like (CSV) | autorizzazione fine |
X-User-Tenant | tenant slug (qui ditta) | filtro multi-tenant |
X-Forwarded-User | alias di X-User-Id per compat | — |
X-Request-Id | UUID per request | tracing correlation |
Il backend non deve mai fidarsi di questi header se provengono da una richiesta che bypassa nginx (es. ingresso diretto su DNS interno). I microservizi PZeta sono configurati per accettare gli X-User-* solo se la sub-request nginx è andata a buon fine.
Do / Don't lato frontend
Do
- Usa sempre fetch con
credentials: 'include'o equivalente (cookie HttpOnly) - Usa il client
@pzeta/fastify-utils-cliento equivalente che gestisce 401 → redirect login automatico - Per upload pesanti su
node-storageusamultipart/form-dataconXMLHttpRequest/fetchbodyFormData - Per download visualizzabili inline (PDF preview, anteprima Excel) usa il query param
?disposition=inlinedove supportato (es.node-excel-export,node-storageper i link temporanei) - Per chiamate idempotenti includi sempre
X-Request-Idlato client (UUID v4) per correlazione log
Don't
- ❌ Non chiamare mai
http://node-X-ditta.ditta.svc.cluster.local:<port>/...dal browser - ❌ Non costruire header
X-User-*lato client — sono iniettati esclusivamente da nginx - ❌ Non gestire manualmente la scadenza del cookie
jwt_token— il refresh è trasparente lato sidecar - ❌ Non chiamare le rotte
/health,/ready,/metricsdei microservizi — sono per Kubernetes/Prometheus, non per la UI - ❌ Non chiamare
/api-docs.jsonda codice di prodotto — è documentazione, non contratto stabile
Eccezioni e curiosità
node-postgrest-sidecaresposto come/apiV1/*con rewrite asimmetrico/api/$2verso il backend. Tipico per query SQL viste tramite PostgREST.node-user-authè l'unico endpoint pubblicamente accessibile senza sessione (enable-global-auth: false). È necessario perché la login arriva qui prima di avere un cookie valido.node-xmlvalidationè esposto come/xmlvalid/*(prefisso accorciato), non/xmlvalidation/*. Convenzione PZeta per servizi tecnici/utility.node-listenha ora un ingress dedicato/listen-admin/*per la gestione amministrativa dei mappingcanale Postgres → topic NATS(CRUD + hot-reload). Il bridge LISTEN/NOTIFY → publish NATS resta interno al pod e non è invocabile via HTTP.node-orchestratorespone SSE suGET /orchestrator/executions/{id}/streamper progress real-time dei workflow. Lato browser usaEventSource(url, { withCredentials: true }).
Pattern frequenti
Login
const res = await fetch("/user-auth/auth/login", {
method: "POST",
credentials: "include",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email, password }),
});
// 200 → cookie HttpOnly settato, redirect alla dashboard
// 401 → credenziali errate, mostra messaggio
Download presigned via node-storage
// 1. Crea link temporaneo (autenticato)
const { token } = await fetch("/storage/temporary-links", {
method: "POST",
credentials: "include",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ idallegato, expiresInDays: 7, maxDownloads: 1 }),
}).then((r) => r.json());
// 2. URL di download condivisibile (no auth, valido finché TTL e contatore download lo permettono)
const url = `https://ditta.pzeta.it/storage/download/${token}`;
Export Excel inline (preview in iframe)
<iframe src="/excel-export/dynamic/123?disposition=inline" />
Avvio workflow + sottoscrizione progress
const { executionId } = await fetch("/orchestrator/executions", {
method: "POST",
credentials: "include",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ workflowId, input }),
}).then((r) => r.json());
const es = new EventSource(
`/orchestrator/executions/${executionId}/stream`,
{ withCredentials: true },
);
es.addEventListener("task.completed", (e) => { /* update UI */ });
es.addEventListener("execution.completed", () => es.close());
Riferimenti
- Architettura del namespace — grafo dipendenze inter-servizio
node-user-auth— strategie auth supportate (nginx/apiKey/oauth/jwt)- Codice del progetto
node-provisioning-orchestratorper gli ingress reali del cluster