Files
voltcerto/app/Console/Commands/CleanMinioBackups.php
Luis Medon e488ae3342 Add feature tests, unit tests, and configuration for Laravel application
- Created ExampleTest and RoutesTest for basic application response and route testing.
- Implemented IsolatedDatabaseTestCase for isolated database testing.
- Added unit tests for various models and services including Contract, CPE, and LeadDetail.
- Developed request validation tests for CreateClientRequest, UpdateClientRequest, and ContractRequest.
- Introduced Vite configuration for asset management in the Laravel application.
2026-10-29 11:30:06 +00:00

112 lines
4.0 KiB
PHP

<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Storage;
class CleanMinioBackups extends Command
{
protected $signature = 'backup:clean-minio';
protected $description = 'Keeps only the last 5 backups in MinIO, safely removing older files';
public function handle()
{
$disk = Storage::disk(env('FILESYSTEM_DISK', 's3'));
$folder = 'laravel-backup';
$keep = 5;
try {
$this->info('Connecting to MinIO bucket...');
$allFiles = $disk->allFiles($folder);
if (empty($allFiles)) {
$this->info('No backup files found in the directory.');
return 0;
}
$this->info('Found ' . count($allFiles) . ' files. Processing...');
$zipFiles = collect($allFiles)
->filter(fn($file) => str_ends_with($file, '.zip'))
->values();
if ($zipFiles->isEmpty()) {
$this->info('No ZIP backup files found.');
return 0;
}
$this->info('Found ' . $zipFiles->count() . ' ZIP files.');
$backups = $zipFiles->map(function ($file) use ($disk) {
try {
$lastModified = (int) $disk->lastModified($file);
return [
'file' => $file,
'lastModified' => $lastModified,
'date' => date('Y-m-d H:i:s', $lastModified),
'estimated' => false
];
} catch (\Exception $e) {
$this->warn("Cannot read metadata for {$file}, using filename for sorting");
preg_match('/(\d{4}-\d{2}-\d{2}-\d{2}-\d{2}-\d{2})/', $file, $matches);
$timestamp = isset($matches[1])
? \DateTime::createFromFormat('Y-m-d-H-i-s', $matches[1])->getTimestamp()
: time();
return [
'file' => $file,
'lastModified' => $timestamp,
'date' => date('Y-m-d H:i:s', $timestamp),
'estimated' => true
];
}
});
$backups = $backups->sortByDesc('lastModified')->values();
$totalBackups = $backups->count();
$toDelete = $backups->slice($keep);
$this->info("Valid backups found: {$totalBackups}");
if ($toDelete->isEmpty()) {
$this->info("Only {$totalBackups} backup(s) found (less than {$keep}), nothing to delete.");
return 0;
}
$this->info("Keeping the latest {$keep}, deleting {$toDelete->count()} old backups.");
$this->info("Backups scheduled for deletion:");
foreach ($toDelete as $item) {
$this->info(" - {$item['file']} ({$item['date']})" . ($item['estimated'] ? ' [estimated]' : ''));
}
$deletedCount = 0;
foreach ($toDelete as $item) {
try {
$disk->delete($item['file']);
$this->info("✓ Deleted: {$item['file']}" . ($item['estimated'] ? ' [estimated]' : ''));
$deletedCount++;
} catch (\Exception $e) {
$this->error("✗ Failed to delete {$item['file']}: {$e->getMessage()}");
}
}
$this->info("Cleanup finished. Deleted {$deletedCount} files. Kept the last {$keep} backups.");
} catch (\Exception $e) {
$this->error("Failed to access backup directory: {$e->getMessage()}");
$this->error('Debug info:');
$this->error('Bucket: ' . env('AWS_BUCKET'));
$this->error('Endpoint: ' . env('AWS_ENDPOINT'));
$this->error('Key: ' . substr(env('AWS_ACCESS_KEY_ID', ''), 0, 5) . '...');
return 1;
}
return 0;
}
}