get('/release/bootstrap', function () { ScopeMiddleware::requireScope(Scope::SUPERUSER_READ, '/release/bootstrap'); global $response; $response->success((new release_manager())->bootstrap()); }); $this->get('/release/runtime', function () { ScopeMiddleware::requireScope(Scope::SUPERUSER_READ, '/release/runtime'); global $response; $response->success((new release_manager())->runtimeForCurrentPrincipal($this->getParametersAsArray())); }); $this->post('/release/timeline/events', function () { ScopeMiddleware::requireScope(Scope::SUPERUSER_WRITE, '/release/timeline/events'); global $response; $payload = $this->requestPayload(); $events = is_array($payload['events'] ?? null) ? $payload['events'] : ($payload['event'] ?? $payload); $context = is_array($payload['context'] ?? null) ? $payload['context'] : []; $response->success((new release_manager())->ingestTimelineEvents( is_array($events) ? $events : [], $context ), 202); }); $this->post('/release/github/webhook', function () { ScopeMiddleware::requireScope(Scope::SUPERUSER_WRITE, '/release/github/webhook'); global $response; try { $headers = function_exists('getallheaders') ? getallheaders() : []; $rawBody = file_get_contents('php://input') ?: ''; $response->success((new release_manager())->handleGithubWebhook($headers, $rawBody), 202); } catch (Throwable $throwable) { $response->error(['message' => $throwable->getMessage()], 401); } }); $this->post('/release/gate/test-runs', function () { ScopeMiddleware::requireScope(Scope::SUPERUSER_WRITE, '/release/gate/test-runs'); global $response; $manager = new release_manager(); if (!$manager->verifyReleaseGateToken($this->releaseGateToken())) { $response->error(['message' => 'Invalid release gate token.'], 401); } try { $response->success($manager->runReleaseTest($this->requestPayload(), null), 202); } catch (Throwable $throwable) { $response->error(['message' => $throwable->getMessage()], 409); } }); $this->post('/release/gate/frontend-version', function () { ScopeMiddleware::requireScope(Scope::SUPERUSER_WRITE, '/release/gate/frontend-version'); global $response; $manager = new release_manager(); if (!$manager->verifyReleaseGateToken($this->releaseGateToken())) { $response->error(['message' => 'Invalid release gate token.'], 401); } try { $release = release_manager::normalizeFrontendVersionGateInput($this->requestPayload()); redis->set('worker_target_version', $release['version']); $observed = (string)(redis->get('worker_target_version') ?? ''); if (!hash_equals($release['version'], $observed)) { throw new \RuntimeException('Frontend release version read-back failed.'); } $response->success([ 'version' => $observed, 'repository' => $release['repository'], 'branch' => $release['branch'], 'build_id' => $release['build_id'], ]); } catch (Throwable $throwable) { $response->error(['message' => $throwable->getMessage()], 422); } }); $this->get('/release/gate/frontend-version', function () { ScopeMiddleware::requireScope(Scope::SUPERUSER_READ, '/release/gate/frontend-version'); global $response; $manager = new release_manager(); if (!$manager->verifyReleaseGateToken($this->releaseGateToken())) { $response->error(['message' => 'Invalid release gate token.'], 401); } $response->success([ 'version' => strtolower(trim((string)(redis->get('worker_target_version') ?? ''))), ]); }); $this->get('/superuser/releases', function () { ScopeMiddleware::requireScope(Scope::SUPERUSER_READ, '/superuser/releases'); global $response; $this->requirePermission('superuser_release_manager_view'); $response->success((new release_manager())->summary()); }, [ 'superuser_release_manager_view' => 'View release manager channels, deployments, and health', ]); $this->get('/superuser/releases/config', function () { ScopeMiddleware::requireScope(Scope::SUPERUSER_READ, '/superuser/releases/config'); global $response; $this->requirePermission('superuser_release_manager_view'); $response->success((new release_manager())->releaseConfig()); }, [ 'superuser_release_manager_view' => 'View Release Manager source configuration', ]); $this->get('/superuser/releases/operations', function () { ScopeMiddleware::requireScope(Scope::SUPERUSER_READ, '/superuser/releases/operations'); global $response; $this->requirePermission('superuser_release_manager_view'); $response->success((new release_manager())->listOperations($this->getParametersAsArray())); }, [ 'superuser_release_manager_view' => 'View release operation runs', ]); $this->get('/superuser/releases/operations/{id}', function () { ScopeMiddleware::requireScope(Scope::SUPERUSER_READ, '/superuser/releases/operations/{id}'); global $response; $this->requirePermission('superuser_release_manager_view'); try { $response->success((new release_manager())->operationDetail($this->routeId())); } catch (Throwable $throwable) { $response->error(['message' => $throwable->getMessage()], 404); } }, [ 'superuser_release_manager_view' => 'Inspect release operation diagnostics', ]); $this->post('/superuser/releases/test-runs', function () { ScopeMiddleware::requireScope(Scope::SUPERUSER_WRITE, '/superuser/releases/test-runs'); global $response; $this->requirePermission('superuser_release_manager_deploy'); $response->success((new release_manager())->runReleaseTest($this->requestPayload(), $this->actorUserId()), 202); }, [ 'superuser_release_manager_deploy' => 'Run Release Manager tests with operation diagnostics', ]); $this->post('/superuser/releases/coolify-cleanup/preview', function () { ScopeMiddleware::requireScope(Scope::SUPERUSER_WRITE, '/superuser/releases/coolify-cleanup/preview'); global $response; $this->requirePermission('superuser_release_manager_deploy'); try { $response->success((new release_manager())->previewCoolifyCleanup($this->requestPayload(), $this->actorUserId())); } catch (Throwable $throwable) { $response->error(['message' => $throwable->getMessage()], 400); } }, [ 'superuser_release_manager_deploy' => 'Preview Release Manager Coolify resource cleanup', ]); $this->post('/superuser/releases/coolify-cleanup/apply', function () { ScopeMiddleware::requireScope(Scope::SUPERUSER_WRITE, '/superuser/releases/coolify-cleanup/apply'); global $response; $this->requirePermission('superuser_release_manager_deploy'); $this->requirePermission('superuser_coolify_manage'); try { $response->success((new release_manager())->applyCoolifyCleanup($this->requestPayload(), $this->actorUserId()), 202); } catch (Throwable $throwable) { $response->error(['message' => $throwable->getMessage()], 400); } }, [ 'superuser_release_manager_deploy' => 'Apply Release Manager Coolify resource cleanup', 'superuser_coolify_manage' => 'Stop or delete Coolify resources managed by Release Manager', ]); $this->post('/superuser/releases/config', function () { ScopeMiddleware::requireScope(Scope::SUPERUSER_WRITE, '/superuser/releases/config'); global $response; $this->requirePermission('superuser_release_manager_manage'); try { $response->success((new release_manager())->updateReleaseConfig($this->requestPayload(), $this->actorUserId())); } catch (Throwable $throwable) { $response->error(['message' => $throwable->getMessage()], 400); } }, [ 'superuser_release_manager_manage' => 'Update Release Manager source configuration', ]); $this->get('/superuser/releases/github/repositories', function () { ScopeMiddleware::requireScope(Scope::SUPERUSER_READ, '/superuser/releases/github/repositories'); global $response; $this->requirePermission('superuser_release_manager_view'); try { $response->success((new release_manager())->listGithubRepositories($this->getParametersAsArray())); } catch (Throwable $throwable) { $response->error(['message' => $throwable->getMessage()], 400); } }, [ 'superuser_release_manager_view' => 'List private GitHub repositories available to Release Manager', ]); $this->get('/superuser/releases/github/branches', function () { ScopeMiddleware::requireScope(Scope::SUPERUSER_READ, '/superuser/releases/github/branches'); global $response; $this->requirePermission('superuser_release_manager_deploy'); try { $response->success((new release_manager())->listGithubBranches($this->getParametersAsArray())); } catch (Throwable $throwable) { $response->error(['message' => $throwable->getMessage()], 400); } }, [ 'superuser_release_manager_deploy' => 'List GitHub branches for a Release Manager repository', ]); $this->get('/superuser/releases/github/commits', function () { ScopeMiddleware::requireScope(Scope::SUPERUSER_READ, '/superuser/releases/github/commits'); global $response; $this->requirePermission('superuser_release_manager_deploy'); try { $response->success((new release_manager())->listGithubCommits($this->getParametersAsArray())); } catch (Throwable $throwable) { $response->error(['message' => $throwable->getMessage()], 400); } }, [ 'superuser_release_manager_deploy' => 'List or resolve GitHub commits for a Release Manager repository', ]); $this->post('/superuser/releases/github/test', function () { ScopeMiddleware::requireScope(Scope::SUPERUSER_WRITE, '/superuser/releases/github/test'); global $response; $this->requirePermission('superuser_release_manager_deploy'); $response->success((new release_manager())->testGithubRepositoryAccess($this->requestPayload())); }, [ 'superuser_release_manager_deploy' => 'Test Release Manager access to a GitHub repository, branch, and commit', ]); $this->get('/superuser/releases/channels', function () { ScopeMiddleware::requireScope(Scope::SUPERUSER_READ, '/superuser/releases/channels'); global $response; $this->requirePermission('superuser_release_manager_view'); $response->success((new release_manager())->listChannels()); }, [ 'superuser_release_manager_view' => 'View release channels', ]); $this->post('/superuser/releases/channels', function () { ScopeMiddleware::requireScope(Scope::SUPERUSER_WRITE, '/superuser/releases/channels'); global $response; $this->requirePermission('superuser_release_manager_manage'); try { $response->success((new release_manager())->createChannel($this->requestPayload(), $this->actorUserId()), 201); } catch (Throwable $throwable) { $response->error(['message' => $throwable->getMessage()], 400); } }, [ 'superuser_release_manager_manage' => 'Create release channels', ]); $this->patch('/superuser/releases/channels/{id}', function () { ScopeMiddleware::requireScope(Scope::SUPERUSER_WRITE, '/superuser/releases/channels/{id}'); global $response; $this->requirePermission('superuser_release_manager_manage'); try { $response->success((new release_manager())->updateChannel($this->routeId(), $this->requestPayload(), $this->actorUserId())); } catch (Throwable $throwable) { $response->error(['message' => $throwable->getMessage()], 400); } }, [ 'superuser_release_manager_manage' => 'Update release channels', ]); $this->post('/superuser/releases/channels/{id}/rollback', function () { ScopeMiddleware::requireScope(Scope::SUPERUSER_WRITE, '/superuser/releases/channels/{id}/rollback'); global $response; $this->requirePermission('superuser_release_manager_rollback'); try { $response->success((new release_manager())->rollbackChannel($this->routeId(), $this->actorUserId())); } catch (Throwable $throwable) { $response->error(['message' => $throwable->getMessage()], 409); } }, [ 'superuser_release_manager_rollback' => 'Rollback an active release channel', ]); $this->post('/superuser/releases/channels/{id}/sync', function () { ScopeMiddleware::requireScope(Scope::SUPERUSER_WRITE, '/superuser/releases/channels/{id}/sync'); global $response; $this->requirePermission('superuser_release_manager_deploy'); try { $response->success((new release_manager())->syncChannel($this->routeId(), $this->actorUserId()), 202); } catch (Throwable $throwable) { $response->error(['message' => $throwable->getMessage()], 409); } }, [ 'superuser_release_manager_deploy' => 'Sync the latest frontend and API branch commits into a release channel', ]); $this->post('/superuser/releases/channels/{id}/bundle', function () { ScopeMiddleware::requireScope(Scope::SUPERUSER_WRITE, '/superuser/releases/channels/{id}/bundle'); global $response; $this->requirePermission('superuser_release_manager_deploy'); try { $response->success((new release_manager())->setChannelBundle($this->routeId(), $this->requestPayload(), $this->actorUserId())); } catch (Throwable $throwable) { $response->error(['message' => $throwable->getMessage()], 409); } }, [ 'superuser_release_manager_deploy' => 'Set the active release bundle for a channel', ]); $this->get('/superuser/releases/assignments', function () { ScopeMiddleware::requireScope(Scope::SUPERUSER_READ, '/superuser/releases/assignments'); global $response; $this->requirePermission('superuser_release_manager_view'); $response->success((new release_manager())->listAssignments()); }, [ 'superuser_release_manager_view' => 'View release channel assignments', ]); $this->get('/superuser/releases/assignment-subjects', function () { ScopeMiddleware::requireScope(Scope::SUPERUSER_READ, '/superuser/releases/assignment-subjects'); global $response; $this->requirePermission('superuser_release_manager_manage'); $response->success((new release_manager())->searchAssignmentSubjects($this->getParametersAsArray())); }, [ 'superuser_release_manager_manage' => 'Search users, subusers, and customers for Release Manager assignments', ]); $this->post('/superuser/releases/assignments', function () { ScopeMiddleware::requireScope(Scope::SUPERUSER_WRITE, '/superuser/releases/assignments'); global $response; $this->requirePermission('superuser_release_manager_manage'); try { $response->success((new release_manager())->createAssignment($this->requestPayload(), $this->actorUserId()), 201); } catch (Throwable $throwable) { $response->error(['message' => $throwable->getMessage()], 400); } }, [ 'superuser_release_manager_manage' => 'Assign users, subusers, or customers to release channels', ]); $this->delete('/superuser/releases/assignments/{id}', function () { ScopeMiddleware::requireScope(Scope::SUPERUSER_WRITE, '/superuser/releases/assignments/{id}'); global $response; $this->requirePermission('superuser_release_manager_manage'); try { $response->success((new release_manager())->deleteAssignment($this->routeId(), $this->actorUserId())); } catch (Throwable $throwable) { $response->error(['message' => $throwable->getMessage()], 404); } }, [ 'superuser_release_manager_manage' => 'Remove release channel assignments', ]); $this->get('/superuser/releases/targets', function () { ScopeMiddleware::requireScope(Scope::SUPERUSER_READ, '/superuser/releases/targets'); global $response; $this->requirePermission('superuser_release_manager_deploy'); $response->success((new release_manager())->listDeploymentTargets()); }, [ 'superuser_release_manager_deploy' => 'View release deployment targets', ]); $this->post('/superuser/releases/targets', function () { ScopeMiddleware::requireScope(Scope::SUPERUSER_WRITE, '/superuser/releases/targets'); global $response; $this->requirePermission('superuser_release_manager_deploy'); try { $response->success((new release_manager())->upsertDeploymentTarget($this->requestPayload(), $this->actorUserId()), 201); } catch (Throwable $throwable) { $response->error(['message' => $throwable->getMessage()], 400); } }, [ 'superuser_release_manager_deploy' => 'Create or update GitHub to Coolify release deployment targets', ]); $this->delete('/superuser/releases/targets/{id}', function () { ScopeMiddleware::requireScope(Scope::SUPERUSER_WRITE, '/superuser/releases/targets/{id}'); global $response; $this->requirePermission('superuser_release_manager_deploy'); try { $response->success((new release_manager())->deleteDeploymentTarget($this->routeId(), $this->actorUserId())); } catch (Throwable $throwable) { $response->error(['message' => $throwable->getMessage()], 404); } }, [ 'superuser_release_manager_deploy' => 'Delete release deployment targets', ]); $this->get('/superuser/releases/service-sets', function () { ScopeMiddleware::requireScope(Scope::SUPERUSER_READ, '/superuser/releases/service-sets'); global $response; $this->requirePermission('superuser_release_manager_view'); $response->success((new release_manager())->listServiceSets()); }, [ 'superuser_release_manager_view' => 'View reusable Release Manager service sets', ]); $this->post('/superuser/releases/service-sets', function () { ScopeMiddleware::requireScope(Scope::SUPERUSER_WRITE, '/superuser/releases/service-sets'); global $response; $this->requirePermission('superuser_release_manager_deploy'); try { $response->success((new release_manager())->createServiceSet($this->requestPayload(), $this->actorUserId()), 201); } catch (Throwable $throwable) { $response->error(['message' => $throwable->getMessage()], 400); } }, [ 'superuser_release_manager_deploy' => 'Create reusable Release Manager service sets', ]); $this->delete('/superuser/releases/service-sets/{id}', function () { ScopeMiddleware::requireScope(Scope::SUPERUSER_WRITE, '/superuser/releases/service-sets/{id}'); global $response; $this->requirePermission('superuser_release_manager_deploy'); try { $response->success( (new release_manager())->deleteServiceSet( $this->routeId(), $this->requestPayload(), $this->actorUserId() ) ); } catch (Throwable $throwable) { $response->error(['message' => $throwable->getMessage()], 400); } }, [ 'superuser_release_manager_deploy' => 'Remove inactive isolated Release Manager service sets', ]); $this->post('/superuser/releases/service-sets/{id}/isolated-data-services', function () { ScopeMiddleware::requireScope(Scope::SUPERUSER_WRITE, '/superuser/releases/service-sets/{id}/isolated-data-services'); global $response; $this->requirePermission('superuser_release_manager_deploy'); try { $response->success( (new release_manager())->completeIsolatedStackDataServices( $this->routeId(), $this->requestPayload(), $this->actorUserId() ), 202 ); } catch (Throwable $throwable) { $response->error(['message' => $throwable->getMessage()], 400); } }, [ 'superuser_release_manager_deploy' => 'Create missing isolated stack database, Redis, and MinIO services', ]); $this->get('/superuser/releases/bundles', function () { ScopeMiddleware::requireScope(Scope::SUPERUSER_READ, '/superuser/releases/bundles'); global $response; $this->requirePermission('superuser_release_manager_view'); $limit = (int)($this->getParameter('limit') ?? 50); $response->success((new release_manager())->listBundles($limit)); }, [ 'superuser_release_manager_view' => 'View Release Manager bundles', ]); $this->post('/superuser/releases/bundles', function () { ScopeMiddleware::requireScope(Scope::SUPERUSER_WRITE, '/superuser/releases/bundles'); global $response; $this->requirePermission('superuser_release_manager_deploy'); try { $response->success((new release_manager())->createBundle($this->requestPayload(), $this->actorUserId()), 201); } catch (Throwable $throwable) { $response->error(['message' => $throwable->getMessage()], 400); } }, [ 'superuser_release_manager_deploy' => 'Create Release Manager full-stack bundles', ]); $this->post('/superuser/releases/bundles/{id}/deploy', function () { ScopeMiddleware::requireScope(Scope::SUPERUSER_WRITE, '/superuser/releases/bundles/{id}/deploy'); global $response; $this->requirePermission('superuser_release_manager_deploy'); try { $response->success((new release_manager())->deployBundle($this->routeId(), $this->actorUserId()), 202); } catch (Throwable $throwable) { $response->error(['message' => $throwable->getMessage()], 409); } }, [ 'superuser_release_manager_deploy' => 'Deploy Release Manager full-stack bundles', ]); $this->post('/superuser/releases/bundles/{id}/promote', function () { ScopeMiddleware::requireScope(Scope::SUPERUSER_WRITE, '/superuser/releases/bundles/{id}/promote'); global $response; $this->requirePermission('superuser_release_manager_deploy'); try { $response->success((new release_manager())->promoteBundle($this->routeId(), $this->actorUserId())); } catch (Throwable $throwable) { $response->error(['message' => $throwable->getMessage()], 409); } }, [ 'superuser_release_manager_deploy' => 'Promote Release Manager bundles without data failover', ]); $this->get('/superuser/releases/deployments', function () { ScopeMiddleware::requireScope(Scope::SUPERUSER_READ, '/superuser/releases/deployments'); global $response; $this->requirePermission('superuser_release_manager_view'); $limit = (int)($this->getParameter('limit') ?? 50); $response->success((new release_manager())->listDeployments($limit)); }, [ 'superuser_release_manager_view' => 'View release deployments', ]); $this->post('/superuser/releases/deployments', function () { ScopeMiddleware::requireScope(Scope::SUPERUSER_WRITE, '/superuser/releases/deployments'); global $response; $this->requirePermission('superuser_release_manager_deploy'); try { $response->success((new release_manager())->startDeployment($this->requestPayload(), $this->actorUserId()), 202); } catch (Throwable $throwable) { $response->error(['message' => $throwable->getMessage()], 409); } }, [ 'superuser_release_manager_deploy' => 'Trigger release deployments from GitHub/Coolify targets', ]); $this->post('/superuser/releases/deployments/{id}/promote', function () { ScopeMiddleware::requireScope(Scope::SUPERUSER_WRITE, '/superuser/releases/deployments/{id}/promote'); global $response; $this->requirePermission('superuser_release_manager_deploy'); try { $response->success((new release_manager())->promoteDeployment($this->routeId(), $this->actorUserId())); } catch (Throwable $throwable) { $response->error(['message' => $throwable->getMessage()], 409); } }, [ 'superuser_release_manager_deploy' => 'Promote a deployment to its release channel', ]); $this->post('/superuser/releases/issues/actions', function () { ScopeMiddleware::requireScope(Scope::SUPERUSER_WRITE, '/superuser/releases/issues/actions'); global $response; $this->requirePermission('superuser_release_manager_deploy'); $response->success((new release_manager())->runIssueAction($this->requestPayload(), $this->actorUserId())); }, [ 'superuser_release_manager_deploy' => 'Run a safe Release Manager issue resolution action', ]); $this->post('/superuser/releases/replay-targets', function () { ScopeMiddleware::requireScope(Scope::SUPERUSER_WRITE, '/superuser/releases/replay-targets'); global $response; $this->requirePermission('superuser_release_manager_replay'); try { $response->success((new release_manager())->setReplayTarget($this->requestPayload(), $this->actorUserId()), 201); } catch (Throwable $throwable) { $response->error(['message' => $throwable->getMessage()], 400); } }, [ 'superuser_release_manager_replay' => 'Enable release timeline replay capture for a user, customer, subuser, or channel', ]); $this->get('/superuser/releases/timeline/sessions', function () { ScopeMiddleware::requireScope(Scope::SUPERUSER_READ, '/superuser/releases/timeline/sessions'); global $response; $this->requirePermission('superuser_release_manager_replay'); $response->success((new release_manager())->listTimelineSessions($this->getParametersAsArray())); }, [ 'superuser_release_manager_replay' => 'List release timeline replay sessions', ]); $this->get('/superuser/releases/timeline/sessions/{traceId}', function () { ScopeMiddleware::requireScope(Scope::SUPERUSER_READ, '/superuser/releases/timeline/sessions/{traceId}'); global $response; $this->requirePermission('superuser_release_manager_replay'); try { $response->success((new release_manager())->timelineSessionDetail((string)$this->fromRoute('traceId'))); } catch (Throwable $throwable) { $response->error(['message' => $throwable->getMessage()], 404); } }, [ 'superuser_release_manager_replay' => 'Inspect one release timeline replay session', ]); $this->get('/superuser/releases/timeline', function () { ScopeMiddleware::requireScope(Scope::SUPERUSER_READ, '/superuser/releases/timeline'); global $response; $this->requirePermission('superuser_release_manager_replay'); $response->success((new release_manager())->searchTimeline($this->getParametersAsArray())); }, [ 'superuser_release_manager_replay' => 'Replay release failure timelines', ]); } private function routeId(): int { $id = (int)$this->fromRoute('id'); $this->requireParameterIntPositive($id, 'id'); return $id; } private function actorUserId(): ?int { try { $user = (new authentication())->get_user(); return $user !== false && isset($user->id) ? (int)$user->id : null; } catch (Throwable) { return null; } } private function requestPayload(): array { $payload = json_decode(file_get_contents('php://input'), true); if (!is_array($payload)) { $payload = []; } if ($_GET !== []) { $payload = array_replace($payload, $_GET); } return $payload; } private function releaseGateToken(): string { $headers = function_exists('getallheaders') ? getallheaders() : []; $authorization = (string)($headers['Authorization'] ?? $headers['authorization'] ?? $_SERVER['HTTP_AUTHORIZATION'] ?? ''); if (preg_match('/^Bearer\s+(.+)$/i', $authorization, $matches) === 1) { return trim($matches[1]); } return trim((string)( $headers['X-Release-Gate-Token'] ?? $headers['x-release-gate-token'] ?? $_SERVER['HTTP_X_RELEASE_GATE_TOKEN'] ?? '' )); } }