From 238957132cdf91ac1b0d5a39b44917ebeb3f5dd1 Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Mon, 22 Sep 2025 19:43:15 +0200 Subject: [PATCH] feat(databases): enhance backup update and deletion logic with validation - Added authorization checks for updating and deleting backups in DatabasesController. - Implemented validation for S3 storage UUID when saving backups, ensuring it belongs to the current team. - Improved error handling during backup deletion with transaction management for better data integrity. --- .../Controllers/Api/DatabasesController.php | 34 ++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/app/Http/Controllers/Api/DatabasesController.php b/app/Http/Controllers/Api/DatabasesController.php index c9617fdb8..62ac36d5c 100644 --- a/app/Http/Controllers/Api/DatabasesController.php +++ b/app/Http/Controllers/Api/DatabasesController.php @@ -17,6 +17,7 @@ use App\Models\ScheduledDatabaseBackup; use App\Models\Server; use App\Models\StandalonePostgresql; use Illuminate\Http\Request; +use Illuminate\Support\Facades\DB; use OpenApi\Attributes as OA; class DatabasesController extends Controller @@ -718,6 +719,24 @@ class DatabasesController extends Controller return response()->json(['message' => 'Database not found.'], 404); } + $this->authorize('update', $database); + + if ($request->boolean('save_s3') && ! $request->filled('s3_storage_uuid')) { + return response()->json([ + 'message' => 'Validation failed.', + 'errors' => ['s3_storage_uuid' => ['The s3_storage_uuid field is required when save_s3 is true.']], + ], 422); + } + if ($request->filled('s3_storage_uuid')) { + $existsInTeam = S3Storage::ownedByCurrentTeam()->where('uuid', $request->s3_storage_uuid)->exists(); + if (! $existsInTeam) { + return response()->json([ + 'message' => 'Validation failed.', + 'errors' => ['s3_storage_uuid' => ['The selected S3 storage is invalid for this team.']], + ], 422); + } + } + $backupConfig = ScheduledDatabaseBackup::ownedByCurrentTeamAPI($teamId)->where('database_id', $database->id) ->where('uuid', $request->scheduled_backup_uuid) ->first(); @@ -745,6 +764,11 @@ class DatabasesController extends Controller $s3Storage = S3Storage::ownedByCurrentTeam()->where('uuid', $backupData['s3_storage_uuid'])->first(); if ($s3Storage) { $backupData['s3_storage_id'] = $s3Storage->id; + } elseif ($request->boolean('save_s3')) { + return response()->json([ + 'message' => 'Validation failed.', + 'errors' => ['s3_storage_uuid' => ['The selected S3 storage is invalid for this team.']], + ], 422); } unset($backupData['s3_storage_uuid']); } @@ -752,7 +776,7 @@ class DatabasesController extends Controller $backupConfig->update($backupData); if ($request->backup_now) { - DatabaseBackupJob::dispatch($backupConfig); + dispatch(new DatabaseBackupJob($backupConfig)); } return response()->json([ @@ -1950,6 +1974,8 @@ class DatabasesController extends Controller return response()->json(['message' => 'Database not found.'], 404); } + $this->authorize('update', $database); + // Find the backup configuration by its UUID $backup = ScheduledDatabaseBackup::ownedByCurrentTeamAPI($teamId)->where('database_id', $database->id) ->where('uuid', $request->scheduled_backup_uuid) @@ -1962,6 +1988,7 @@ class DatabasesController extends Controller $deleteS3 = filter_var($request->query->get('delete_s3', false), FILTER_VALIDATE_BOOLEAN); try { + DB::beginTransaction(); // Get all executions for this backup configuration $executions = $backup->executions()->get(); @@ -1980,11 +2007,14 @@ class DatabasesController extends Controller // Delete the backup configuration itself $backup->delete(); + DB::commit(); return response()->json([ 'message' => 'Backup configuration and all executions deleted.', ]); } catch (\Exception $e) { + DB::rollBack(); + return response()->json(['message' => 'Failed to delete backup: '.$e->getMessage()], 500); } } @@ -2071,6 +2101,8 @@ class DatabasesController extends Controller return response()->json(['message' => 'Database not found.'], 404); } + $this->authorize('update', $database); + // Find the backup configuration by its UUID $backup = ScheduledDatabaseBackup::ownedByCurrentTeamAPI($teamId)->where('database_id', $database->id) ->where('uuid', $request->scheduled_backup_uuid)