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; } $environments = $this->extractEnvironments($project); $applications = $this->formatEnvironmentApplications($environments, $client); $composes = $this->formatEnvironmentComposes($environments, $client); $services = $this->formatEnvironmentServices($environments); $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 extractEnvironments(array $project): array { $environments = Arr::get($project, 'environments', []); if (is_array($environments) && ! empty($environments)) { return $environments; } return [[ 'name' => Arr::get($project, 'name'), 'applications' => Arr::get($project, 'applications', []), 'compose' => Arr::get($project, 'compose', []), 'mysql' => Arr::get($project, 'mysql', []), 'postgres' => Arr::get($project, 'postgres', []), 'mariadb' => Arr::get($project, 'mariadb', []), 'mongo' => Arr::get($project, 'mongo', []), 'redis' => Arr::get($project, 'redis', []), ]]; } protected function formatEnvironmentApplications(array $environments, DokployClient $client): array { return collect($environments) ->flatMap(function (array $environment) use ($client) { $applications = Arr::get($environment, 'applications', []); $environmentName = Arr::get($environment, 'name'); return $this->formatApplications(is_array($applications) ? $applications : [], $client, $environmentName); }) ->values() ->all(); } protected function formatEnvironmentComposes(array $environments, DokployClient $client): array { return collect($environments) ->flatMap(function (array $environment) use ($client) { $composes = Arr::get($environment, 'compose', []); $environmentName = Arr::get($environment, 'name'); return collect(is_array($composes) ? $composes : []) ->map(function (array $compose) use ($client, $environmentName) { $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', 'environment' => $environmentName, '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(); }) ->values() ->all(); } protected function formatEnvironmentServices(array $environments): array { return collect($environments) ->flatMap(function (array $environment) { $environmentName = Arr::get($environment, 'name'); return collect([ ...$this->normalizeServiceList((array) Arr::get($environment, 'compose', []), 'compose', 'composeId', 'composeStatus', $environmentName), ...$this->normalizeServiceList((array) Arr::get($environment, 'mysql', []), 'mysql', 'mysqlId', 'applicationStatus', $environmentName), ...$this->normalizeServiceList((array) Arr::get($environment, 'postgres', []), 'postgres', 'postgresId', 'applicationStatus', $environmentName), ...$this->normalizeServiceList((array) Arr::get($environment, 'mariadb', []), 'mariadb', 'mariadbId', 'applicationStatus', $environmentName), ...$this->normalizeServiceList((array) Arr::get($environment, 'mongo', []), 'mongo', 'mongoId', 'applicationStatus', $environmentName), ...$this->normalizeServiceList((array) Arr::get($environment, 'redis', []), 'redis', 'redisId', 'applicationStatus', $environmentName), ]); }) ->filter(fn (array $service) => filled($service['name'])) ->values() ->all(); } protected function formatApplications(array $applications, DokployClient $client, ?string $environment = null): array { return collect($applications) ->map(function (array $application) use ($client, $environment) { $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'), 'environment' => $environment, '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 normalizeServiceList(array $services, string $type, string $idKey, string $statusKey, ?string $environment = null): array { return collect($services) ->map(function (array $service) use ($type, $idKey, $statusKey, $environment) { 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'), 'environment' => $environment, ]; }) ->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(); } }