Files

65 lines
1.9 KiB
TypeScript
Raw Permalink Normal View History

import Docker from "dockerode";
import type { Config, Widget } from "@/lib/config/schema";
const HOST_RULE_PATTERN = /Host\(`([^`]+)`\)/;
function extractHostname(labels: Record<string, string>, 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<Widget[]> {
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" | "database" }> =>
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;
}