id; if (!isset($this->clients[$key])) { $options = ['timeout' => 30]; if (str_starts_with($endpoint->host, 'unix://')) { $options['curl'] = [CURLOPT_UNIX_SOCKET_PATH => str_replace('unix://', '', $endpoint->host)]; $options['base_uri'] = 'http://localhost/'; } else { $options['base_uri'] = $endpoint->host . '/'; if ($endpoint->tls_enabled) { $options['verify'] = true; $options['cert'] = $endpoint->tls_cert; $options['ssl_key'] = $endpoint->tls_key; } } $this->clients[$key] = new Client($options); } return $this->clients[$key]; } public function testConnection(DockerEndpoint $endpoint): array { $response = $this->client($endpoint)->get('v1.43/info'); return json_decode($response->getBody()->getContents(), true) ?? []; } public function syncEndpoint(DockerEndpoint $endpoint): void { $client = $this->client($endpoint); // Sync info $info = json_decode($client->get('v1.43/info')->getBody()->getContents(), true); $endpoint->update([ 'status' => 'online', 'info' => $info, 'last_sync_at' => now(), ]); // Sync containers $containers = json_decode($client->get('v1.43/containers/json?all=1')->getBody()->getContents(), true) ?? []; foreach ($containers as $c) { Container::updateOrCreate( ['endpoint_id' => $endpoint->id, 'container_id' => $c['Id']], [ 'name' => ltrim($c['Names'][0] ?? '', '/'), 'image' => $c['Image'], 'image_id' => $c['ImageID'], 'status' => $c['Status'], 'state' => $c['State'], 'ports' => $c['Ports'], 'labels' => $c['Labels'], 'networks' => array_keys($c['NetworkSettings']['Networks'] ?? []), 'created_at_unix' => $c['Created'], ] ); } } public function action(DockerEndpoint $endpoint, string $containerId, string $action): bool { $allowed = ['start', 'stop', 'restart', 'pause', 'unpause', 'kill']; if (!in_array($action, $allowed)) { throw new \InvalidArgumentException("Invalid action: {$action}"); } $this->client($endpoint)->post("v1.43/containers/{$containerId}/{$action}"); return true; } public function logs(DockerEndpoint $endpoint, string $containerId, int $tail = 100): string { $response = $this->client($endpoint)->get("v1.43/containers/{$containerId}/logs", [ 'query' => ['stdout' => 1, 'stderr' => 1, 'tail' => $tail, 'timestamps' => 1], ]); return $response->getBody()->getContents(); } public function stats(DockerEndpoint $endpoint, string $containerId): array { $response = $this->client($endpoint)->get("v1.43/containers/{$containerId}/stats", [ 'query' => ['stream' => 0, 'one-shot' => 1], ]); return json_decode($response->getBody()->getContents(), true) ?? []; } public function deployStack(DockerEndpoint $endpoint, string $name, string $composeContent, array $envVars = []): bool { // Write compose file and deploy via API or exec // For Swarm use stacks API; for local use compose integration return true; } public function removeContainer(DockerEndpoint $endpoint, string $containerId, bool $force = false): bool { $this->client($endpoint)->delete("v1.43/containers/{$containerId}", [ 'query' => ['force' => $force ? 1 : 0], ]); return true; } }