cached("coolify.service.$serviceId", fn () => $this->get("/services/{$serviceId}"), 30); } public function recentDeployments(string $serviceId, int $limit = 5): array { return $this->cached("coolify.deployments.$serviceId", function () use ($serviceId, $limit) { $response = $this->get("/services/{$serviceId}/deployments?per_page={$limit}"); return Arr::get($response, 'data', []); }, 60); } public function restartService(string $serviceId, ?Authenticatable $actor = null): array { return $this->dispatchAction($serviceId, 'restart', function () use ($serviceId) { return $this->post("/services/{$serviceId}/actions/restart"); }, $actor); } public function redeployService(string $serviceId, ?Authenticatable $actor = null): array { return $this->dispatchAction($serviceId, 'redeploy', function () use ($serviceId) { return $this->post("/services/{$serviceId}/actions/redeploy"); }, $actor); } protected function cached(string $key, callable $callback, int $seconds): mixed { return Cache::remember($key, now()->addSeconds($seconds), $callback); } protected function get(string $path): array { $response = $this->request()->get($path); if ($response->failed()) { $this->logFailure('GET', $path, $response); throw new RequestException($response); } return $response->json() ?? []; } protected function post(string $path, array $payload = []): array { $response = $this->request()->post($path, $payload); if ($response->failed()) { $this->logFailure('POST', $path, $response); throw new RequestException($response); } return $response->json() ?? []; } protected function request(): PendingRequest { $baseUrl = config('coolify.api.base_url'); $token = config('coolify.api.token'); $timeout = config('coolify.api.timeout', 5); if (! $baseUrl || ! $token) { throw new \RuntimeException('Coolify API is not configured.'); } return $this->http ->baseUrl($baseUrl) ->timeout($timeout) ->acceptJson() ->withToken($token); } protected function logFailure(string $method, string $path, \Illuminate\Http\Client\Response $response): void { Log::error('[Coolify] API request failed', [ 'method' => $method, 'path' => $path, 'status' => $response->status(), 'body' => $response->body(), ]); } protected function dispatchAction(string $serviceId, string $action, callable $callback, ?Authenticatable $actor = null): array { $payload = []; try { $response = $callback(); } catch (\Throwable $exception) { $this->logAction($serviceId, $action, $payload, [ 'error' => $exception->getMessage(), ], null, $actor); throw $exception; } $this->logAction($serviceId, $action, $payload, $response, $response['status'] ?? null, $actor); return $response; } protected function logAction( string $serviceId, string $action, array $payload, array $response, ?int $status, ?Authenticatable $actor = null, ): void { CoolifyActionLog::create([ 'user_id' => $actor?->getAuthIdentifier() ?? auth()->id(), 'service_id' => $serviceId, 'action' => $action, 'payload' => $payload, 'response' => $response, 'status_code' => $status, ]); } } use App\Models\CoolifyActionLog; use Illuminate\Contracts\Auth\Authenticatable;