Files
coolify/app/Livewire/Project/Database/CreateScheduledBackup.php
T
Andras BacsaiandClaude Opus 5.5 232e8b8616 fix(backups): back up all databases by default for Odoo services
Odoo keeps its data in databases that the user creates in Odoo, so the
POSTGRES_DB=postgres database that the backup used was empty. Also show the
sections of the edit backup schedule modal without nested cards.

Closes #7555

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 22:49:38 +01:00

151 lines
5.2 KiB
PHP

<?php
namespace App\Livewire\Project\Database;
use App\Models\ScheduledDatabaseBackup;
use App\Models\Service;
use App\Models\ServiceDatabase;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Livewire\Attributes\Locked;
use Livewire\Attributes\Validate;
use Livewire\Component;
class CreateScheduledBackup extends Component
{
use AuthorizesRequests;
#[Validate(['required', 'string'])]
public $frequency;
#[Locked]
public $database = null;
#[Locked]
public ?Service $service = null;
public ?string $selectedDatabaseUuid = null;
public bool $enabled = true;
public function mount(): void
{
if ($this->service) {
$this->authorize('view', $this->service);
$this->selectedDatabaseUuid = $this->availableDatabases()->first()?->uuid;
}
}
public function submit()
{
try {
$database = $this->selectedDatabase();
if (! $database) {
$this->addError('selectedDatabaseUuid', 'Select a database owned by this service.');
return;
}
$this->authorize('manageBackups', $database);
if (! $database->isBackupSolutionAvailable()) {
$this->dispatch('error', 'Scheduled backups are not supported for this database type.');
return;
}
$this->validate();
$isValid = validate_cron_expression($this->frequency);
if (! $isValid) {
$this->dispatch('error', 'Invalid Cron / Human expression.');
return;
}
$payload = [
'enabled' => true,
'frequency' => $this->frequency,
'save_s3' => false,
's3_storage_id' => null,
'database_id' => $database->id,
'database_type' => $database->getMorphClass(),
'team_id' => $database->team()->id,
];
if ($database->type() === 'standalone-postgresql') {
$payload['databases_to_backup'] = $database->postgres_db;
} elseif ($database->type() === 'standalone-mysql') {
$payload['databases_to_backup'] = $database->mysql_database;
} elseif ($database->type() === 'standalone-mariadb') {
$payload['databases_to_backup'] = $database->mariadb_database;
} elseif ($database->type() === 'standalone-clickhouse') {
$payload['databases_to_backup'] = $database->clickhouse_db;
}
// Odoo keeps its data in databases that the user creates in Odoo, so the database
// that the container env names (POSTGRES_DB=postgres) stays empty.
if ($database instanceof ServiceDatabase && $database->service?->service_type === 'odoo') {
$payload['dump_all'] = true;
}
$databaseBackup = ScheduledDatabaseBackup::create($payload);
auditLog('ui.database.backup_schedule_created', [
'team_id' => $database->team()?->id,
'database_uuid' => $database->uuid,
'database_name' => $database->name,
'backup_uuid' => $databaseBackup->uuid,
'frequency' => $databaseBackup->frequency,
'save_s3' => (bool) $databaseBackup->save_s3,
]);
if ($database->getMorphClass() === ServiceDatabase::class) {
$service = $database->service;
$this->redirectRoute('project.service.volume-backups.index', [
'project_uuid' => $service->project()->uuid,
'environment_uuid' => $service->environment->uuid,
'service_uuid' => $service->uuid,
'backup_uuid' => $databaseBackup->uuid,
], navigate: true);
} else {
$this->redirectRoute('project.database.backup.execution', [
'project_uuid' => $database->project()->uuid,
'environment_uuid' => $database->environment->uuid,
'database_uuid' => $database->uuid,
'backup_uuid' => $databaseBackup->uuid,
], navigate: true);
}
} catch (\Throwable $e) {
return handleError($e, $this);
} finally {
$this->frequency = '';
}
}
public function render()
{
return view('livewire.project.database.create-scheduled-backup', [
'databaseOptions' => $this->availableDatabases()->map(fn (ServiceDatabase $database): array => [
'value' => $database->uuid,
'label' => $database->human_name ?: $database->name,
])->values()->all(),
]);
}
private function availableDatabases()
{
if (! $this->service) {
return collect();
}
return $this->service->databases->filter(fn (ServiceDatabase $database): bool => $database->isBackupSolutionAvailable());
}
private function selectedDatabase()
{
if (! $this->service) {
return $this->database;
}
return $this->availableDatabases()->firstWhere('uuid', $this->selectedDatabaseUuid);
}
}