diff --git a/app/Livewire/Project/Database/BackupEdit.php b/app/Livewire/Project/Database/BackupEdit.php index 853b918b8..6ebbf951e 100644 --- a/app/Livewire/Project/Database/BackupEdit.php +++ b/app/Livewire/Project/Database/BackupEdit.php @@ -49,14 +49,17 @@ class BackupEdit extends Component #[Validate(['required', 'integer'])] public ?int $databaseBackupRetentionDaysLocally = 0; + #[Validate(['required', 'numeric', 'min:0'])] + public ?float $databaseBackupRetentionMaxStorageLocally = 0; + #[Validate(['required', 'integer'])] public ?int $databaseBackupRetentionAmountS3 = 0; #[Validate(['required', 'integer'])] public ?int $databaseBackupRetentionDaysS3 = 0; - #[Validate(['required', 'integer'])] - public ?int $databaseBackupRetentionMaxStorageS3 = 0; + #[Validate(['required', 'numeric', 'min:0'])] + public ?float $databaseBackupRetentionMaxStorageS3 = 0; #[Validate(['required', 'boolean'])] public bool $saveS3 = false; @@ -87,6 +90,7 @@ class BackupEdit extends Component $this->backup->frequency = $this->frequency; $this->backup->database_backup_retention_amount_locally = $this->databaseBackupRetentionAmountLocally; $this->backup->database_backup_retention_days_locally = $this->databaseBackupRetentionDaysLocally; + $this->backup->database_backup_retention_max_storage_locally = $this->databaseBackupRetentionMaxStorageLocally; $this->backup->database_backup_retention_amount_s3 = $this->databaseBackupRetentionAmountS3; $this->backup->database_backup_retention_days_s3 = $this->databaseBackupRetentionDaysS3; $this->backup->database_backup_retention_max_storage_s3 = $this->databaseBackupRetentionMaxStorageS3; @@ -102,6 +106,7 @@ class BackupEdit extends Component $this->timezone = data_get($this->backup->server(), 'settings.server_timezone', 'Instance timezone'); $this->databaseBackupRetentionAmountLocally = $this->backup->database_backup_retention_amount_locally; $this->databaseBackupRetentionDaysLocally = $this->backup->database_backup_retention_days_locally; + $this->databaseBackupRetentionMaxStorageLocally = $this->backup->database_backup_retention_max_storage_locally; $this->databaseBackupRetentionAmountS3 = $this->backup->database_backup_retention_amount_s3; $this->databaseBackupRetentionDaysS3 = $this->backup->database_backup_retention_days_s3; $this->databaseBackupRetentionMaxStorageS3 = $this->backup->database_backup_retention_max_storage_s3; diff --git a/bootstrap/helpers/databases.php b/bootstrap/helpers/databases.php index 90c18dc6d..6a834ee6f 100644 --- a/bootstrap/helpers/databases.php +++ b/bootstrap/helpers/databases.php @@ -263,8 +263,9 @@ function deleteOldBackupsLocally($backup): Collection $retentionAmount = $backup->database_backup_retention_amount_locally; $retentionDays = $backup->database_backup_retention_days_locally; + $maxStorageGB = $backup->database_backup_retention_max_storage_locally; - if ($retentionAmount === 0 && $retentionDays === 0) { + if ($retentionAmount === 0 && $retentionDays === 0 && $maxStorageGB === 0) { return collect(); } @@ -281,6 +282,26 @@ function deleteOldBackupsLocally($backup): Collection $backupsToDelete = $backupsToDelete->merge($oldBackups); } + if ($maxStorageGB > 0) { + $maxStorageBytes = $maxStorageGB * pow(1024, 3); + $totalSize = 0; + $backupsOverLimit = collect(); + + $backupsToCheck = $successfulBackups->skip(1); + + foreach ($backupsToCheck as $backupExecution) { + $totalSize += (int) $backupExecution->size; + if ($totalSize > $maxStorageBytes) { + $backupsOverLimit = $successfulBackups->filter( + fn ($b) => $b->created_at->utc() <= $backupExecution->created_at->utc() + )->skip(1); + break; + } + } + + $backupsToDelete = $backupsToDelete->merge($backupsOverLimit); + } + $backupsToDelete = $backupsToDelete->unique('id'); $processedBackups = collect(); @@ -345,14 +366,18 @@ function deleteOldBackupsFromS3($backup): Collection } if ($maxStorageGB > 0) { - $maxStorageBytes = $maxStorageGB * 1024 * 1024 * 1024; + $maxStorageBytes = $maxStorageGB * pow(1024, 3); $totalSize = 0; $backupsOverLimit = collect(); - foreach ($successfulBackups as $backup) { - $totalSize += (int) $backup->size; + $backupsToCheck = $successfulBackups->skip(1); + + foreach ($backupsToCheck as $backupExecution) { + $totalSize += (int) $backupExecution->size; if ($totalSize > $maxStorageBytes) { - $backupsOverLimit = $successfulBackups->filter(fn ($b) => $b->created_at->utc() <= $backup->created_at->utc()); + $backupsOverLimit = $successfulBackups->filter( + fn ($b) => $b->created_at->utc() <= $backupExecution->created_at->utc() + )->skip(1); break; } } diff --git a/database/migrations/2025_01_13_130238_add_backup_retention_fields_to_scheduled_database_backups_table.php b/database/migrations/2025_01_13_130238_add_backup_retention_fields_to_scheduled_database_backups_table.php index f5371ee7a..f06bc367e 100644 --- a/database/migrations/2025_01_13_130238_add_backup_retention_fields_to_scheduled_database_backups_table.php +++ b/database/migrations/2025_01_13_130238_add_backup_retention_fields_to_scheduled_database_backups_table.php @@ -12,10 +12,11 @@ return new class extends Migration $table->renameColumn('number_of_backups_locally', 'database_backup_retention_amount_locally'); $table->integer('database_backup_retention_amount_locally')->default(0)->nullable(false)->change(); $table->integer('database_backup_retention_days_locally')->default(0)->nullable(false); + $table->decimal('database_backup_retention_max_storage_locally', 17, 7)->default(0)->nullable(false); $table->integer('database_backup_retention_amount_s3')->default(0)->nullable(false); $table->integer('database_backup_retention_days_s3')->default(0)->nullable(false); - $table->integer('database_backup_retention_max_storage_s3')->default(0)->nullable(false); + $table->decimal('database_backup_retention_max_storage_s3', 17, 7)->default(0)->nullable(false); }); } @@ -25,6 +26,7 @@ return new class extends Migration $table->renameColumn('database_backup_retention_amount_locally', 'number_of_backups_locally')->nullable(true)->change(); $table->dropColumn([ 'database_backup_retention_days_locally', + 'database_backup_retention_max_storage_locally', 'database_backup_retention_amount_s3', 'database_backup_retention_days_s3', 'database_backup_retention_max_storage_s3', diff --git a/resources/views/livewire/project/database/backup-edit.blade.php b/resources/views/livewire/project/database/backup-edit.blade.php index 4f3ebe8f8..f1f604a7d 100644 --- a/resources/views/livewire/project/database/backup-edit.blade.php +++ b/resources/views/livewire/project/database/backup-edit.blade.php @@ -75,6 +75,13 @@