Add Dockhand integration
Third live integration: container status across every Docker host Dockhand manages, with start/stop/restart actions — matching the "dashboard + basic actions" depth from the plan. Talks to Dockhand's own aggregating REST API (bearer tokens, dh_...) rather than the raw Docker Engine API on each host directly, so one credential covers every host Dockhand is already connected to. Adapter built against Dockhand's real published OpenAPI spec (fetched from https://github.com/strausmann/mcp-dockhand/blob/main/docs/dockhand-openapi.json, 257 documented paths) since the instance itself isn't reachable from here — same "verify against the real shape, don't guess" approach as Gitea, just via the spec instead of a live instance: - GET /api/environments — the Docker hosts Dockhand knows about - GET /api/containers?env=<id>&all=true — containers per host ({id, name, image, state, status}) - POST /api/containers/{id}/{start,stop,restart}?env=<id> server/src/integrations/dockhand/adapter.ts fans the environments call out to one containers call per host (Promise.all) and flattens the result, tagging each container with its environment; one unreachable host returns an empty list for that host rather than failing the whole dashboard view. Follows the same config-in-UI + encrypted-credential pattern as Tailscale and Gitea. Verified: full build passes. Since Dockhand isn't reachable from here, ran a 14-check HTTP test against the live server instead of the real API — role gating, credential non-leakage, disabled-integration blocking, and (critically) re-confirmed the wrong_type crash-safety pattern holds for this third adapter type: hitting the Dockhand routes on a differently-typed integration row returns a clean 400 rather than crashing the process, and the server keeps responding to /health afterward. Real container data and the start/stop/restart actions still need verification once this app runs on the user's LAN where Dockhand is reachable. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
f54709c7a8
commit
9c45a806c8
7 files changed
+407
-1
No files matched your search
@@ -222,6 +222,21 @@ export interface GiteaRepo {
|
||||
latestRun: GiteaWorkflowRun | null;
|
||||
}
|
||||
|
||||
export interface DockhandContainer {
|
||||
id: string;
|
||||
name: string;
|
||||
image: string;
|
||||
state: string;
|
||||
status: string;
|
||||
environmentId: number;
|
||||
environmentName: string;
|
||||
}
|
||||
|
||||
export interface DockhandContainersResponse {
|
||||
containers: DockhandContainer[];
|
||||
summary: { total: number; running: number };
|
||||
}
|
||||
|
||||
export class UnauthorizedError extends Error {}
|
||||
export class ForbiddenError extends Error {}
|
||||
|
||||
@@ -400,5 +415,24 @@ export const api = {
|
||||
{ method: "POST" },
|
||||
),
|
||||
},
|
||||
dockhand: {
|
||||
containers: (integrationId: number) =>
|
||||
request<DockhandContainersResponse>(`/api/integrations/${integrationId}/dockhand/containers`),
|
||||
start: (integrationId: number, envId: number, containerId: string) =>
|
||||
request<void>(
|
||||
`/api/integrations/${integrationId}/dockhand/environments/${envId}/containers/${encodeURIComponent(containerId)}/start`,
|
||||
{ method: "POST" },
|
||||
),
|
||||
stop: (integrationId: number, envId: number, containerId: string) =>
|
||||
request<void>(
|
||||
`/api/integrations/${integrationId}/dockhand/environments/${envId}/containers/${encodeURIComponent(containerId)}/stop`,
|
||||
{ method: "POST" },
|
||||
),
|
||||
restart: (integrationId: number, envId: number, containerId: string) =>
|
||||
request<void>(
|
||||
`/api/integrations/${integrationId}/dockhand/environments/${envId}/containers/${encodeURIComponent(containerId)}/restart`,
|
||||
{ method: "POST" },
|
||||
),
|
||||
},
|
||||
},
|
||||
};
|
||||
Reference in new issue
Block a user