route('organisation')); $application = $organisation->applications()->findOrFail($request->route('application')); return inertia('environments/Create', [ 'application' => $application, ]); } public function store(StoreEnvironmentRequest $request): RedirectResponse { $organisation = Organisation::findOrFail($request->route('organisation')); $application = $organisation->applications()->findOrFail($request->route('application')); $environment = app(CreateLaravelEnvironment::class)->execute( application: $application, name: $request->string('name')->toString(), branch: $request->string('branch')->toString(), phpVersion: $request->string('php_version')->toString(), ); return redirect() ->route('environments.show', [ 'organisation' => $organisation->id, 'application' => $application->id, 'environment' => $environment->id, ]) ->with('success', 'Environment created.'); } public function show(Request $request): Response { $organisation = Organisation::findOrFail($request->route('organisation')); $application = $organisation->applications()->findOrFail($request->route('application')); $environment = $application->environments() ->with([ 'services.replicas', 'services.endpoints', 'services.slices', 'services.operations.steps', 'attachments.service', 'attachments.serviceSlice', 'variables', 'buildArtifacts.builtByService', 'operations.steps', 'operations.children.target', ]) ->findOrFail($request->route('environment')); $serverCount = $this->serverIdsFor($environment)->count(); return inertia('environments/Show', [ 'application' => $application, 'environment' => $environment, 'deploymentRequirements' => [ 'registryRequired' => $organisation->registries()->doesntExist() && $serverCount > 1, 'registryCount' => $organisation->registries()->count(), 'serverCount' => $serverCount, ], 'gatewayRoutePreviews' => $this->gatewayRoutePreviews($environment), ]); } public function edit(Request $request): Response { $organisation = Organisation::findOrFail($request->route('organisation')); $application = $organisation->applications()->findOrFail($request->route('application')); $environment = $application->environments() ->with('services') ->findOrFail($request->route('environment')); return inertia('environments/Edit', [ 'application' => $application, 'environment' => $environment, 'schedulerModes' => array_values(SchedulerMode::toArray()), 'buildStrategies' => array_values(BuildStrategy::toArray()), ]); } public function update(UpdateEnvironmentRequest $request): RedirectResponse { $organisation = Organisation::findOrFail($request->route('organisation')); $application = $organisation->applications()->findOrFail($request->route('application')); $environment = $application->environments() ->with('services') ->findOrFail($request->route('environment')); $schedulerTargetServiceId = $request->integer('scheduler_target_service_id') ?: null; if ($schedulerTargetServiceId !== null) { abort_unless($environment->services()->whereKey($schedulerTargetServiceId)->exists(), 422); } $environment->update([ 'name' => $request->string('name')->toString(), 'branch' => $request->string('branch')->toString(), 'status' => $request->string('status')->toString(), 'scheduler_enabled' => $request->boolean('scheduler_enabled'), 'scheduler_target_service_id' => $schedulerTargetServiceId, 'scheduler_mode' => $request->enum('scheduler_mode', SchedulerMode::class), 'build_config' => [ ...($environment->build_config ?? []), 'build_strategy' => $request->enum('build_strategy', BuildStrategy::class)?->value, 'php_version' => $request->string('php_version')->toString(), 'document_root' => $request->string('document_root')->toString(), 'health_path' => $request->string('health_path')->toString(), 'js_package_manager' => $request->string('js_package_manager')->toString(), 'js_build_command' => $request->filled('js_build_command') ? $request->string('js_build_command')->toString() : null, ], ]); return redirect() ->route('environments.show', [ 'organisation' => $organisation->id, 'application' => $application->id, 'environment' => $environment->id, ]) ->with('success', 'Environment updated.'); } public function destroy(Request $request): RedirectResponse { $organisation = Organisation::findOrFail($request->route('organisation')); $application = $organisation->applications()->findOrFail($request->route('application')); $environment = $application->environments()->findOrFail($request->route('environment')); $environment->delete(); return redirect() ->route('applications.show', [ 'organisation' => $organisation->id, 'application' => $application->id, ]) ->with('success', 'Environment deleted.'); } /** * @return \Illuminate\Support\Collection */ private function serverIdsFor(Environment $environment): Collection { return $environment->services ->flatMap(fn ($service) => [ $service->server_id, ...$service->replicas->pluck('server_id')->all(), ]) ->filter() ->unique() ->values(); } /** * @return Collection */ private function gatewayRoutePreviews(Environment $environment): Collection { $upstreams = $this->previewGatewayUpstreams($environment); $renderer = app(CaddyRouteRenderer::class); return $environment->attachments ->filter(fn ($attachment): bool => $attachment->role === EnvironmentAttachmentRole::GATEWAY) ->map(fn ($attachment): array => [ 'attachment_id' => $attachment->id, 'caddyfile' => $renderer->render($attachment, $upstreams), ]) ->values(); } /** * @return array */ private function previewGatewayUpstreams(Environment $environment): array { return $environment->services ->filter(fn ($service): bool => $service->type === ServiceType::LARAVEL && in_array('web', $service->process_roles ?? [], true)) ->flatMap(fn ($service) => $service->replicas->map( fn ($replica): string => ($replica->internal_host ?: $replica->container_name).':'.($replica->internal_port ?: 80) )) ->values() ->whenEmpty(fn ($upstreams) => $upstreams->push('web:80')) ->all(); } }