feat(backup): implement custom database type selection and enhance scheduled backups management
This commit is contained in:
		@@ -19,6 +19,8 @@ class ScheduledBackups extends Component
 | 
			
		||||
 | 
			
		||||
    public $s3s;
 | 
			
		||||
 | 
			
		||||
    public string $custom_type = 'mysql';
 | 
			
		||||
 | 
			
		||||
    protected $listeners = ['refreshScheduledBackups'];
 | 
			
		||||
 | 
			
		||||
    protected $queryString = ['selectedBackupId'];
 | 
			
		||||
@@ -49,6 +51,14 @@ class ScheduledBackups extends Component
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function setCustomType()
 | 
			
		||||
    {
 | 
			
		||||
        $this->database->custom_type = $this->custom_type;
 | 
			
		||||
        $this->database->save();
 | 
			
		||||
        $this->dispatch('success', 'Database type set.');
 | 
			
		||||
        $this->refreshScheduledBackups();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function delete($scheduled_backup_id): void
 | 
			
		||||
    {
 | 
			
		||||
        $this->database->scheduledBackups->find($scheduled_backup_id)->delete();
 | 
			
		||||
@@ -62,5 +72,6 @@ class ScheduledBackups extends Component
 | 
			
		||||
        if ($id) {
 | 
			
		||||
            $this->setSelectedBackup($id);
 | 
			
		||||
        }
 | 
			
		||||
        $this->dispatch('refreshScheduledBackups');
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -98,6 +98,7 @@ class Database extends Component
 | 
			
		||||
                'is_log_drain_enabled' => $serviceDatabase->is_log_drain_enabled,
 | 
			
		||||
                'image' => $serviceDatabase->image,
 | 
			
		||||
                'service_id' => $service->id,
 | 
			
		||||
                'is_migrated' => true,
 | 
			
		||||
            ]);
 | 
			
		||||
            $serviceDatabase->delete();
 | 
			
		||||
            DB::commit();
 | 
			
		||||
 
 | 
			
		||||
@@ -24,7 +24,7 @@ class Index extends Component
 | 
			
		||||
 | 
			
		||||
    public $s3s;
 | 
			
		||||
 | 
			
		||||
    protected $listeners = ['generateDockerCompose'];
 | 
			
		||||
    protected $listeners = ['generateDockerCompose', 'refreshScheduledBackups' => '$refresh'];
 | 
			
		||||
 | 
			
		||||
    public function mount()
 | 
			
		||||
    {
 | 
			
		||||
 
 | 
			
		||||
@@ -88,6 +88,7 @@ class ServiceApplicationView extends Component
 | 
			
		||||
                'is_log_drain_enabled' => $serviceApplication->is_log_drain_enabled,
 | 
			
		||||
                'image' => $serviceApplication->image,
 | 
			
		||||
                'service_id' => $service->id,
 | 
			
		||||
                'is_migrated' => true,
 | 
			
		||||
            ]);
 | 
			
		||||
            $serviceApplication->delete();
 | 
			
		||||
            DB::commit();
 | 
			
		||||
 
 | 
			
		||||
@@ -16,6 +16,7 @@ class ServiceDatabase extends BaseModel
 | 
			
		||||
        static::deleting(function ($service) {
 | 
			
		||||
            $service->persistentStorages()->delete();
 | 
			
		||||
            $service->fileStorages()->delete();
 | 
			
		||||
            $service->scheduledBackups()->delete();
 | 
			
		||||
        });
 | 
			
		||||
        static::saving(function ($service) {
 | 
			
		||||
            if ($service->isDirty('status')) {
 | 
			
		||||
@@ -77,6 +78,9 @@ class ServiceDatabase extends BaseModel
 | 
			
		||||
 | 
			
		||||
    public function databaseType()
 | 
			
		||||
    {
 | 
			
		||||
        if (filled($this->custom_type)) {
 | 
			
		||||
            return 'standalone-'.$this->custom_type;
 | 
			
		||||
        }
 | 
			
		||||
        $image = str($this->image)->before(':');
 | 
			
		||||
        if ($image->contains('supabase/postgres')) {
 | 
			
		||||
            $finalImage = 'supabase/postgres';
 | 
			
		||||
@@ -141,6 +145,7 @@ class ServiceDatabase extends BaseModel
 | 
			
		||||
            str($this->databaseType())->contains('postgres') ||
 | 
			
		||||
            str($this->databaseType())->contains('postgis') ||
 | 
			
		||||
            str($this->databaseType())->contains('mariadb') ||
 | 
			
		||||
            str($this->databaseType())->contains('mongo');
 | 
			
		||||
            str($this->databaseType())->contains('mongo') ||
 | 
			
		||||
            filled($this->custom_type);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,33 +1,52 @@
 | 
			
		||||
<div>
 | 
			
		||||
    <div class="flex flex-col gap-2">
 | 
			
		||||
        @forelse($database->scheduledBackups as $backup)
 | 
			
		||||
            @if ($type == 'database')
 | 
			
		||||
                <a class="box"
 | 
			
		||||
                    href="{{ route('project.database.backup.execution', [...$parameters, 'backup_uuid' => $backup->uuid]) }}">
 | 
			
		||||
                    <div class="flex flex-col">
 | 
			
		||||
                        <div>Frequency: {{ $backup->frequency }}
 | 
			
		||||
                            ({{ data_get($backup->server(), 'settings.server_timezone', 'Instance timezone') }})
 | 
			
		||||
                        </div>
 | 
			
		||||
                        <div>Last backup: {{ data_get($backup->latest_log, 'status', 'No backup yet') }}</div>
 | 
			
		||||
        @if ($database->is_migrated && blank($database->custom_type))
 | 
			
		||||
            <div>
 | 
			
		||||
                <div>Select the type of
 | 
			
		||||
                    database to enable automated backups.</div>
 | 
			
		||||
                <div class="pb-4"> If your database is not listed, automated backups are not supported.</div>
 | 
			
		||||
                <form wire:submit="setCustomType" class="flex gap-2 items-end">
 | 
			
		||||
                    <div class="w-96">
 | 
			
		||||
                        <x-forms.select label="Type" id="custom_type">
 | 
			
		||||
                            <option selected value="mysql">MySQL</option>
 | 
			
		||||
                            <option value="mariadb">MariaDB</option>
 | 
			
		||||
                            <option value="postgresql">PostgreSQL</option>
 | 
			
		||||
                            <option value="mongodb">MongoDB</option>
 | 
			
		||||
                        </x-forms.select>
 | 
			
		||||
                    </div>
 | 
			
		||||
                </a>
 | 
			
		||||
            @else
 | 
			
		||||
                <div class="box" wire:click="setSelectedBackup('{{ data_get($backup, 'id') }}')">
 | 
			
		||||
                    <div @class([
 | 
			
		||||
                        'border-coollabs' =>
 | 
			
		||||
                            data_get($backup, 'id') === data_get($selectedBackup, 'id'),
 | 
			
		||||
                        'flex flex-col border-l-2 border-transparent',
 | 
			
		||||
                    ])>
 | 
			
		||||
                        <div>Frequency: {{ $backup->frequency }}
 | 
			
		||||
                            ({{ data_get($backup->server(), 'settings.server_timezone', 'Instance timezone') }})
 | 
			
		||||
                    <x-forms.button type="submit">Set</x-forms.button>
 | 
			
		||||
                </form>
 | 
			
		||||
            </div>
 | 
			
		||||
        @else
 | 
			
		||||
            @forelse($database->scheduledBackups as $backup)
 | 
			
		||||
                @if ($type == 'database')
 | 
			
		||||
                    <a class="box"
 | 
			
		||||
                        href="{{ route('project.database.backup.execution', [...$parameters, 'backup_uuid' => $backup->uuid]) }}">
 | 
			
		||||
                        <div class="flex flex-col">
 | 
			
		||||
                            <div>Frequency: {{ $backup->frequency }}
 | 
			
		||||
                                ({{ data_get($backup->server(), 'settings.server_timezone', 'Instance timezone') }})
 | 
			
		||||
                            </div>
 | 
			
		||||
                            <div>Last backup: {{ data_get($backup->latest_log, 'status', 'No backup yet') }}</div>
 | 
			
		||||
                        </div>
 | 
			
		||||
                    </a>
 | 
			
		||||
                @else
 | 
			
		||||
                    <div class="box" wire:click="setSelectedBackup('{{ data_get($backup, 'id') }}')">
 | 
			
		||||
                        <div @class([
 | 
			
		||||
                            'border-coollabs' =>
 | 
			
		||||
                                data_get($backup, 'id') === data_get($selectedBackup, 'id'),
 | 
			
		||||
                            'flex flex-col border-l-2 border-transparent',
 | 
			
		||||
                        ])>
 | 
			
		||||
                            <div>Frequency: {{ $backup->frequency }}
 | 
			
		||||
                                ({{ data_get($backup->server(), 'settings.server_timezone', 'Instance timezone') }})
 | 
			
		||||
                            </div>
 | 
			
		||||
                            <div>Last backup: {{ data_get($backup->latest_log, 'status', 'No backup yet') }}</div>
 | 
			
		||||
                        </div>
 | 
			
		||||
                        <div>Last backup: {{ data_get($backup->latest_log, 'status', 'No backup yet') }}</div>
 | 
			
		||||
                    </div>
 | 
			
		||||
                </div>
 | 
			
		||||
            @endif
 | 
			
		||||
        @empty
 | 
			
		||||
            <div>No scheduled backups configured.</div>
 | 
			
		||||
        @endforelse
 | 
			
		||||
                @endif
 | 
			
		||||
            @empty
 | 
			
		||||
                <div>No scheduled backups configured.</div>
 | 
			
		||||
            @endforelse
 | 
			
		||||
        @endif
 | 
			
		||||
    </div>
 | 
			
		||||
    @if ($type === 'service-database' && $selectedBackup)
 | 
			
		||||
        <div class="pt-10">
 | 
			
		||||
 
 | 
			
		||||
@@ -138,7 +138,7 @@
 | 
			
		||||
                                    <div class="text-xs">{{ $database->status }}</div>
 | 
			
		||||
                                </div>
 | 
			
		||||
                                <div class="flex items-center px-4">
 | 
			
		||||
                                    @if ($database->isBackupSolutionAvailable())
 | 
			
		||||
                                    @if ($database->isBackupSolutionAvailable() || $database->is_migrated)
 | 
			
		||||
                                        <a class="mx-4 text-xs font-bold hover:underline"
 | 
			
		||||
                                            href="{{ route('project.service.index', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'service_uuid' => $service->uuid, 'stack_service_uuid' => $database->uuid]) }}#backups">
 | 
			
		||||
                                            Backups
 | 
			
		||||
 
 | 
			
		||||
@@ -10,7 +10,7 @@
 | 
			
		||||
            <a class="menu-item" :class="activeTab === 'general' && 'menu-item-active'"
 | 
			
		||||
                @click.prevent="activeTab = 'general'; window.location.hash = 'general'; if(window.location.search) window.location.search = ''"
 | 
			
		||||
                href="#">General</a>
 | 
			
		||||
            @if ($serviceDatabase?->isBackupSolutionAvailable())
 | 
			
		||||
            @if ($serviceDatabase?->isBackupSolutionAvailable() || $serviceDatabase?->is_migrated)
 | 
			
		||||
                <a :class="activeTab === 'backups' && 'menu-item-active'" class="menu-item"
 | 
			
		||||
                    @click.prevent="activeTab = 'backups'; window.location.hash = 'backups'" href="#backups">Backups</a>
 | 
			
		||||
            @endif
 | 
			
		||||
@@ -33,18 +33,20 @@
 | 
			
		||||
                <div x-cloak x-show="activeTab === 'general'" class="h-full">
 | 
			
		||||
                    <livewire:project.service.database :database="$serviceDatabase" />
 | 
			
		||||
                </div>
 | 
			
		||||
                @if ($serviceDatabase->isBackupSolutionAvailable())
 | 
			
		||||
                @if ($serviceDatabase?->isBackupSolutionAvailable() || $serviceDatabase?->is_migrated)
 | 
			
		||||
                    <div x-cloak x-show="activeTab === 'backups'">
 | 
			
		||||
                        <div class="flex gap-2 ">
 | 
			
		||||
                            <h2 class="pb-4">Scheduled Backups</h2>
 | 
			
		||||
                            <x-modal-input buttonTitle="+ Add" title="New Scheduled Backup">
 | 
			
		||||
                                <livewire:project.database.create-scheduled-backup :database="$serviceDatabase" />
 | 
			
		||||
                            </x-modal-input>
 | 
			
		||||
                            @if (filled($serviceDatabase->custom_type))
 | 
			
		||||
                                <x-modal-input buttonTitle="+ Add" title="New Scheduled Backup">
 | 
			
		||||
                                    <livewire:project.database.create-scheduled-backup :database="$serviceDatabase" />
 | 
			
		||||
                                </x-modal-input>
 | 
			
		||||
                            @endif
 | 
			
		||||
                        </div>
 | 
			
		||||
                        <livewire:project.database.scheduled-backups :database="$serviceDatabase" />
 | 
			
		||||
                    </div>
 | 
			
		||||
                @endif
 | 
			
		||||
            </div>
 | 
			
		||||
        @endisset
 | 
			
		||||
    </div>
 | 
			
		||||
</div>
 | 
			
		||||
</div>
 | 
			
		||||
 
 | 
			
		||||
@@ -13,8 +13,7 @@
 | 
			
		||||
                confirmationLabel="Please confirm the execution of the actions by entering the Service Application Name below"
 | 
			
		||||
                shortConfirmationLabel="Service Application Name" step3ButtonText="Permanently Delete" />
 | 
			
		||||
            <x-modal-confirmation title="Confirm Service Application Deletion?" buttonTitle="Delete" isErrorButton
 | 
			
		||||
                submitAction="delete" {{-- :checkboxes="$checkboxes"  --}} :actions="['The selected service application container will be stopped and permanently deleted.']"
 | 
			
		||||
                confirmationText="{{ Str::headline($application->name) }}"
 | 
			
		||||
                submitAction="delete" :actions="['The selected service application container will be stopped and permanently deleted.']" confirmationText="{{ Str::headline($application->name) }}"
 | 
			
		||||
                confirmationLabel="Please confirm the execution of the actions by entering the Service Application Name below"
 | 
			
		||||
                shortConfirmationLabel="Service Application Name" step3ButtonText="Permanently Delete" />
 | 
			
		||||
        </div>
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user