feat(files): add Files tab and routes for applications, services and databases

This commit is contained in:
Aditya Tripathi
2026-08-26 13:56:00 +00:00
parent 5e0349a93e
commit f5cb0091d9
6 changed files with 58 additions and 4 deletions
+30 -1
View File
@@ -40,8 +40,14 @@ class FileBrowser extends Component
public $upload;
public function mount($resource): void
/** @var array<string, mixed> */
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) {
@@ -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'],
@@ -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'],
@@ -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'],
];
@@ -1,4 +1,16 @@
<div>
<x-slot:title>
{{ data_get_str($resource, 'name')->limit(10) }} > Files | Coolify
</x-slot>
@if ($type === 'application')
<livewire:project.application.heading :application="$resource" wire:key="application-heading-files" />
@elseif ($type === 'service')
<livewire:project.service.heading :service="$resource" :parameters="$parameters" title="Files" />
@else
<livewire:project.database.heading :database="$resource" />
@endif
<x-application.settings-section title="Files"
helper="Browse and manage files inside the running container." :flush="true">
+4
View File
@@ -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');