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(); } }