feat(offline): la policy arriva dove le pagine si aprono davvero

Punto 12. Le cinque librerie erano già state portate in casa (28ee44b): nessun
template carica più niente dalla rete, il worker di PDF.js è locale in tutti e
quattro i file che lo impostano, i font sono woff2 nel pacchetto. Verificato riga
per riga, e le impronte SHA-256 in VERSIONS.md corrispondono ancora.

Mancava però la seconda metà dell'intervento, e mancava dove conta. La
Content-Security-Policy a sola origine locale esisteva sul backend, cioè sulle
risposte API; le pagine HTML le serve il client Flask, che non mandava alcuna
policy. La regola stava scritta dove non poteva essere infranta e assente dove
poteva. Ora il client la manda su ogni risposta.

Serve meno a difendere e più a non far tornare indietro il punto: un tag verso un
CDN aggiunto fra sei mesi viene rifiutato dal browser alla scrivania, dove c'è la
rete e l'errore si legge in console, invece che in reparto dove la rete non c'è.

Tailwind era ancora agganciato a `tailwindcss@3` nel Dockerfile: stesso difetto
che il documento cita per le librerie del browser, un gradino più in basso. Fissato
a 3.4.19, che è la versione con cui l'immagine in esercizio è stata costruita.

I test non renderizzano niente: leggono i sorgenti, perché il difetto che devono
impedire si scrive in un template e non si vede finché non si stacca la rete.
Controllano anche le impronte — una libreria sostituita sul posto tiene lo stesso
nome e la stessa riga in tabella, e l'hash è l'unica parte che se ne accorge.

Annotato in VERSIONS.md che `html5-qrcode` è l'unica libreria dichiarata e mai
caricata: lo scanner da fotocamera non è incluso da nessuna pagina, il lettore che
l'operatore usa è un campo di testo. Va portata in casa prima di accenderlo.

README aggiornato a V3.0.0: novità della versione punto per punto, ruolo
Supervisor, librerie locali al posto dei CDN, stato dei test.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Adriano Dal Pastro
2026-07-28 21:58:13 +00:00
parent 78816dfe8c
commit c4a429d952
5 changed files with 335 additions and 15 deletions
+43 -1
View File
@@ -64,7 +64,49 @@ db5ff4db83e580426280e9337a58dc57d3a83784a1b03ad80914651594441d52 fonts/jetbrain
```
Verifica: `sha256sum -c` da questa cartella, oppure
`sha256sum *.js fonts/*.woff2`.
`sha256sum *.js fonts/*.woff2`. Il confronto è anche un test
(`tests/test_offline.py::test_the_shipped_libraries_are_the_ones_that_were_checked`):
una libreria sostituita sul posto tiene lo stesso nome e la stessa riga in tabella,
e l'impronta è l'unica parte che se ne accorge.
## La regola che tiene il punto chiuso
Le copie locali risolvono il problema una volta; quello che lo tiene risolto è la
**Content-Security-Policy** servita dal client Flask (`app.py`, `CSP`), che ammette
solo l'origine locale. Prima esisteva solo sul backend — cioè sulle risposte API,
non sulle pagine che il browser disegna: la regola stava scritta dove non poteva
essere infranta e mancava dove poteva.
Con la policy in vigore, un `<script src="https://cdn…">` aggiunto fra sei mesi viene
rifiutato dal browser **alla scrivania**, dove c'è la rete e l'errore si legge in
console, invece che in reparto dove la rete non c'è.
## Tailwind
Tailwind non sta in questa cartella: è compilato in fase di build dell'immagine
(`Dockerfile.frontend`) e il CSS finito viene servito da `static/css/tailwind.css`.
Vale però lo stesso vincolo — la versione è fissata a `tailwindcss@3.4.19`. Con
`tailwindcss@3` ogni ricostruzione poteva prendere una minor diversa e produrre un
foglio di stile diverso senza che nessuno l'avesse validato.
Nota per l'installazione on-premise: la **costruzione** dell'immagine richiede rete
(npm, apt, uv). È l'esecuzione a non richiederla. In reparto va portata l'immagine
già costruita, non il repository da compilare sul posto.
## Una sesta libreria, oggi non caricata
`static/js/barcode.js` e `templates/components/barcode_scanner.html` implementano lo
scanner QR **con la fotocamera** e si appoggiano a `html5-qrcode`, che non è mai
caricata da nessuna pagina: il componente non è incluso da nessuna parte e la
libreria non è in questa cartella. A runtime non rompe niente — è codice morto.
La lettura del barcode che l'operatore usa davvero è un campo di testo in
`measure/select_recipe.html`: il lettore hardware la digita, e non serve nessuna
libreria.
Se un giorno si accende lo scanner da fotocamera, `html5-qrcode` va portata qui
**prima**, altrimenti in reparto quel pulsante mostra «scanner_lib_not_loaded» e
basta.
## Aggiornare una libreria