Files
keystone/app/Http/Controllers/EnvironmentController.php
Harry Bayliss 5b977c1f41
Some checks failed
CI / Lint (push) Failing after 22s
CI / Tests (push) Failing after 33s
wowowowowo
2026-05-28 15:15:41 +01:00

208 lines
8.2 KiB
PHP

<?php
namespace App\Http\Controllers;
use App\Actions\Applications\CreateLaravelEnvironment;
use App\Enums\BuildStrategy;
use App\Enums\EnvironmentAttachmentRole;
use App\Enums\SchedulerMode;
use App\Enums\ServiceType;
use App\Http\Requests\StoreEnvironmentRequest;
use App\Http\Requests\UpdateEnvironmentRequest;
use App\Models\Environment;
use App\Models\Organisation;
use App\Support\CaddyRouteRenderer;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Collection;
use Inertia\Response;
class EnvironmentController extends Controller
{
public function create(Request $request): Response
{
$organisation = Organisation::findOrFail($request->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<int, int>
*/
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<int, array{attachment_id: int, caddyfile: string}>
*/
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<int, string>
*/
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();
}
}