refactor(backups): centralize storage deletion guard

This commit is contained in:
Andras Bacsai 2026-07-19 23:20:46 +02:00
parent 4e03be755d
commit bf72fd5d88
6 changed files with 38 additions and 21 deletions

View file

@ -4910,13 +4910,7 @@ public function delete_storage(Request $request): JsonResponse
], 422);
}
if ($storage->scheduledBackups()->exists()) {
return response()->json([
'message' => $storage instanceof LocalFileVolume
? 'Delete this directory backup schedule and its archives before deleting the directory.'
: 'Delete this volume backup schedule and its archives before deleting the volume.',
], 422);
}
$storage->abortIfScheduledBackupsExist();
if ($storage instanceof LocalFileVolume) {
$storage->deleteStorageOnServer();

View file

@ -4484,13 +4484,7 @@ public function delete_storage(Request $request): JsonResponse
], 422);
}
if ($storage->scheduledBackups()->exists()) {
return response()->json([
'message' => $storage instanceof LocalFileVolume
? 'Delete this directory backup schedule and its archives before deleting the directory.'
: 'Delete this volume backup schedule and its archives before deleting the volume.',
], 422);
}
$storage->abortIfScheduledBackupsExist();
if ($storage instanceof LocalFileVolume) {
$storage->deleteStorageOnServer();

View file

@ -2911,13 +2911,7 @@ public function delete_storage(Request $request): JsonResponse
], 422);
}
if ($storage->scheduledBackups()->exists()) {
return response()->json([
'message' => $storage instanceof LocalFileVolume
? 'Delete this directory backup schedule and its archives before deleting the directory.'
: 'Delete this volume backup schedule and its archives before deleting the volume.',
], 422);
}
$storage->abortIfScheduledBackupsExist();
if ($storage instanceof LocalFileVolume) {
$storage->deleteStorageOnServer();

View file

@ -96,6 +96,13 @@ public function scheduledBackups(): MorphMany
return $this->morphMany(ScheduledVolumeBackup::class, 'backupable');
}
public function abortIfScheduledBackupsExist(): void
{
if ($this->scheduledBackups()->exists()) {
abort(422, 'Delete this directory backup schedule and its archives before deleting the directory.');
}
}
public function loadStorageOnServer()
{
if ($this->is_host_file) {

View file

@ -56,6 +56,13 @@ public function scheduledBackups(): MorphMany
return $this->morphMany(ScheduledVolumeBackup::class, 'backupable');
}
public function abortIfScheduledBackupsExist(): void
{
if ($this->scheduledBackups()->exists()) {
abort(422, 'Delete this volume backup schedule and its archives before deleting the volume.');
}
}
protected function customizeName($value)
{
return str($value)->trim()->value;

View file

@ -37,6 +37,7 @@
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\Storage;
use Livewire\Livewire;
use Symfony\Component\HttpKernel\Exception\HttpException;
uses(RefreshDatabase::class);
@ -1456,6 +1457,26 @@ function signInForVolumeBackups($testCase, Team $team): User
expect($volume->fresh())->not->toBeNull();
});
it('aborts storage deletion when scheduled backups exist', function () {
$team = Team::factory()->create();
[$application, $volume] = createVolumeBackupApplication($team);
$directory = createApplicationBackupDirectory($application);
$volume->scheduledBackups()->create([
'team_id' => $team->id,
'frequency' => 'daily',
]);
$directory->scheduledBackups()->create([
'team_id' => $team->id,
'frequency' => 'daily',
]);
expect(fn () => $volume->abortIfScheduledBackupsExist())
->toThrow(HttpException::class, 'Delete this volume backup schedule and its archives before deleting the volume.')
->and(fn () => $directory->abortIfScheduledBackupsExist())
->toThrow(HttpException::class, 'Delete this directory backup schedule and its archives before deleting the directory.');
});
it('deletes disabled volume backup archives before deleting their application', function () {
Process::fake();
Queue::fake();