UI Integration

Regole di chiamata UI → microservizi backend, auth flow nginx, header propagati ed eccezioni.

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_token HttpOnly settato dopo login a POST /user-auth/auth/login
  • Auth flow ingress: ogni richiesta passa per nginx → auth_requestnode-user-auth/nginx/auth → al successo gli header X-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-auth può 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 pubblicoMicroservizioRuoloAuth nginx
/user-auth/*node-user-authLogin, logout, refresh, validazione sessioneoff (è l'auth backend stesso)
/user-profiling/*node-user-profilingRuoli, gruppi, permessi RBAC/ABACon
/apiV1/*node-postgrest-sidecarAccesso dati PostgREST (rewrite /api/*)on
/orchestrator/*node-orchestratorWorkflow, executions, registry, webhookson
/scheduler/*node-schedulerJob schedulation
/notification/*node-notificationNotifiche multi-canaleon
/storage/*node-storageUpload/download file, link temporaneion
/print/*node-printStampa documenti PDF/etichetteon
/renderer/*node-rendererRendering template Handlebarson
/excel-export/*node-excel-exportExport Excel/CSVon
/excel-import/*node-excel-importImport Excel/CSVon
/xmlvalid/*node-xmlvalidationValidazione XML/XSDon
/listen-admin/*node-listenControl plane mapping Postgres → NATSon

Il prefisso /listen-admin è dedicato all'amministrazione del bridge node-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):

Diagramma disponibile lato client.

Caso 401 (cookie scaduto/invalido):

Diagramma disponibile lato client.

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 iniettatoValoreTipico uso backend
X-User-IdUUID utenteRLS PostgREST, audit log, ownership check
X-User-Emailemail utentelogging, notifiche
X-User-Rolesruoli (CSV)RBAC route-level
X-User-Scopesscope OAuth-like (CSV)autorizzazione fine
X-User-Tenanttenant slug (qui ditta)filtro multi-tenant
X-Forwarded-Useralias di X-User-Id per compat
X-Request-IdUUID per requesttracing 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-client o equivalente che gestisce 401 → redirect login automatico
  • Per upload pesanti su node-storage usa multipart/form-data con XMLHttpRequest/fetch body FormData
  • Per download visualizzabili inline (PDF preview, anteprima Excel) usa il query param ?disposition=inline dove supportato (es. node-excel-export, node-storage per i link temporanei)
  • Per chiamate idempotenti includi sempre X-Request-Id lato 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, /metrics dei microservizi — sono per Kubernetes/Prometheus, non per la UI
  • ❌ Non chiamare /api-docs.json da codice di prodotto — è documentazione, non contratto stabile

Eccezioni e curiosità

  • node-postgrest-sidecar esposto come /apiV1/* con rewrite asimmetrico /api/$2 verso 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-listen ha ora un ingress dedicato /listen-admin/* per la gestione amministrativa dei mapping canale Postgres → topic NATS (CRUD + hot-reload). Il bridge LISTEN/NOTIFY → publish NATS resta interno al pod e non è invocabile via HTTP.
  • node-orchestrator espone SSE su GET /orchestrator/executions/{id}/stream per progress real-time dei workflow. Lato browser usa EventSource(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-orchestrator per gli ingress reali del cluster