205 lines
7.4 KiB
PHP
205 lines
7.4 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers;
|
|
|
|
use App\Actions\GenerateRandomSlug;
|
|
use App\Enums\OperationKind;
|
|
use App\Enums\OperationStatus;
|
|
use App\Enums\ServerStatus;
|
|
use App\Jobs\Servers\WaitForServerToConnect;
|
|
use App\Models\Organisation;
|
|
use App\Models\Provider;
|
|
use Illuminate\Http\RedirectResponse;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\Cache;
|
|
use Illuminate\Support\Str;
|
|
use Inertia\Response;
|
|
|
|
class ServerController extends Controller
|
|
{
|
|
public function index(Request $request): Response
|
|
{
|
|
$organisation = Organisation::findOrFail($request->route('organisation'));
|
|
|
|
return inertia('servers/Index', [
|
|
'servers' => $organisation->servers()->paginate(30),
|
|
'networks' => $organisation->networks()
|
|
->with(['servers' => fn ($query) => $query->select('id', 'network_id', 'name', 'private_ip', 'status')])
|
|
->get(),
|
|
]);
|
|
}
|
|
|
|
public function create(Request $request): Response
|
|
{
|
|
$organisation = Organisation::findOrFail($request->route('organisation'));
|
|
|
|
$locations = null;
|
|
$serverTypes = null;
|
|
$images = null;
|
|
|
|
if ($request->has('provider')) {
|
|
$provider = Provider::findOrFail($request->provider);
|
|
$providerService = $provider->service();
|
|
|
|
if ($providerService) {
|
|
$locations = Cache::remember($request->provider.'.locations', now()->addHour(), function () use ($providerService) {
|
|
return $providerService->getLocations();
|
|
});
|
|
$serverTypes = Cache::remember($request->provider.'.serverTypes', now()->addHour(), function () use ($providerService) {
|
|
return $providerService->getServerTypes();
|
|
});
|
|
$images = Cache::remember($request->provider.'.images', now()->addHour(), function () use ($providerService) {
|
|
return $providerService->getImages();
|
|
});
|
|
}
|
|
}
|
|
|
|
return inertia('servers/Create', [
|
|
'providers' => $organisation->providers,
|
|
'locations' => $locations,
|
|
'serverTypes' => $serverTypes,
|
|
'images' => $images,
|
|
]);
|
|
}
|
|
|
|
public function store(Request $request): RedirectResponse
|
|
{
|
|
$request->validate([
|
|
'provider' => ['required', 'exists:providers,id'],
|
|
'server_type' => ['required', 'string'],
|
|
'location' => ['required', 'string'],
|
|
'image' => ['required', 'string'],
|
|
'network_zone' => ['nullable', 'string'],
|
|
]);
|
|
|
|
$sudoPassword = Str::random(32);
|
|
/** @var Provider $provider */
|
|
$provider = Provider::findOrFail($request->provider);
|
|
$providerService = $provider->service();
|
|
|
|
if (! $providerService) {
|
|
return back()->with('error', 'Invalid provider');
|
|
}
|
|
|
|
$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}";
|
|
$createdNetwork = $providerService->createNetwork(
|
|
name: $networkName,
|
|
networkZone: $networkZone
|
|
);
|
|
|
|
$network = $provider->networks()->create([
|
|
'organisation_id' => $provider->organisation_id,
|
|
'external_id' => $createdNetwork->id,
|
|
'name' => $createdNetwork->name,
|
|
'ip_range' => $createdNetwork->ipRange,
|
|
'network_zone' => $networkZone,
|
|
]);
|
|
}
|
|
|
|
$createdServer = $providerService->createServer(
|
|
name: app(GenerateRandomSlug::class)->execute(), // @todo allow custom name
|
|
serverType: $request->server_type,
|
|
location: $request->location,
|
|
image: $request->image,
|
|
networkId: $network->external_id,
|
|
);
|
|
|
|
$organisation = Organisation::findOrFail($request->route('organisation'));
|
|
|
|
$server = $organisation->servers()->create([
|
|
'name' => $createdServer->name,
|
|
'provider_id' => $provider->id,
|
|
'external_id' => $createdServer->id,
|
|
'ipv4' => $createdServer->ipv4,
|
|
'ipv6' => $createdServer->ipv6,
|
|
'private_ip' => $createdServer->privateIp,
|
|
'provider_status' => $createdServer->status,
|
|
'status' => ServerStatus::WAITING_FOR_PROVIDER,
|
|
'region' => $request->location,
|
|
'os' => $request->image,
|
|
'plan' => $request->server_type,
|
|
'user' => 'keystone',
|
|
'network_id' => $network->id,
|
|
]);
|
|
|
|
dispatch(new WaitForServerToConnect(
|
|
server: $server,
|
|
rootPassword: $createdServer->rootPassword,
|
|
sudoPassword: $sudoPassword,
|
|
))->delay(now()->addSeconds(5));
|
|
|
|
session()->flash('sudo_password', $sudoPassword);
|
|
|
|
return redirect()->route('servers.show', ['organisation' => $organisation->id, 'server' => $server->id]);
|
|
}
|
|
|
|
public function show(Request $request): Response
|
|
{
|
|
$organisation = Organisation::findOrFail($request->route('organisation'));
|
|
$server = $organisation->servers()->findOrFail($request->route('server'));
|
|
|
|
return inertia('servers/Show', [
|
|
'server' => $server->load(
|
|
'firewallRules',
|
|
'network',
|
|
'operations.steps',
|
|
'operations.children.target',
|
|
'services.slices',
|
|
'services.endpoints',
|
|
'serviceOperations.steps',
|
|
'serviceOperations.children.target',
|
|
'serviceOperations.target',
|
|
),
|
|
]);
|
|
}
|
|
|
|
public function destroy(Request $request): RedirectResponse
|
|
{
|
|
$organisation = Organisation::findOrFail($request->route('organisation'));
|
|
$server = $organisation->servers()->findOrFail($request->route('server'));
|
|
|
|
$server->delete();
|
|
|
|
return redirect()
|
|
->route('servers.index', ['organisation' => $organisation->id])
|
|
->with('success', 'Server deleted.');
|
|
}
|
|
|
|
public function heal(Request $request): RedirectResponse
|
|
{
|
|
$organisation = Organisation::findOrFail($request->route('organisation'));
|
|
$server = $organisation->servers()->findOrFail($request->route('server'));
|
|
|
|
$operation = $server->operations()->create([
|
|
'kind' => OperationKind::SERVER_PROVISION,
|
|
'status' => OperationStatus::PENDING,
|
|
]);
|
|
|
|
foreach ([
|
|
'Check server shell' => 'true',
|
|
'Check Docker' => 'docker --version && docker compose version',
|
|
'Check Keystone directories' => 'test -d /home/keystone && test -d /home/keystone/services',
|
|
] as $order => $script) {
|
|
$operation->steps()->create([
|
|
'name' => $order,
|
|
'order' => $operation->steps()->count() + 1,
|
|
'status' => OperationStatus::PENDING,
|
|
'script' => $script,
|
|
]);
|
|
}
|
|
|
|
return redirect()
|
|
->route('servers.show', ['organisation' => $organisation->id, 'server' => $server->id])
|
|
->with('success', 'Server heal operation queued.');
|
|
}
|
|
}
|