server list, logs removed, driver/plan wip

This commit is contained in:
2025-03-31 12:41:09 +00:00
parent 678581b822
commit 75f2ecb7bf
14 changed files with 164 additions and 47 deletions

View File

@@ -0,0 +1,15 @@
<?php
namespace App\Data\Deployments;
class Plan
{
/**
* @param Step[] $steps
*/
public function __construct(
public array $steps = [],
) {
//
}
}

View File

@@ -0,0 +1,11 @@
<?php
namespace App\Data\Deployments;
class Step
{
public function __construct(
) {
//
}
}

View File

@@ -0,0 +1,8 @@
<?php
namespace App\Drivers;
interface DatabaseDriver extends Driver
{
//
}

8
app/Drivers/Driver.php Normal file
View File

@@ -0,0 +1,8 @@
<?php
namespace App\Drivers;
interface Driver
{
//
}

View File

@@ -0,0 +1,10 @@
<?php
namespace App\Drivers\Postgres;
use App\Drivers\DatabaseDriver;
class Postgres17Driver implements DatabaseDriver
{
// @todo
}

View File

@@ -0,0 +1,11 @@
<?php
namespace App\Enums;
enum ServiceStatus: string
{
case RUNNING = 'running';
case STOPPED = 'stopped';
case ERROR = 'error';
case UNKNOWN = 'unknown';
}

View File

@@ -92,13 +92,6 @@ class ServerController extends Controller
sudoPassword: $sudoPassword, sudoPassword: $sudoPassword,
))->delay(now()->addSeconds(5)); ))->delay(now()->addSeconds(5));
logger('server created');
logger($createdServer->id);
logger($createdServer->ipv4);
logger($createdServer->ipv6);
logger($createdServer->rootPassword);
logger($sudoPassword);
session()->flash('sudo_password', $sudoPassword); session()->flash('sudo_password', $sudoPassword);
return redirect()->route('servers.show', ['organisation' => $organisation->id, 'server' => $server->id]); return redirect()->route('servers.show', ['organisation' => $organisation->id, 'server' => $server->id]);

View File

@@ -43,21 +43,12 @@ class ProvisionServer implements ShouldQueue, ShouldBeEncrypted
]); ]);
logger('executing script on server'); logger('executing script on server');
if (! $result->isSuccessful()) { if (! $result->isSuccessful()) {
logger('failed to execute script on server');
logger($result->getOutput());
logger($result->getErrorOutput());
logger($result->getExitCode());
$this->server->update([ $this->server->update([
'status' => ServerStatus::PROVISIONING_FAILED, 'status' => ServerStatus::PROVISIONING_FAILED,
]); ]);
return; return;
} }
logger('script executed on server');
logger($result->getOutput());
logger($result->getErrorOutput());
logger($result->getExitCode());
$this->server->update([ $this->server->update([
'status' => ServerStatus::PROVISIONING, 'status' => ServerStatus::PROVISIONING,
]); ]);

View File

@@ -34,12 +34,6 @@ class WaitForServerToConnect implements ShouldQueue, ShouldBeEncrypted
->execute('echo "Connected"'); ->execute('echo "Connected"');
if (! $process->isSuccessful()) { if (! $process->isSuccessful()) {
logger('root pw: ' . $this->rootPassword);
logger('server not reachable');
logger('exit code' . $process->getExitCode());
logger('output');
logger($process->getOutput());
logger($process->getErrorOutput());
$this->release(15); $this->release(15);
return; return;
} }

View File

@@ -2,6 +2,8 @@
namespace App\Models; namespace App\Models;
use App\Drivers\Driver;
use App\Enums\ServiceStatus;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\Relations\HasMany;
@@ -10,6 +12,13 @@ class Service extends Model
{ {
protected $guarded = []; protected $guarded = [];
protected function casts(): array
{
return [
'status' => ServiceStatus::class,
];
}
public function server(): BelongsTo public function server(): BelongsTo
{ {
return $this->belongsTo(Server::class); return $this->belongsTo(Server::class);
@@ -19,4 +28,9 @@ class Service extends Model
{ {
return $this->hasMany(Slice::class); return $this->hasMany(Slice::class);
} }
public function driver()//: Driver
{
// @todo. This is the class that controls the service
}
} }

View File

@@ -12,9 +12,14 @@ return new class extends Migration
Schema::create('services', function (Blueprint $table) { Schema::create('services', function (Blueprint $table) {
$table->id(); $table->id();
$table->foreignIdFor(Server::class); $table->foreignIdFor(Server::class);
$table->string('category');
$table->string('type');
$table->string('name'); $table->string('name');
$table->string('status');
$table->string('category'); // database / cache / webserver
$table->string('type'); // postgres / redis / caddy
$table->string('version'); // 17 / 7 / 2
$table->string('driver_name');
$table->string('container_name')->nullable();
$table->string('container_id')->nullable();
$table->timestamps(); $table->timestamps();
}); });
} }

View File

@@ -20,10 +20,18 @@ const environment = usePage().props.environment ?? null;
<header <header
class="flex h-16 shrink-0 items-center gap-2 border-b border-sidebar-border/70 px-6 transition-[width,height] ease-in-out group-has-[[data-collapsible=icon]]/sidebar-wrapper:h-12 md:px-4" class="flex h-16 shrink-0 items-center gap-2 border-b border-sidebar-border/70 px-6 transition-[width,height] ease-in-out group-has-[[data-collapsible=icon]]/sidebar-wrapper:h-12 md:px-4"
> >
<div class="flex items-center gap-4"> <div class="flex w-full items-center gap-4">
<SidebarTrigger class="-ml-1" /> <SidebarTrigger class="-ml-1" />
<div class="gap-0.25 flex items-center"> <template v-if="breadcrumbs.length > 0">
<Button :as="organisation ? Link : 'button'" :href="organisation ? route('organisations.show', { organisation: organisation?.id }) : null" variant="ghost" size="xs"> <Breadcrumbs :breadcrumbs="breadcrumbs" />
</template>
<div class="gap-0.25 ml-auto flex items-center">
<Button
:as="organisation ? Link : 'button'"
:href="organisation ? route('organisations.show', { organisation: organisation?.id }) : null"
variant="ghost"
size="xs"
>
{{ organisation?.name ?? 'Select Organisation' }} {{ organisation?.name ?? 'Select Organisation' }}
</Button> </Button>
<DropdownMenu> <DropdownMenu>
@@ -42,15 +50,18 @@ const environment = usePage().props.environment ?? null;
</div> </div>
<div v-if="organisation" class="gap-0.25 flex items-center"> <div v-if="organisation" class="gap-0.25 flex items-center">
<Button <Button
:disabled="!organisation?.applications?.length"
:as="application ? Link : 'button'" :as="application ? Link : 'button'"
:href="application ? route('applications.show', { organisation: application.organisation_id, application: application.id }) : null" :href="
application ? route('applications.show', { organisation: application.organisation_id, application: application.id }) : null
"
variant="ghost" variant="ghost"
size="xs" size="xs"
> >
{{ application?.name ?? 'Select Application' }} {{ application?.name ?? 'Application' }}
</Button> </Button>
<DropdownMenu> <DropdownMenu>
<DropdownMenuTrigger :as="Button" size="iconxs" variant="ghost"> <DropdownMenuTrigger :as="Button" size="iconxs" variant="ghost" :disabled="!organisation?.applications?.length">
<ChevronsUpDown class="size-3" /> <ChevronsUpDown class="size-3" />
</DropdownMenuTrigger> </DropdownMenuTrigger>
<DropdownMenuContent> <DropdownMenuContent>
@@ -63,32 +74,44 @@ const environment = usePage().props.environment ?? null;
</DropdownMenuContent> </DropdownMenuContent>
</DropdownMenu> </DropdownMenu>
</div> </div>
<div v-if="application" class="gap-0.25 flex items-center"> <div class="gap-0.25 flex items-center">
<Button <Button
:disabled="!application?.environments?.length"
:as="environment ? Link : 'button'" :as="environment ? Link : 'button'"
:href="environment ? route('environments.show', { organisation: application.organisation_id, application: application.id, environment: environment.id }) : null" :href="
environment
? route('environments.show', {
organisation: application.organisation_id,
application: application.id,
environment: environment.id,
})
: null
"
variant="ghost" variant="ghost"
size="xs" size="xs"
> >
{{ environment?.name ?? 'Select Environment' }} {{ environment?.name ?? 'Environment' }}
</Button> </Button>
<DropdownMenu> <DropdownMenu>
<DropdownMenuTrigger :as="Button" size="iconxs" variant="ghost"> <DropdownMenuTrigger :as="Button" size="iconxs" variant="ghost" :disabled="!application?.environments?.length">
<ChevronsUpDown class="size-3" /> <ChevronsUpDown class="size-3" />
</DropdownMenuTrigger> </DropdownMenuTrigger>
<DropdownMenuContent> <DropdownMenuContent>
<DropdownMenuItem <DropdownMenuItem
v-for="env in application?.environments" v-for="env in application?.environments"
:as="Link" :as="Link"
:href="route('environments.show', { organisation: application.organisation_id, application: application.id, environment: env.id })" :href="
route('environments.show', {
organisation: application.organisation_id,
application: application.id,
environment: env.id,
})
"
>{{ env.name }}</DropdownMenuItem >{{ env.name }}</DropdownMenuItem
> >
</DropdownMenuContent> </DropdownMenuContent>
</DropdownMenu> </DropdownMenu>
</div> </div>
<template v-if="breadcrumbs.length > 0">
<Breadcrumbs :breadcrumbs="breadcrumbs" />
</template>
</div> </div>
</header> </header>
</template> </template>

View File

@@ -1,6 +1,7 @@
<script setup lang="ts"> <script setup>
import { Card, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
import AppLayout from '@/layouts/AppLayout.vue'; import AppLayout from '@/layouts/AppLayout.vue';
import { Head } from '@inertiajs/vue3'; import { Head, Link } from '@inertiajs/vue3';
const props = defineProps({ const props = defineProps({
servers: { servers: {
@@ -13,9 +14,28 @@ const props = defineProps({
<template> <template>
<Head title="Dashboard" /> <Head title="Dashboard" />
<AppLayout> <AppLayout :breadcrumbs="[
<div class="flex h-full flex-1 flex-col gap-4 rounded-xl p-4"> {
{{ servers.data }} title: 'Servers',
href: route('servers.index', {
organisation: $page.props.organisation.id,
}),
},
]">
<div class="grid gap-4 rounded-xl p-4 md:grid-cols-2 lg:grid-cols-3">
<Card v-for="server in servers.data" :key="`server{$servers.id}`" class="w-full relative">
<CardHeader>
<CardTitle>{{ server.name }}</CardTitle>
<CardDescription
><span class="inline-block rounded-md bg-green-200 px-2 text-xs uppercase text-green-800">{{ server.status }}</span> &bull;
{{ server.ipv4 || server.ipv6 }}</CardDescription
>
</CardHeader>
<Link :href="route('servers.show', {
organisation: $page.props.organisation.id,
server: server.id,
})" class="absolute inset-0"></Link>
</Card>
<div>@todo pagination</div> <div>@todo pagination</div>
</div> </div>

View File

@@ -1,8 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
import AppLayout from '@/layouts/AppLayout.vue'; import AppLayout from '@/layouts/AppLayout.vue';
import { type BreadcrumbItem } from '@/types';
import { Head } from '@inertiajs/vue3'; import { Head } from '@inertiajs/vue3';
import PlaceholderPattern from '../components/PlaceholderPattern.vue';
const props = defineProps({ const props = defineProps({
server: { server: {
@@ -15,13 +13,29 @@ const props = defineProps({
<template> <template>
<Head :title="server.name" /> <Head :title="server.name" />
<AppLayout> <AppLayout
:breadcrumbs="[
{
title: 'Servers',
href: route('servers.index', {
organisation: $page.props.organisation.id,
}),
},
{
title: server.name,
href: route('servers.show', {
organisation: $page.props.organisation.id,
server: server.id,
}),
},
]"
>
<div class="flex h-full flex-1 flex-col gap-4 rounded-xl p-4"> <div class="flex h-full flex-1 flex-col gap-4 rounded-xl p-4">
{{ server }} {{ server }}
<div v-if="$page.props.flash?.server_credentials" class="p-5"> <div v-if="$page.props.flash?.server_credentials" class="p-5">
<div class="mb-4 text-sm font-medium text-gray-900 dark:text-white"> <div class="mb-4 text-sm font-medium text-gray-900 dark:text-white">
WILL NOT BE SHOWN AGAIN: WILL NOT BE SHOWN AGAIN:
{{ $page.props.flash.server_credentials }} {{ $page.props.flash.server_credentials }}
</div> </div>
</div> </div>