fix(settings): load instance backup page without a backup schedule

/settings/backup returned 500 when the coolify-db resource existed without
a scheduled backup. Configure backup now reuses the existing coolify-db and
creates only the missing schedule for the root team, without forcing id 0.

Closes #12074
This commit is contained in:
Andras Bacsai
2026-10-04 20:33:22 +02:00
parent 16a8c7999d
commit cfe9e0eee0
3 changed files with 117 additions and 40 deletions
+1 -3
View File
@@ -257,10 +257,8 @@ class Init extends Command
$database = StandalonePostgresql::withTrashed()->find(0);
if ($database && $database->trashed()) {
$database->restore();
$scheduledBackup = ScheduledDatabaseBackup::find(0);
if (! $scheduledBackup) {
if (! $database->scheduledBackups()->exists()) {
ScheduledDatabaseBackup::create([
'id' => 0,
'enabled' => true,
'save_s3' => false,
'frequency' => '0 0 * * *',
+39 -37
View File
@@ -71,7 +71,7 @@ class SettingsBackup extends Component
$this->backup->enabled = false;
$this->backup->save();
}
$this->executions = $this->backup->executions;
$this->executions = $this->backup?->executions ?? [];
}
$this->settings = $settings;
$this->s3s = $s3s;
@@ -81,44 +81,46 @@ class SettingsBackup extends Component
{
try {
$this->authorize('update', $this->settings);
$server = Server::findOrFail(0);
$out = instant_remote_process(['docker inspect coolify-db'], $server);
$envs = format_docker_envs_to_json($out);
$postgres_password = $envs['POSTGRES_PASSWORD'];
$postgres_user = $envs['POSTGRES_USER'];
$postgres_db = $envs['POSTGRES_DB'];
$this->database = new StandalonePostgresql;
$this->database->forceFill([
'id' => 0,
'name' => 'coolify-db',
'description' => 'Coolify database',
'postgres_user' => $postgres_user,
'postgres_password' => $postgres_password,
'postgres_db' => $postgres_db,
'status' => 'running',
'destination_type' => StandaloneDocker::class,
'destination_id' => 0,
]);
$this->database->save();
$this->backup = ScheduledDatabaseBackup::create([
'id' => 0,
'enabled' => true,
'save_s3' => false,
'frequency' => '0 0 * * *',
'database_id' => $this->database->id,
'database_type' => StandalonePostgresql::class,
'team_id' => currentTeam()->id,
]);
$this->database = StandalonePostgresql::whereName('coolify-db')->first();
if (! $this->database) {
$server = Server::findOrFail(0);
$out = instant_remote_process(['docker inspect coolify-db'], $server);
$envs = format_docker_envs_to_json($out);
$this->database = new StandalonePostgresql;
$this->database->forceFill([
'id' => 0,
'name' => 'coolify-db',
'description' => 'Coolify database',
'postgres_user' => $envs['POSTGRES_USER'],
'postgres_password' => $envs['POSTGRES_PASSWORD'],
'postgres_db' => $envs['POSTGRES_DB'],
'status' => 'running',
'destination_type' => StandaloneDocker::class,
'destination_id' => 0,
]);
$this->database->save();
}
$this->backup = $this->database->scheduledBackups()->first()
?? ScheduledDatabaseBackup::create([
'enabled' => true,
'save_s3' => false,
'frequency' => '0 0 * * *',
'database_id' => $this->database->id,
'database_type' => StandalonePostgresql::class,
'team_id' => 0,
]);
$this->database->refresh();
$this->backup->refresh();
auditLog('ui.database.backup_schedule_created', [
'team_id' => $this->backup->team_id,
'database_uuid' => $this->database->uuid,
'database_name' => $this->database->name,
'backup_uuid' => $this->backup->uuid,
'frequency' => $this->backup->frequency,
'save_s3' => (bool) $this->backup->save_s3,
]);
if ($this->backup->wasRecentlyCreated) {
auditLog('ui.database.backup_schedule_created', [
'team_id' => $this->backup->team_id,
'database_uuid' => $this->database->uuid,
'database_name' => $this->database->name,
'backup_uuid' => $this->backup->uuid,
'frequency' => $this->backup->frequency,
'save_s3' => (bool) $this->backup->save_s3,
]);
}
$this->s3s = S3Storage::whereTeamId(0)->get();
$this->uuid = $this->database->uuid;
@@ -2,11 +2,15 @@
use App\Livewire\SettingsBackup;
use App\Models\InstanceSettings;
use App\Models\ScheduledDatabaseBackup;
use App\Models\Server;
use App\Models\StandaloneDocker;
use App\Models\StandalonePostgresql;
use App\Models\Team;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Blade;
use Illuminate\Support\Facades\Process;
use Illuminate\Support\Once;
use Livewire\Livewire;
@@ -84,3 +88,76 @@ test('instance backup settings show a configure button when backup is not set up
->assertSee('Configure backup')
->assertSeeHtml('wire:click="addCoolifyDatabase"');
});
function settingsBackupInstanceDatabaseWithoutSchedule(): StandalonePostgresql
{
Server::flushIdentityMap();
$rootTeam = Team::find(0) ?? Team::factory()->create(['id' => 0]);
$server = Server::factory()->create([
'id' => 0,
'team_id' => $rootTeam->id,
'ip' => '127.0.0.1',
]);
$server->settings()->update([
'is_reachable' => true,
'is_usable' => true,
'force_disabled' => false,
]);
StandaloneDocker::query()->where('server_id', $server->id)->firstOrFail()->forceFill(['id' => 0])->save();
InstanceSettings::forceCreate(['id' => 0]);
Once::flush();
$database = new StandalonePostgresql;
$database->forceFill([
'id' => 0,
'name' => 'coolify-db',
'description' => 'Coolify database',
'postgres_user' => 'coolify',
'postgres_password' => 'password',
'postgres_db' => 'coolify',
'status' => 'running',
'destination_type' => StandaloneDocker::class,
'destination_id' => 0,
]);
$database->save();
$user = User::factory()->create();
$rootTeam->members()->attach($user->id, ['role' => 'admin']);
test()->actingAs($user);
session(['currentTeam' => ['id' => $rootTeam->id]]);
return $database;
}
test('instance backup settings render when the instance database has no backup schedule', function () {
settingsBackupInstanceDatabaseWithoutSchedule();
Livewire::test(SettingsBackup::class)
->assertOk()
->assertSet('executions', [])
->assertSee('Backup is not configured')
->assertSee('Configure backup');
});
test('configure backup creates only the missing schedule for an existing instance database', function () {
$database = settingsBackupInstanceDatabaseWithoutSchedule();
$otherTeam = Team::factory()->create();
auth()->user()->teams()->attach($otherTeam->id, ['role' => 'owner']);
session(['currentTeam' => ['id' => $otherTeam->id]]);
Process::fake();
Livewire::test(SettingsBackup::class)
->call('addCoolifyDatabase')
->assertHasNoErrors()
->assertSee('Instance database');
Process::assertNothingRan();
$backup = $database->scheduledBackups()->sole();
expect(StandalonePostgresql::query()->where('name', 'coolify-db')->count())->toBe(1)
->and($backup->id)->not->toBe(0)
->and($backup->team_id)->toBe(0)
->and($backup->enabled)->toBeTruthy()
->and($backup->frequency)->toBe('0 0 * * *')
->and(ScheduledDatabaseBackup::query()->count())->toBe(1);
});