++ add reset program
This commit is contained in:
58
HANDOFF.md
58
HANDOFF.md
@@ -248,7 +248,7 @@ Tre ritocchi minori ma con qualche implicazione:
|
||||
`permission:settings.manage` e il link in sidebar è sotto
|
||||
`@can('settings.manage')`.
|
||||
|
||||
### Backup completo `.zip`: dati + file, e cambio password forzato al restore
|
||||
### Backup completo `.zip`, ripristino, azzeramento
|
||||
|
||||
L'export XML da solo non ricostruisce un'installazione: `pdf_path`,
|
||||
`thumbnail_path` e `logo_path` sono percorsi sul disco `public`, quindi un
|
||||
@@ -263,11 +263,65 @@ Scelte non ovvie:
|
||||
|
||||
- **Rotte HTTP, non Livewire.** Lo `streamDownload` di Livewire passa il file
|
||||
base64 dentro la risposta JSON: un archivio con i PDF dei territori non ci
|
||||
sta. `backup/download` e `backup/restore` sono due rotte normali nel gruppo
|
||||
sta. `backup/file/{op}` e `backup/restore` sono due rotte normali nel gruppo
|
||||
`settings.manage`. Per lo stesso motivo `ZipArchive::addFile()` (che legge
|
||||
dal disco in `close()`) e gli stream in estrazione: nessun PDF passa intero
|
||||
per la memoria del processo.
|
||||
|
||||
- **Tutto in coda, con avanzamento reale.** Backup, ripristino e azzeramento
|
||||
sono job (`Jobs/CreateBackupArchive`, `RestoreBackupArchive`,
|
||||
`ResetApplication`) che scrivono su `Services/BackupOperationState`, e
|
||||
`Livewire/Settings/BackupManager` lo interroga con `wire:poll`. In coda
|
||||
perché un'installazione con qualche centinaio di PDF supera i 300 secondi di
|
||||
`fastcgi_read_timeout` e il browser vedrebbe cadere la connessione a metà
|
||||
compressione, senza modo di distinguerlo da un errore.
|
||||
|
||||
La barra conta unità di lavoro completate, non tempo. Per la compressione
|
||||
questo costa qualcosa: `ZipArchive` non scrive niente finché non si chiama
|
||||
`close()`, quindi contare le `addFile()` porterebbe al 100% in un istante per
|
||||
poi restare fermo per tutta la scrittura. `BackupArchive::create()` chiude e
|
||||
riapre l'archivio a lotti (`FLUSH_MASSIMI = 20`): si paga una ricopiatura
|
||||
delle entry già presenti — byte a byte, senza ricomprimerle — e in cambio
|
||||
ogni avanzamento corrisponde a byte davvero finiti sul disco. Il lotto è
|
||||
`totale/20`, così la ricopiatura resta limitata a ~10 volte la dimensione
|
||||
dell'archivio quanti che siano i file. Estrazione e riscrittura dei file sono
|
||||
cicli nostri, lì l'avanzamento è per-file senza artifici.
|
||||
|
||||
- **L'azzeramento ha due tempi, separati dal download.** Il pulsante nella zona
|
||||
pericolosa (`avviaAzzeramento`) crea *solo* il backup e si ferma in
|
||||
`attesa-download`; la cancellazione parte da un secondo clic esplicito
|
||||
(`confermaAzzeramento`), che rifiuta se lo stato non registra un
|
||||
`servito_at`. Il primo tentativo faceva partire il reset dalla coda dello
|
||||
`streamDownload`, "dopo l'ultimo byte" — **non funziona**: con
|
||||
`fastcgi_buffering on` (il default, e `docker/nginx/default.conf` non lo
|
||||
disattiva) nginx accetta l'intera risposta dal processo PHP, che quindi
|
||||
prosegue anche se il browser ha chiuso la connessione al primo byte. Un
|
||||
download annullato azzerava lo stesso. Il server può verificare di aver
|
||||
*spedito* l'archivio, non che sia arrivato: quella verifica è dell'operatore,
|
||||
e per questo la conferma è umana.
|
||||
|
||||
- **Nell'azzeramento i file si cancellano prima del database.** Svuotare il
|
||||
disco è l'unico passo che può fallire per cause esterne (permessi sul
|
||||
volume), e fallire lì non ha ancora toccato niente. Nell'ordine inverso un
|
||||
errore sui file lascerebbe database azzerato e disco pieno di PDF orfani.
|
||||
Collegato: `svuotaDiscoPubblico()` ricontrolla il disco dopo la cancellazione
|
||||
e solleva se qualcosa sopravvive, invece di restituire il conteggio iniziale.
|
||||
Flysystem non solleva se una `unlink` fallisce, e basta un file scritto da un
|
||||
processo con un altro utente (l'entrypoint lavora da root, php-fpm e il
|
||||
worker da www-data) perché l'operazione si dichiari riuscita lasciando il
|
||||
disco popolato.
|
||||
|
||||
- **L'azzeramento tiene in vita l'amministratore che l'ha chiesto.** Un'installazione
|
||||
appena creata ha un amministratore; cancellarli tutti lascerebbe
|
||||
l'applicazione senza nessuno che possa accedere fino al riavvio del container
|
||||
(l'admin iniziale lo ricrea `entrypoint.sh` da `INITIAL_ADMIN_*`, che in
|
||||
esecuzione possono non esserci). Gli altri utenti si eliminano uno alla volta
|
||||
via Eloquent, non con un delete di massa: è il trait `HasRoles` di Spatie a
|
||||
staccare ruoli e permessi nell'evento `deleting`, che un delete di massa non
|
||||
fa scattare. Il registro attività si svuota **per ultimo** — quelle
|
||||
cancellazioni scrivono righe nuove — e subito dopo si scrive l'unica riga che
|
||||
sopravvive, quella dell'azzeramento stesso.
|
||||
|
||||
- **Ordine del restore: staging → database → file.** I file si estraggono
|
||||
prima in `storage/app/backup-restore/<uuid>`, così un archivio corrotto o
|
||||
fuori limite fallisce mentre i dati correnti sono ancora intatti; poi
|
||||
|
||||
Reference in New Issue
Block a user