Files
fotospiel-app/app/Filament/Widgets/DokployPlatformHealth.php
Codex Agent 87f348462b
Some checks failed
linter / quality (push) Has been cancelled
tests / ci (push) Has been cancelled
tests / ui (push) Has been cancelled
Load Dokploy project details for compose data
2026-01-29 11:00:53 +01:00

363 lines
15 KiB
PHP

<?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
{
$projects = $this->loadProjects();
return [
'projects' => $projects,
'composes' => empty($projects) ? $this->loadComposes() : [],
];
}
protected function loadProjects(): array
{
$client = app(DokployClient::class);
$projectMap = config('dokploy.projects', []);
$results = [];
if (empty($projectMap)) {
return [];
}
foreach ($projectMap as $label => $projectId) {
$project = [];
$projectIdString = (string) $projectId;
try {
$project = $client->project($projectIdString);
} catch (\Throwable $exception) {
$project = [];
}
if (empty($project)) {
$project = $client->findProject($projectIdString) ?? [];
$resolvedProjectId = Arr::get($project, 'projectId');
if ($resolvedProjectId) {
try {
$project = $client->project((string) $resolvedProjectId);
} catch (\Throwable $exception) {
$project = $project;
}
}
}
if (! $project) {
$results[] = [
'label' => ucfirst((string) $label),
'project_id' => $projectIdString,
'name' => $projectIdString,
'status' => 'unreachable',
'error' => "Project {$projectIdString} not found.",
'applications' => [],
'services' => [],
'composes' => [],
'updated_at' => null,
];
continue;
}
$applicationsPayload = Arr::get($project, 'applications', []);
$applications = $this->formatApplications(is_array($applicationsPayload) ? $applicationsPayload : [], $client);
$composes = $this->formatProjectComposes($project, $client);
$services = $this->formatProjectServices($project);
$results[] = [
'label' => ucfirst((string) $label),
'project_id' => Arr::get($project, 'projectId', $projectIdString),
'name' => Arr::get($project, 'name') ?? Arr::get($project, 'projectName') ?? $projectIdString,
'description' => Arr::get($project, 'description'),
'status' => $this->deriveProjectStatus($applications, $services, $composes),
'applications' => $applications,
'composes' => $composes,
'services' => $services,
'updated_at' => Arr::get($project, 'updatedAt') ?? Arr::get($project, 'createdAt'),
'applications_count' => count($applications),
'composes_count' => count($composes),
'services_count' => count($services),
];
}
return $results;
}
protected function loadComposes(): array
{
$client = app(DokployClient::class);
$composeMap = config('dokploy.composes', []);
$results = [];
foreach ($composeMap as $label => $composeId) {
try {
$status = $client->composeStatus($composeId);
$deployments = $client->composeDeployments($composeId, 1);
$compose = Arr::get($status, 'compose', []);
$services = $this->formatServices(Arr::get($status, 'services', []));
$results[] = [
'label' => ucfirst($label),
'compose_id' => $composeId,
'name' => Arr::get($compose, 'name') ?? Arr::get($compose, 'appName') ?? $composeId,
'status' => Arr::get($compose, 'composeStatus', 'unknown'),
'services' => $services,
'last_deploy' => Arr::get($deployments, '0.createdAt')
?? Arr::get($deployments, '0.created_at')
?? Arr::get($compose, 'updatedAt')
?? Arr::get($compose, 'lastDeploymentAt'),
];
} catch (\Throwable $exception) {
$results[] = [
'label' => ucfirst($label),
'compose_id' => $composeId,
'name' => $composeId,
'status' => 'unreachable',
'error' => $exception->getMessage(),
'services' => [],
'last_deploy' => null,
];
}
}
if (empty($results)) {
return [
[
'label' => 'Dokploy',
'compose_id' => '-',
'status' => 'unconfigured',
'error' => 'Set DOKPLOY_PROJECT_IDS or DOKPLOY_COMPOSE_IDS in .env to enable monitoring.',
],
];
}
return $results;
}
protected function formatApplications(array $applications, DokployClient $client): array
{
return collect($applications)
->map(function (array $application) use ($client) {
$applicationId = $this->extractApplicationId($application);
$statusPayload = [];
if ($applicationId) {
try {
$statusPayload = $client->applicationStatus($applicationId);
} catch (\Throwable $exception) {
$statusPayload = [];
}
}
$applicationDetails = Arr::get($statusPayload, 'application', []);
$monitoring = Arr::get($statusPayload, 'monitoring', []);
$status = Arr::get($application, 'applicationStatus')
?? Arr::get($application, 'status')
?? Arr::get($applicationDetails, 'applicationStatus')
?? Arr::get($applicationDetails, 'status')
?? 'unknown';
return [
'id' => $applicationId ?? Arr::get($application, 'id'),
'name' => Arr::get($application, 'name')
?? Arr::get($application, 'appName')
?? Arr::get($applicationDetails, 'name')
?? Arr::get($applicationDetails, 'appName')
?? $applicationId,
'status' => $status,
'repository' => Arr::get($application, 'repository')
?? Arr::get($applicationDetails, 'repository')
?? Arr::get($application, 'repo')
?? Arr::get($applicationDetails, 'repo'),
'branch' => Arr::get($application, 'branch')
?? Arr::get($applicationDetails, 'branch')
?? Arr::get($application, 'gitBranch')
?? Arr::get($applicationDetails, 'gitBranch'),
'url' => Arr::get($application, 'url')
?? Arr::get($applicationDetails, 'url')
?? Arr::get($application, 'domain')
?? Arr::get($applicationDetails, 'domain'),
'server' => Arr::get($application, 'serverName')
?? Arr::get($applicationDetails, 'serverName')
?? Arr::get($application, 'server'),
'last_deploy' => Arr::get($application, 'lastDeploymentAt')
?? Arr::get($applicationDetails, 'lastDeploymentAt')
?? Arr::get($application, 'updatedAt')
?? Arr::get($applicationDetails, 'updatedAt')
?? Arr::get($application, 'createdAt'),
'monitoring' => $this->formatMonitoring($monitoring),
];
})
->filter(fn (array $application) => filled($application['name']))
->values()
->all();
}
protected function extractApplicationId(array $application): ?string
{
return Arr::get($application, 'applicationId')
?? Arr::get($application, 'appId')
?? Arr::get($application, 'id');
}
protected function formatProjectServices(array $project): array
{
return collect([
...$this->normalizeServiceList((array) Arr::get($project, 'compose', []), 'compose', 'composeId', 'composeStatus'),
...$this->normalizeServiceList((array) Arr::get($project, 'mysql', []), 'mysql', 'mysqlId', 'applicationStatus'),
...$this->normalizeServiceList((array) Arr::get($project, 'postgres', []), 'postgres', 'postgresId', 'applicationStatus'),
...$this->normalizeServiceList((array) Arr::get($project, 'mariadb', []), 'mariadb', 'mariadbId', 'applicationStatus'),
...$this->normalizeServiceList((array) Arr::get($project, 'mongo', []), 'mongo', 'mongoId', 'applicationStatus'),
...$this->normalizeServiceList((array) Arr::get($project, 'redis', []), 'redis', 'redisId', 'applicationStatus'),
])
->filter(fn (array $service) => filled($service['name']))
->values()
->all();
}
protected function formatProjectComposes(array $project, DokployClient $client): array
{
$composes = (array) Arr::get($project, 'compose', []);
return collect($composes)
->map(function (array $compose) use ($client) {
$composeId = Arr::get($compose, 'composeId') ?? Arr::get($compose, 'id');
$statusPayload = [];
$deployments = [];
if ($composeId) {
try {
$statusPayload = $client->composeStatus($composeId);
$deployments = $client->composeDeployments($composeId, 1);
} catch (\Throwable $exception) {
$statusPayload = [];
$deployments = [];
}
}
$composeDetails = Arr::get($statusPayload, 'compose', []);
return [
'id' => $composeId,
'name' => Arr::get($compose, 'name')
?? Arr::get($compose, 'appName')
?? Arr::get($composeDetails, 'name')
?? Arr::get($composeDetails, 'appName')
?? $composeId,
'status' => Arr::get($compose, 'composeStatus')
?? Arr::get($compose, 'status')
?? Arr::get($composeDetails, 'composeStatus')
?? Arr::get($composeDetails, 'status')
?? 'unknown',
'last_deploy' => Arr::get($deployments, '0.createdAt')
?? Arr::get($deployments, '0.created_at')
?? Arr::get($compose, 'updatedAt')
?? Arr::get($composeDetails, 'updatedAt'),
'services' => $this->formatServices(Arr::get($statusPayload, 'services', [])),
];
})
->filter(fn (array $compose) => filled($compose['name']))
->values()
->all();
}
protected function normalizeServiceList(array $services, string $type, string $idKey, string $statusKey): array
{
return collect($services)
->map(function (array $service) use ($type, $idKey, $statusKey) {
return [
'type' => $type,
'id' => Arr::get($service, $idKey) ?? Arr::get($service, 'id'),
'name' => Arr::get($service, 'name') ?? Arr::get($service, 'appName') ?? Arr::get($service, 'serviceName'),
'status' => Arr::get($service, $statusKey) ?? Arr::get($service, 'status') ?? Arr::get($service, 'composeStatus', 'unknown'),
'version' => Arr::get($service, 'dockerImage') ?? Arr::get($service, 'image'),
'external_port' => Arr::get($service, 'externalPort'),
];
})
->values()
->all();
}
protected function formatMonitoring(array $monitoring): array
{
$metrics = [];
$allowed = [
'cpuPercent' => 'CPU',
'cpu' => 'CPU',
'memoryPercent' => 'Memory',
'memory' => 'Memory',
'uptime' => 'Uptime',
];
foreach ($allowed as $key => $label) {
$value = Arr::get($monitoring, $key);
if (filled($value) && ! is_array($value)) {
$metrics[] = [
'label' => $label,
'value' => $value,
];
}
}
return $metrics;
}
protected function deriveProjectStatus(array $applications, array $services, array $composes): string
{
$statuses = collect($applications)
->pluck('status')
->merge(collect($services)->pluck('status'))
->merge(collect($composes)->pluck('status'))
->filter()
->map(fn ($status) => strtolower((string) $status))
->values();
if ($statuses->contains(fn ($status) => in_array($status, ['error', 'failed', 'unreachable', 'unhealthy'], true))) {
return 'error';
}
if ($statuses->contains(fn ($status) => in_array($status, ['deploying', 'pending', 'starting'], true))) {
return 'deploying';
}
if ($statuses->contains(fn ($status) => in_array($status, ['stopped', 'inactive', 'paused'], true))) {
return 'warning';
}
if ($statuses->contains(fn ($status) => in_array($status, ['done', 'running', 'healthy'], true))) {
return 'done';
}
return 'unknown';
}
protected function formatServices(array $services): array
{
return collect($services)
->map(function ($service) {
return [
'name' => Arr::get($service, 'serviceName') ?? Arr::get($service, 'name') ?? Arr::get($service, 'containerName'),
'status' => Arr::get($service, 'status') ?? Arr::get($service, 'state') ?? Arr::get($service, 'composeStatus', 'unknown'),
];
})
->filter(fn ($service) => filled($service['name']))
->values()
->all();
}
}