Files
homarr/packages/definitions/src/docker-integration-match.ts
T
Thomas 13c9e5e181 fix(release): repair v1.70.0 quality failures (#6279)
* fix(release): repair v1.70.0 quality failures

* style: auto-fix lint and format
2026-07-10 21:59:57 +02:00

83 lines
2.9 KiB
TypeScript

import { objectKeys } from "@homarr/common";
import { integrationDefs } from "./integration";
import type { IntegrationKind } from "./integration";
export const extractContainerImageName = (image: string): string => image.split("/").at(-1)?.split(":").at(0) ?? "";
const extractIconSlug = (iconUrl: string): string => {
const filename = iconUrl.split("/").pop() ?? "";
return filename.replace(/\.(svg|png)$/, "").toLowerCase();
};
export const integrationIconSlugs: Record<IntegrationKind, string> = Object.fromEntries(
objectKeys(integrationDefs).map((kind) => [kind, extractIconSlug(integrationDefs[kind].iconUrl)]),
) as Record<IntegrationKind, string>;
const integrationAliases: Partial<Record<IntegrationKind, readonly string[]>> = {
piHole: ["pihole", "pi-hole"],
adGuardHome: ["adguardhome", "adguard-home", "adguard"],
homeAssistant: ["homeassistant", "home-assistant", "hass"],
openmediavault: ["omv"],
qBittorrent: ["qbittorrent"],
sabNzbd: ["sabnzbd"],
nzbGet: ["nzbget"],
unifiController: ["unifi", "unifi-controller"],
jellyseerr: ["jellyseerr"],
overseerr: ["overseerr"],
dashDot: ["dashdot", "dash-dot", "dash."],
speedtestTracker: ["speedtest-tracker"],
uptimeKuma: ["uptime-kuma"],
audiobookshelf: ["audiobookshelf"],
navidrome: ["navidrome"],
paperlessNgx: ["paperless-ngx", "paperless"],
patchmon: ["patchmon", "patch-mon"],
coolify: ["coolify"],
truenas: ["truenas"],
bazarr: ["bazarr"],
synology: ["synology", "diskstation"],
};
export const matchIntegrationKind = (search: string): IntegrationKind | null => {
const normalized = search.toLowerCase().trim();
if (!normalized) return null;
for (const kind of objectKeys(integrationDefs)) {
if (kind.toLowerCase() === normalized) return kind;
if (integrationDefs[kind].name.toLowerCase() === normalized) return kind;
}
for (const kind of objectKeys(integrationDefs)) {
if (integrationIconSlugs[kind] === normalized) return kind;
}
for (const [kind, aliases] of Object.entries(integrationAliases) as [IntegrationKind, readonly string[]][]) {
if (aliases.some((alias) => alias === normalized)) return kind;
}
for (const kind of objectKeys(integrationDefs)) {
if (normalized.includes(kind.toLowerCase())) return kind;
if (normalized.includes(integrationIconSlugs[kind])) return kind;
}
return null;
};
const notDockerDiscoverable = new Set<IntegrationKind>(["ical", "mock"]);
interface ContainerMatchInput {
image: string;
name: string;
}
export const matchIntegrationKindFromContainer = (container: ContainerMatchInput): IntegrationKind | null => {
const imageName = extractContainerImageName(container.image);
const fromImage = matchIntegrationKind(imageName);
if (fromImage && !notDockerDiscoverable.has(fromImage)) return fromImage;
const fromName = matchIntegrationKind(container.name);
if (fromName && !notDockerDiscoverable.has(fromName)) return fromName;
return null;
};