import Docker from "dockerode"; import type { Config, Widget } from "@/lib/config/schema"; const HOST_RULE_PATTERN = /Host\(`([^`]+)`\)/; function extractHostname(labels: Record, prefix: string): string | null { for (const [key, value] of Object.entries(labels)) { if (key.startsWith(`${prefix}.http.routers.`) && key.endsWith(".rule")) { const match = HOST_RULE_PATTERN.exec(value); if (match) return match[1]; } } return null; } export async function discoverDockerWidgets(config: Config): Promise { const settings = config.discovery.docker; if (!settings.enabled) return []; const docker = new Docker({ socketPath: settings.socketPath }); const containers = await docker.listContainers({ all: false }); const manualContainerNames = new Set( config.groups.flatMap((group) => group.widgets .filter( (widget): widget is Extract => widget.type === "docker" || widget.type === "database" ) .map((widget) => widget.containerName) ) ); const prefix = settings.labelPrefix; const discovered: Widget[] = []; for (const container of containers) { const labels = container.Labels ?? {}; if (labels[`${prefix}.enable`] !== "true") continue; if (settings.excludeLabels.some((label) => labels[label] !== undefined)) continue; const name = container.Names[0]?.replace(/^\//, ""); if (!name || manualContainerNames.has(name)) continue; if (settings.network) { const containerNetwork = labels[`${prefix}.docker.network`]; if (containerNetwork && containerNetwork !== settings.network) continue; } const hostname = extractHostname(labels, prefix); discovered.push({ type: "docker", name, containerName: name, href: hostname ? `https://${hostname}` : undefined, showStats: true, interval: "10s", }); } return discovered; }