diff --git a/app/Livewire/Project/Shared/FileBrowser.php b/app/Livewire/Project/Shared/FileBrowser.php index c07a38ce82..0daf5a8979 100644 --- a/app/Livewire/Project/Shared/FileBrowser.php +++ b/app/Livewire/Project/Shared/FileBrowser.php @@ -40,8 +40,14 @@ class FileBrowser extends Component public $upload; - public function mount($resource): void + /** @var array */ + public array $parameters = []; + + public function mount($resource = null): void { + $this->parameters = get_route_parameters(); + $resource = $resource ?? $this->resolveResourceFromRoute(); + // Security gate first, before any server or container resolution. $this->authorize('canAccessTerminal'); $this->authorize('view', $resource); @@ -255,6 +261,29 @@ class FileBrowser extends Component }; } + protected function resolveResourceFromRoute() + { + $parameters = get_route_parameters(); + $teamId = data_get(auth()->user()->currentTeam(), 'id'); + + if ($uuid = data_get($parameters, 'application_uuid')) { + return Application::ownedByCurrentTeam()->where('uuid', $uuid)->firstOrFail(); + } + if ($uuid = data_get($parameters, 'service_uuid')) { + return Service::ownedByCurrentTeam()->where('uuid', $uuid)->firstOrFail(); + } + if ($uuid = data_get($parameters, 'database_uuid')) { + $resource = getResourceByUuid($uuid, $teamId); + if (is_null($resource)) { + abort(404); + } + + return $resource; + } + + abort(404); + } + protected function resolveType($resource): string { return match (true) { diff --git a/resources/views/components/application/configuration-sidebar.blade.php b/resources/views/components/application/configuration-sidebar.blade.php index 078f3cafa4..36578a043b 100644 --- a/resources/views/components/application/configuration-sidebar.blade.php +++ b/resources/views/components/application/configuration-sidebar.blade.php @@ -51,6 +51,12 @@ 'navigate' => false, 'visible' => ! $application->destination->server->isSwarm() && auth()->user()?->can('canAccessTerminal'), ], + [ + 'label' => 'Files', + 'route' => 'project.application.files', + 'active' => $currentRoute === 'project.application.files', + 'visible' => ! $application->destination->server->isSwarm() && auth()->user()?->can('canAccessTerminal'), + ], [ 'label' => 'Deployment Logs', 'route' => 'project.application.deployment.index', @@ -142,6 +148,7 @@ 'Persistent Storage' => 'storages', 'Backups' => 'database', 'Terminal' => 'browser-terminal', + 'Files' => 'folder', 'Deployment Logs' => 'time-back', 'Runtime Logs' => 'unordered-list', 'Git Source' => 'sources', @@ -161,7 +168,7 @@ // Discord-style groups for the settings sidebar $menuGroups = [ 'Settings' => ['General', 'Domains', 'Environment Variables', 'Persistent Storage', 'Advanced', 'Swarm', 'Healthcheck'], - 'Observe & troubleshoot' => ['Runtime Logs', 'Deployment Logs', 'Terminal', 'Metrics'], + 'Observe & troubleshoot' => ['Runtime Logs', 'Deployment Logs', 'Terminal', 'Files', 'Metrics'], 'Deploy' => ['Git Source', 'Servers', 'Preview Deployments'], 'Automation' => ['Scheduled Tasks', 'Webhooks', 'Backups'], 'Operations' => ['Resource Operations', 'Resource Limits', 'Rollback', 'Tags', 'Danger Zone'], diff --git a/resources/views/components/database/configuration-sidebar.blade.php b/resources/views/components/database/configuration-sidebar.blade.php index 2c62641f85..cfcff9b605 100644 --- a/resources/views/components/database/configuration-sidebar.blade.php +++ b/resources/views/components/database/configuration-sidebar.blade.php @@ -16,6 +16,7 @@ ['label' => 'Servers', 'route' => 'project.database.servers', 'icon' => 'servers'], ['label' => 'Runtime Logs', 'route' => 'project.database.logs', 'icon' => 'unordered-list', 'navigate' => false], ['label' => 'Terminal', 'route' => 'project.database.command', 'icon' => 'browser-terminal', 'navigate' => false, 'visible' => auth()->user()?->can('canAccessTerminal')], + ['label' => 'Files', 'route' => 'project.database.files', 'icon' => 'folder', 'visible' => auth()->user()?->can('canAccessTerminal')], ['label' => 'Webhooks', 'route' => 'project.database.webhooks', 'icon' => 'notifications'], ['label' => 'Healthcheck', 'route' => 'project.database.healthcheck', 'icon' => 'feedback'], ['label' => 'Resource Limits', 'route' => 'project.database.resource-limits', 'icon' => 'cpu'], @@ -33,7 +34,7 @@ $menuGroups = [ 'Settings' => ['General', 'Environment Variables', 'Persistent Storage', 'Healthcheck'], - 'Observe & troubleshoot' => ['Runtime Logs', 'Terminal', 'Metrics'], + 'Observe & troubleshoot' => ['Runtime Logs', 'Terminal', 'Files', 'Metrics'], 'Deploy' => ['Servers'], 'Automation' => ['Webhooks', 'Backups', 'Import Backup'], 'Operations' => ['Resource Operations', 'Resource Limits', 'Tags', 'Danger Zone'], diff --git a/resources/views/components/service/configuration-sidebar.blade.php b/resources/views/components/service/configuration-sidebar.blade.php index 49dbff49ae..5d2388ec62 100644 --- a/resources/views/components/service/configuration-sidebar.blade.php +++ b/resources/views/components/service/configuration-sidebar.blade.php @@ -15,6 +15,7 @@ ['label' => 'Backups', 'route' => 'project.service.volume-backups.index', 'icon' => 'database'], ['label' => 'Runtime Logs', 'route' => 'project.service.logs', 'icon' => 'unordered-list', 'navigate' => false], ['label' => 'Terminal', 'route' => 'project.service.command', 'icon' => 'browser-terminal', 'navigate' => false, 'visible' => auth()->user()?->can('canAccessTerminal')], + ['label' => 'Files', 'route' => 'project.service.files', 'icon' => 'folder', 'visible' => auth()->user()?->can('canAccessTerminal')], ['label' => 'Scheduled Tasks', 'route' => 'project.service.scheduled-tasks.show', 'icon' => 'calendar'], ['label' => 'Webhooks', 'route' => 'project.service.webhooks', 'icon' => 'notifications'], ['label' => 'Resource Operations', 'route' => 'project.service.resource-operations', 'icon' => 'server-update'], @@ -32,7 +33,7 @@ $menuGroups = [ 'Settings' => ['General', 'Domains', 'Environment Variables', 'Persistent Storage'], - 'Observe & troubleshoot' => ['Runtime Logs', 'Terminal'], + 'Observe & troubleshoot' => ['Runtime Logs', 'Terminal', 'Files'], 'Automation' => ['Scheduled Tasks', 'Webhooks', 'Backups'], 'Operations' => ['Resource Operations', 'Tags', 'Danger Zone'], ]; diff --git a/resources/views/livewire/project/shared/file-browser.blade.php b/resources/views/livewire/project/shared/file-browser.blade.php index 2389d72af9..41d209c7aa 100644 --- a/resources/views/livewire/project/shared/file-browser.blade.php +++ b/resources/views/livewire/project/shared/file-browser.blade.php @@ -1,4 +1,16 @@
+ + {{ data_get_str($resource, 'name')->limit(10) }} > Files | Coolify + + + @if ($type === 'application') + + @elseif ($type === 'service') + + @else + + @endif + diff --git a/routes/web.php b/routes/web.php index b240209c60..435dcc9ed6 100644 --- a/routes/web.php +++ b/routes/web.php @@ -41,6 +41,7 @@ use App\Livewire\Project\Service\Index as ServiceIndex; use App\Livewire\Project\Service\VolumeBackup\Index; use App\Livewire\Project\Service\VolumeBackup\Show; use App\Livewire\Project\Shared\ExecuteContainerCommand; +use App\Livewire\Project\Shared\FileBrowser; use App\Livewire\Project\Shared\Logs; use App\Livewire\Project\Show as ProjectShow; use App\Livewire\Security\ApiTokens; @@ -297,6 +298,7 @@ Route::middleware(['auth', 'verified'])->group(function () { Route::get('/deployment/{deployment_uuid}', DeploymentShow::class)->name('project.application.deployment.show'); Route::get('/logs', Logs::class)->name('project.application.logs'); Route::get('/terminal', ExecuteContainerCommand::class)->name('project.application.command')->middleware('can.access.terminal'); + Route::get('/files', FileBrowser::class)->name('project.application.files')->middleware('can.access.terminal'); Route::get('/tasks/{task_uuid}', ApplicationConfiguration::class)->name('project.application.scheduled-tasks'); }); Route::prefix('project/{project_uuid}/environment/{environment_uuid}/database/{database_uuid}')->group(function () { @@ -315,6 +317,7 @@ Route::middleware(['auth', 'verified'])->group(function () { Route::get('/logs', Logs::class)->name('project.database.logs'); Route::get('/terminal', ExecuteContainerCommand::class)->name('project.database.command')->middleware('can.access.terminal'); + Route::get('/files', FileBrowser::class)->name('project.database.files')->middleware('can.access.terminal'); Route::get('/backups', DatabaseBackupIndex::class)->name('project.database.backup.index'); Route::get('/backups/{backup_uuid}', DatabaseBackupExecution::class)->name('project.database.backup.execution'); Route::get('/backups/{backup_uuid}/s3', DatabaseBackupExecution::class)->name('project.database.backup.s3'); @@ -340,6 +343,7 @@ Route::middleware(['auth', 'verified'])->group(function () { Route::get('/tags', ServiceConfiguration::class)->name('project.service.tags'); Route::get('/danger', ServiceConfiguration::class)->name('project.service.danger'); Route::get('/terminal', ExecuteContainerCommand::class)->name('project.service.command')->middleware('can.access.terminal'); + Route::get('/files', FileBrowser::class)->name('project.service.files')->middleware('can.access.terminal'); Route::get('/{stack_service_uuid}/backups', ServiceDatabaseBackups::class)->name('project.service.database.backups'); Route::get('/{stack_service_uuid}/backups/{backup_uuid}', ServiceDatabaseBackups::class)->name('project.service.database.backup.show'); Route::get('/{stack_service_uuid}/backups/{backup_uuid}/s3', ServiceDatabaseBackups::class)->name('project.service.database.backup.s3');