118 lines
3.8 KiB
PHP
118 lines
3.8 KiB
PHP
<?php
|
|
|
|
namespace App\Jobs;
|
|
|
|
use App\Livewire\Settings\XmlExchange;
|
|
use App\Services\BackupArchive;
|
|
use App\Services\BackupOperationState;
|
|
use Illuminate\Bus\Queueable;
|
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
|
use Illuminate\Foundation\Bus\Dispatchable;
|
|
use Illuminate\Queue\InteractsWithQueue;
|
|
use Illuminate\Queue\SerializesModels;
|
|
use Illuminate\Support\Facades\Log;
|
|
|
|
/**
|
|
* Costruisce il pacchetto di backup in coda.
|
|
*
|
|
* In coda e non nella richiesta perche' un'installazione con qualche centinaio
|
|
* di PDF supera comodamente i 300 secondi di `fastcgi_read_timeout`: il browser
|
|
* si vedrebbe chiudere la connessione a meta' compressione, senza modo di
|
|
* distinguerlo da un errore.
|
|
*/
|
|
class CreateBackupArchive implements ShouldQueue
|
|
{
|
|
use Dispatchable;
|
|
use InteractsWithQueue;
|
|
use Queueable;
|
|
use SerializesModels;
|
|
|
|
public int $tries = 1;
|
|
|
|
/** Sotto il --timeout=600 del worker: se sfora, e' il worker a ucciderlo. */
|
|
public int $timeout = 570;
|
|
|
|
/**
|
|
* @param bool $perAzzeramento Quando true il job non chiude l'operazione
|
|
* ma la lascia in attesa del download: e'
|
|
* l'azzeramento che aspetta di avere
|
|
* l'archivio al sicuro prima di partire.
|
|
*/
|
|
public function __construct(
|
|
public string $operationId,
|
|
public bool $perAzzeramento = false,
|
|
) {
|
|
}
|
|
|
|
public function handle(BackupOperationState $stato, BackupArchive $archivio, XmlExchange $exchange): void
|
|
{
|
|
$stato->markRunning($this->operationId);
|
|
$stato->fase($this->operationId, 'Lettura dei dati gestionali');
|
|
|
|
$destinazione = self::percorso($this->operationId);
|
|
|
|
try {
|
|
$this->rimuoviArchiviVecchi($archivio);
|
|
|
|
$xml = $exchange->currentXmlBackup();
|
|
|
|
$stato->fase($this->operationId, 'Compressione dei file');
|
|
|
|
$archivio->create(
|
|
$xml,
|
|
$destinazione,
|
|
fn (int $fatti, int $totale) => $stato->avanza($this->operationId, $fatti, $totale)
|
|
);
|
|
} catch (\Throwable $e) {
|
|
Log::error('Creazione del backup non riuscita.', ['exception' => $e]);
|
|
$stato->markFailed($this->operationId, $e->getMessage());
|
|
|
|
return;
|
|
}
|
|
|
|
$risultato = [
|
|
'file' => basename($destinazione),
|
|
'nome_download' => 'termanager2-backup-' . now()->format('Ymd-His') . '.zip',
|
|
'dimensione' => filesize($destinazione) ?: 0,
|
|
];
|
|
|
|
if ($this->perAzzeramento) {
|
|
$stato->log($this->operationId, 'Archivio pronto. In attesa che il download sia completato.');
|
|
$stato->markAttesaDownload($this->operationId, $risultato);
|
|
|
|
return;
|
|
}
|
|
|
|
$stato->log($this->operationId, 'Backup pronto per il download.');
|
|
$stato->markCompleted($this->operationId, $risultato);
|
|
}
|
|
|
|
public static function percorso(string $operationId): string
|
|
{
|
|
return storage_path('app/backups/' . $operationId . '.zip');
|
|
}
|
|
|
|
/**
|
|
* Gli archivi restano su disco finche' il browser non li scarica, quindi un
|
|
* download mai completato lascerebbe un file per sempre. Si ripulisce
|
|
* all'inizio di ogni nuovo backup invece di aggiungere un task pianificato:
|
|
* il momento in cui serve spazio e' esattamente questo.
|
|
*/
|
|
private function rimuoviArchiviVecchi(BackupArchive $archivio): void
|
|
{
|
|
$cartella = storage_path('app/backups');
|
|
|
|
if (! is_dir($cartella)) {
|
|
return;
|
|
}
|
|
|
|
$limite = now()->subDay()->getTimestamp();
|
|
|
|
foreach (glob($cartella . '/*.zip') ?: [] as $file) {
|
|
if (filemtime($file) < $limite) {
|
|
@unlink($file);
|
|
}
|
|
}
|
|
}
|
|
}
|