Merge branch 'dev' into feat/openwebui-integration

Resolve conflicts by keeping both openWebUi and dev-side registry entries
(integration/widget kinds, creators, docs sitemap). Take dev's next.config
turbopack settings. Reset SQLite migration base to dev's 0040_add_widget_secret;
the integration.creator_id migrations are regenerated for all dialects in a
follow-up commit.
This commit is contained in:
Thomas Camlong committed 2026-07-11 18:11:03 +02:00
commit 411c809c55
517 files changed
+30974 -14645

No files matched your search

@@ -31,11 +31,11 @@ const integrationAliases: Partial<Record<IntegrationKind, readonly string[]>> =
audiobookshelf: ["audiobookshelf"],
navidrome: ["navidrome"],
paperlessNgx: ["paperless-ngx", "paperless"],
searchCh: ["search-ch"],
gitHubContainerRegistry: ["ghcr"],
linuxServerIO: ["linuxserver"],
patchmon: ["patchmon", "patch-mon"],
coolify: ["coolify"],
truenas: ["truenas"],
bazarr: ["bazarr"],
synology: ["synology", "diskstation"],
};
export const matchIntegrationKind = (search: string): IntegrationKind | null => {
@@ -63,19 +63,7 @@ export const matchIntegrationKind = (search: string): IntegrationKind | null =>
return null;
};
const notDockerDiscoverable = new Set<IntegrationKind>([
"dockerHub",
"github",
"gitlab",
"npm",
"codeberg",
"linuxServerIO",
"gitHubContainerRegistry",
"quay",
"ical",
"searchCh",
"mock",
]);
const notDockerDiscoverable = new Set<IntegrationKind>(["ical", "mock"]);
interface ContainerMatchInput {
image: string;
@@ -60,8 +60,8 @@ export type HomarrDocumentationPath =
| "/docs/tags/connections"
| "/docs/tags/custom-widgets"
| "/docs/tags/customization"
| "/docs/tags/data-sources"
| "/docs/tags/data"
| "/docs/tags/data-sources"
| "/docs/tags/database"
| "/docs/tags/developer"
| "/docs/tags/development"
@@ -99,8 +99,8 @@ export type HomarrDocumentationPath =
| "/docs/tags/redis"
| "/docs/tags/responsive"
| "/docs/tags/roles"
| "/docs/tags/search-engines"
| "/docs/tags/search"
| "/docs/tags/search-engines"
| "/docs/tags/secrets"
| "/docs/tags/security"
| "/docs/tags/self-signed"
@@ -132,15 +132,6 @@ export type HomarrDocumentationPath =
| "/docs/advanced/running-as-different-user"
| "/docs/advanced/single-sign-on"
| "/docs/advanced/styling"
| "/docs/category/advanced"
| "/docs/category/community"
| "/docs/category/developer-guides"
| "/docs/category/getting-started"
| "/docs/category/installation-1"
| "/docs/category/installation"
| "/docs/category/integrations"
| "/docs/category/management"
| "/docs/category/widgets"
| "/docs/community/donate"
| "/docs/community/faq"
| "/docs/community/get-in-touch"
@@ -168,17 +159,13 @@ export type HomarrDocumentationPath =
| "/docs/integrations/archiveteam-warrior"
| "/docs/integrations/aria2"
| "/docs/integrations/audiobookshelf"
| "/docs/integrations/bazarr"
| "/docs/integrations/beszel"
| "/docs/integrations/codeberg"
| "/docs/integrations/coolify"
| "/docs/integrations/dash-dot"
| "/docs/integrations/deluge"
| "/docs/integrations/docker-hub"
| "/docs/integrations/docker"
| "/docs/integrations/emby"
| "/docs/integrations/github-containerregistry"
| "/docs/integrations/github"
| "/docs/integrations/gitlab"
| "/docs/integrations/glances"
| "/docs/integrations/gluetun"
| "/docs/integrations/gotify"
@@ -189,34 +176,33 @@ export type HomarrDocumentationPath =
| "/docs/integrations/jellyseerr"
| "/docs/integrations/kubernetes"
| "/docs/integrations/lidarr"
| "/docs/integrations/linux-server-io"
| "/docs/integrations/navidrome"
| "/docs/integrations/nextcloud"
| "/docs/integrations/npm"
| "/docs/integrations/ntfy"
| "/docs/integrations/nzbget"
| "/docs/integrations/open-media-vault"
| "/docs/integrations/opnsense"
| "/docs/integrations/overseerr"
| "/docs/integrations/paperless-ngx"
| "/docs/integrations/patchmon"
| "/docs/integrations/peanut"
| "/docs/integrations/pi-hole"
| "/docs/integrations/plex"
| "/docs/integrations/prowlarr"
| "/docs/integrations/proxmox"
| "/docs/integrations/q-bittorent"
| "/docs/integrations/quay"
| "/docs/integrations/radarr"
| "/docs/integrations/readarr"
| "/docs/integrations/sabnzbd"
| "/docs/integrations/search-ch"
| "/docs/integrations/seerr"
| "/docs/integrations/slskd"
| "/docs/integrations/sonarr"
| "/docs/integrations/speedtest-tracker"
| "/docs/integrations/synology"
| "/docs/integrations/tdarr"
| "/docs/integrations/technitium-dns"
| "/docs/integrations/tracearr"
| "/docs/integrations/traefik"
| "/docs/integrations/transmission"
| "/docs/integrations/truenas"
| "/docs/integrations/umami"
@@ -239,6 +225,7 @@ export type HomarrDocumentationPath =
| "/docs/widgets/anchor-note"
| "/docs/widgets/app"
| "/docs/widgets/audio-stats"
| "/docs/widgets/bazarr"
| "/docs/widgets/beszel-alerts"
| "/docs/widgets/beszel-system-grid"
| "/docs/widgets/beszel-system-stats"
@@ -258,6 +245,7 @@ export type HomarrDocumentationPath =
| "/docs/widgets/immich-album-carousel"
| "/docs/widgets/immich-server-stats"
| "/docs/widgets/indexer-manager"
| "/docs/widgets/media-missing"
| "/docs/widgets/media-releases"
| "/docs/widgets/media-request-list"
| "/docs/widgets/media-request-stats"
@@ -269,15 +257,18 @@ export type HomarrDocumentationPath =
| "/docs/widgets/notebook"
| "/docs/widgets/notifications"
| "/docs/widgets/paperless-ngx"
| "/docs/widgets/patchmon"
| "/docs/widgets/releases"
| "/docs/widgets/rss-feed"
| "/docs/widgets/smart-home-entity-state"
| "/docs/widgets/smart-home-execute-automation"
| "/docs/widgets/speedtest-tracker"
| "/docs/widgets/stock-price"
| "/docs/widgets/system-disks"
| "/docs/widgets/system-resources"
| "/docs/widgets/timetable"
| "/docs/widgets/tracearr"
| "/docs/widgets/traefik"
| "/docs/widgets/umami"
| "/docs/widgets/ups"
| "/docs/widgets/uptime-kuma"
@@ -298,6 +289,7 @@ export type HomarrDocumentationPath =
| "/docs/integrations/lidarr"
| "/docs/integrations/readarr"
| "/docs/integrations/prowlarr"
| "/docs/integrations/bazarr"
| "/docs/integrations/jellyfin"
| "/docs/integrations/emby"
| "/docs/integrations/plex"
@@ -316,24 +308,17 @@ export type HomarrDocumentationPath =
| "/docs/integrations/nextcloud"
| "/docs/integrations/unifi-controller"
| "/docs/integrations/opnsense"
| "/docs/integrations/github"
| "/docs/integrations/docker-hub"
| "/docs/integrations/gitlab"
| "/docs/integrations/npm"
| "/docs/integrations/codeberg"
| "/docs/integrations/linux-server-io"
| "/docs/integrations/github-containerregistry"
| "/docs/integrations/quay"
| "/docs/integrations/ntfy"
| "/docs/integrations/gotify"
| "/docs/integrations/ical"
| "/docs/integrations/anchor"
| "/docs/integrations/truenas"
| "/docs/integrations/synology"
| "/docs/integrations/unraid"
| "/docs/integrations/coolify"
| "/docs/integrations/search-ch"
| "/docs/integrations/immich"
| "/docs/integrations/paperless-ngx"
| "/docs/integrations/patchmon"
| "/docs/integrations/tracearr"
| "/docs/integrations/speedtest-tracker"
| "/docs/integrations/uptime-kuma"
@@ -344,6 +329,7 @@ export type HomarrDocumentationPath =
| "/docs/integrations/peanut"
| "/docs/integrations/beszel"
| "/docs/integrations/open-webui"
| "/docs/integrations/traefik"
| "/docs/widgets/clock"
| "/docs/widgets/weather"
| "/docs/widgets/app"
@@ -362,6 +348,7 @@ export type HomarrDocumentationPath =
| "/docs/widgets/media-request-list"
| "/docs/widgets/media-request-stats"
| "/docs/widgets/media-transcoding"
| "/docs/widgets/media-missing"
| "/docs/widgets/minecraft-server-status"
| "/docs/widgets/network-controller-summary"
| "/docs/widgets/network-controller-status"
@@ -376,10 +363,13 @@ export type HomarrDocumentationPath =
| "/docs/widgets/notifications"
| "/docs/widgets/system-resources"
| "/docs/widgets/coolify"
| "/docs/widgets/system-disks"
| "/docs/widgets/timetable"
| "/docs/widgets/immich-server-stats"
| "/docs/widgets/immich-album-carousel"
| "/docs/widgets/paperless-ngx"
| "/docs/widgets/patchmon"
| "/docs/widgets/bazarr"
| "/docs/widgets/tracearr"
| "/docs/widgets/speedtest-tracker"
| "/docs/widgets/uptime-kuma"
@@ -391,5 +381,6 @@ export type HomarrDocumentationPath =
| "/docs/widgets/beszel-system-grid"
| "/docs/widgets/beszel-alerts"
| "/docs/widgets/beszel-system-stats"
| "/docs/widgets/traefik"
| "/docs/widgets/custom-api"
| "/docs/widgets/open-webui";
@@ -13,6 +13,7 @@ export const integrationDocSlugs: Record<IntegrationKind, string | null> = {
lidarr: "lidarr",
readarr: "readarr",
prowlarr: "prowlarr",
bazarr: "bazarr",
jellyfin: "jellyfin",
emby: "emby",
plex: "plex",
@@ -31,24 +32,17 @@ export const integrationDocSlugs: Record<IntegrationKind, string | null> = {
nextcloud: "nextcloud",
unifiController: "unifi-controller",
opnsense: "opnsense",
github: "github",
dockerHub: "docker-hub",
gitlab: "gitlab",
npm: "npm",
codeberg: "codeberg",
linuxServerIO: "linux-server-io",
gitHubContainerRegistry: "github-containerregistry",
quay: "quay",
ntfy: "ntfy",
gotify: "gotify",
ical: "ical",
anchor: "anchor",
truenas: "truenas",
synology: "synology",
unraid: "unraid",
coolify: "coolify",
searchCh: "search-ch",
immich: "immich",
paperlessNgx: "paperless-ngx",
patchmon: "patchmon",
tracearr: "tracearr",
speedtestTracker: "speedtest-tracker",
uptimeKuma: "uptime-kuma",
@@ -58,6 +52,7 @@ export const integrationDocSlugs: Record<IntegrationKind, string | null> = {
gluetun: "gluetun",
peaNut: "peanut",
beszel: "beszel",
traefik: "traefik",
archiveTeamWarrior: null,
openWebUi: "open-webui",
mock: null,
@@ -19,6 +19,7 @@ export const widgetDocSlugs: Record<WidgetKind, string | null> = {
"mediaRequests-requestList": "media-request-list",
"mediaRequests-requestStats": "media-request-stats",
mediaTranscoding: "media-transcoding",
mediaMissing: "media-missing",
minecraftServerStatus: "minecraft-server-status",
networkControllerSummary: "network-controller-summary",
networkControllerStatus: "network-controller-status",
@@ -33,11 +34,13 @@ export const widgetDocSlugs: Record<WidgetKind, string | null> = {
notifications: "notifications",
systemResources: "system-resources",
coolify: "coolify",
systemDisks: null,
systemDisks: "system-disks",
timetable: "timetable",
"immich-serverStats": "immich-server-stats",
"immich-albumCarousel": "immich-album-carousel",
paperlessNgx: "paperless-ngx",
patchmon: "patchmon",
bazarr: "bazarr",
tracearr: "tracearr",
speedtestTracker: "speedtest-tracker",
uptimeKuma: "uptime-kuma",
@@ -50,6 +53,7 @@ export const widgetDocSlugs: Record<WidgetKind, string | null> = {
beszelSystemGrid: "beszel-system-grid",
beszelAlerts: "beszel-alerts",
beszelSystemStats: "beszel-system-stats",
traefik: "traefik",
customApi: "custom-api",
openWebUi: "open-webui",
};
+1
View File
@@ -17,3 +17,4 @@ export * from "./emptysuperjson";
export * from "./hotkeys";
export * from "./docker-integration-match";
export * from "./integration-url-template";
export * from "./release-provider";
+43 -75
View File
@@ -13,6 +13,8 @@ export const integrationSecretKindObject = {
topic: { isPublic: true, multiline: false },
opnsenseApiKey: { isPublic: false, multiline: false },
opnsenseApiSecret: { isPublic: false, multiline: false },
patchmonApiKey: { isPublic: true, multiline: false },
patchmonApiSecret: { isPublic: false, multiline: false },
url: { isPublic: false, multiline: false },
privateKey: { isPublic: false, multiline: true },
githubAppId: { isPublic: true, multiline: false },
@@ -96,7 +98,7 @@ export const integrationDefs = {
name: "Sonarr",
secretKinds: [["apiKey"]],
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/sonarr.svg",
category: ["calendar"],
category: ["calendar", "mediaOrganizer"],
documentationUrl: createDocumentationLink("/docs/integrations/sonarr"),
defaultPort: 8989,
apiKeySettingsPath: "/settings/general",
@@ -105,7 +107,7 @@ export const integrationDefs = {
name: "Radarr",
secretKinds: [["apiKey"]],
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/radarr.svg",
category: ["calendar"],
category: ["calendar", "mediaOrganizer"],
documentationUrl: createDocumentationLink("/docs/integrations/radarr"),
defaultPort: 7878,
apiKeySettingsPath: "/settings/general",
@@ -137,6 +139,15 @@ export const integrationDefs = {
defaultPort: 9696,
apiKeySettingsPath: "/settings/general",
},
bazarr: {
name: "Bazarr",
secretKinds: [["apiKey"]],
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/bazarr.svg",
category: ["subtitleManager"],
documentationUrl: createDocumentationLink("/docs/integrations/bazarr"),
defaultPort: 6767,
apiKeySettingsPath: "/settings/general",
},
jellyfin: {
name: "Jellyfin",
secretKinds: [["username", "password"], ["apiKey"]],
@@ -283,70 +294,6 @@ export const integrationDefs = {
documentationUrl: createDocumentationLink("/docs/integrations/opnsense"),
defaultPort: 443,
},
github: {
name: "Github",
secretKinds: [[], ["personalAccessToken"], ["githubAppId", "githubInstallationId", "privateKey"]],
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/github.svg",
category: ["releasesProvider"],
defaultUrl: "https://api.github.com",
documentationUrl: createDocumentationLink("/docs/integrations/github"),
},
dockerHub: {
name: "Docker Hub",
secretKinds: [[], ["username", "personalAccessToken"]],
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/docker.svg",
category: ["releasesProvider"],
defaultUrl: "https://hub.docker.com",
documentationUrl: createDocumentationLink("/docs/integrations/docker-hub"),
},
gitlab: {
name: "Gitlab",
secretKinds: [[], ["personalAccessToken"]],
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/gitlab.svg",
category: ["releasesProvider"],
defaultUrl: "https://gitlab.com",
documentationUrl: createDocumentationLink("/docs/integrations/gitlab"),
},
npm: {
name: "NPM",
secretKinds: [[]],
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/npm.svg",
category: ["releasesProvider"],
defaultUrl: "https://registry.npmjs.org",
documentationUrl: createDocumentationLink("/docs/integrations/npm"),
},
codeberg: {
name: "Codeberg",
secretKinds: [[], ["personalAccessToken"]],
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/codeberg.svg",
category: ["releasesProvider"],
defaultUrl: "https://codeberg.org",
documentationUrl: createDocumentationLink("/docs/integrations/codeberg"),
},
linuxServerIO: {
name: "LinuxServer.io",
secretKinds: [[]],
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/linuxserver-io.svg",
category: ["releasesProvider"],
defaultUrl: "https://api.linuxserver.io",
documentationUrl: createDocumentationLink("/docs/integrations/linux-server-io"),
},
gitHubContainerRegistry: {
name: "GitHub Container Registry",
secretKinds: [[], ["personalAccessToken"], ["githubAppId", "githubInstallationId", "privateKey"]],
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/github.svg",
category: ["releasesProvider"],
defaultUrl: "https://api.github.com",
documentationUrl: createDocumentationLink("/docs/integrations/github"),
},
quay: {
name: "Quay",
secretKinds: [[], ["personalAccessToken"]],
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/png/quay.png",
category: ["releasesProvider"],
defaultUrl: "https://quay.io",
documentationUrl: createDocumentationLink("/docs/integrations/quay"),
},
ntfy: {
name: "ntfy",
secretKinds: [["topic"], ["topic", "apiKey"]],
@@ -385,6 +332,14 @@ export const integrationDefs = {
documentationUrl: createDocumentationLink("/docs/integrations/truenas"),
defaultPort: 80,
},
synology: {
name: "Synology DiskStation",
secretKinds: [["username", "password"]],
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/synology.svg",
category: ["healthMonitoring"],
documentationUrl: createDocumentationLink("/docs/integrations/synology"),
defaultPort: 5000,
},
unraid: {
name: "Unraid",
secretKinds: [["apiKey"]],
@@ -401,13 +356,6 @@ export const integrationDefs = {
documentationUrl: createDocumentationLink("/docs/integrations/coolify"),
defaultPort: 8000,
},
searchCh: {
name: "Search.ch",
secretKinds: [[]],
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/png/search-ch.png",
category: ["timetable"],
documentationUrl: createDocumentationLink("/docs/integrations/search-ch"),
},
immich: {
name: "Immich",
secretKinds: [["apiKey"]],
@@ -424,6 +372,15 @@ export const integrationDefs = {
documentationUrl: null,
defaultPort: 8000,
},
patchmon: {
name: "PatchMon",
secretKinds: [["patchmonApiKey", "patchmonApiSecret"]],
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/patchmon.svg",
category: ["healthMonitoring"],
documentationUrl: createDocumentationLink("/docs/integrations/patchmon"),
defaultPort: 3413,
apiKeySettingsPath: "/settings/api-keys",
},
tracearr: {
name: "Tracearr",
secretKinds: [["apiKey"]],
@@ -496,13 +453,21 @@ export const integrationDefs = {
category: ["vpn"],
documentationUrl: createDocumentationLink("/docs/integrations/gluetun"),
},
traefik: {
name: "Traefik",
secretKinds: [[], ["username", "password"], ["apiKey"]],
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/traefik.svg",
category: ["reverseProxy"],
documentationUrl: createDocumentationLink("/docs/integrations/traefik"),
defaultPort: 8080,
},
archiveTeamWarrior: {
name: "ArchiveTeam Warrior",
secretKinds: [[], ["username", "password"]],
iconUrl: "https://cdn.jsdelivr.net/gh/selfhst/icons/png/archiveteam-warrior.png",
category: ["archiving"],
defaultUrl: "http://localhost:8001",
documentationUrl: null,
defaultUrl: "http://localhost:8001",
},
openWebUi: {
name: "Open WebUI",
@@ -533,6 +498,7 @@ export const integrationDefs = {
"smartHomeServer",
"ups",
"uptimeMonitoring",
"mediaOrganizer",
],
documentationUrl: null,
},
@@ -598,6 +564,7 @@ export const integrationCategories = [
"dnsHole",
"mediaService",
"calendar",
"mediaOrganizer",
"mediaSearch",
"mediaRelease",
"mediaRequest",
@@ -611,7 +578,6 @@ export const integrationCategories = [
"search",
"mediaTranscoding",
"networkController",
"releasesProvider",
"notifications",
"firewall",
"timetable",
@@ -627,6 +593,8 @@ export const integrationCategories = [
"mediaLibrary",
"uptimeMonitoring",
"ai",
"subtitleManager",
"reverseProxy",
] as const;
export type IntegrationCategory = (typeof integrationCategories)[number];
@@ -0,0 +1,78 @@
import { objectKeys } from "@homarr/common";
export const releaseProviderDefs = {
github: {
name: "Github",
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/github.svg",
defaultUrl: "https://api.github.com",
},
dockerHub: {
name: "Docker Hub",
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/docker.svg",
defaultUrl: "https://hub.docker.com",
},
gitlab: {
name: "Gitlab",
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/gitlab.svg",
defaultUrl: "https://gitlab.com",
},
npm: {
name: "NPM",
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/npm.svg",
defaultUrl: "https://registry.npmjs.org",
},
codeberg: {
name: "Codeberg",
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/codeberg.svg",
defaultUrl: "https://codeberg.org",
},
linuxServerIO: {
name: "LinuxServer.io",
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/linuxserver-io.svg",
defaultUrl: "https://api.linuxserver.io",
},
gitHubContainerRegistry: {
name: "GitHub Container Registry",
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/github.svg",
defaultUrl: "https://ghcr.io",
},
quay: {
name: "Quay",
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/png/quay.png",
defaultUrl: "https://quay.io",
},
} as const;
export const releaseProviderKinds = objectKeys(releaseProviderDefs);
export type ReleaseProviderKind = keyof typeof releaseProviderDefs;
export const getReleaseProviderName = (provider: ReleaseProviderKind) => releaseProviderDefs[provider].name;
export const getReleaseProviderIconUrl = (provider: ReleaseProviderKind) => releaseProviderDefs[provider].iconUrl;
export const getReleaseProviderDefaultUrl = (provider: ReleaseProviderKind) => releaseProviderDefs[provider].defaultUrl;
const releaseProviderRegistryPrefixes: Partial<Record<ReleaseProviderKind, string>> = {
dockerHub: "docker.io/",
gitHubContainerRegistry: "ghcr.io/",
linuxServerIO: "lscr.io/",
quay: "quay.io/",
};
const removeContainerImageVersion = (identifier: string) => {
const digestIndex = identifier.indexOf("@");
const withoutDigest = digestIndex === -1 ? identifier : identifier.slice(0, digestIndex);
const tagIndex = withoutDigest.indexOf(":", withoutDigest.lastIndexOf("/") + 1);
return tagIndex === -1 ? withoutDigest : withoutDigest.slice(0, tagIndex);
};
export const normalizeReleaseProviderIdentifier = (provider: ReleaseProviderKind, identifier: string) => {
const trimmedIdentifier = identifier.trim();
const registryPrefix = releaseProviderRegistryPrefixes[provider];
const withoutRegistry =
registryPrefix && trimmedIdentifier.startsWith(registryPrefix)
? trimmedIdentifier.slice(registryPrefix.length)
: trimmedIdentifier;
// ponytail: only strip container image versions/digests for providers that use them
return registryPrefix ? removeContainerImageVersion(withoutRegistry) : withoutRegistry;
};
@@ -0,0 +1,32 @@
import { describe, expect, it } from "vitest";
import { normalizeReleaseProviderIdentifier } from "../release-provider";
describe("normalizeReleaseProviderIdentifier", () => {
it("normalizes full ghcr image references", () => {
expect(normalizeReleaseProviderIdentifier("gitHubContainerRegistry", "ghcr.io/muchobien/pocketbase:latest")).toBe(
"muchobien/pocketbase",
);
expect(normalizeReleaseProviderIdentifier("gitHubContainerRegistry", "ghcr.io/homarr-labs/homarr:latest")).toBe(
"homarr-labs/homarr",
);
});
it("keeps existing ghcr owner/name identifiers unchanged", () => {
expect(normalizeReleaseProviderIdentifier("gitHubContainerRegistry", "homarr-labs/homarr")).toBe(
"homarr-labs/homarr",
);
});
it("normalizes container image identifiers for other registries", () => {
expect(normalizeReleaseProviderIdentifier("dockerHub", "postgres:17")).toBe("postgres");
expect(normalizeReleaseProviderIdentifier("dockerHub", "docker.io/library/postgres:17")).toBe("library/postgres");
expect(normalizeReleaseProviderIdentifier("dockerHub", "library/postgres@sha256:abc123")).toBe("library/postgres");
expect(normalizeReleaseProviderIdentifier("linuxServerIO", "lscr.io/linuxserver/radarr:latest")).toBe(
"linuxserver/radarr",
);
expect(normalizeReleaseProviderIdentifier("quay", "quay.io/prometheus/prometheus:v3.0.0")).toBe(
"prometheus/prometheus",
);
});
});
@@ -20,27 +20,31 @@ export const widgetIntegrationSupport: Partial<Record<WidgetKind, readonly Integ
"mediaRequests-requestList": getIntegrationKindsByCategory("mediaRequest"),
"mediaRequests-requestStats": getIntegrationKindsByCategory("mediaRequest"),
mediaTranscoding: getIntegrationKindsByCategory("mediaTranscoding"),
mediaMissing: getIntegrationKindsByCategory("mediaOrganizer"),
networkControllerSummary: getIntegrationKindsByCategory("networkController"),
networkControllerStatus: getIntegrationKindsByCategory("networkController"),
indexerManager: getIntegrationKindsByCategory("indexerManager"),
healthMonitoring: getIntegrationKindsByCategory("healthMonitoring"),
healthMonitoring: getIntegrationKindsByCategory("healthMonitoring").filter((kind) => kind !== "patchmon"),
firewall: getIntegrationKindsByCategory("firewall"),
notifications: getIntegrationKindsByCategory("notifications"),
mediaReleases: ["emby", "jellyfin", "plex"],
systemResources: ["dashDot", "openmediavault", "truenas", "unraid", "glances"],
systemDisks: ["dashDot", "openmediavault", "truenas", "unraid"],
systemResources: ["dashDot", "openmediavault", "truenas", "unraid", "glances", "synology"],
systemDisks: ["dashDot", "openmediavault", "truenas", "unraid", "synology"],
coolify: ["coolify"],
timetable: ["searchCh"],
"immich-serverStats": ["immich"],
"immich-albumCarousel": ["immich"],
paperlessNgx: ["paperlessNgx"],
patchmon: ["patchmon"],
bazarr: ["bazarr"],
tracearr: ["tracearr"],
speedtestTracker: ["speedtestTracker"],
uptimeKuma: ["uptimeKuma"],
audioStats: ["navidrome", "audiobookshelf"],
vpn: getIntegrationKindsByCategory("vpn"),
archiveTeamWarrior: ["archiveTeamWarrior"],
anchorNote: ["anchor"],
openWebUi: ["openWebUi"],
traefik: ["traefik"],
};
export const getWidgetKindsForIntegration = (integrationKind: IntegrationKind): WidgetKind[] => {
@@ -81,18 +85,7 @@ export const featuredIntegrations: readonly IntegrationKind[] = [
"jellyfin",
];
export const hiddenFromOnboarding = new Set<IntegrationKind>([
"searchCh",
"github",
"dockerHub",
"gitlab",
"npm",
"codeberg",
"linuxServerIO",
"gitHubContainerRegistry",
"quay",
"mock",
]);
export const hiddenFromOnboarding = new Set<IntegrationKind>(["mock"]);
export const defaultBookmarkApps = [
{
+7
View File
@@ -17,6 +17,7 @@ export const widgetKinds = [
"mediaRequests-requestList",
"mediaRequests-requestStats",
"mediaTranscoding",
"mediaMissing",
"minecraftServerStatus",
"networkControllerSummary",
"networkControllerStatus",
@@ -36,6 +37,8 @@ export const widgetKinds = [
"immich-serverStats",
"immich-albumCarousel",
"paperlessNgx",
"patchmon",
"bazarr",
"tracearr",
"speedtestTracker",
"uptimeKuma",
@@ -48,6 +51,7 @@ export const widgetKinds = [
"beszelSystemGrid",
"beszelAlerts",
"beszelSystemStats",
"traefik",
"customApi",
"openWebUi",
] as const;
@@ -57,4 +61,7 @@ export const widgetDefaultSizes: Partial<Record<WidgetKind, { width: number; hei
uptimeKuma: { width: 2, height: 3 },
audioStats: { width: 2, height: 2 },
paperlessNgx: { width: 2, height: 2 },
patchmon: { width: 2, height: 2 },
mediaMissing: { width: 4, height: 3 },
bazarr: { width: 2, height: 2 },
};