Merge remote-tracking branch 'origin/next' into pr-7764-shadow/add-shared-server-env

This commit is contained in:
Andras Bacsai
2026-03-31 10:52:31 +02:00
767 changed files with 61894 additions and 15664 deletions
+47 -14
View File
@@ -9,6 +9,7 @@ use App\Http\Controllers\Api\HetznerController;
use App\Http\Controllers\Api\OtherController;
use App\Http\Controllers\Api\ProjectController;
use App\Http\Controllers\Api\ResourcesController;
use App\Http\Controllers\Api\ScheduledTasksController;
use App\Http\Controllers\Api\SecurityController;
use App\Http\Controllers\Api\ServersController;
use App\Http\Controllers\Api\ServicesController;
@@ -54,7 +55,7 @@ Route::group([
Route::post('/projects/{uuid}/environments', [ProjectController::class, 'create_environment'])->middleware(['api.ability:write']);
Route::delete('/projects/{uuid}/environments/{environment_name_or_uuid}', [ProjectController::class, 'delete_environment'])->middleware(['api.ability:write']);
Route::post('/projects', [ProjectController::class, 'create_project'])->middleware(['api.ability:read']);
Route::post('/projects', [ProjectController::class, 'create_project'])->middleware(['api.ability:write']);
Route::patch('/projects/{uuid}', [ProjectController::class, 'update_project'])->middleware(['api.ability:write']);
Route::delete('/projects/{uuid}', [ProjectController::class, 'delete_project'])->middleware(['api.ability:write']);
@@ -70,7 +71,7 @@ Route::group([
Route::get('/cloud-tokens/{uuid}', [CloudProviderTokensController::class, 'show'])->middleware(['api.ability:read']);
Route::patch('/cloud-tokens/{uuid}', [CloudProviderTokensController::class, 'update'])->middleware(['api.ability:write']);
Route::delete('/cloud-tokens/{uuid}', [CloudProviderTokensController::class, 'destroy'])->middleware(['api.ability:write']);
Route::post('/cloud-tokens/{uuid}/validate', [CloudProviderTokensController::class, 'validateToken'])->middleware(['api.ability:read']);
Route::post('/cloud-tokens/{uuid}/validate', [CloudProviderTokensController::class, 'validateToken'])->middleware(['api.ability:write']);
Route::match(['get', 'post'], '/deploy', [DeployController::class, 'deploy'])->middleware(['api.ability:deploy']);
Route::get('/deployments', [DeployController::class, 'deployments'])->middleware(['api.ability:read']);
@@ -83,9 +84,9 @@ Route::group([
Route::get('/servers/{uuid}/domains', [ServersController::class, 'domains_by_server'])->middleware(['api.ability:read']);
Route::get('/servers/{uuid}/resources', [ServersController::class, 'resources_by_server'])->middleware(['api.ability:read']);
Route::get('/servers/{uuid}/validate', [ServersController::class, 'validate_server'])->middleware(['api.ability:read']);
Route::get('/servers/{uuid}/validate', [ServersController::class, 'validate_server'])->middleware(['api.ability:write']);
Route::post('/servers', [ServersController::class, 'create_server'])->middleware(['api.ability:read']);
Route::post('/servers', [ServersController::class, 'create_server'])->middleware(['api.ability:write']);
Route::patch('/servers/{uuid}', [ServersController::class, 'update_server'])->middleware(['api.ability:write']);
Route::delete('/servers/{uuid}', [ServersController::class, 'delete_server'])->middleware(['api.ability:write']);
@@ -106,7 +107,7 @@ Route::group([
/**
* @deprecated Use POST /api/v1/services instead. This endpoint creates a Service, not an Application and is a unstable duplicate of POST /api/v1/services.
*/
*/
Route::post('/applications/dockercompose', [ApplicationsController::class, 'create_dockercompose_application'])->middleware(['api.ability:write']);
Route::get('/applications/{uuid}', [ApplicationsController::class, 'application_by_uuid'])->middleware(['api.ability:read']);
@@ -119,10 +120,14 @@ Route::group([
Route::patch('/applications/{uuid}/envs', [ApplicationsController::class, 'update_env_by_uuid'])->middleware(['api.ability:write']);
Route::delete('/applications/{uuid}/envs/{env_uuid}', [ApplicationsController::class, 'delete_env_by_uuid'])->middleware(['api.ability:write']);
Route::get('/applications/{uuid}/logs', [ApplicationsController::class, 'logs_by_uuid'])->middleware(['api.ability:read']);
Route::get('/applications/{uuid}/storages', [ApplicationsController::class, 'storages'])->middleware(['api.ability:read']);
Route::post('/applications/{uuid}/storages', [ApplicationsController::class, 'create_storage'])->middleware(['api.ability:write']);
Route::patch('/applications/{uuid}/storages', [ApplicationsController::class, 'update_storage'])->middleware(['api.ability:write']);
Route::delete('/applications/{uuid}/storages/{storage_uuid}', [ApplicationsController::class, 'delete_storage'])->middleware(['api.ability:write']);
Route::match(['get', 'post'], '/applications/{uuid}/start', [ApplicationsController::class, 'action_deploy'])->middleware(['api.ability:write']);
Route::match(['get', 'post'], '/applications/{uuid}/restart', [ApplicationsController::class, 'action_restart'])->middleware(['api.ability:write']);
Route::match(['get', 'post'], '/applications/{uuid}/stop', [ApplicationsController::class, 'action_stop'])->middleware(['api.ability:write']);
Route::match(['get', 'post'], '/applications/{uuid}/start', [ApplicationsController::class, 'action_deploy'])->middleware(['api.ability:deploy']);
Route::match(['get', 'post'], '/applications/{uuid}/restart', [ApplicationsController::class, 'action_restart'])->middleware(['api.ability:deploy']);
Route::match(['get', 'post'], '/applications/{uuid}/stop', [ApplicationsController::class, 'action_stop'])->middleware(['api.ability:deploy']);
Route::get('/github-apps', [GithubController::class, 'list_github_apps'])->middleware(['api.ability:read']);
Route::post('/github-apps', [GithubController::class, 'create_github_app'])->middleware(['api.ability:write']);
@@ -151,9 +156,20 @@ Route::group([
Route::delete('/databases/{uuid}/backups/{scheduled_backup_uuid}', [DatabasesController::class, 'delete_backup_by_uuid'])->middleware(['api.ability:write']);
Route::delete('/databases/{uuid}/backups/{scheduled_backup_uuid}/executions/{execution_uuid}', [DatabasesController::class, 'delete_execution_by_uuid'])->middleware(['api.ability:write']);
Route::match(['get', 'post'], '/databases/{uuid}/start', [DatabasesController::class, 'action_deploy'])->middleware(['api.ability:write']);
Route::match(['get', 'post'], '/databases/{uuid}/restart', [DatabasesController::class, 'action_restart'])->middleware(['api.ability:write']);
Route::match(['get', 'post'], '/databases/{uuid}/stop', [DatabasesController::class, 'action_stop'])->middleware(['api.ability:write']);
Route::get('/databases/{uuid}/storages', [DatabasesController::class, 'storages'])->middleware(['api.ability:read']);
Route::post('/databases/{uuid}/storages', [DatabasesController::class, 'create_storage'])->middleware(['api.ability:write']);
Route::patch('/databases/{uuid}/storages', [DatabasesController::class, 'update_storage'])->middleware(['api.ability:write']);
Route::delete('/databases/{uuid}/storages/{storage_uuid}', [DatabasesController::class, 'delete_storage'])->middleware(['api.ability:write']);
Route::get('/databases/{uuid}/envs', [DatabasesController::class, 'envs'])->middleware(['api.ability:read']);
Route::post('/databases/{uuid}/envs', [DatabasesController::class, 'create_env'])->middleware(['api.ability:write']);
Route::patch('/databases/{uuid}/envs/bulk', [DatabasesController::class, 'create_bulk_envs'])->middleware(['api.ability:write']);
Route::patch('/databases/{uuid}/envs', [DatabasesController::class, 'update_env_by_uuid'])->middleware(['api.ability:write']);
Route::delete('/databases/{uuid}/envs/{env_uuid}', [DatabasesController::class, 'delete_env_by_uuid'])->middleware(['api.ability:write']);
Route::match(['get', 'post'], '/databases/{uuid}/start', [DatabasesController::class, 'action_deploy'])->middleware(['api.ability:deploy']);
Route::match(['get', 'post'], '/databases/{uuid}/restart', [DatabasesController::class, 'action_restart'])->middleware(['api.ability:deploy']);
Route::match(['get', 'post'], '/databases/{uuid}/stop', [DatabasesController::class, 'action_stop'])->middleware(['api.ability:deploy']);
Route::get('/services', [ServicesController::class, 'services'])->middleware(['api.ability:read']);
Route::post('/services', [ServicesController::class, 'create_service'])->middleware(['api.ability:write']);
@@ -162,15 +178,32 @@ Route::group([
Route::patch('/services/{uuid}', [ServicesController::class, 'update_by_uuid'])->middleware(['api.ability:write']);
Route::delete('/services/{uuid}', [ServicesController::class, 'delete_by_uuid'])->middleware(['api.ability:write']);
Route::get('/services/{uuid}/storages', [ServicesController::class, 'storages'])->middleware(['api.ability:read']);
Route::post('/services/{uuid}/storages', [ServicesController::class, 'create_storage'])->middleware(['api.ability:write']);
Route::patch('/services/{uuid}/storages', [ServicesController::class, 'update_storage'])->middleware(['api.ability:write']);
Route::delete('/services/{uuid}/storages/{storage_uuid}', [ServicesController::class, 'delete_storage'])->middleware(['api.ability:write']);
Route::get('/services/{uuid}/envs', [ServicesController::class, 'envs'])->middleware(['api.ability:read']);
Route::post('/services/{uuid}/envs', [ServicesController::class, 'create_env'])->middleware(['api.ability:write']);
Route::patch('/services/{uuid}/envs/bulk', [ServicesController::class, 'create_bulk_envs'])->middleware(['api.ability:write']);
Route::patch('/services/{uuid}/envs', [ServicesController::class, 'update_env_by_uuid'])->middleware(['api.ability:write']);
Route::delete('/services/{uuid}/envs/{env_uuid}', [ServicesController::class, 'delete_env_by_uuid'])->middleware(['api.ability:write']);
Route::match(['get', 'post'], '/services/{uuid}/start', [ServicesController::class, 'action_deploy'])->middleware(['api.ability:write']);
Route::match(['get', 'post'], '/services/{uuid}/restart', [ServicesController::class, 'action_restart'])->middleware(['api.ability:write']);
Route::match(['get', 'post'], '/services/{uuid}/stop', [ServicesController::class, 'action_stop'])->middleware(['api.ability:write']);
Route::match(['get', 'post'], '/services/{uuid}/start', [ServicesController::class, 'action_deploy'])->middleware(['api.ability:deploy']);
Route::match(['get', 'post'], '/services/{uuid}/restart', [ServicesController::class, 'action_restart'])->middleware(['api.ability:deploy']);
Route::match(['get', 'post'], '/services/{uuid}/stop', [ServicesController::class, 'action_stop'])->middleware(['api.ability:deploy']);
Route::get('/applications/{uuid}/scheduled-tasks', [ScheduledTasksController::class, 'scheduled_tasks_by_application_uuid'])->middleware(['api.ability:read']);
Route::post('/applications/{uuid}/scheduled-tasks', [ScheduledTasksController::class, 'create_scheduled_task_by_application_uuid'])->middleware(['api.ability:write']);
Route::patch('/applications/{uuid}/scheduled-tasks/{task_uuid}', [ScheduledTasksController::class, 'update_scheduled_task_by_application_uuid'])->middleware(['api.ability:write']);
Route::delete('/applications/{uuid}/scheduled-tasks/{task_uuid}', [ScheduledTasksController::class, 'delete_scheduled_task_by_application_uuid'])->middleware(['api.ability:write']);
Route::get('/applications/{uuid}/scheduled-tasks/{task_uuid}/executions', [ScheduledTasksController::class, 'executions_by_application_uuid'])->middleware(['api.ability:read']);
Route::get('/services/{uuid}/scheduled-tasks', [ScheduledTasksController::class, 'scheduled_tasks_by_service_uuid'])->middleware(['api.ability:read']);
Route::post('/services/{uuid}/scheduled-tasks', [ScheduledTasksController::class, 'create_scheduled_task_by_service_uuid'])->middleware(['api.ability:write']);
Route::patch('/services/{uuid}/scheduled-tasks/{task_uuid}', [ScheduledTasksController::class, 'update_scheduled_task_by_service_uuid'])->middleware(['api.ability:write']);
Route::delete('/services/{uuid}/scheduled-tasks/{task_uuid}', [ScheduledTasksController::class, 'delete_scheduled_task_by_service_uuid'])->middleware(['api.ability:write']);
Route::get('/services/{uuid}/scheduled-tasks/{task_uuid}/executions', [ScheduledTasksController::class, 'executions_by_service_uuid'])->middleware(['api.ability:read']);
});
Route::group([
+29 -12
View File
@@ -62,6 +62,7 @@ use App\Livewire\Server\Show as ServerShow;
use App\Livewire\Server\Swarm as ServerSwarm;
use App\Livewire\Settings\Advanced as SettingsAdvanced;
use App\Livewire\Settings\Index as SettingsIndex;
use App\Livewire\Settings\ScheduledJobs as SettingsScheduledJobs;
use App\Livewire\Settings\Updates as SettingsUpdates;
use App\Livewire\SettingsBackup;
use App\Livewire\SettingsEmail;
@@ -85,13 +86,12 @@ use App\Livewire\Team\Index as TeamIndex;
use App\Livewire\Team\Member\Index as TeamMemberIndex;
use App\Livewire\Terminal\Index as TerminalIndex;
use App\Models\ScheduledDatabaseBackupExecution;
use App\Models\ServiceDatabase;
use App\Providers\RouteServiceProvider;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\Facades\Storage;
use Symfony\Component\HttpFoundation\StreamedResponse;
Route::get('/admin', AdminIndex::class)->name('admin.index');
Route::post('/forgot-password', [Controller::class, 'forgot_password'])->name('password.forgot')->middleware('throttle:forgot-password');
Route::get('/realtime', [Controller::class, 'realtime_test'])->middleware('auth');
Route::get('/verify', [Controller::class, 'verify'])->middleware('auth')->name('verify.email');
@@ -109,6 +109,7 @@ Route::middleware(['auth', 'verified'])->group(function () {
});
Route::get('/', Dashboard::class)->name('dashboard');
Route::get('/admin', AdminIndex::class)->name('admin.index');
Route::get('/onboarding', BoardingIndex::class)->name('onboarding');
Route::get('/subscription', SubscriptionShow::class)->name('subscription.show');
@@ -121,6 +122,7 @@ Route::middleware(['auth', 'verified'])->group(function () {
Route::get('/settings/backup', SettingsBackup::class)->name('settings.backup');
Route::get('/settings/email', SettingsEmail::class)->name('settings.email');
Route::get('/settings/oauth', SettingsOauth::class)->name('settings.oauth');
Route::get('/settings/scheduled-jobs', SettingsScheduledJobs::class)->name('settings.scheduled-jobs');
Route::get('/profile', ProfileIndex::class)->name('profile');
@@ -140,6 +142,7 @@ Route::middleware(['auth', 'verified'])->group(function () {
Route::prefix('storages')->group(function () {
Route::get('/', StorageIndex::class)->name('storage.index');
Route::get('/{storage_uuid}', StorageShow::class)->name('storage.show');
Route::get('/{storage_uuid}/resources', StorageShow::class)->name('storage.resources');
});
Route::prefix('shared-variables')->group(function () {
Route::get('/', SharedVariablesIndex::class)->name('shared-variables.index');
@@ -165,22 +168,36 @@ Route::middleware(['auth', 'verified'])->group(function () {
}
return response()->json(['authenticated' => false], 401);
})->name('terminal.auth');
})->name('terminal.auth')->middleware('can.access.terminal');
Route::post('/terminal/auth/ips', function () {
if (auth()->check()) {
$team = auth()->user()->currentTeam();
$ipAddresses = $team->servers->where('settings.is_terminal_enabled', true)->pluck('ip')->toArray();
$ipAddresses = $team->servers
->where('settings.is_terminal_enabled', true)
->pluck('ip')
->filter()
->values();
return response()->json(['ipAddresses' => $ipAddresses], 200);
if (isDev()) {
$ipAddresses = $ipAddresses->merge([
'coolify-testing-host',
'host.docker.internal',
'localhost',
'127.0.0.1',
base_ip(),
])->filter()->unique()->values();
}
return response()->json(['ipAddresses' => $ipAddresses->all()], 200);
}
return response()->json(['ipAddresses' => []], 401);
})->name('terminal.auth.ips');
})->name('terminal.auth.ips')->middleware('can.access.terminal');
Route::prefix('invitations')->group(function () {
Route::get('/{uuid}', [Controller::class, 'acceptInvitation'])->name('team.invitation.accept');
Route::get('/{uuid}/revoke', [Controller::class, 'revokeInvitation'])->name('team.invitation.revoke');
Route::get('/{uuid}', [Controller::class, 'showInvitation'])->name('team.invitation.show');
Route::post('/{uuid}', [Controller::class, 'acceptInvitation'])->name('team.invitation.accept');
});
Route::get('/projects', ProjectIndex::class)->name('project.index');
@@ -217,7 +234,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('/tasks/{task_uuid}', ScheduledTaskShow::class)->name('project.application.scheduled-tasks');
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 () {
Route::get('/', DatabaseConfiguration::class)->name('project.database.configuration');
@@ -251,7 +268,7 @@ Route::middleware(['auth', 'verified'])->group(function () {
Route::get('/{stack_service_uuid}/backups', ServiceDatabaseBackups::class)->name('project.service.database.backups');
Route::get('/{stack_service_uuid}/import', ServiceIndex::class)->name('project.service.database.import')->middleware('can.update.resource');
Route::get('/{stack_service_uuid}', ServiceIndex::class)->name('project.service.index');
Route::get('/tasks/{task_uuid}', ScheduledTaskShow::class)->name('project.service.scheduled-tasks');
Route::get('/tasks/{task_uuid}', ServiceConfiguration::class)->name('project.service.scheduled-tasks');
});
Route::get('/servers', ServerIndex::class)->name('server.index');
@@ -331,7 +348,7 @@ Route::middleware(['auth'])->group(function () {
}
}
$filename = data_get($execution, 'filename');
if ($execution->scheduledDatabaseBackup->database->getMorphClass() === \App\Models\ServiceDatabase::class) {
if ($execution->scheduledDatabaseBackup->database->getMorphClass() === ServiceDatabase::class) {
$server = $execution->scheduledDatabaseBackup->database->service->destination->server;
} else {
$server = $execution->scheduledDatabaseBackup->database->destination->server;
@@ -372,7 +389,7 @@ Route::middleware(['auth'])->group(function () {
'Content-Type' => 'application/octet-stream',
'Content-Disposition' => 'attachment; filename="'.basename($filename).'"',
]);
} catch (\Throwable $e) {
} catch (Throwable $e) {
return response()->json(['message' => $e->getMessage()], 500);
}
})->name('download.backup');