++ fix restore backup
This commit is contained in:
16
.env.example
16
.env.example
@@ -81,6 +81,22 @@ CACHE_STORE=redis
|
|||||||
SESSION_DRIVER=redis
|
SESSION_DRIVER=redis
|
||||||
QUEUE_CONNECTION=redis
|
QUEUE_CONNECTION=redis
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# Dimensione massima dei file caricati (MB)
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# Governa insieme nginx (client_max_body_size), PHP (upload_max_filesize e
|
||||||
|
# post_max_size) e la validazione dell'applicazione: li genera l'entrypoint da
|
||||||
|
# questo unico valore, cosi' non possono andare fuori sincrono.
|
||||||
|
#
|
||||||
|
# Il file piu' grande che passa di qui e' il pacchetto di ripristino, che
|
||||||
|
# contiene tutti i PDF dei territori: con qualche centinaio di mappe si arriva
|
||||||
|
# facilmente a oltre un giga. Se un caricamento muore senza spiegazioni e la
|
||||||
|
# barra si pianta, e' quasi sempre questo il limite che si e' incontrato.
|
||||||
|
#
|
||||||
|
# Alzarlo non costa memoria: nginx accumula il corpo su disco e PHP lavora su un
|
||||||
|
# file temporaneo. Serve pero' spazio libero nel volume.
|
||||||
|
MAX_UPLOAD_MB=2048
|
||||||
|
|
||||||
# DEVE essere false se accedi in HTTP (tipicamente per IP:porta).
|
# DEVE essere false se accedi in HTTP (tipicamente per IP:porta).
|
||||||
#
|
#
|
||||||
# Con true, il cookie di sessione viene marcato "secure" e il browser non lo
|
# Con true, il cookie di sessione viene marcato "secure" e il browser non lo
|
||||||
|
|||||||
25
HANDOFF.md
25
HANDOFF.md
@@ -353,6 +353,31 @@ Scelte non ovvie:
|
|||||||
già vuota. Con il `pull` l'amministratore non vedeva **mai** il riepilogo del
|
già vuota. Con il `pull` l'amministratore non vedeva **mai** il riepilogo del
|
||||||
proprio ripristino.
|
proprio ripristino.
|
||||||
|
|
||||||
|
- **Il tetto degli upload è una variabile sola, `MAX_UPLOAD_MB`.** Prima viveva
|
||||||
|
in tre posti scollegati — `client_max_body_size` in
|
||||||
|
`docker/nginx/default.conf`, `upload_max_filesize`/`post_max_size` in
|
||||||
|
`docker/php/php.ini`, e la regola `max:` nella validazione — e bastava che
|
||||||
|
uno restasse indietro perché il più piccolo vincesse nel modo peggiore.
|
||||||
|
Adesso `entrypoint.sh` genera i primi due (`conf.d/upload-limit.conf` e
|
||||||
|
`conf.d/zz-upload-limit.ini`) e `config/app.php` alimenta la terza.
|
||||||
|
|
||||||
|
**Non rimettere `client_max_body_size` nel blocco `server` di
|
||||||
|
`default.conf`**: una direttiva lì dentro vince su quella generata a livello
|
||||||
|
http e reintroduce in silenzio proprio il disallineamento che questa
|
||||||
|
disposizione esiste per evitare. La validazione tiene un 2% di margine sotto
|
||||||
|
il valore di nginx, così a fermare un pacchetto fuori misura è Laravel, che
|
||||||
|
sa spiegarsi, e non nginx.
|
||||||
|
|
||||||
|
Il sintomo, quando il limite viene superato, non assomiglia affatto a un
|
||||||
|
errore di dimensione: nginx risponde **413 dopo aver accettato zero byte** e
|
||||||
|
chiude. Il browser ha appena iniziato a spedire, la barra si pianta a una
|
||||||
|
percentuale bassa e nessun evento utile arriva all'`XMLHttpRequest` — pagina
|
||||||
|
muta, con l'aria di essersi bloccata. Da qui i tre presidi nella vista:
|
||||||
|
controllo della dimensione *prima* di caricare, `xhr.timeout` con i relativi
|
||||||
|
gestori di `timeout`/`abort`/`error` (senza, un caricamento fermo non produce
|
||||||
|
più nulla per sempre), e il codice HTTP dentro il messaggio, perché 413 e 419
|
||||||
|
non hanno un corpo JSON da cui ricavare una spiegazione.
|
||||||
|
|
||||||
- **Nell'azzeramento i file si cancellano prima del database.** Svuotare il
|
- **Nell'azzeramento i file si cancellano prima del database.** Svuotare il
|
||||||
disco è l'unico passo che può fallire per cause esterne (permessi sul
|
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
|
volume), e fallire lì non ha ancora toccato niente. Nell'ordine inverso un
|
||||||
|
|||||||
21
README.md
21
README.md
@@ -330,6 +330,7 @@ ridichiara le variabili applicative: le prende dal `.env` così com'è.
|
|||||||
| `APP_TIMEZONE` | `Europe/Rome` | Fuso orario |
|
| `APP_TIMEZONE` | `Europe/Rome` | Fuso orario |
|
||||||
| `LOG_LEVEL` | `warning` | `debug` per diagnosticare |
|
| `LOG_LEVEL` | `warning` | `debug` per diagnosticare |
|
||||||
| `SESSION_SECURE_COOKIE` | `false` | `true` solo servendo in HTTPS |
|
| `SESSION_SECURE_COOKIE` | `false` | `true` solo servendo in HTTPS |
|
||||||
|
| `MAX_UPLOAD_MB` | `2048` | Tetto dei file caricati: governa insieme nginx, PHP e la validazione |
|
||||||
| `APP_BIND` | `0.0.0.0` | `127.0.0.1` dietro un reverse proxy |
|
| `APP_BIND` | `0.0.0.0` | `127.0.0.1` dietro un reverse proxy |
|
||||||
| `APP_PORT` | `8080` | Porta dell'applicazione sull'host |
|
| `APP_PORT` | `8080` | Porta dell'applicazione sull'host |
|
||||||
| `DB_BIND` | `127.0.0.1` | Interfaccia della porta MariaDB |
|
| `DB_BIND` | `127.0.0.1` | Interfaccia della porta MariaDB |
|
||||||
@@ -422,9 +423,23 @@ Il pacchetto **non** contiene `APP_KEY`: i nomi dei proclamatori sono cifrati
|
|||||||
con quella chiave, quindi il ripristino va fatto su un'installazione che ha la
|
con quella chiave, quindi il ripristino va fatto su un'installazione che ha la
|
||||||
**stessa** `APP_KEY` di quando il backup è stato creato.
|
**stessa** `APP_KEY` di quando il backup è stato creato.
|
||||||
|
|
||||||
Il tetto per l'upload è **256 MB** (`client_max_body_size` in
|
Il tetto per l'upload lo decide **`MAX_UPLOAD_MB`** nel `.env` (default: 2048,
|
||||||
`docker/nginx/default.conf` e `upload_max_filesize`/`post_max_size` in
|
cioè 2 GB). Da quel valore l'entrypoint genera `client_max_body_size` per nginx
|
||||||
`docker/php/php.ini`). Oltre quella soglia usa il backup dei volumi qui sotto.
|
e `upload_max_filesize`/`post_max_size` per PHP, e la validazione lo rilegge:
|
||||||
|
un solo numero per tutta la catena.
|
||||||
|
|
||||||
|
> Se un caricamento si pianta a una percentuale bassa **senza messaggi**, è
|
||||||
|
> quasi sempre questo il limite. Superato il tetto nginx risponde `413` dopo
|
||||||
|
> aver accettato zero byte: il browser si vede chiudere la connessione a metà
|
||||||
|
> invio e la barra resta ferma. Dalla versione attuale la pagina controlla la
|
||||||
|
> dimensione *prima* di iniziare e lo dice subito, ma se aggiorni un'installazione
|
||||||
|
> vecchia il sintomo è quello.
|
||||||
|
|
||||||
|
Per alzarlo servono un `MAX_UPLOAD_MB` più grande **e** un redeploy (i limiti si
|
||||||
|
scrivono all'avvio del container). Alzarlo non costa memoria — nginx accumula il
|
||||||
|
corpo su disco e PHP lavora su un file temporaneo — ma serve spazio libero nel
|
||||||
|
volume. Oltre qualche giga conviene comunque il backup dei volumi qui sotto: un
|
||||||
|
upload lunghissimo da browser è fragile.
|
||||||
|
|
||||||
### Zona pericolosa: azzera tutto
|
### Zona pericolosa: azzera tutto
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,32 @@ use Illuminate\Support\Str;
|
|||||||
*/
|
*/
|
||||||
class BackupController extends Controller
|
class BackupController extends Controller
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* Tetto per il pacchetto in ingresso, in kilobyte.
|
||||||
|
*
|
||||||
|
* Deriva da `app.max_upload_mb`, da cui l'entrypoint ricava anche i limiti
|
||||||
|
* di nginx e di PHP. Il valore qui sta *sotto* quelli di un margine: se a
|
||||||
|
* fermare la richiesta e' nginx, il browser riceve un 413 senza corpo — o
|
||||||
|
* niente affatto — invece di un messaggio; se la ferma la validazione, chi
|
||||||
|
* carica legge cosa e' successo. Il margine copre anche l'intestazione
|
||||||
|
* multipart, che gonfia il corpo oltre la dimensione del file.
|
||||||
|
*
|
||||||
|
* La vista lo legge da qui per rifiutare il file prima ancora di iniziare a
|
||||||
|
* caricarlo: una sola fonte per un solo numero.
|
||||||
|
*/
|
||||||
|
public static function maxUploadKb(): int
|
||||||
|
{
|
||||||
|
$mb = max(1, (int) config('app.max_upload_mb', 2048));
|
||||||
|
|
||||||
|
// 2% di margine, minimo 1 MB, per l'incapsulamento multipart.
|
||||||
|
return (int) (($mb - max(1, (int) ($mb * 0.02))) * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function maxUploadMb(): int
|
||||||
|
{
|
||||||
|
return (int) (self::maxUploadKb() / 1000);
|
||||||
|
}
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private readonly BackupOperationState $stato,
|
private readonly BackupOperationState $stato,
|
||||||
) {
|
) {
|
||||||
@@ -76,16 +102,12 @@ class BackupController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function upload(Request $request)
|
public function upload(Request $request)
|
||||||
{
|
{
|
||||||
// 256 MB e' il tetto reale della pila, non una scelta di questa
|
|
||||||
// validazione: piu' in su intervengono prima `client_max_body_size` di
|
|
||||||
// nginx e `upload_max_filesize`/`post_max_size` di PHP, che rifiutano
|
|
||||||
// la richiesta con un 413 opaco invece di un messaggio leggibile.
|
|
||||||
$request->validate([
|
$request->validate([
|
||||||
'backupZip' => ['required', 'file', 'mimes:zip', 'max:256000'],
|
'backupZip' => ['required', 'file', 'mimes:zip', 'max:' . self::maxUploadKb()],
|
||||||
], [
|
], [
|
||||||
'backupZip.required' => 'Seleziona il pacchetto di backup da ripristinare.',
|
'backupZip.required' => 'Seleziona il pacchetto di backup da ripristinare.',
|
||||||
'backupZip.mimes' => 'Il pacchetto di backup deve essere un file .zip.',
|
'backupZip.mimes' => 'Il pacchetto di backup deve essere un file .zip.',
|
||||||
'backupZip.max' => 'Il pacchetto supera i 256 MB accettati dal server.',
|
'backupZip.max' => 'Il pacchetto supera i ' . self::maxUploadMb() . ' MB accettati dal server.',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Stesso blocco del componente Livewire, ripetuto qui perche' questa e'
|
// Stesso blocco del componente Livewire, ripetuto qui perche' questa e'
|
||||||
|
|||||||
@@ -21,4 +21,21 @@ return [
|
|||||||
'driver' => 'file',
|
'driver' => 'file',
|
||||||
],
|
],
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Dimensione massima, in MB, di un file caricato dall'interfaccia —
|
||||||
|
* in pratica il pacchetto di ripristino, che e' di gran lunga il piu'
|
||||||
|
* grande.
|
||||||
|
*
|
||||||
|
* E' l'unica fonte del numero: l'entrypoint ne ricava
|
||||||
|
* `client_max_body_size` per nginx e `upload_max_filesize`/`post_max_size`
|
||||||
|
* per PHP, e la validazione di BackupController lo rilegge da qui. Prima
|
||||||
|
* viveva in tre file separati, e bastava che uno restasse indietro perche'
|
||||||
|
* il caricamento morisse con un 413 muto invece di un messaggio.
|
||||||
|
*
|
||||||
|
* Alzarlo non costa memoria: nginx accumula il corpo su disco e PHP lavora
|
||||||
|
* su un file temporaneo. Il limite pratico e' la pazienza di chi carica e
|
||||||
|
* la tenuta della connessione.
|
||||||
|
*/
|
||||||
|
'max_upload_mb' => (int) env('MAX_UPLOAD_MB', 2048),
|
||||||
|
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -6,9 +6,15 @@ server {
|
|||||||
|
|
||||||
charset utf-8;
|
charset utf-8;
|
||||||
|
|
||||||
# Territory PDF/ZIP imports are the largest uploads; keep this in step with
|
# NOTE: client_max_body_size is deliberately NOT set here.
|
||||||
# upload_max_filesize / post_max_size in docker/php/php.ini.
|
#
|
||||||
client_max_body_size 256M;
|
# entrypoint.sh writes it to /etc/nginx/conf.d/upload-limit.conf from
|
||||||
|
# MAX_UPLOAD_MB, together with PHP's upload_max_filesize/post_max_size, so
|
||||||
|
# that one variable governs the whole chain. A value in this server block
|
||||||
|
# would override the generated one and silently reinstate the mismatch this
|
||||||
|
# arrangement exists to prevent — the symptom being a 413 with no body,
|
||||||
|
# returned after zero bytes, which leaves the browser's upload bar frozen
|
||||||
|
# and the page with nothing to report.
|
||||||
|
|
||||||
# Behind Dokploy's Traefik, the real client IP arrives in X-Forwarded-For.
|
# Behind Dokploy's Traefik, the real client IP arrives in X-Forwarded-For.
|
||||||
real_ip_header X-Forwarded-For;
|
real_ip_header X-Forwarded-For;
|
||||||
|
|||||||
@@ -118,6 +118,28 @@ map \$host \$app_forwarded_proto {
|
|||||||
EOF
|
EOF
|
||||||
ok "Forwarded scheme set to ${FORWARDED_PROTO}."
|
ok "Forwarded scheme set to ${FORWARDED_PROTO}."
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# 2c. Upload ceiling, from a single variable.
|
||||||
|
# nginx, PHP and the Laravel validation rule must agree: when they don't,
|
||||||
|
# the smallest one wins and does so badly — nginx answers 413 with no body
|
||||||
|
# after accepting zero bytes, so the browser's upload bar simply freezes and
|
||||||
|
# the page has nothing to report. MAX_UPLOAD_MB drives all three
|
||||||
|
# (config/app.php reads the same variable for the validation rule).
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
MAX_UPLOAD_MB="${MAX_UPLOAD_MB:-2048}"
|
||||||
|
|
||||||
|
cat > /etc/nginx/conf.d/upload-limit.conf <<EOF
|
||||||
|
client_max_body_size ${MAX_UPLOAD_MB}m;
|
||||||
|
EOF
|
||||||
|
|
||||||
|
cat > /usr/local/etc/php/conf.d/zz-upload-limit.ini <<EOF
|
||||||
|
; Generated by entrypoint.sh from MAX_UPLOAD_MB — do not edit by hand.
|
||||||
|
; The zz- prefix makes it load after custom.ini, so these win.
|
||||||
|
upload_max_filesize = ${MAX_UPLOAD_MB}M
|
||||||
|
post_max_size = ${MAX_UPLOAD_MB}M
|
||||||
|
EOF
|
||||||
|
ok "Upload ceiling set to ${MAX_UPLOAD_MB} MB."
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
# 3. Storage symlink (public/storage -> storage/app/public)
|
# 3. Storage symlink (public/storage -> storage/app/public)
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
[PHP]
|
[PHP]
|
||||||
upload_max_filesize = 256M
|
; upload_max_filesize e post_max_size NON stanno qui: li genera entrypoint.sh
|
||||||
post_max_size = 256M
|
; in conf.d/zz-upload-limit.ini a partire da MAX_UPLOAD_MB, insieme al
|
||||||
|
; client_max_body_size di nginx, cosi' una sola variabile governa tutta la
|
||||||
|
; catena. Rimetterli qui non farebbe danno (zz- viene caricato dopo e vince),
|
||||||
|
; ma sarebbe una seconda fonte per lo stesso numero.
|
||||||
memory_limit = 256M
|
memory_limit = 256M
|
||||||
max_execution_time = 120
|
max_execution_time = 120
|
||||||
max_input_vars = 3000
|
max_input_vars = 3000
|
||||||
|
|||||||
@@ -41,11 +41,42 @@
|
|||||||
// Svuotato subito: se la password è sbagliata si ridigita, e nel
|
// Svuotato subito: se la password è sbagliata si ridigita, e nel
|
||||||
// frattempo non resta nel campo di una scheda lasciata aperta.
|
// frattempo non resta nel campo di una scheda lasciata aperta.
|
||||||
this.passwordAzzeramento = '';
|
this.passwordAzzeramento = '';
|
||||||
$wire.confermaAzzeramento(password);
|
// this.$wire e non $wire: dentro un metodo di x-data la forma
|
||||||
|
// esplicita e' l'unica su cui si possa contare.
|
||||||
|
this.$wire.confermaAzzeramento(password);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Il file e' troppo grande? Si scopre qui, prima di caricare.
|
||||||
|
*
|
||||||
|
* Superato il tetto, e' nginx a chiudere la connessione a meta'
|
||||||
|
* trasferimento: la barra si pianta a una percentuale qualsiasi e non
|
||||||
|
* arriva nessun evento utile, quindi la pagina resta muta con l'aria di
|
||||||
|
* essersi bloccata. Controllare la dimensione prima di partire rende
|
||||||
|
* quel caso un messaggio immediato invece di un'attesa senza fine.
|
||||||
|
*/
|
||||||
|
zipTroppoGrande(file) {
|
||||||
|
const maxByte = {{ \App\Http\Controllers\Settings\BackupController::maxUploadKb() }} * 1000;
|
||||||
|
|
||||||
|
if (!file || file.size <= maxByte) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const mb = (n) => (n / 1000000).toFixed(0);
|
||||||
|
this.erroreUpload = `Il pacchetto pesa ${mb(file.size)} MB e supera il limite di ${mb(maxByte)} MB accettato dal server. `
|
||||||
|
+ `Per archivi piu' grandi usa il ripristino dai volumi descritto nel README, oppure chiedi di alzare il limite.`;
|
||||||
|
|
||||||
|
return true;
|
||||||
},
|
},
|
||||||
|
|
||||||
inviaRipristino(event) {
|
inviaRipristino(event) {
|
||||||
const form = event.target;
|
const form = event.target;
|
||||||
|
const file = form.querySelector('input[name=backupZip]').files[0];
|
||||||
|
|
||||||
|
if (this.zipTroppoGrande(file)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const formData = new FormData(form);
|
const formData = new FormData(form);
|
||||||
|
|
||||||
this.erroreUpload = '';
|
this.erroreUpload = '';
|
||||||
@@ -57,6 +88,11 @@
|
|||||||
xhr.setRequestHeader('Accept', 'application/json');
|
xhr.setRequestHeader('Accept', 'application/json');
|
||||||
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
|
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
|
||||||
|
|
||||||
|
// Un caricamento che non si muove per cinque minuti e' fermo, non
|
||||||
|
// lento: senza questo non scatterebbe piu' nessun evento e la
|
||||||
|
// pagina resterebbe in attesa per sempre.
|
||||||
|
xhr.timeout = 300000;
|
||||||
|
|
||||||
xhr.upload.addEventListener('progress', (e) => {
|
xhr.upload.addEventListener('progress', (e) => {
|
||||||
if (e.lengthComputable) {
|
if (e.lengthComputable) {
|
||||||
this.uploadPercento = Math.round((e.loaded / e.total) * 100);
|
this.uploadPercento = Math.round((e.loaded / e.total) * 100);
|
||||||
@@ -73,25 +109,64 @@
|
|||||||
this.zipSelezionato = '';
|
this.zipSelezionato = '';
|
||||||
form.reset();
|
form.reset();
|
||||||
// Da qui in poi l'avanzamento arriva dal job, non dall'upload.
|
// Da qui in poi l'avanzamento arriva dal job, non dall'upload.
|
||||||
$wire.seguiOperazione(payload.operation_id);
|
this.$wire.seguiOperazione(payload.operation_id);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 409: c'è già un'operazione in corso. Il pacchetto è stato
|
// Qualsiasi esito diverso: la barra torna a zero, perche'
|
||||||
// caricato per niente, quindi la barra torna a zero: lasciarla
|
// lasciarla dov'era farebbe credere che il ripristino sia
|
||||||
// al 100% farebbe credere che il ripristino sia partito.
|
// partito. Il codice HTTP entra nel messaggio: 413 e 419 non
|
||||||
|
// producono un corpo JSON, quindi senza di esso resterebbe solo
|
||||||
|
// un generico "errore" che non dice cosa fare.
|
||||||
this.uploadPercento = 0;
|
this.uploadPercento = 0;
|
||||||
this.erroreUpload = payload.message
|
this.erroreUpload = this.messaggioErrore(xhr.status, payload);
|
||||||
|| (payload.errors && payload.errors.backupZip ? payload.errors.backupZip[0] : 'Errore durante il caricamento del pacchetto.');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
xhr.addEventListener('error', () => {
|
xhr.addEventListener('error', () => {
|
||||||
this.uploadInCorso = false;
|
this.uploadInCorso = false;
|
||||||
this.erroreUpload = 'Errore di rete durante il caricamento del pacchetto.';
|
this.uploadPercento = 0;
|
||||||
|
this.erroreUpload = 'Errore di rete durante il caricamento: connessione interrotta dal server. '
|
||||||
|
+ 'Succede tipicamente quando il pacchetto supera il limite accettato.';
|
||||||
|
});
|
||||||
|
|
||||||
|
xhr.addEventListener('timeout', () => {
|
||||||
|
this.uploadInCorso = false;
|
||||||
|
this.uploadPercento = 0;
|
||||||
|
this.erroreUpload = 'Il caricamento non ha risposto entro 5 minuti ed e\\' stato interrotto.';
|
||||||
|
});
|
||||||
|
|
||||||
|
xhr.addEventListener('abort', () => {
|
||||||
|
this.uploadInCorso = false;
|
||||||
|
this.uploadPercento = 0;
|
||||||
|
this.erroreUpload = 'Caricamento annullato.';
|
||||||
});
|
});
|
||||||
|
|
||||||
xhr.send(formData);
|
xhr.send(formData);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
messaggioErrore(status, payload) {
|
||||||
|
if (payload.message) {
|
||||||
|
return payload.message;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (payload.errors && payload.errors.backupZip) {
|
||||||
|
return payload.errors.backupZip[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status === 413) {
|
||||||
|
return 'Il pacchetto e\\' troppo grande per il server (413). Alza client_max_body_size in nginx e post_max_size in PHP, oppure usa il ripristino dai volumi.';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status === 419) {
|
||||||
|
return 'Sessione scaduta durante il caricamento. Ricarica la pagina e riprova.';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status === 0) {
|
||||||
|
return 'Il server ha chiuso la connessione durante il caricamento, senza rispondere. Di solito significa che il pacchetto supera il limite accettato.';
|
||||||
|
}
|
||||||
|
|
||||||
|
return `Errore durante il caricamento del pacchetto (HTTP ${status}).`;
|
||||||
|
},
|
||||||
}"
|
}"
|
||||||
x-on:scarica-backup.window="window.location.href = $event.detail.url">
|
x-on:scarica-backup.window="window.location.href = $event.detail.url">
|
||||||
|
|
||||||
@@ -262,10 +337,22 @@
|
|||||||
@submit.prevent="if (confirm('Confermi il ripristino? I dati gestionali e i file correnti verranno sostituiti, e tutti gli utenti dovranno impostare una nuova password.')) inviaRipristino($event)">
|
@submit.prevent="if (confirm('Confermi il ripristino? I dati gestionali e i file correnti verranno sostituiti, e tutti gli utenti dovranno impostare una nuova password.')) inviaRipristino($event)">
|
||||||
@csrf
|
@csrf
|
||||||
<div>
|
<div>
|
||||||
|
{{-- La dimensione si controlla gia' alla selezione: sapere
|
||||||
|
subito che il file e' fuori limite evita di aspettare un
|
||||||
|
caricamento destinato a essere tagliato. --}}
|
||||||
<input name="backupZip" type="file" accept=".zip,application/zip"
|
<input name="backupZip" type="file" accept=".zip,application/zip"
|
||||||
x-on:change="zipSelezionato = $event.target.files[0] ? $event.target.files[0].name : ''; erroreUpload = ''"
|
x-on:change="
|
||||||
|
erroreUpload = '';
|
||||||
|
uploadPercento = 0;
|
||||||
|
const f = $event.target.files[0];
|
||||||
|
zipSelezionato = f ? f.name + ' (' + (f.size / 1000000).toFixed(1) + ' MB)' : '';
|
||||||
|
if (f) zipTroppoGrande(f);
|
||||||
|
"
|
||||||
class="block w-full text-sm text-gray-600 file:mr-3 file:py-2 file:px-4 file:rounded-lg file:border-0 file:text-sm file:font-medium file:bg-red-50 file:text-red-700 hover:file:bg-red-100">
|
class="block w-full text-sm text-gray-600 file:mr-3 file:py-2 file:px-4 file:rounded-lg file:border-0 file:text-sm file:font-medium file:bg-red-50 file:text-red-700 hover:file:bg-red-100">
|
||||||
<p x-show="erroreUpload" x-cloak class="text-red-500 text-xs mt-1" x-text="erroreUpload"></p>
|
<p class="mt-1 text-xs text-gray-500">
|
||||||
|
Dimensione massima accettata: {{ \App\Http\Controllers\Settings\BackupController::maxUploadMb() }} MB.
|
||||||
|
</p>
|
||||||
|
<p x-show="erroreUpload" x-cloak class="text-red-600 text-xs mt-1 font-medium" x-text="erroreUpload"></p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{-- Caricamento: percentuale reale dell'upload, non del ripristino. --}}
|
{{-- Caricamento: percentuale reale dell'upload, non del ripristino. --}}
|
||||||
|
|||||||
Reference in New Issue
Block a user