Implement Keystone environment deployments

This commit is contained in:
2026-05-13 16:11:23 +01:00
parent 65d3142d03
commit aa680b25fd
175 changed files with 10258 additions and 740 deletions

View File

@@ -2,26 +2,65 @@
namespace App\Http\Controllers;
use App\Actions\Applications\CreateLaravelEnvironment;
use App\Actions\Applications\GenerateDeployKey;
use App\Actions\Applications\VerifyRepositoryAccess;
use App\Enums\RepositoryType;
use App\Enums\ServerStatus;
use App\Http\Requests\StoreApplicationRequest;
use App\Models\Application;
use App\Models\Organisation;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Inertia\Response;
class ApplicationController extends Controller
{
public function index(Request $request)
public function index(Request $request): Response
{
$organisation = Organisation::with('applications.instances.server')->findOrFail($request->route('organisation'));
$organisation = Organisation::with('applications.environments.services')->findOrFail($request->route('organisation'));
return inertia('applications/Index', [
'applications' => $organisation->applications,
]);
}
public function show(Request $request)
public function create(Request $request): Response
{
Organisation::findOrFail($request->route('organisation'));
return inertia('applications/Create');
}
public function store(StoreApplicationRequest $request): RedirectResponse
{
$organisation = Organisation::findOrFail($request->route('organisation'));
$application = $organisation->applications()->create([
'name' => $request->string('name')->toString(),
'repository_url' => $request->string('repository_url')->toString(),
'repository_type' => RepositoryType::GIT,
'default_branch' => $request->string('default_branch')->toString(),
]);
app(GenerateDeployKey::class)->execute($application);
app(CreateLaravelEnvironment::class)->execute($application->refresh(), $request->string('environment_name')->toString());
return redirect()
->route('applications.show', ['organisation' => $organisation->id, 'application' => $application->id])
->with('success', 'Application created. Add the deploy key to your repository before verifying access.');
}
public function show(Request $request): Response
{
$id = $request->route('application');
$application = Application::with(['instances.server', 'organisation'])->findOrFail($id);
$organisation = Organisation::findOrFail($request->route('organisation'));
$application = Application::with([
'environments.services.slices',
'environments.attachments.service',
'environments.variables',
'organisation',
])->whereBelongsTo($organisation)->findOrFail($id);
return inertia('applications/Show', [
'application' => $application,
@@ -35,4 +74,16 @@ class ApplicationController extends Controller
}),
]);
}
public function verifyRepository(Request $request): RedirectResponse
{
$organisation = Organisation::findOrFail($request->route('organisation'));
$application = $organisation->applications()->findOrFail($request->route('application'));
if (! app(VerifyRepositoryAccess::class)->execute($application)) {
return back()->with('error', 'Repository access could not be verified.');
}
return back()->with('success', 'Repository access verified.');
}
}

View File

@@ -0,0 +1,57 @@
<?php
namespace App\Http\Controllers;
use App\Actions\Environments\AttachManagedService;
use App\Enums\EnvironmentAttachmentRole;
use App\Enums\ServiceType;
use App\Http\Requests\StoreEnvironmentAttachmentRequest;
use App\Models\Organisation;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Inertia\Response;
class EnvironmentAttachmentController extends Controller
{
public function create(Request $request): Response
{
$organisation = Organisation::findOrFail($request->route('organisation'));
$application = $organisation->applications()->findOrFail($request->route('application'));
$environment = $application->environments()->findOrFail($request->route('environment'));
return inertia('environment-attachments/Create', [
'application' => $application,
'environment' => $environment,
'services' => $organisation->services()
->whereIn('type', [ServiceType::POSTGRES->value, ServiceType::VALKEY->value, ServiceType::CADDY->value])
->orderBy('name')
->get(['id', 'name', 'type', 'category']),
'roles' => array_values(EnvironmentAttachmentRole::toArray()),
]);
}
public function store(StoreEnvironmentAttachmentRequest $request): RedirectResponse
{
$organisation = Organisation::findOrFail($request->route('organisation'));
$application = $organisation->applications()->findOrFail($request->route('application'));
$environment = $application->environments()->findOrFail($request->route('environment'));
$service = $organisation->services()->findOrFail($request->integer('service_id'));
app(AttachManagedService::class)->execute(
environment: $environment,
service: $service,
role: $request->enum('role', EnvironmentAttachmentRole::class),
name: $request->filled('name') ? $request->string('name')->toString() : null,
envPrefix: $request->filled('env_prefix') ? $request->string('env_prefix')->toString() : null,
isPrimary: $request->boolean('is_primary', true),
);
return redirect()
->route('environments.show', [
'organisation' => $organisation->id,
'application' => $application->id,
'environment' => $environment->id,
])
->with('success', 'Managed service attached.');
}
}

View File

@@ -0,0 +1,32 @@
<?php
namespace App\Http\Controllers;
use App\Models\Organisation;
use Illuminate\Http\Request;
use Inertia\Response;
class EnvironmentController extends Controller
{
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.slices',
'services.operations.steps',
'attachments.service',
'attachments.serviceSlice',
'variables',
'operations.steps',
])
->findOrFail($request->route('environment'));
return inertia('environments/Show', [
'application' => $application,
'environment' => $environment,
]);
}
}

View File

@@ -0,0 +1,29 @@
<?php
namespace App\Http\Controllers;
use App\Jobs\Environments\DeployEnvironment;
use App\Models\Application;
use App\Models\Environment;
use App\Models\Organisation;
use Illuminate\Http\RedirectResponse;
class EnvironmentDeploymentController extends Controller
{
public function store(Organisation $organisation, Application $application, Environment $environment): RedirectResponse
{
abort_unless(
(int) $application->organisation_id === (int) $organisation->id
&& (int) $environment->application_id === (int) $application->id,
404,
);
dispatch(new DeployEnvironment($environment));
return redirect()->route('environments.show', [
'organisation' => $organisation->id,
'application' => $application->id,
'environment' => $environment->id,
]);
}
}

View File

@@ -0,0 +1,24 @@
<?php
namespace App\Http\Controllers;
use App\Actions\Environments\CreateMigrationOperation;
use App\Models\Organisation;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
class EnvironmentMigrationController extends Controller
{
public function store(Request $request): RedirectResponse
{
$organisation = Organisation::findOrFail($request->route('organisation'));
$application = $organisation->applications()->findOrFail($request->route('application'));
$environment = $application->environments()->findOrFail($request->route('environment'));
app(CreateMigrationOperation::class)->execute($environment);
return redirect()
->route('applications.show', ['organisation' => $organisation->id, 'application' => $application->id])
->with('success', 'Migration operation created.');
}
}

View File

@@ -0,0 +1,45 @@
<?php
namespace App\Http\Controllers;
use App\Enums\EnvironmentVariableSource;
use App\Http\Requests\StoreEnvironmentVariableRequest;
use App\Models\Organisation;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Inertia\Response;
class EnvironmentVariableController extends Controller
{
public function create(Request $request): Response
{
$organisation = Organisation::findOrFail($request->route('organisation'));
$application = $organisation->applications()->findOrFail($request->route('application'));
$environment = $application->environments()->findOrFail($request->route('environment'));
return inertia('environment-variables/Create', [
'application' => $application,
'environment' => $environment,
]);
}
public function store(StoreEnvironmentVariableRequest $request): RedirectResponse
{
$organisation = Organisation::findOrFail($request->route('organisation'));
$application = $organisation->applications()->findOrFail($request->route('application'));
$environment = $application->environments()->findOrFail($request->route('environment'));
$environment->variables()->updateOrCreate([
'key' => $request->string('key')->toString(),
], [
'value' => $request->string('value')->toString(),
'source' => EnvironmentVariableSource::USER,
'service_slice_id' => null,
'overridable' => true,
]);
return redirect()
->route('applications.show', ['organisation' => $organisation->id, 'application' => $application->id])
->with('success', 'Environment variable saved.');
}
}

View File

@@ -0,0 +1,24 @@
<?php
namespace App\Http\Controllers;
use App\Actions\Environments\CreateLaravelWorkerService;
use App\Models\Organisation;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
class EnvironmentWorkerController extends Controller
{
public function store(Request $request): RedirectResponse
{
$organisation = Organisation::findOrFail($request->route('organisation'));
$application = $organisation->applications()->findOrFail($request->route('application'));
$environment = $application->environments()->findOrFail($request->route('environment'));
app(CreateLaravelWorkerService::class)->execute($environment);
return redirect()
->route('applications.show', ['organisation' => $organisation->id, 'application' => $application->id])
->with('success', 'Worker service created.');
}
}

View File

@@ -1,36 +0,0 @@
<?php
namespace App\Http\Controllers;
use App\Actions\Applications\CreateInstance;
use App\Models\Application;
use App\Models\Server;
use Illuminate\Http\Request;
class InstanceController extends Controller
{
public function store(Request $request, Application $application)
{
$validated = $request->validate([
'server_id' => 'required|exists:servers,id',
'branch' => 'required|string|max:255',
'config' => 'sometimes|array',
]);
$server = Server::findOrFail($validated['server_id']);
$instance = (new CreateInstance())->execute(
$application,
$server,
$validated['branch'],
$validated['config'] ?? []
);
return redirect()
->route('applications.show', [
'organisation' => $application->organisation_id,
'application' => $application->id
])
->with('success', 'Instance created successfully');
}
}

View File

@@ -0,0 +1,61 @@
<?php
namespace App\Http\Controllers;
use App\Models\Organisation;
use Inertia\Response;
class OnboardingController extends Controller
{
public function show(Organisation $organisation): Response
{
$organisation->loadCount(['providers', 'sourceProviders', 'registries', 'servers', 'applications']);
$steps = [
[
'key' => 'organisation',
'label' => 'Organisation',
'complete' => true,
'href' => route('organisations.show', ['organisation' => $organisation->id]),
],
[
'key' => 'provider',
'label' => 'Provider',
'complete' => $organisation->providers_count > 0,
'href' => route('organisations.show', ['organisation' => $organisation->id]),
],
[
'key' => 'source',
'label' => 'Source',
'complete' => $organisation->source_providers_count > 0,
'href' => route('source-providers.create', ['organisation' => $organisation->id]),
],
[
'key' => 'registry',
'label' => 'Registry',
'complete' => $organisation->registries_count > 0,
'href' => route('registries.create', ['organisation' => $organisation->id]),
],
[
'key' => 'server',
'label' => 'Server',
'complete' => $organisation->servers_count > 0,
'href' => route('servers.create', ['organisation' => $organisation->id]),
],
[
'key' => 'application',
'label' => 'Application',
'complete' => $organisation->applications_count > 0,
'href' => route('applications.create', ['organisation' => $organisation->id]),
],
];
$next = collect($steps)->firstWhere('complete', false) ?? $steps[array_key_last($steps)];
return inertia('onboarding/Show', [
'organisation' => $organisation,
'steps' => $steps,
'nextStep' => $next,
]);
}
}

View File

@@ -13,6 +13,8 @@ class OrganisationController extends Controller
{
return inertia('organisations/Show', [
'providers' => Inertia::lazy(fn () => Provider::whereOrganisationId($request->route('organisation'))->get()),
'registries' => Inertia::lazy(fn () => Organisation::findOrFail($request->route('organisation'))->registries()->get()),
'sourceProviders' => Inertia::lazy(fn () => Organisation::findOrFail($request->route('organisation'))->sourceProviders()->get()),
'organisation' => Organisation::withCount('servers', 'applications', 'members')->findOrFail($request->route('organisation')),
]);
}

View File

@@ -0,0 +1,41 @@
<?php
namespace App\Http\Controllers;
use App\Enums\RegistryType;
use App\Http\Requests\StoreRegistryRequest;
use App\Models\Organisation;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Inertia\Response;
class RegistryController extends Controller
{
public function create(Request $request): Response
{
Organisation::findOrFail($request->route('organisation'));
return inertia('registries/Create', [
'registryTypes' => array_values(RegistryType::toArray()),
]);
}
public function store(StoreRegistryRequest $request): RedirectResponse
{
$organisation = Organisation::findOrFail($request->route('organisation'));
$organisation->registries()->create([
'name' => $request->string('name')->toString(),
'type' => $request->enum('type', RegistryType::class),
'url' => rtrim($request->string('url')->toString(), '/'),
'credentials' => [
'username' => $request->string('username')->toString(),
'password' => $request->string('password')->toString(),
],
]);
return redirect()
->route('organisations.show', ['organisation' => $organisation->id])
->with('success', 'Registry created.');
}
}

View File

@@ -75,12 +75,12 @@ class ServerController extends Controller
}
$networkZone = $request->network_zone ?? 'global';
// Look for an existing network with the same network_zone
$network = $provider->networks()
->where('network_zone', $networkZone)
->first();
if (! $network) {
// We need to create a network with the correct network zone
$networkName = "keystone-{$networkZone}";
@@ -141,7 +141,7 @@ class ServerController extends Controller
$server = $organisation->servers()->findOrFail($request->route('server'));
return inertia('servers/Show', [
'server' => $server->load('services.slices', 'serviceDeployments.steps', 'serviceDeployments.target'),
'server' => $server->load('services.slices', 'serviceOperations.steps', 'serviceOperations.target'),
]);
}
}

View File

@@ -5,13 +5,16 @@ namespace App\Http\Controllers;
use App\Actions\Services\CreateService;
use App\Enums\ServiceCategory;
use App\Enums\ServiceType;
use App\Http\Requests\StoreServiceRequest;
use App\Http\Requests\UpdateServiceRequest;
use App\Models\Server;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Validation\Rule;
use Inertia\Response;
class ServiceController extends Controller
{
public function create(Request $request)
public function create(Request $request): Response
{
$server = Server::findOrFail($request->route('server'));
@@ -21,19 +24,8 @@ class ServiceController extends Controller
]);
}
public function store(Request $request)
public function store(StoreServiceRequest $request): RedirectResponse
{
$request->validate([
'name' => ['required', 'string', 'max:255'],
'category' => ['required', Rule::enum(ServiceCategory::class)],
'type' => ['required', Rule::enum(ServiceType::class)],
'version' => ['required', 'string', function ($key, $value, $fail) use ($request) {
if (!isset(config('keystone.services')[$request->category][$request->type]['versions'][$value])) {
$fail('The selected version is invalid.');
}
}],
]);
$server = Server::findOrFail($request->route('server'));
$service = app(CreateService::class)->execute(
@@ -52,4 +44,44 @@ class ServiceController extends Controller
'service' => $service,
]);
}
public function show(Request $request): Response
{
$server = Server::findOrFail($request->route('server'));
$service = $server->services()
->with(['replicas', 'slices', 'operations.steps', 'environment.application'])
->findOrFail($request->route('service'));
return inertia('services/Show', [
'server' => $server,
'service' => $service,
]);
}
public function edit(Request $request): Response
{
$server = Server::findOrFail($request->route('server'));
$service = $server->services()->findOrFail($request->route('service'));
return inertia('services/Edit', [
'server' => $server,
'service' => $service,
]);
}
public function update(UpdateServiceRequest $request): RedirectResponse
{
$server = Server::findOrFail($request->route('server'));
$service = $server->services()->findOrFail($request->route('service'));
$service->update($request->validated());
return redirect()
->route('services.show', [
'organisation' => $server->organisation_id,
'server' => $server->id,
'service' => $service->id,
])
->with('success', 'Service updated.');
}
}

View File

@@ -0,0 +1,48 @@
<?php
namespace App\Http\Controllers;
use App\Actions\Services\CreateStatefulServiceUpdateOperation;
use App\Enums\ServiceType;
use App\Http\Requests\StoreServiceUpdateRequest;
use App\Models\Organisation;
use App\Models\Server;
use App\Models\Service;
use Illuminate\Http\RedirectResponse;
use Inertia\Response;
class ServiceUpdateController extends Controller
{
public function create(Organisation $organisation, Server $server, Service $service): Response
{
abort_unless((int) $server->organisation_id === (int) $organisation->id && (int) $service->server_id === (int) $server->id, 404);
abort_unless(in_array($service->type, [ServiceType::POSTGRES, ServiceType::VALKEY], true), 404);
return inertia('services/updates/Create', [
'server' => $server,
'service' => $service,
'backupAvailable' => (bool) ($service->config['backup_enabled'] ?? false),
]);
}
public function store(
StoreServiceUpdateRequest $request,
Organisation $organisation,
Server $server,
Service $service,
CreateStatefulServiceUpdateOperation $createStatefulServiceUpdateOperation,
): RedirectResponse {
abort_unless((int) $server->organisation_id === (int) $organisation->id && (int) $service->server_id === (int) $server->id, 404);
$createStatefulServiceUpdateOperation->execute(
service: $service,
imageDigest: $request->string('image_digest')->toString(),
backupRequested: $request->boolean('backup_requested'),
);
return redirect()->route('servers.show', [
'organisation' => $organisation->id,
'server' => $server->id,
]);
}
}

View File

@@ -0,0 +1,38 @@
<?php
namespace App\Http\Controllers;
use App\Enums\SourceProviderType;
use App\Http\Requests\StoreSourceProviderRequest;
use App\Models\Organisation;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Inertia\Response;
class SourceProviderController extends Controller
{
public function create(Request $request): Response
{
Organisation::findOrFail($request->route('organisation'));
return inertia('source-providers/Create', [
'sourceProviderTypes' => array_values(SourceProviderType::toArray()),
]);
}
public function store(StoreSourceProviderRequest $request): RedirectResponse
{
$organisation = Organisation::findOrFail($request->route('organisation'));
$organisation->sourceProviders()->create([
'name' => $request->string('name')->toString(),
'type' => $request->enum('type', SourceProviderType::class),
'url' => $request->filled('url') ? rtrim($request->string('url')->toString(), '/') : null,
'config' => [],
]);
return redirect()
->route('organisations.show', ['organisation' => $organisation->id])
->with('success', 'Source provider created.');
}
}

View File

@@ -4,6 +4,7 @@ namespace App\Http\Middleware;
use App\Models\Application;
use App\Models\Organisation;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Http\Request;
use Inertia\Middleware;
use Tighten\Ziggy\Ziggy;
@@ -29,8 +30,12 @@ class HandleInertiaRequests extends Middleware
return [
...parent::share($request),
'name' => config('app.name'),
'organisation' => $request->route('organisation') ? Organisation::with('applications')->findOrFail($request->route('organisation')) : null,
'application' => $request->route('application') ? Application::with('environments')->findOrFail($request->route('application')) : null,
'organisation' => $request->route('organisation')
? Organisation::with('applications')->findOrFail($this->routeKey($request->route('organisation')))
: null,
'application' => $request->route('application')
? Application::with('environments')->findOrFail($this->routeKey($request->route('application')))
: null,
'flash' => [
'server_credentials' => $request->session()->has('sudo_password') ? [
'sudo_password' => $request->session()->get('sudo_password'),
@@ -45,4 +50,9 @@ class HandleInertiaRequests extends Middleware
],
];
}
private function routeKey(mixed $routeValue): mixed
{
return $routeValue instanceof Model ? $routeValue->getKey() : $routeValue;
}
}

View File

@@ -0,0 +1,31 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class StoreApplicationRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [
'name' => ['required', 'string', 'max:255'],
'repository_url' => ['required', 'string', 'max:255', 'regex:/^(git@[^:]+:.+|ssh:\/\/.+)$/i'],
'default_branch' => ['required', 'string', 'max:255', 'regex:/^[A-Za-z0-9._\/-]+$/'],
'environment_name' => ['required', 'string', 'max:255'],
];
}
}

View File

@@ -0,0 +1,34 @@
<?php
namespace App\Http\Requests;
use App\Enums\EnvironmentAttachmentRole;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;
class StoreEnvironmentAttachmentRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [
'service_id' => ['required', 'integer', 'exists:services,id'],
'role' => ['required', Rule::enum(EnvironmentAttachmentRole::class)],
'name' => ['nullable', 'string', 'max:255'],
'env_prefix' => ['nullable', 'string', 'max:32', 'regex:/^[A-Z][A-Z0-9_]*$/'],
'is_primary' => ['boolean'],
];
}
}

View File

@@ -0,0 +1,29 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class StoreEnvironmentVariableRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [
'key' => ['required', 'string', 'max:255', 'regex:/^[A-Z][A-Z0-9_]*$/'],
'value' => ['nullable', 'string'],
];
}
}

View File

@@ -0,0 +1,34 @@
<?php
namespace App\Http\Requests;
use App\Enums\RegistryType;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;
class StoreRegistryRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [
'name' => ['required', 'string', 'max:255'],
'type' => ['required', Rule::enum(RegistryType::class)],
'url' => ['required', 'string', 'max:255'],
'username' => ['nullable', 'string', 'max:255'],
'password' => ['nullable', 'string', 'max:255'],
];
}
}

View File

@@ -0,0 +1,56 @@
<?php
namespace App\Http\Requests;
use App\Enums\ServiceCategory;
use App\Enums\ServiceType;
use App\Models\Server;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;
class StoreServiceRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [
'name' => ['required', 'string', 'max:255'],
'category' => ['required', Rule::enum(ServiceCategory::class)],
'type' => ['required', Rule::enum(ServiceType::class)],
'version' => ['required', 'string', function (string $attribute, mixed $value, \Closure $fail): void {
if (! isset(config('keystone.services')[$this->category][$this->type]['versions'][$value])) {
$fail('The selected version is invalid.');
}
}],
];
}
public function after(): array
{
return [
function ($validator): void {
if ($this->category !== ServiceCategory::GATEWAY->value) {
return;
}
$server = Server::find($this->route('server'));
if ($server?->services()->where('category', ServiceCategory::GATEWAY)->exists()) {
$validator->errors()->add('category', 'This server already has a gateway service.');
}
},
];
}
}

View File

@@ -0,0 +1,26 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class StoreServiceUpdateRequest extends FormRequest
{
public function authorize(): bool
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [
'image_digest' => ['required', 'string', 'starts_with:sha256:'],
'backup_requested' => ['sometimes', 'boolean'],
];
}
}

View File

@@ -0,0 +1,32 @@
<?php
namespace App\Http\Requests;
use App\Enums\SourceProviderType;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;
class StoreSourceProviderRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [
'name' => ['required', 'string', 'max:255'],
'type' => ['required', Rule::enum(SourceProviderType::class)],
'url' => ['nullable', 'string', 'max:255'],
];
}
}

View File

@@ -0,0 +1,31 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class UpdateServiceRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [
'name' => ['required', 'string', 'max:255'],
'desired_replicas' => ['required', 'integer', 'min:0', 'max:25'],
'default_cpu_limit' => ['nullable', 'numeric', 'min:0.125', 'max:64'],
'default_memory_limit_mb' => ['nullable', 'integer', 'min:64', 'max:1048576'],
];
}
}