diff --git a/apps/nextjs/src/app/[locale]/init/_steps/integrations/docker-discovery-panel.tsx b/apps/nextjs/src/app/[locale]/init/_steps/integrations/docker-discovery-panel.tsx index dac79c1d0..88da9cb71 100644 --- a/apps/nextjs/src/app/[locale]/init/_steps/integrations/docker-discovery-panel.tsx +++ b/apps/nextjs/src/app/[locale]/init/_steps/integrations/docker-discovery-panel.tsx @@ -36,6 +36,7 @@ export interface DiscoveredApp { containerId: string; containerName: string; suggestedUrl: string; + publishedPort: number | null; iconUrl: string | null; } diff --git a/apps/nextjs/src/app/[locale]/init/_steps/integrations/init-integrations.tsx b/apps/nextjs/src/app/[locale]/init/_steps/integrations/init-integrations.tsx index 9657dfe81..bc4590708 100644 --- a/apps/nextjs/src/app/[locale]/init/_steps/integrations/init-integrations.tsx +++ b/apps/nextjs/src/app/[locale]/init/_steps/integrations/init-integrations.tsx @@ -22,7 +22,7 @@ import { IconArrowRight, IconInfoCircle, IconSettings } from "@tabler/icons-reac import { clientApi } from "@homarr/api/client"; import type { IntegrationKind, UrlTemplateMode } from "@homarr/definitions"; -import { buildIntegrationUrl, getIntegrationName } from "@homarr/definitions"; +import { buildAppUrl, buildIntegrationUrl, getIntegrationName } from "@homarr/definitions"; import { showErrorNotification } from "@homarr/notifications"; import { useScopedI18n } from "@homarr/translation/client"; import { IntegrationAvatar } from "@homarr/ui"; @@ -137,11 +137,16 @@ export const InitIntegrations = () => { const appsToCreate = dockerApps.filter((app) => selectedAppIds.has(app.containerId)); if (appsToCreate.length > 0) { await createApps( - appsToCreate.map((app) => ({ - name: app.containerName, - href: app.suggestedUrl || null, - iconUrl: app.iconUrl, - })), + appsToCreate.map((app) => { + const href = baseHost + ? buildAppUrl(app.containerName, baseHost, urlMode, app.publishedPort ?? undefined) + : app.suggestedUrl; + return { + name: app.containerName, + href: href || null, + iconUrl: app.iconUrl, + }; + }), ); } await setupIntegrations(); diff --git a/packages/api/src/router/onboard/onboard-router.ts b/packages/api/src/router/onboard/onboard-router.ts index 38d151ed7..4f43a9533 100644 --- a/packages/api/src/router/onboard/onboard-router.ts +++ b/packages/api/src/router/onboard/onboard-router.ts @@ -157,6 +157,7 @@ export const onboardRouter = createTRPCRouter({ containerId: string; containerName: string; suggestedUrl: string; + publishedPort: number | null; iconUrl: string | null; }[], }; @@ -228,6 +229,7 @@ export const onboardRouter = createTRPCRouter({ containerId: container.Id, containerName, suggestedUrl, + publishedPort, iconUrl, }); } diff --git a/packages/definitions/src/integration-url-template.ts b/packages/definitions/src/integration-url-template.ts index f47387414..62bfa0dea 100644 --- a/packages/definitions/src/integration-url-template.ts +++ b/packages/definitions/src/integration-url-template.ts @@ -6,6 +6,14 @@ export type UrlTemplateMode = "hostPort" | "subdomain"; const getSlugForKind = (kind: IntegrationKind): string => integrationIconSlugs[kind]; +const buildUrl = (slug: string, host: string, mode: UrlTemplateMode, port?: number): string => { + const modeBuilders: Record string> = { + subdomain: () => `https://${slug}.${host}`, + hostPort: () => (port ? `http://${host}:${port}` : `http://${host}`), + }; + return modeBuilders[mode](); +}; + export const buildIntegrationUrl = ( kind: IntegrationKind, baseHost: string, @@ -14,16 +22,18 @@ export const buildIntegrationUrl = ( ): string => { const host = baseHost.trim().replace(/\/+$/, ""); if (!host) return ""; - - const slug = getSlugForKind(kind); - - const modeBuilders: Record string> = { - subdomain: () => `https://${slug}.${host}`, - hostPort: () => { - const port = dockerPort ?? getIntegrationDefaultPort(kind); - return port ? `http://${host}:${port}` : `http://${host}`; - }, - }; - - return modeBuilders[mode](); + const port = dockerPort ?? getIntegrationDefaultPort(kind); + return buildUrl(getSlugForKind(kind), host, mode, port ?? undefined); +}; + +export const buildAppUrl = ( + containerName: string, + baseHost: string, + mode: UrlTemplateMode, + dockerPort?: number, +): string => { + const host = baseHost.trim().replace(/\/+$/, ""); + if (!host) return ""; + const slug = containerName.toLowerCase().replace(/[^a-z0-9-]/g, "-"); + return buildUrl(slug, host, mode, dockerPort); };