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.
This commit is contained in:
Andras Bacsai
2025-09-22 19:43:15 +02:00
parent 33d25f418e
commit 238957132c

View File

@@ -17,6 +17,7 @@ use App\Models\ScheduledDatabaseBackup;
use App\Models\Server; use App\Models\Server;
use App\Models\StandalonePostgresql; use App\Models\StandalonePostgresql;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use OpenApi\Attributes as OA; use OpenApi\Attributes as OA;
class DatabasesController extends Controller class DatabasesController extends Controller
@@ -718,6 +719,24 @@ class DatabasesController extends Controller
return response()->json(['message' => 'Database not found.'], 404); 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) $backupConfig = ScheduledDatabaseBackup::ownedByCurrentTeamAPI($teamId)->where('database_id', $database->id)
->where('uuid', $request->scheduled_backup_uuid) ->where('uuid', $request->scheduled_backup_uuid)
->first(); ->first();
@@ -745,6 +764,11 @@ class DatabasesController extends Controller
$s3Storage = S3Storage::ownedByCurrentTeam()->where('uuid', $backupData['s3_storage_uuid'])->first(); $s3Storage = S3Storage::ownedByCurrentTeam()->where('uuid', $backupData['s3_storage_uuid'])->first();
if ($s3Storage) { if ($s3Storage) {
$backupData['s3_storage_id'] = $s3Storage->id; $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']); unset($backupData['s3_storage_uuid']);
} }
@@ -752,7 +776,7 @@ class DatabasesController extends Controller
$backupConfig->update($backupData); $backupConfig->update($backupData);
if ($request->backup_now) { if ($request->backup_now) {
DatabaseBackupJob::dispatch($backupConfig); dispatch(new DatabaseBackupJob($backupConfig));
} }
return response()->json([ return response()->json([
@@ -1950,6 +1974,8 @@ class DatabasesController extends Controller
return response()->json(['message' => 'Database not found.'], 404); return response()->json(['message' => 'Database not found.'], 404);
} }
$this->authorize('update', $database);
// Find the backup configuration by its UUID // Find the backup configuration by its UUID
$backup = ScheduledDatabaseBackup::ownedByCurrentTeamAPI($teamId)->where('database_id', $database->id) $backup = ScheduledDatabaseBackup::ownedByCurrentTeamAPI($teamId)->where('database_id', $database->id)
->where('uuid', $request->scheduled_backup_uuid) ->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); $deleteS3 = filter_var($request->query->get('delete_s3', false), FILTER_VALIDATE_BOOLEAN);
try { try {
DB::beginTransaction();
// Get all executions for this backup configuration // Get all executions for this backup configuration
$executions = $backup->executions()->get(); $executions = $backup->executions()->get();
@@ -1980,11 +2007,14 @@ class DatabasesController extends Controller
// Delete the backup configuration itself // Delete the backup configuration itself
$backup->delete(); $backup->delete();
DB::commit();
return response()->json([ return response()->json([
'message' => 'Backup configuration and all executions deleted.', 'message' => 'Backup configuration and all executions deleted.',
]); ]);
} catch (\Exception $e) { } catch (\Exception $e) {
DB::rollBack();
return response()->json(['message' => 'Failed to delete backup: '.$e->getMessage()], 500); 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); return response()->json(['message' => 'Database not found.'], 404);
} }
$this->authorize('update', $database);
// Find the backup configuration by its UUID // Find the backup configuration by its UUID
$backup = ScheduledDatabaseBackup::ownedByCurrentTeamAPI($teamId)->where('database_id', $database->id) $backup = ScheduledDatabaseBackup::ownedByCurrentTeamAPI($teamId)->where('database_id', $database->id)
->where('uuid', $request->scheduled_backup_uuid) ->where('uuid', $request->scheduled_backup_uuid)