node-renderer

Rendering server-side di template documenti.
document-output
/rendererauth: nginx

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 per templateId registrato o templateContent inline 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/vcl con endpoint services.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) e supporto al hot-reload
  • 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
  • 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 task render-template, render-batch (rendering parallelo con concurrency configurabile), validate-template, get-template-info, cache-warm
  • Rate limiting granulare su quattro 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/):

LayerContenuto
domain/Interfacce (ITemplateService, ITemplateCompiler, IDataService, IRenderingStrategy, IRenderingPipelineExtension), errori applicativi (RenderingError, TemplateNotFoundError, TemplateValidationError, DataFetchError)
application/TemplateCompilerService, TemplateCommandService, TemplateManagementService, TemplateSyntaxAnalyzer, RenderContextBuilder, HelperManager, ConfigurationService
infrastructure/HandlebarsCompiler, provider template (DbTemplateProvider, MemoryTemplateProvider, FileTemplateProvider, VcsTemplateProvider), provider dati (StaticDataProvider, UrlDataProvider), pipeline extensions (AsyncHelpersPipelineExtension, LoggingPipelineExtension), OrchestratorBootstrap
presentation/Controllers (PostRenderController, GetRenderController, CRUD template, helpers, configuration), route plugin (RenderRoutes, TemplateManagementRoutes, ConfigurationRoutes, MonitoringRoutes), 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-print riceve la richiesta di stampa documento dal frontend, chiama POST /render con templateId="fattura-vendita" e i dati della testata/righe, ottiene l'HTML sorgente e lo converte in PDF prima di archiviarlo su node-storage e inviarlo al device
  • Email transazionale: node-notification invoca il task orchestrator render-template per 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-orchestrator distribuisce N documenti (es. mailing massivo, contratti per gruppo di clienti) sul task render-batch con parallel=true e maxConcurrency=5 per il rendering parallelo, poi inoltra ogni output al servizio downstream
  • Etichette e ricevute: per i flussi di lavanderia industriale, node-print chiede al renderer una stringa ZPL/EPL parametrica (template testuale, non HTML) da inviare alla stampante termica di etichettatura capi

Identità & esposizione

CampoValore
Categoriadocument-output
Versione cluster1.0.14
Imagegitea.pzetatouch.it/pzeta_touch/node-renderer:1.0.13
URL pubblicohttps://ditta.pzeta.it/renderer
Path regex ingress`/renderer(/
Rewrite a backend/$2
DNS internonode-renderer-ditta.ditta.svc.cluster.local:3000
Auth nginxauth_requestnode-user-auth
Repositorynode-renderer
Endpoint REST12 (vedi sezione "API reference")

Endpoint operazionali

Endpoint convenzionali esposti da tutti i microservizi PZeta basati su @pzeta/fastify-utils:

Path pubblicoScopo
https://ditta.pzeta.it/renderer/healthliveness probe
https://ditta.pzeta.it/renderer/readyreadiness probe
https://ditta.pzeta.it/renderer/metricsmetriche Prometheus
https://ditta.pzeta.it/renderer/api-docs.jsonspec OpenAPI runtime (richiede OPENAPI_EXPOSE_IN_PRODUCTION=true)
https://ditta.pzeta.it/renderer/api-docsSwagger UI (solo in NODE_ENV !== production)

Configurazione

Variabili d'ambiente che un integratore deve conoscere (per la lista completa vedi .env.example del repo):

VariabileRuolo
TEMPLATE_PROVIDERSLista CSV dei provider attivi: file, database, vcs, memory. L'ordine determina la priorità di lookup (chain of responsibility)
TEMPLATE_FILE_PATH / TEMPLATE_FILE_EXTCartella radice e estensione (default .hbs) per il provider file
DATABASE_HOST / _PORT / _USER / _PASSWORD / _NAMEConnessione PostgreSQL per il provider database — alternativa: DATABASE_URL
DATABASE_TABLE / DATABASE_ID_COL / DATABASE_CONTENT_COLTabella e colonne (default templates / id / content) — i nomi sono validati per evitare SQL injection
VERSION_CONTROL_API_URL / _CATEGORYEndpoint del repository Git remoto per il provider vcs (default https://services.pzeta.it/git, categoria template)
DATA_PROVIDERSSorgenti dati abilitate per il render (static, url)
ORCHESTRATOR_ENABLED / ORCHESTRATOR_URL / ORCHESTRATOR_API_KEYRegistrazione del modulo presso node-orchestrator per ricevere task
CACHE_ENABLED / CACHE_TTLCache di template e compilazioni (TTL in secondi, max 86400)
RATE_LIMIT_*_MAX / _WINDOWLimiti per bucket (global, infrastructure, template-mgmt, rendering, monitoring)
ALLOWED_DOWNLOAD_DOMAINS / TRUSTED_DATA_DOMAINSWhitelist di domini per fetch dati esterni e propagazione token Bearer
HTML_CSP_POLICY / ALLOW_NO_ESCAPEHardening 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.

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):

Infrastruttura (PostgreSQL, NATS, Redis, MinIO) non è elencata qui — vedi sezione Architettura del singolo servizio.

Loading OpenAPI…
Loading NATS contracts…