mirror of
https://github.com/tiennm99/coolify.git
synced 2026-10-20 00:15:06 +00:00
Merge remote-tracking branch 'origin/next' into feat/noindex-domains
This commit is contained in:
commit
003e44d123
1274 files changed
+156179
-25249
No files matched your search
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Livewire\Project\Service;
|
||||
|
||||
use App\Models\ScheduledDatabaseBackup;
|
||||
use App\Models\Service;
|
||||
use App\Models\ServiceDatabase;
|
||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||
@@ -17,16 +18,28 @@ class DatabaseBackups extends Component
|
||||
|
||||
public array $parameters;
|
||||
|
||||
public array $backupParameters = [];
|
||||
|
||||
public array $query;
|
||||
|
||||
public bool $isImportSupported = false;
|
||||
|
||||
public ?ScheduledDatabaseBackup $backup = null;
|
||||
|
||||
public string $section = 'index';
|
||||
|
||||
public $s3s;
|
||||
|
||||
protected $listeners = ['refreshScheduledBackups' => '$refresh'];
|
||||
|
||||
public function mount()
|
||||
{
|
||||
try {
|
||||
$this->parameters = get_route_parameters();
|
||||
$this->parameters = array_filter(
|
||||
get_route_parameters(),
|
||||
fn (string $key): bool => $key !== 'backup_uuid',
|
||||
ARRAY_FILTER_USE_KEY,
|
||||
);
|
||||
$this->query = request()->query();
|
||||
$project = currentTeam()
|
||||
->projects()
|
||||
@@ -58,6 +71,21 @@ class DatabaseBackups extends Component
|
||||
$dbType = $this->serviceDatabase->databaseType();
|
||||
$supportedTypes = ['mysql', 'mariadb', 'postgres', 'mongo'];
|
||||
$this->isImportSupported = collect($supportedTypes)->contains(fn ($type) => str_contains($dbType, $type));
|
||||
|
||||
if (request()->route('backup_uuid')) {
|
||||
$this->backup = $this->serviceDatabase->scheduledBackups()
|
||||
->where('uuid', request()->route('backup_uuid'))
|
||||
->firstOrFail();
|
||||
$this->s3s = currentTeam()->s3s;
|
||||
$this->backupParameters = [...$this->parameters, 'backup_uuid' => $this->backup->uuid];
|
||||
$this->section = match (request()->route()?->getName()) {
|
||||
'project.service.database.backup.s3' => 's3',
|
||||
'project.service.database.backup.retention' => 'retention',
|
||||
'project.service.database.backup.executions' => 'executions',
|
||||
'project.service.database.backup.danger' => 'danger',
|
||||
default => 'general',
|
||||
};
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -77,7 +77,6 @@ class EditCompose extends Component
|
||||
{
|
||||
try {
|
||||
$this->authorize('update', $this->service);
|
||||
$this->dispatch('info', 'Saving new docker compose...');
|
||||
$this->dispatch('saveCompose', $this->dockerComposeRaw);
|
||||
$this->dispatch('refreshStorages');
|
||||
} catch (\Throwable $e) {
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace App\Livewire\Project\Service;
|
||||
|
||||
use App\Models\Application;
|
||||
use App\Models\LocalFileVolume;
|
||||
use App\Models\ScheduledVolumeBackup;
|
||||
use App\Models\ServiceApplication;
|
||||
use App\Models\ServiceDatabase;
|
||||
use App\Models\StandaloneClickhouse;
|
||||
@@ -15,6 +16,7 @@ use App\Models\StandaloneMysql;
|
||||
use App\Models\StandalonePostgresql;
|
||||
use App\Models\StandaloneRedis;
|
||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||
use Livewire\Attributes\On;
|
||||
use Livewire\Attributes\Validate;
|
||||
use Livewire\Component;
|
||||
|
||||
@@ -34,6 +36,10 @@ class FileStorage extends Component
|
||||
|
||||
public bool $isReadOnly = false;
|
||||
|
||||
public bool $hasEnabledBackup = false;
|
||||
|
||||
public ?string $backupUrl = null;
|
||||
|
||||
#[Validate(['nullable'])]
|
||||
public ?string $content = null;
|
||||
|
||||
@@ -65,6 +71,53 @@ class FileStorage extends Component
|
||||
|
||||
$this->isReadOnly = $this->fileStorage->shouldBeReadOnlyInUI() || $this->fileStorage->is_too_large;
|
||||
$this->syncData();
|
||||
$this->refreshBackupStatus();
|
||||
}
|
||||
|
||||
#[On('refreshVolumeBackups')]
|
||||
public function refreshBackupStatus(): void
|
||||
{
|
||||
$backup = $this->fileStorage->is_directory
|
||||
? $this->fileStorage->scheduledBackups()->first()
|
||||
: null;
|
||||
|
||||
$this->hasEnabledBackup = $backup?->enabled ?? false;
|
||||
$this->backupUrl = null;
|
||||
|
||||
if (! $this->hasEnabledBackup) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->resource instanceof ServiceDatabase) {
|
||||
$this->backupUrl = route('project.service.database.backups', [
|
||||
'project_uuid' => $this->resource->service->project()->uuid,
|
||||
'environment_uuid' => $this->resource->service->environment->uuid,
|
||||
'service_uuid' => $this->resource->service->uuid,
|
||||
'stack_service_uuid' => $this->resource->uuid,
|
||||
]);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (! $this->resource instanceof Application) {
|
||||
$this->hasEnabledBackup = false;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$parameters = [
|
||||
'project_uuid' => $this->resource->project()->uuid,
|
||||
'environment_uuid' => $this->resource->environment->uuid,
|
||||
'application_uuid' => $this->resource->uuid,
|
||||
];
|
||||
$hasOtherBackups = ScheduledVolumeBackup::query()
|
||||
->forApplication($this->resource)
|
||||
->where('id', '!=', $backup->id)
|
||||
->exists();
|
||||
|
||||
$this->backupUrl = $hasOtherBackups
|
||||
? route('project.application.backup.index', [...$parameters, 'search' => $this->fileStorage->fs_path])
|
||||
: route('project.application.backup.show', [...$parameters, 'backup_uuid' => $backup->uuid]);
|
||||
}
|
||||
|
||||
public function syncData(bool $toModel = false): void
|
||||
@@ -135,6 +188,10 @@ class FileStorage extends Component
|
||||
try {
|
||||
$this->authorize('update', $this->resource);
|
||||
|
||||
if ($this->fileStorage->scheduledBackups()->exists()) {
|
||||
throw new \RuntimeException('Delete this directory backup schedule and its archives before converting it to a file.');
|
||||
}
|
||||
|
||||
if ($this->fileStorage->is_host_file) {
|
||||
throw new \Exception('Host file mounts are bind-only and cannot be converted.');
|
||||
}
|
||||
@@ -162,6 +219,12 @@ class FileStorage extends Component
|
||||
return 'The provided password is incorrect.';
|
||||
}
|
||||
|
||||
if ($this->fileStorage->scheduledBackups()->exists()) {
|
||||
$this->dispatch('error', 'Delete this directory backup schedule and its archives before deleting the directory.');
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
$message = 'File deleted.';
|
||||
if ($this->fileStorage->is_directory) {
|
||||
@@ -174,6 +237,7 @@ class FileStorage extends Component
|
||||
$this->fileStorage->deleteStorageOnServer();
|
||||
}
|
||||
$this->fileStorage->delete();
|
||||
$this->dispatch('configurationChanged');
|
||||
$this->dispatch('success', $message);
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
|
||||
@@ -282,6 +282,18 @@ class Index extends Component
|
||||
}
|
||||
}
|
||||
|
||||
public function enablePublicAccess(): void
|
||||
{
|
||||
$this->isPublic = true;
|
||||
$this->instantSave();
|
||||
}
|
||||
|
||||
public function disablePublicAccess(): void
|
||||
{
|
||||
$this->isPublic = false;
|
||||
$this->instantSave();
|
||||
}
|
||||
|
||||
public function instantSave()
|
||||
{
|
||||
try {
|
||||
|
||||
@@ -140,6 +140,7 @@ class StackForm extends Component
|
||||
{
|
||||
$this->dockerComposeRaw = $raw;
|
||||
$this->submit(notify: true);
|
||||
$this->dispatch('compose-save-finished');
|
||||
}
|
||||
|
||||
public function instantSave()
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire\Project\Service;
|
||||
|
||||
use App\Models\Service;
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Livewire\Component;
|
||||
|
||||
class Status extends Component
|
||||
{
|
||||
public Service $service;
|
||||
|
||||
public function getListeners(): array
|
||||
{
|
||||
$teamId = auth()->user()->currentTeam()->id;
|
||||
|
||||
return [
|
||||
"echo-private:team.{$teamId},ServiceStatusChanged" => 'refreshStatus',
|
||||
"echo-private:team.{$teamId},ServiceChecked" => 'refreshStatus',
|
||||
];
|
||||
}
|
||||
|
||||
public function refreshStatus(): void
|
||||
{
|
||||
$this->service->refresh()->load(['applications', 'databases']);
|
||||
}
|
||||
|
||||
public function render(): View
|
||||
{
|
||||
return view('livewire.project.service.status');
|
||||
}
|
||||
}
|
||||
@@ -37,6 +37,14 @@ class Storage extends Component
|
||||
|
||||
public string $file_storage_directory_destination = '';
|
||||
|
||||
public string $activeTab = 'volumes';
|
||||
|
||||
public int $cachedVolumeCount = 0;
|
||||
|
||||
public int $cachedFileCount = 0;
|
||||
|
||||
public int $cachedDirectoryCount = 0;
|
||||
|
||||
public function getListeners()
|
||||
{
|
||||
$teamId = auth()->user()->currentTeam()->id;
|
||||
@@ -57,12 +65,18 @@ class Storage extends Component
|
||||
}
|
||||
|
||||
if ($this->resource->getMorphClass() === Application::class) {
|
||||
if ($this->resource->destination->server->isSwarm()) {
|
||||
$this->resource->loadMissing('destination.server', 'environment.project');
|
||||
if ($this->resource->destination?->server?->isSwarm()) {
|
||||
$this->isSwarm = true;
|
||||
}
|
||||
}
|
||||
|
||||
$this->refreshStorages();
|
||||
// Counts only on mount — child All (volumes) / file list load their own payloads.
|
||||
$this->loadVolumeCount();
|
||||
$this->loadFileStorageMetaCounts();
|
||||
$this->activeTab = $this->resolveDefaultTab();
|
||||
$this->fileStorage = collect();
|
||||
$this->loadFileStorageForActiveTab();
|
||||
}
|
||||
|
||||
public function refreshStoragesFromEvent()
|
||||
@@ -73,37 +87,110 @@ class Storage extends Component
|
||||
|
||||
public function refreshStorages()
|
||||
{
|
||||
$this->fileStorage = $this->resource->fileStorages()->get()->each(function (LocalFileVolume $fs) {
|
||||
// Avoid loading full volume models onto this parent (child All owns that snapshot).
|
||||
$this->resource->unsetRelation('persistentStorages');
|
||||
$this->loadVolumeCount();
|
||||
$this->loadFileStorageMetaCounts();
|
||||
$this->loadFileStorageForActiveTab();
|
||||
}
|
||||
|
||||
public function setActiveTab(string $tab): void
|
||||
{
|
||||
if (! in_array($tab, ['volumes', 'files', 'directories'], true)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->activeTab = $tab;
|
||||
$this->loadFileStorageForActiveTab();
|
||||
}
|
||||
|
||||
private function resolveDefaultTab(): string
|
||||
{
|
||||
if ($this->volumeCount > 0) {
|
||||
return 'volumes';
|
||||
}
|
||||
|
||||
if ($this->fileCount > 0) {
|
||||
return 'files';
|
||||
}
|
||||
|
||||
if ($this->directoryCount > 0) {
|
||||
return 'directories';
|
||||
}
|
||||
|
||||
return 'volumes';
|
||||
}
|
||||
|
||||
private function loadVolumeCount(): void
|
||||
{
|
||||
$this->cachedVolumeCount = $this->resource->persistentStorages()->count();
|
||||
}
|
||||
|
||||
/**
|
||||
* Counts only — avoids loading file contents into the Livewire snapshot on the volumes tab.
|
||||
*/
|
||||
private function loadFileStorageMetaCounts(): void
|
||||
{
|
||||
$this->cachedFileCount = $this->resource->fileStorages()->where('is_directory', false)->count();
|
||||
$this->cachedDirectoryCount = $this->resource->fileStorages()->where('is_directory', true)->count();
|
||||
}
|
||||
|
||||
/**
|
||||
* Load full file/directory mounts only for the active tab (content only on files).
|
||||
*/
|
||||
private function loadFileStorageForActiveTab(): void
|
||||
{
|
||||
if ($this->activeTab === 'volumes') {
|
||||
// Keep snapshot small while the volumes tab is shown.
|
||||
$this->fileStorage = collect();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$query = $this->resource->fileStorages();
|
||||
|
||||
if ($this->activeTab === 'files') {
|
||||
$query->where('is_directory', false);
|
||||
} else {
|
||||
$query->where('is_directory', true);
|
||||
}
|
||||
|
||||
$this->fileStorage = $query->get()->each(function (LocalFileVolume $fs): void {
|
||||
if ($this->activeTab !== 'files') {
|
||||
$fs->content = null;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (strlen((string) $fs->content) > LocalFileVolume::MAX_CONTENT_SIZE) {
|
||||
$fs->content = LocalFileVolume::TOO_LARGE_PLACEHOLDER;
|
||||
}
|
||||
});
|
||||
$this->resource->load('persistentStorages.resource');
|
||||
}
|
||||
|
||||
public function getFilesProperty()
|
||||
{
|
||||
return $this->fileStorage->where('is_directory', false);
|
||||
return collect($this->fileStorage)->where('is_directory', false);
|
||||
}
|
||||
|
||||
public function getDirectoriesProperty()
|
||||
{
|
||||
return $this->fileStorage->where('is_directory', true);
|
||||
return collect($this->fileStorage)->where('is_directory', true);
|
||||
}
|
||||
|
||||
public function getVolumeCountProperty()
|
||||
{
|
||||
return $this->resource->persistentStorages()->count();
|
||||
return $this->cachedVolumeCount;
|
||||
}
|
||||
|
||||
public function getFileCountProperty()
|
||||
{
|
||||
return $this->files->count();
|
||||
return $this->cachedFileCount;
|
||||
}
|
||||
|
||||
public function getDirectoryCountProperty()
|
||||
{
|
||||
return $this->directories->count();
|
||||
return $this->cachedDirectoryCount;
|
||||
}
|
||||
|
||||
public function submitPersistentVolume()
|
||||
@@ -130,11 +217,13 @@ class Storage extends Component
|
||||
'resource_id' => $this->resource->id,
|
||||
'resource_type' => $this->resource->getMorphClass(),
|
||||
]);
|
||||
$this->resource->refresh();
|
||||
$this->clearForm();
|
||||
$this->activeTab = 'volumes';
|
||||
$this->refreshStorages();
|
||||
$this->dispatch('configurationChanged');
|
||||
$this->dispatch('success', 'Volume added successfully');
|
||||
$this->dispatch('closeStorageModal', 'volume');
|
||||
$this->clearForm();
|
||||
$this->refreshStorages();
|
||||
$this->dispatch('refreshStorages');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
@@ -163,10 +252,13 @@ class Storage extends Component
|
||||
'resource_type' => get_class($this->resource),
|
||||
]);
|
||||
|
||||
$this->clearForm();
|
||||
$this->activeTab = 'files';
|
||||
$this->refreshStorages();
|
||||
$this->dispatch('configurationChanged');
|
||||
$this->dispatch('success', 'File mount added successfully');
|
||||
$this->dispatch('closeStorageModal', 'file');
|
||||
$this->clearForm();
|
||||
$this->refreshStorages();
|
||||
$this->dispatch('refreshStorages');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
@@ -195,10 +287,13 @@ class Storage extends Component
|
||||
'resource_type' => get_class($this->resource),
|
||||
]);
|
||||
|
||||
$this->clearForm();
|
||||
$this->activeTab = 'files';
|
||||
$this->refreshStorages();
|
||||
$this->dispatch('configurationChanged');
|
||||
$this->dispatch('success', 'Host file mount added successfully');
|
||||
$this->dispatch('closeStorageModal', 'host-file');
|
||||
$this->clearForm();
|
||||
$this->refreshStorages();
|
||||
$this->dispatch('refreshStorages');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
@@ -231,10 +326,13 @@ class Storage extends Component
|
||||
'resource_type' => get_class($this->resource),
|
||||
]);
|
||||
|
||||
$this->clearForm();
|
||||
$this->activeTab = 'directories';
|
||||
$this->refreshStorages();
|
||||
$this->dispatch('configurationChanged');
|
||||
$this->dispatch('success', 'Directory mount added successfully');
|
||||
$this->dispatch('closeStorageModal', 'directory');
|
||||
$this->clearForm();
|
||||
$this->refreshStorages();
|
||||
$this->dispatch('refreshStorages');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,144 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire\Project\Service\VolumeBackup;
|
||||
|
||||
use App\Models\LocalFileVolume;
|
||||
use App\Models\LocalPersistentVolume;
|
||||
use App\Models\Service;
|
||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||
use Illuminate\Support\Collection;
|
||||
use Livewire\Attributes\Locked;
|
||||
use Livewire\Component;
|
||||
|
||||
class Create extends Component
|
||||
{
|
||||
use AuthorizesRequests;
|
||||
|
||||
#[Locked]
|
||||
public Service $service;
|
||||
|
||||
public ?string $selectedTargetKey = null;
|
||||
|
||||
public ?string $targetKey = null;
|
||||
|
||||
public bool $targetLocked = false;
|
||||
|
||||
public string $frequency = 'daily';
|
||||
|
||||
public Collection $targets;
|
||||
|
||||
protected function rules(): array
|
||||
{
|
||||
return [
|
||||
'targetKey' => ['required', 'string', 'regex:/^(volume|directory):[1-9][0-9]*$/'],
|
||||
'frequency' => ['required', 'string'],
|
||||
];
|
||||
}
|
||||
|
||||
public function mount(): void
|
||||
{
|
||||
$this->authorize('view', $this->service);
|
||||
$this->targetLocked = $this->selectedTargetKey !== null;
|
||||
$this->targetKey = $this->selectedTargetKey;
|
||||
$this->targets = $this->availableTargets();
|
||||
$this->targetKey ??= data_get($this->targets->first(), 'key');
|
||||
$this->loadSelectedBackup();
|
||||
}
|
||||
|
||||
public function updatedTargetKey(): void
|
||||
{
|
||||
$this->loadSelectedBackup();
|
||||
}
|
||||
|
||||
public function submit(): void
|
||||
{
|
||||
$this->authorize('update', $this->service);
|
||||
$this->validate();
|
||||
$target = $this->selectedTarget();
|
||||
|
||||
if (! $target) {
|
||||
$this->addError('targetKey', 'Select a volume or directory owned by this service.');
|
||||
|
||||
return;
|
||||
}
|
||||
if (! validate_cron_expression($this->frequency)) {
|
||||
$this->addError('frequency', 'The frequency must be a valid cron or human expression.');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$backup = $target->scheduledBackups()->updateOrCreate([], [
|
||||
'team_id' => currentTeam()->id,
|
||||
'frequency' => $this->frequency,
|
||||
'enabled' => true,
|
||||
]);
|
||||
$this->dispatch('success', $backup->wasRecentlyCreated ? 'Scheduled storage backup created.' : 'Scheduled storage backup updated.');
|
||||
redirectRoute($this, 'project.service.volume-backups.show', [
|
||||
'project_uuid' => $this->service->project()->uuid,
|
||||
'environment_uuid' => $this->service->environment->uuid,
|
||||
'service_uuid' => $this->service->uuid,
|
||||
'backup_uuid' => $backup->uuid,
|
||||
]);
|
||||
} catch (\Throwable $exception) {
|
||||
handleError($exception, $this);
|
||||
}
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.project.application.backup.create');
|
||||
}
|
||||
|
||||
private function availableTargets(): Collection
|
||||
{
|
||||
$resources = $this->service->applications()->get()->concat($this->service->databases()->get());
|
||||
$targets = collect();
|
||||
|
||||
foreach ($resources as $resource) {
|
||||
$label = str($resource->name)->headline();
|
||||
$targets->push(...$resource->persistentStorages()->orderBy('name')->get()->map(fn (LocalPersistentVolume $volume): array => [
|
||||
'key' => 'volume:'.$volume->id,
|
||||
'type' => 'Volume · '.$label,
|
||||
'name' => $volume->name,
|
||||
]));
|
||||
$targets->push(...$resource->fileStorages()
|
||||
->where('is_directory', true)
|
||||
->where('is_host_file', false)
|
||||
->orderBy('fs_path')
|
||||
->get()
|
||||
->map(fn (LocalFileVolume $directory): array => [
|
||||
'key' => 'directory:'.$directory->id,
|
||||
'type' => 'Directory · '.$label,
|
||||
'name' => $directory->fs_path,
|
||||
]));
|
||||
}
|
||||
|
||||
return $this->targetLocked
|
||||
? $targets->where('key', $this->selectedTargetKey)->values()
|
||||
: $targets->values();
|
||||
}
|
||||
|
||||
private function loadSelectedBackup(): void
|
||||
{
|
||||
$backup = $this->selectedTarget()?->scheduledBackups()->first();
|
||||
if ($backup) {
|
||||
$this->frequency = $backup->frequency;
|
||||
}
|
||||
}
|
||||
|
||||
private function selectedTarget(): LocalPersistentVolume|LocalFileVolume|null
|
||||
{
|
||||
[$type, $id] = array_pad(explode(':', (string) $this->targetKey, 2), 2, null);
|
||||
if (! ctype_digit((string) $id) || ! in_array($type, ['volume', 'directory'], true)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$target = ($type === 'volume' ? LocalPersistentVolume::query() : LocalFileVolume::query())->find((int) $id);
|
||||
if (! $target || ($type === 'directory' && (! $target->is_directory || $target->is_host_file))) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $target->resource?->service_id === $this->service->id ? $target : null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire\Project\Service\VolumeBackup;
|
||||
|
||||
use App\Models\ScheduledDatabaseBackup;
|
||||
use App\Models\ScheduledVolumeBackup;
|
||||
use App\Models\Service;
|
||||
use App\Models\ServiceDatabase;
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||
use Livewire\Component;
|
||||
|
||||
class Index extends Component
|
||||
{
|
||||
use AuthorizesRequests;
|
||||
|
||||
public Service $service;
|
||||
|
||||
public array $parameters;
|
||||
|
||||
public string $search = '';
|
||||
|
||||
protected $listeners = ['refreshVolumeBackups' => '$refresh'];
|
||||
|
||||
public function mount(): void
|
||||
{
|
||||
$this->service = $this->findService();
|
||||
$this->authorize('view', $this->service);
|
||||
$this->parameters = get_route_parameters();
|
||||
$this->search = request()->string('search')->toString();
|
||||
}
|
||||
|
||||
public function render(): View
|
||||
{
|
||||
$databaseTargets = $this->service->databases->filter(
|
||||
fn (ServiceDatabase $database): bool => $database->isBackupSolutionAvailable(),
|
||||
);
|
||||
|
||||
$databaseBackups = ScheduledDatabaseBackup::query()
|
||||
->with(['database', 'latest_log', 's3'])
|
||||
->withCount('executions')
|
||||
->where('database_type', (new ServiceDatabase)->getMorphClass())
|
||||
->whereHasMorph('database', [ServiceDatabase::class], fn ($query) => $query->where('service_id', $this->service->id))
|
||||
->latest()
|
||||
->get();
|
||||
|
||||
$backups = ScheduledVolumeBackup::query()
|
||||
->with(['backupable.resource', 'latestExecution', 's3'])
|
||||
->withCount('executions')
|
||||
->forService($this->service)
|
||||
->latest()
|
||||
->get();
|
||||
|
||||
return view('livewire.project.service.volume-backup.index', [
|
||||
'backups' => $backups,
|
||||
'databaseBackups' => $databaseBackups,
|
||||
'databaseTargets' => $databaseTargets,
|
||||
]);
|
||||
}
|
||||
|
||||
private function findService(): Service
|
||||
{
|
||||
$project = currentTeam()->projects()->where('uuid', request()->route('project_uuid'))->firstOrFail();
|
||||
$environment = $project->environments()->where('uuid', request()->route('environment_uuid'))->firstOrFail();
|
||||
|
||||
return $environment->services()
|
||||
->with(['server.settings', 'environment.project'])
|
||||
->where('uuid', request()->route('service_uuid'))
|
||||
->firstOrFail();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire\Project\Service\VolumeBackup;
|
||||
|
||||
use App\Models\ScheduledVolumeBackup;
|
||||
use App\Models\Service;
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||
use Livewire\Component;
|
||||
|
||||
class Show extends Component
|
||||
{
|
||||
use AuthorizesRequests;
|
||||
|
||||
public Service $service;
|
||||
|
||||
public ScheduledVolumeBackup $backup;
|
||||
|
||||
public array $parameters;
|
||||
|
||||
public string $section = 'general';
|
||||
|
||||
public function mount(): void
|
||||
{
|
||||
$project = currentTeam()->projects()->where('uuid', request()->route('project_uuid'))->firstOrFail();
|
||||
$environment = $project->environments()->where('uuid', request()->route('environment_uuid'))->firstOrFail();
|
||||
$this->service = $environment->services()
|
||||
->with(['server.settings', 'environment.project'])
|
||||
->where('uuid', request()->route('service_uuid'))
|
||||
->firstOrFail();
|
||||
$this->authorize('view', $this->service);
|
||||
|
||||
$this->backup = ScheduledVolumeBackup::query()
|
||||
->with('backupable.resource')
|
||||
->where('uuid', request()->route('backup_uuid'))
|
||||
->forService($this->service)
|
||||
->firstOrFail();
|
||||
$this->parameters = get_route_parameters();
|
||||
$this->section = match (request()->route()?->getName()) {
|
||||
'project.service.volume-backups.s3' => 's3',
|
||||
'project.service.volume-backups.retention' => 'retention',
|
||||
'project.service.volume-backups.executions' => 'executions',
|
||||
'project.service.volume-backups.danger' => 'danger',
|
||||
default => 'general',
|
||||
};
|
||||
}
|
||||
|
||||
public function render(): View
|
||||
{
|
||||
return view('livewire.project.service.volume-backup.show');
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user