363 lines
15 KiB
PHP
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();
|
|
}
|
|
}
|