feat(backup): add disable local backup option and related logic for S3 uploads
This commit is contained in:
@@ -351,6 +351,12 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
$size = $this->calculate_size();
|
$size = $this->calculate_size();
|
||||||
if ($this->backup->save_s3) {
|
if ($this->backup->save_s3) {
|
||||||
$this->upload_to_s3();
|
$this->upload_to_s3();
|
||||||
|
|
||||||
|
// If local backup is disabled, delete the local file immediately after S3 upload
|
||||||
|
if ($this->backup->disable_local_backup) {
|
||||||
|
deleteBackupsLocally($this->backup_location, $this->server);
|
||||||
|
$this->add_to_backup_output('Local backup file deleted after S3 upload (disable_local_backup enabled).');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->team->notify(new BackupSuccess($this->backup, $this->database, $database));
|
$this->team->notify(new BackupSuccess($this->backup, $this->database, $database));
|
||||||
|
@@ -64,6 +64,9 @@ class BackupEdit extends Component
|
|||||||
#[Validate(['required', 'boolean'])]
|
#[Validate(['required', 'boolean'])]
|
||||||
public bool $saveS3 = false;
|
public bool $saveS3 = false;
|
||||||
|
|
||||||
|
#[Validate(['required', 'boolean'])]
|
||||||
|
public bool $disableLocalBackup = false;
|
||||||
|
|
||||||
#[Validate(['nullable', 'integer'])]
|
#[Validate(['nullable', 'integer'])]
|
||||||
public ?int $s3StorageId = 1;
|
public ?int $s3StorageId = 1;
|
||||||
|
|
||||||
@@ -98,6 +101,7 @@ class BackupEdit extends Component
|
|||||||
$this->backup->database_backup_retention_days_s3 = $this->databaseBackupRetentionDaysS3;
|
$this->backup->database_backup_retention_days_s3 = $this->databaseBackupRetentionDaysS3;
|
||||||
$this->backup->database_backup_retention_max_storage_s3 = $this->databaseBackupRetentionMaxStorageS3;
|
$this->backup->database_backup_retention_max_storage_s3 = $this->databaseBackupRetentionMaxStorageS3;
|
||||||
$this->backup->save_s3 = $this->saveS3;
|
$this->backup->save_s3 = $this->saveS3;
|
||||||
|
$this->backup->disable_local_backup = $this->disableLocalBackup;
|
||||||
$this->backup->s3_storage_id = $this->s3StorageId;
|
$this->backup->s3_storage_id = $this->s3StorageId;
|
||||||
$this->backup->databases_to_backup = $this->databasesToBackup;
|
$this->backup->databases_to_backup = $this->databasesToBackup;
|
||||||
$this->backup->dump_all = $this->dumpAll;
|
$this->backup->dump_all = $this->dumpAll;
|
||||||
@@ -115,6 +119,7 @@ class BackupEdit extends Component
|
|||||||
$this->databaseBackupRetentionDaysS3 = $this->backup->database_backup_retention_days_s3;
|
$this->databaseBackupRetentionDaysS3 = $this->backup->database_backup_retention_days_s3;
|
||||||
$this->databaseBackupRetentionMaxStorageS3 = $this->backup->database_backup_retention_max_storage_s3;
|
$this->databaseBackupRetentionMaxStorageS3 = $this->backup->database_backup_retention_max_storage_s3;
|
||||||
$this->saveS3 = $this->backup->save_s3;
|
$this->saveS3 = $this->backup->save_s3;
|
||||||
|
$this->disableLocalBackup = $this->backup->disable_local_backup ?? false;
|
||||||
$this->s3StorageId = $this->backup->s3_storage_id;
|
$this->s3StorageId = $this->backup->s3_storage_id;
|
||||||
$this->databasesToBackup = $this->backup->databases_to_backup;
|
$this->databasesToBackup = $this->backup->databases_to_backup;
|
||||||
$this->dumpAll = $this->backup->dump_all;
|
$this->dumpAll = $this->backup->dump_all;
|
||||||
@@ -193,6 +198,12 @@ class BackupEdit extends Component
|
|||||||
if (! is_numeric($this->backup->s3_storage_id)) {
|
if (! is_numeric($this->backup->s3_storage_id)) {
|
||||||
$this->backup->s3_storage_id = null;
|
$this->backup->s3_storage_id = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Validate that disable_local_backup can only be true when S3 backup is enabled
|
||||||
|
if ($this->backup->disable_local_backup && ! $this->backup->save_s3) {
|
||||||
|
throw new \Exception('Local backup can only be disabled when S3 backup is enabled.');
|
||||||
|
}
|
||||||
|
|
||||||
$isValid = validate_cron_expression($this->backup->frequency);
|
$isValid = validate_cron_expression($this->backup->frequency);
|
||||||
if (! $isValid) {
|
if (! $isValid) {
|
||||||
throw new \Exception('Invalid Cron / Human expression');
|
throw new \Exception('Invalid Cron / Human expression');
|
||||||
|
@@ -237,6 +237,12 @@ function removeOldBackups($backup): void
|
|||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
if ($backup->executions) {
|
if ($backup->executions) {
|
||||||
|
// If local backup is disabled, mark all executions as having local storage deleted
|
||||||
|
if ($backup->disable_local_backup && $backup->save_s3) {
|
||||||
|
$backup->executions()
|
||||||
|
->where('local_storage_deleted', false)
|
||||||
|
->update(['local_storage_deleted' => true]);
|
||||||
|
} else {
|
||||||
$localBackupsToDelete = deleteOldBackupsLocally($backup);
|
$localBackupsToDelete = deleteOldBackupsLocally($backup);
|
||||||
if ($localBackupsToDelete->isNotEmpty()) {
|
if ($localBackupsToDelete->isNotEmpty()) {
|
||||||
$backup->executions()
|
$backup->executions()
|
||||||
@@ -244,6 +250,7 @@ function removeOldBackups($backup): void
|
|||||||
->update(['local_storage_deleted' => true]);
|
->update(['local_storage_deleted' => true]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ($backup->save_s3 && $backup->executions) {
|
if ($backup->save_s3 && $backup->executions) {
|
||||||
$s3BackupsToDelete = deleteOldBackupsFromS3($backup);
|
$s3BackupsToDelete = deleteOldBackupsFromS3($backup);
|
||||||
@@ -254,10 +261,18 @@ function removeOldBackups($backup): void
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Delete executions where both local and S3 storage are marked as deleted
|
||||||
|
// or where only S3 is enabled and S3 storage is deleted
|
||||||
|
if ($backup->disable_local_backup && $backup->save_s3) {
|
||||||
|
$backup->executions()
|
||||||
|
->where('s3_storage_deleted', true)
|
||||||
|
->delete();
|
||||||
|
} else {
|
||||||
$backup->executions()
|
$backup->executions()
|
||||||
->where('local_storage_deleted', true)
|
->where('local_storage_deleted', true)
|
||||||
->where('s3_storage_deleted', true)
|
->where('s3_storage_deleted', true)
|
||||||
->delete();
|
->delete();
|
||||||
|
}
|
||||||
|
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
throw $e;
|
throw $e;
|
||||||
|
@@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::table('scheduled_database_backups', function (Blueprint $table) {
|
||||||
|
$table->boolean('disable_local_backup')->default(false)->after('save_s3');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('scheduled_database_backups', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('disable_local_backup');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
@@ -18,7 +18,7 @@
|
|||||||
shortConfirmationLabel="Database Name" />
|
shortConfirmationLabel="Database Name" />
|
||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
<div class="w-48 pb-2">
|
<div class="w-64 pb-2">
|
||||||
<x-forms.checkbox instantSave label="Backup Enabled" id="backupEnabled" />
|
<x-forms.checkbox instantSave label="Backup Enabled" id="backupEnabled" />
|
||||||
@if ($s3s->count() > 0)
|
@if ($s3s->count() > 0)
|
||||||
<x-forms.checkbox instantSave label="S3 Enabled" id="saveS3" />
|
<x-forms.checkbox instantSave label="S3 Enabled" id="saveS3" />
|
||||||
@@ -26,11 +26,18 @@
|
|||||||
<x-forms.checkbox instantSave helper="No validated S3 storage available." label="S3 Enabled" id="saveS3"
|
<x-forms.checkbox instantSave helper="No validated S3 storage available." label="S3 Enabled" id="saveS3"
|
||||||
disabled />
|
disabled />
|
||||||
@endif
|
@endif
|
||||||
|
@if ($backup->save_s3)
|
||||||
|
<x-forms.checkbox instantSave label="Disable Local Backup" id="disableLocalBackup"
|
||||||
|
helper="When enabled, backup files will be deleted from local storage immediately after uploading to S3. This requires S3 backup to be enabled." />
|
||||||
|
@else
|
||||||
|
<x-forms.checkbox disabled label="Disable Local Backup" id="disableLocalBackup"
|
||||||
|
helper="When enabled, backup files will be deleted from local storage immediately after uploading to S3. This requires S3 backup to be enabled." />
|
||||||
|
@endif
|
||||||
</div>
|
</div>
|
||||||
@if ($backup->save_s3)
|
@if ($backup->save_s3)
|
||||||
<div class="pb-6">
|
<div class="pb-6">
|
||||||
<x-forms.select id="s3StorageId" label="S3 Storage" required>
|
<x-forms.select id="s3StorageId" label="S3 Storage" required>
|
||||||
<option value="default">Select a S3 storage</option>
|
<option value="default" disabled>Select a S3 storage</option>
|
||||||
@foreach ($s3s as $s3)
|
@foreach ($s3s as $s3)
|
||||||
<option value="{{ $s3->id }}">{{ $s3->name }}</option>
|
<option value="{{ $s3->id }}">{{ $s3->name }}</option>
|
||||||
@endforeach
|
@endforeach
|
||||||
|
Reference in New Issue
Block a user