admin widget zu dokploy geswitched

This commit is contained in:
Codex Agent
2025-11-18 16:45:56 +01:00
parent 6720ad84cf
commit 125c624588
22 changed files with 693 additions and 512 deletions

View File

@@ -112,14 +112,16 @@ class PostResource extends Resource
->columnSpanFull(),
])
->columns(2),
]),
TextInput::make('slug')
->label('Slug')
->required()
->unique(BlogPost::class, 'slug', ignoreRecord: true)
->maxLength(255),
])
->columnSpanFull(),
Section::make('Bild und Kategorie')
->schema([
TextInput::make('slug')
->label('Slug')
->required()
->unique(BlogPost::class, 'slug', ignoreRecord: true)
->maxLength(255)
->columnSpanFull(),
FileUpload::make('featured_image')
->label('Featured Image')
->image()
@@ -194,6 +196,8 @@ class PostResource extends Resource
->label('Titel (DE)')
->getStateUsing(fn ($record) => $record->getTranslation('title', 'de'))
->searchable()
->limit(50)
->url(fn ($record) => static::getUrl('edit', ['record' => $record]))
->sortable(),
TextColumn::make('category_label')
->label('Kategorie')
@@ -225,10 +229,10 @@ class PostResource extends Resource
->trueIcon('heroicon-o-check-circle')
->falseIcon('heroicon-o-x-circle'),
TextColumn::make('published_at')
->label('Veröffentlicht am')
->dateTime()
->sortable()
->toggleable(isToggledHiddenByDefault: true),
->label('Veröffentlichungsdatum')
->icon('heroicon-o-calendar')
->dateTime('d.m.Y H:i')
->sortable(),
TextColumn::make('created_at')
->label('Erstellt am')
->dateTime()
@@ -240,9 +244,9 @@ class PostResource extends Resource
->label('Veröffentlicht'),
])
->actions([
ViewAction::make(),
EditAction::make(),
DeleteAction::make(),
DeleteAction::make()
->icon('heroicon-o-trash')
->label(''),
])
->bulkActions([
BulkActionGroup::make([

View File

@@ -1,16 +0,0 @@
<?php
namespace App\Filament\Resources\CoolifyActionLogs\Pages;
use App\Filament\Resources\CoolifyActionLogs\CoolifyActionLogResource;
use Filament\Resources\Pages\ManageRecords;
class ManageCoolifyActionLogs extends ManageRecords
{
protected static string $resource = CoolifyActionLogResource::class;
protected function getHeaderActions(): array
{
return [];
}
}

View File

@@ -1,9 +1,9 @@
<?php
namespace App\Filament\Resources\CoolifyActionLogs;
namespace App\Filament\Resources\InfrastructureActionLogs;
use App\Filament\Resources\CoolifyActionLogs\Pages\ManageCoolifyActionLogs;
use App\Models\CoolifyActionLog;
use App\Filament\Resources\InfrastructureActionLogs\Pages\ManageInfrastructureActionLogs;
use App\Models\InfrastructureActionLog;
use BackedEnum;
use Filament\Resources\Resource;
use Filament\Support\Icons\Heroicon;
@@ -11,9 +11,9 @@ use Filament\Tables;
use Filament\Tables\Table;
use UnitEnum;
class CoolifyActionLogResource extends Resource
class InfrastructureActionLogResource extends Resource
{
protected static ?string $model = CoolifyActionLog::class;
protected static ?string $model = InfrastructureActionLog::class;
protected static string|BackedEnum|null $navigationIcon = Heroicon::OutlinedRectangleStack;
@@ -64,7 +64,7 @@ class CoolifyActionLogResource extends Resource
public static function getPages(): array
{
return [
'index' => ManageCoolifyActionLogs::route('/'),
'index' => ManageInfrastructureActionLogs::route('/'),
];
}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace App\Filament\Resources\InfrastructureActionLogs\Pages;
use App\Filament\Resources\InfrastructureActionLogs\InfrastructureActionLogResource;
use Filament\Resources\Pages\ManageRecords;
class ManageInfrastructureActionLogs extends ManageRecords
{
protected static string $resource = InfrastructureActionLogResource::class;
protected function getHeaderActions(): array
{
return [];
}
}

View File

@@ -1,127 +0,0 @@
<?php
namespace App\Filament\SuperAdmin\Pages;
use App\Models\CoolifyActionLog;
use App\Services\Coolify\CoolifyClient;
use BackedEnum;
use Filament\Notifications\Notification;
use Filament\Pages\Page;
use Illuminate\Support\Arr;
class CoolifyDeployments extends Page
{
protected static string|BackedEnum|null $navigationIcon = 'heroicon-o-wrench-screwdriver';
protected static ?string $navigationLabel = 'Infrastructure';
protected static ?string $title = 'Infrastructure Controls';
protected string $view = 'filament.super-admin.pages.coolify-deployments';
public array $services = [];
public array $recentLogs = [];
public ?string $coolifyWebUrl = null;
public function mount(CoolifyClient $client): void
{
$this->coolifyWebUrl = config('coolify.web_url');
$this->refreshServices($client);
$this->refreshLogs();
}
public function restart(string $serviceId): void
{
$this->performAction($serviceId, 'restart');
}
public function redeploy(string $serviceId): void
{
$this->performAction($serviceId, 'redeploy');
}
protected function performAction(string $serviceId, string $action): void
{
$client = app(CoolifyClient::class);
if (! $this->isKnownService($serviceId)) {
Notification::make()
->danger()
->title('Unknown service')
->body("The service ID {$serviceId} is not configured.")
->send();
return;
}
try {
$action === 'restart'
? $client->restartService($serviceId, auth()->user())
: $client->redeployService($serviceId, auth()->user());
Notification::make()
->success()
->title(ucfirst($action).' requested')
->body("Coolify accepted the {$action} action for {$serviceId}.")
->send();
} catch (\Throwable $exception) {
Notification::make()
->danger()
->title('Coolify request failed')
->body($exception->getMessage())
->send();
}
$this->refreshServices($client);
$this->refreshLogs();
}
protected function refreshServices(CoolifyClient $client): void
{
$serviceMap = config('coolify.services', []);
$results = [];
foreach ($serviceMap as $label => $id) {
try {
$status = $client->serviceStatus($id);
$results[] = [
'label' => ucfirst($label),
'service_id' => $id,
'status' => Arr::get($status, 'data.status', 'unknown'),
];
} catch (\Throwable $e) {
$results[] = [
'label' => ucfirst($label),
'service_id' => $id,
'status' => 'error',
];
}
}
$this->services = $results;
}
protected function refreshLogs(): void
{
$this->recentLogs = CoolifyActionLog::query()
->with('user')
->latest()
->limit(5)
->get()
->map(fn ($log) => [
'created_at' => $log->created_at->diffForHumans(),
'user' => $log->user?->name ?? 'System',
'service_id' => $log->service_id,
'action' => $log->action,
'status_code' => $log->status_code,
])
->toArray();
}
protected function isKnownService(string $serviceId): bool
{
return in_array($serviceId, array_values(config('coolify.services', [])), true);
}
}

View File

@@ -0,0 +1,129 @@
<?php
namespace App\Filament\SuperAdmin\Pages;
use App\Models\InfrastructureActionLog;
use App\Services\Dokploy\DokployClient;
use BackedEnum;
use Filament\Notifications\Notification;
use Filament\Pages\Page;
use Illuminate\Support\Arr;
class DokployDeployments extends Page
{
protected static string|BackedEnum|null $navigationIcon = 'heroicon-o-wrench-screwdriver';
protected static ?string $navigationLabel = 'Infrastructure';
protected static ?string $title = 'Infrastructure Controls';
protected string $view = 'filament.super-admin.pages.dokploy-deployments';
public array $applications = [];
public array $recentLogs = [];
public ?string $dokployWebUrl = null;
public function mount(DokployClient $client): void
{
$this->dokployWebUrl = config('dokploy.web_url');
$this->refreshApplications($client);
$this->refreshLogs();
}
public function reload(string $applicationId): void
{
$this->performAction($applicationId, 'reload');
}
public function redeploy(string $applicationId): void
{
$this->performAction($applicationId, 'redeploy');
}
protected function performAction(string $applicationId, string $action): void
{
$client = app(DokployClient::class);
if (! $this->isKnownApplication($applicationId)) {
Notification::make()
->danger()
->title('Unknown service')
->body("The application ID {$applicationId} is not configured.")
->send();
return;
}
try {
$action === 'reload'
? $client->reloadApplication($applicationId, auth()->user())
: $client->redeployApplication($applicationId, auth()->user());
Notification::make()
->success()
->title(ucfirst($action).' requested')
->body("Dokploy accepted the {$action} action for {$applicationId}.")
->send();
} catch (\Throwable $exception) {
Notification::make()
->danger()
->title('Dokploy request failed')
->body($exception->getMessage())
->send();
}
$this->refreshApplications($client);
$this->refreshLogs();
}
protected function refreshApplications(DokployClient $client): void
{
$applicationMap = config('dokploy.applications', []);
$results = [];
foreach ($applicationMap as $label => $id) {
try {
$status = $client->applicationStatus($id);
$application = Arr::get($status, 'application', []);
$results[] = [
'label' => ucfirst($label),
'application_id' => $id,
'status' => Arr::get($application, 'applicationStatus', 'unknown'),
];
} catch (\Throwable $e) {
$results[] = [
'label' => ucfirst($label),
'application_id' => $id,
'status' => 'error',
];
}
}
$this->applications = $results;
}
protected function refreshLogs(): void
{
$this->recentLogs = InfrastructureActionLog::query()
->with('user')
->latest()
->limit(5)
->get()
->map(fn ($log) => [
'created_at' => $log->created_at->diffForHumans(),
'user' => $log->user?->name ?? 'System',
'service_id' => $log->service_id,
'action' => $log->action,
'status_code' => $log->status_code,
])
->toArray();
}
protected function isKnownApplication(string $applicationId): bool
{
return in_array($applicationId, array_values(config('dokploy.applications', [])), true);
}
}

View File

@@ -1,62 +0,0 @@
<?php
namespace App\Filament\Widgets;
use App\Services\Coolify\CoolifyClient;
use Filament\Widgets\Widget;
use Illuminate\Support\Arr;
class CoolifyPlatformHealth extends Widget
{
protected string $view = 'filament.widgets.coolify-platform-health';
protected ?string $pollingInterval = '60s';
protected function getViewData(): array
{
return [
'services' => $this->loadServices(),
];
}
protected function loadServices(): array
{
$client = app(CoolifyClient::class);
$serviceMap = config('coolify.services', []);
$results = [];
foreach ($serviceMap as $label => $serviceId) {
try {
$status = $client->serviceStatus($serviceId);
$results[] = [
'label' => ucfirst($label),
'service_id' => $serviceId,
'status' => Arr::get($status, 'data.status', 'unknown'),
'cpu' => Arr::get($status, 'data.metrics.cpu_percent'),
'memory' => Arr::get($status, 'data.metrics.memory_percent'),
'last_deploy' => Arr::get($status, 'data.last_deployment.finished_at'),
];
} catch (\Throwable $exception) {
$results[] = [
'label' => ucfirst($label),
'service_id' => $serviceId,
'status' => 'unreachable',
'error' => $exception->getMessage(),
];
}
}
if (empty($results)) {
return [
[
'label' => 'Coolify',
'service_id' => '-',
'status' => 'unconfigured',
'error' => 'Set COOLIFY_SERVICE_IDS in .env to enable monitoring.',
],
];
}
return $results;
}
}

View File

@@ -0,0 +1,91 @@
<?php
namespace App\Filament\Widgets;
use App\Services\Dokploy\DokployClient;
use Filament\Widgets\Widget;
use Illuminate\Support\Arr;
class DokployPlatformHealth extends Widget
{
protected string $view = 'filament.widgets.dokploy-platform-health';
protected ?string $pollingInterval = '60s';
protected function getViewData(): array
{
return [
'applications' => $this->loadApplications(),
];
}
protected function loadApplications(): array
{
$client = app(DokployClient::class);
$applicationMap = config('dokploy.applications', []);
$results = [];
foreach ($applicationMap as $label => $applicationId) {
try {
$status = $client->applicationStatus($applicationId);
$deployments = $client->recentDeployments($applicationId, 1);
$application = Arr::get($status, 'application', []);
$monitoring = Arr::get($status, 'monitoring', []);
$results[] = [
'label' => ucfirst($label),
'application_id' => $applicationId,
'app_name' => Arr::get($application, 'appName') ?? Arr::get($application, 'name'),
'status' => Arr::get($application, 'applicationStatus', 'unknown'),
'cpu' => $this->extractMetric($monitoring, [
'metrics.cpuPercent',
'metrics.cpu_percent',
'cpuPercent',
'cpu_percent',
]),
'memory' => $this->extractMetric($monitoring, [
'metrics.memoryPercent',
'metrics.memory_percent',
'memoryPercent',
'memory_percent',
]),
'last_deploy' => Arr::get($deployments, '0.createdAt')
?? Arr::get($deployments, '0.created_at')
?? Arr::get($application, 'updatedAt')
?? Arr::get($application, 'lastDeploymentAt'),
];
} catch (\Throwable $exception) {
$results[] = [
'label' => ucfirst($label),
'application_id' => $applicationId,
'status' => 'unreachable',
'error' => $exception->getMessage(),
];
}
}
if (empty($results)) {
return [
[
'label' => 'Dokploy',
'application_id' => '-',
'status' => 'unconfigured',
'error' => 'Set DOKPLOY_APPLICATION_IDS in .env to enable monitoring.',
],
];
}
return $results;
}
protected function extractMetric(array $source, array $candidates): mixed
{
foreach ($candidates as $key) {
if (Arr::has($source, $key)) {
return Arr::get($source, $key);
}
}
return null;
}
}