node-renderer
node-renderer
In sintesi
Motore di rendering testuale server-side basato su Handlebars: compila template parametrici (HTML, testo, frammenti JSON, intestazioni email) combinandoli con i dati forniti in input e restituisce il documento sorgente al chiamante. Si posiziona a monte della filiera documentale del tenant ditta: produce il contenuto rappresentativo del documento, lasciando a node-print la trasformazione in PDF/ZPL e il dispatch al device, e a node-notification il recapito quando si tratta di email o messaggi. Il servizio è stateless rispetto al singolo render, ma persiste su PostgreSQL la libreria di template gestita dagli operatori e supporta sorgenti template multiple (file, DB, VCS, memoria, contenuto inline).
Funzionalità principali
- Rendering Handlebars sincrono via REST (
POST /render,GET /render) con risoluzione del template pertemplateIdregistrato otemplateContentinline e dati provenienti da JSON diretto, URL esterno o dataset statico - Multi-sorgente template tramite provider pattern:
file(filesystem./templates/*.hbs),database(tabella PostgreSQL parametrica con colonne id/content configurabili),vcs(repository Git via@pzeta/vclcon endpointservices.pzeta.it/git),memory(template in-process per test e fixture) - Gestione CRUD libreria template su PostgreSQL (
GET/POST/PUT/DELETE /templates,POST /templates/{id}/validate) con validazione strutturale (sintassi Handlebars, helper noti, riferimenti dato), supporto al hot-reload e metadati di tracciabilità (createdAt,updatedAt,size) esposti suGET /templateseGET /templates/{id} - Helper library estesa: catalogo introspettabile (
GET /helpers,/helpers/categories,/helpers/search,/helpers/{name}) con helper di formatting, condizionali, iterazione, manipolazione date/numeri e helper temporanei iniettabili a richiesta - Configuration API runtime (
GET /config,PUT /config,POST /config/validate,GET /config/schema) per ispezionare la configurazione attiva e applicare modifiche parziali a caldo: la scrittura è gated daCONFIG_ADMIN_KEY(headerX-Admin-Key) e ritorna 503 quando la chiave non è valorizzata, riducendo la superficie d'attacco in produzione - Endpoint operativi dedicati: oltre a
/healthe/metricsPrometheus forniti dai plugin@pzeta/fastify-utils, il servizio esponeGET /(banner di servizio conname/version/timestamp),GET /health-fast(probe minima senza dipendenze),GET /status(stringaOKper load balancer e probe esterni),GET /metrics-basic(uptime e memoria in MB in JSON, consumata dalla dashboardvue-renderer) - Cache di compilazione e di template opzionale (TTL configurabile via
CACHE_TTL, massimo 24h) sia sui sorgenti che sui template compilati Handlebars, per abbattere le latenze sui rendering ripetitivi - Integrazione orchestrator via
@pzeta/orchestrator-node: il servizio si registra come modulo con i taskrender-template,render-batch(rendering parallelo con concurrency configurabile),validate-template,get-template-info,cache-warm - Rate limiting granulare su cinque bucket (
global,infrastructure,template-management,rendering,monitoring) per proteggere il servizio dagli abusi senza penalizzare le probe operazionali - Sicurezza render: domini permessi per data-URL esterni (
ALLOWED_DOWNLOAD_DOMAINS), CSP iniettabile su risposte HTML (HTML_CSP_POLICY), escape automatico Handlebars con opt-out controllato (ALLOW_NO_ESCAPE), validazione Zod di tutte le request
Architettura
Stack: Fastify v5 · Inversify (DI con constructor injection) · PostgreSQL via driver pg per la libreria template · Handlebars 4 come template engine · @pzeta/vcl per accesso a repository Git remoti · @pzeta/orchestrator-node come SDK di registrazione al hub · @pzeta/fastify-utils (auth, security, OpenAPI, healthcheck) · @pzeta/log con correlation IDs · Zod per validazione schema-first.
Layout DDD (src/):
| Layer | Contenuto |
|---|---|
domain/ | Interfacce (ITemplateService, ITemplateCompiler, IDataService, IRenderingStrategy, IRenderingPipelineExtension), errori applicativi (RenderingError, TemplateNotFoundError, TemplateValidationError, DataFetchError) |
application/ | TemplateCompilerService, TemplateCommandService, TemplateQueryService, TemplateManagementService (singola istanza condivisa fra i controller CRUD), TemplateSyntaxAnalyzer, RenderContextBuilder, HelperManager, HelpersIntrospectionService, ConfigurationService, UnifiedErrorService |
infrastructure/ | HandlebarsCompiler, provider template (DbTemplateProvider, MemoryTemplateProvider, FileTemplateProvider, VcsTemplateProvider — registrato con chiave vcl allineata al manifest del provider), provider dati (StaticDataProvider, UrlDataProvider), pipeline extensions (AsyncHelpersPipelineExtension, LoggingPipelineExtension), OrchestratorBootstrap |
presentation/ | Controllers (PostRenderController, GetRenderController, ListTemplatesController/GetTemplateController/CreateTemplateController/UpdateTemplateController/DeleteTemplateController/ValidateTemplateController, HelpersController, ConfigurationController), route plugin (RenderRoutes, TemplateManagementRoutes, ConfigurationRoutes, MonitoringRoutes con endpoint /, /health-fast, /status, /metrics-basic), schemi Zod/JSON-Schema |
Pattern adottati: Strategy (sorgenti template intercambiabili), Factory (TemplateProviderFactory, TemplateCompilerFactory, WebServerFactory), Chain of Responsibility (fallback fra provider quando un template non è trovato sulla sorgente primaria), Repository (astrazione IDatabaseConnection su PostgreSQL), Pipeline Extension (estensioni in coda al compilatore: helper async, logging strutturato).
Differenza rispetto a node-print: questo servizio è il renderer puro — produce il documento sorgente (stringa) a partire da template + dati. node-print è invece il device gateway: prende il sorgente già renderizzato (o invoca questo servizio per ottenerlo), lo converte nel formato richiesto dal dispositivo (PDF, ZPL, etichetta termica) e gestisce la coda di stampa verso la stampante fisica o la scarica come file. Le due responsabilità sono separate per consentire di renderizzare lo stesso template anche per scenari non di stampa (email HTML, anteprima a video, payload di report).
Casi d'uso
- Fattura PDF:
node-printriceve la richiesta di stampa documento dal frontend, chiamaPOST /rendercontemplateId="fattura-vendita"e i dati della testata/righe, ottiene l'HTML sorgente e lo converte in PDF prima di archiviarlo sunode-storagee inviarlo al device - Email transazionale:
node-notificationinvoca il task orchestratorrender-templateper produrre il corpo HTML (e l'oggetto come secondo render) di una mail di conferma ordine, partendo da template registrati sul DB del renderer e dati pescati dal workflow - Report custom: il frontend Vue richiede un'anteprima parametrica di un report (estratto conto, riepilogo turno, scheda capo lavanderia) tramite
GET /render?templateId=...&data=..., riceve HTML da iniettare in un'area di stampa client-side - Generazione batch documenti: un workflow
node-orchestratordistribuisce N documenti (es. mailing massivo, contratti per gruppo di clienti) sul taskrender-batchconparallel=trueemaxConcurrency=5per il rendering parallelo, poi inoltra ogni output al servizio downstream - Etichette e ricevute: per i flussi di lavanderia industriale,
node-printchiede al renderer una stringa ZPL/EPL parametrica (template testuale, non HTML) da inviare alla stampante termica di etichettatura capi - Dashboard operativa: la SPA
vue-rendererinterrogaGET /metrics-basic,GET /health-fasteGET /templates(con timestampcreatedAt/updatedAtesize) per costruire una vista di gestione della libreria template senza dover parsare il formato Prometheus
Identità & esposizione
| Campo | Valore |
|---|---|
| Categoria | document-output |
| Versione cluster | 1.0.14 |
| Image | gitea.pzetatouch.it/pzeta_touch/node-renderer:1.0.13 |
| URL pubblico | https://ditta.pzeta.it/renderer |
| Path regex ingress | `/renderer(/ |
| Rewrite a backend | /$2 |
| DNS interno | node-renderer-ditta.ditta.svc.cluster.local:3000 |
| Auth nginx | auth_request → node-user-auth |
| Repository | node-renderer |
| Endpoint REST | 16 (vedi sezione "API reference") |
Endpoint operazionali
Endpoint convenzionali esposti da tutti i microservizi PZeta basati su @pzeta/fastify-utils:
| Path pubblico | Scopo |
|---|---|
https://ditta.pzeta.it/renderer/health | liveness probe |
https://ditta.pzeta.it/renderer/ready | readiness probe |
https://ditta.pzeta.it/renderer/metrics | metriche Prometheus |
https://ditta.pzeta.it/renderer/api-docs.json | spec OpenAPI runtime (richiede OPENAPI_EXPOSE_IN_PRODUCTION=true) |
https://ditta.pzeta.it/renderer/api-docs | Swagger UI (solo in NODE_ENV !== production) |
Configurazione
Variabili d'ambiente che un integratore deve conoscere (per la lista completa vedi .env.example del repo):
| Variabile | Ruolo |
|---|---|
TEMPLATE_PROVIDERS | Lista CSV dei provider attivi: file, database, vcs, memory. L'ordine determina la priorità di lookup (chain of responsibility) |
TEMPLATE_FILE_PATH / TEMPLATE_FILE_EXT | Cartella radice e estensione (default .hbs) per il provider file |
DATABASE_HOST / _PORT / _USER / _PASSWORD / _NAME | Connessione PostgreSQL per il provider database — alternativa: DATABASE_URL |
DATABASE_TABLE / DATABASE_ID_COL / DATABASE_CONTENT_COL | Tabella e colonne (default templates / id / content) — i nomi sono validati per evitare SQL injection |
VERSION_CONTROL_API_URL / _CATEGORY | Endpoint del repository Git remoto per il provider vcs (default https://services.pzeta.it/git, categoria template) |
DATA_PROVIDERS | Sorgenti dati abilitate per il render (static, url) |
ORCHESTRATOR_ENABLED / ORCHESTRATOR_URL / ORCHESTRATOR_API_KEY | Registrazione del modulo presso node-orchestrator per ricevere task |
CACHE_ENABLED / CACHE_TTL | Cache di template e compilazioni (TTL in secondi, max 86400) |
RATE_LIMIT_*_MAX / _WINDOW | Limiti per bucket (global, infrastructure, template-mgmt, rendering, monitoring) |
CONFIG_ADMIN_KEY | Chiave amministrativa richiesta per PUT /config (header X-Admin-Key). Se vuota, l'endpoint di scrittura risponde 503 Service Unavailable |
ALLOWED_DOWNLOAD_DOMAINS / TRUSTED_DATA_DOMAINS | Whitelist di domini per fetch dati esterni e propagazione token Bearer |
HTML_CSP_POLICY / ALLOW_NO_ESCAPE | Hardening output HTML: CSP iniettata sulle risposte rendered, opt-in per disabilitare l'escape Handlebars |
Lo storage dei file template grandi e degli asset binari richiamati dai template (loghi, immagini intestazione) resta su node-storage: il renderer riceve solo i riferimenti via dato di input e Handlebars produce gli URL nel sorgente HTML.
Configuration & monitoring API
Il servizio espone un layer di introspezione e governance separato dagli endpoint di rendering, pensato per operator e SRE:
| Endpoint | Scopo |
|---|---|
GET /config | Snapshot della configurazione runtime corrente; query ?includeSchema=true&includeMetadata=true arricchisce la risposta con lo schema Zod e metadati di provenienza (source: env, default, override) |
PUT /config | Aggiorna a caldo la configurazione runtime con merge parziale (partial=true) o sostituzione completa. Protetto da X-Admin-Key validato contro CONFIG_ADMIN_KEY: in assenza della env il servizio risponde 503 per disabilitare l'endpoint in deploy production-only |
POST /config/validate | Valida un payload di configurazione senza applicarlo (utile in pipeline CI per validare ConfigMap di k8s prima del rollout) |
GET /config/schema | Restituisce lo schema di validazione completo della configurazione, consumato dal frontend vue-renderer per costruire la UI di editing |
GET / | Banner di servizio ({ message, service, version, timestamp }) — utile come probe iniziale e come riferimento per il frontend di admin |
GET /health-fast | Health check ultraleggero (non interroga DB/orchestrator): risponde 200 finché il process Node è vivo. Pensato per liveness frequente |
GET /status | Restituisce la stringa OK su 200 — formato richiesto da alcuni load balancer/uptime checker che non parsano JSON |
GET /metrics-basic | Restituisce uptime e memoryUsage.{rss, heapUsed, heapTotal} in MB (due decimali) in JSON. Consumato dalla dashboard vue-renderer, complementare a /metrics Prometheus |
I quattro endpoint infrastructure (/, /health-fast, /status, /metrics-basic) sono i nuovi path esposti dalla v1.0.14: in src/presentation/routes/MonitoringRoutes.ts sono registrati nel tier rate-limit infrastructure, mentre la Configuration API resta nel tier template-management con il filtro admin-key applicato come hook onRequest.
Note eventing NATS
Il servizio non pubblica e non si sottoscrive direttamente a subject NATS applicativi: lo scanner statico riporta zero publish/subscribe espliciti. La comunicazione asincrona avviene tramite la SDK @pzeta/orchestrator-node, che incapsula la registrazione al hub e la consegna delle reply ai task: il renderer riceve invocazioni dei task render-template, render-batch, validate-template, get-template-info, cache-warm su subject derivati dal manifest e risponde request/reply. Tutta la logica di retry, dispatch e timeline rimane in node-orchestrator.
Quando ORCHESTRATOR_ENABLED=false (default in sviluppo) il servizio espone solo l'API REST sincrona, utile per chiamate dirette da node-print e dal frontend Vue.
Dipendenze e dipendenti
Dipende da (servizi che questo servizio chiama):
Consumato da (chi chiama questo servizio):
node-printfrontend Vue
Infrastruttura (PostgreSQL, NATS, Redis, MinIO) non è elencata qui — vedi sezione Architettura del singolo servizio.