From 778533a62e7b0316479824c5e6b6fa23a085c3e6 Mon Sep 17 00:00:00 2001 From: Thomas Date: Mon, 3 Aug 2026 17:37:53 +0200 Subject: [PATCH] fix: revert regressions from recent integration/releases/app-href changes (#6514) --- .../app/[locale]/manage/integrations/edit/[id]/page.tsx | 7 ------- .../src/app/[locale]/manage/integrations/new/page.tsx | 5 +---- apps/nextjs/src/app/[locale]/manage/integrations/page.tsx | 5 +---- apps/nextjs/src/app/[locale]/manage/layout.tsx | 2 +- packages/api/src/router/home.ts | 2 +- packages/api/src/router/integration/integration-router.ts | 6 ++---- packages/api/src/router/widgets/releases.ts | 1 - packages/definitions/src/docs/homarr-docs-sitemap.ts | 1 - packages/spotlight/src/modes/command/global-group.tsx | 4 +--- packages/spotlight/src/modes/page/pages-search-group.tsx | 2 +- packages/validation/src/app.ts | 4 ---- 11 files changed, 8 insertions(+), 31 deletions(-) diff --git a/apps/nextjs/src/app/[locale]/manage/integrations/edit/[id]/page.tsx b/apps/nextjs/src/app/[locale]/manage/integrations/edit/[id]/page.tsx index ed2da1aab..8cd8bf01b 100644 --- a/apps/nextjs/src/app/[locale]/manage/integrations/edit/[id]/page.tsx +++ b/apps/nextjs/src/app/[locale]/manage/integrations/edit/[id]/page.tsx @@ -1,7 +1,5 @@ -import { notFound } from "next/navigation"; import { Container, Fieldset, Group, Stack, Title } from "@mantine/core"; -import { auth } from "@homarr/auth/next"; import { api } from "@homarr/api/server"; import { getIntegrationName } from "@homarr/definitions"; import { getI18n, getScopedI18n } from "@homarr/translation/server"; @@ -18,11 +16,6 @@ interface EditIntegrationPageProps { export default async function EditIntegrationPage(props: EditIntegrationPageProps) { const params = await props.params; - const session = await auth(); - - if (!session?.user.permissions.includes("integration-full-all")) { - notFound(); - } const editT = await getScopedI18n("integration.page.edit"); const t = await getI18n(); const integration = await api.integration.byId({ id: params.id }).catch(catchTrpcNotFound); diff --git a/apps/nextjs/src/app/[locale]/manage/integrations/new/page.tsx b/apps/nextjs/src/app/[locale]/manage/integrations/new/page.tsx index d6f1cce98..3194134c3 100644 --- a/apps/nextjs/src/app/[locale]/manage/integrations/new/page.tsx +++ b/apps/nextjs/src/app/[locale]/manage/integrations/new/page.tsx @@ -23,10 +23,7 @@ interface NewIntegrationPageProps { export default async function IntegrationsNewPage(props: NewIntegrationPageProps) { const searchParams = await props.searchParams; const session = await auth(); - if ( - !session?.user.permissions.includes("integration-full-all") || - !session?.user.permissions.includes("integration-create") - ) { + if (!session?.user.permissions.includes("integration-create")) { notFound(); } diff --git a/apps/nextjs/src/app/[locale]/manage/integrations/page.tsx b/apps/nextjs/src/app/[locale]/manage/integrations/page.tsx index 692329896..0eeb25c1a 100644 --- a/apps/nextjs/src/app/[locale]/manage/integrations/page.tsx +++ b/apps/nextjs/src/app/[locale]/manage/integrations/page.tsx @@ -1,5 +1,5 @@ import { Fragment } from "react"; -import { notFound, redirect } from "next/navigation"; +import { redirect } from "next/navigation"; import { AccordionControl, AccordionItem, @@ -50,9 +50,6 @@ export default async function IntegrationsPage(props: IntegrationsPageProps) { if (!session) { redirect("/auth/login"); } - if (!session.user.permissions.includes("integration-full-all")) { - notFound(); - } const integrations = await api.integration.all(); const t = await getScopedI18n("integration"); diff --git a/apps/nextjs/src/app/[locale]/manage/layout.tsx b/apps/nextjs/src/app/[locale]/manage/layout.tsx index ba2440c25..1cd0d3d2e 100644 --- a/apps/nextjs/src/app/[locale]/manage/layout.tsx +++ b/apps/nextjs/src/app/[locale]/manage/layout.tsx @@ -71,7 +71,7 @@ export default async function ManageLayout({ children }: PropsWithChildren) { icon: IconAffiliateFilled, href: "/manage/integrations", label: t("items.integrations"), - hidden: !session?.user.permissions.includes("integration-full-all"), + hidden: !session?.user.permissions.includes("integration-create"), "data-onboarding-tour-id": "manage-integrations", }, { diff --git a/packages/api/src/router/home.ts b/packages/api/src/router/home.ts index a9cee2c21..4b63f9326 100644 --- a/packages/api/src/router/home.ts +++ b/packages/api/src/router/home.ts @@ -92,7 +92,7 @@ export const homeRouter = createTRPCRouter({ }); } - if (ctx.session?.user.permissions.includes("integration-full-all")) { + if (ctx.session?.user.permissions.includes("integration-create")) { statistics.push({ titleKey: "integration", subtitleKey: "resources", diff --git a/packages/api/src/router/integration/integration-router.ts b/packages/api/src/router/integration/integration-router.ts index 2b40dc2a8..af237bb31 100644 --- a/packages/api/src/router/integration/integration-router.ts +++ b/packages/api/src/router/integration/integration-router.ts @@ -62,13 +62,12 @@ export const integrationRouter = createTRPCRouter({ requiredSecrets: def.secretKinds, })); }), - all: permissionRequiredProcedure - .requiresPermission("integration-full-all") + all: protectedProcedure .meta({ mcp: { enabled: true, description: - "List all configured integrations (connections to services like Sonarr, Radarr, Plex, etc.). Requires integration-full-all permission. Returns each integration's id, name, kind, url, and permissions. Use the 'id' field as 'integrationId' in other tools. Check permissions.hasUseAccess before reading data and permissions.hasInteractAccess before performing actions — false means the API key owner lacks that permission level for this integration, not an error", + "List all configured integrations (connections to services like Sonarr, Radarr, Plex, etc.). Returns each integration's id, name, kind, url, and permissions. Use the 'id' field as 'integrationId' in other tools. Check permissions.hasUseAccess before reading data and permissions.hasInteractAccess before performing actions — false means the API key owner lacks that permission level for this integration, not an error", }, }) .query(async ({ ctx }) => { @@ -297,7 +296,6 @@ export const integrationRouter = createTRPCRouter({ }; }), create: permissionRequiredProcedure - .requiresPermission("integration-full-all") .requiresPermission("integration-create") .meta({ mcp: { diff --git a/packages/api/src/router/widgets/releases.ts b/packages/api/src/router/widgets/releases.ts index 7480de536..d234a75c1 100644 --- a/packages/api/src/router/widgets/releases.ts +++ b/packages/api/src/router/widgets/releases.ts @@ -104,7 +104,6 @@ export const releasesRouter = createTRPCRouter({ id: repositoryId, provider: repository.provider, timestamp: response.timestamp, - success: true as const, ...response.data, }; } catch (error) { diff --git a/packages/definitions/src/docs/homarr-docs-sitemap.ts b/packages/definitions/src/docs/homarr-docs-sitemap.ts index 7185a2fdd..22bc5f75b 100644 --- a/packages/definitions/src/docs/homarr-docs-sitemap.ts +++ b/packages/definitions/src/docs/homarr-docs-sitemap.ts @@ -119,7 +119,6 @@ export type HomarrDocumentationPath = | "/docs/tags/variables" | "/docs/tags/widgets" | "/docs/advanced/command-line" - | "/docs/advanced/command-line/development" | "/docs/advanced/command-line/fix-usernames" | "/docs/advanced/command-line/integrations" | "/docs/advanced/command-line/password-recovery" diff --git a/packages/spotlight/src/modes/command/global-group.tsx b/packages/spotlight/src/modes/command/global-group.tsx index fa8f53c5f..fd277b55e 100644 --- a/packages/spotlight/src/modes/command/global-group.tsx +++ b/packages/spotlight/src/modes/command/global-group.tsx @@ -95,9 +95,7 @@ export const globalCommandGroup = createGroup({ icon: IconPlug, name: tOption("newIntegration.label"), useInteraction: interaction.children(newIntegrationChildrenOptions), - hidden: - !session?.user.permissions.includes("integration-full-all") || - !session?.user.permissions.includes("integration-create"), + hidden: !session?.user.permissions.includes("integration-create"), }, { commandKey: "newUser", diff --git a/packages/spotlight/src/modes/page/pages-search-group.tsx b/packages/spotlight/src/modes/page/pages-search-group.tsx index 1b3b0ee68..66fe1ce4f 100644 --- a/packages/spotlight/src/modes/page/pages-search-group.tsx +++ b/packages/spotlight/src/modes/page/pages-search-group.tsx @@ -82,7 +82,7 @@ export const pagesSearchGroup = createGroup<{ icon: IconPlug, path: "/manage/integrations", name: t("manageIntegration.label"), - hidden: !session?.user.permissions.includes("integration-full-all"), + hidden: !session, }, { icon: IconSearch, diff --git a/packages/validation/src/app.ts b/packages/validation/src/app.ts index e3b94218b..21a4335be 100644 --- a/packages/validation/src/app.ts +++ b/packages/validation/src/app.ts @@ -5,10 +5,6 @@ export const appHrefSchema = z .trim() .url() .regex(/^(?!javascript)[a-zA-Z]*:\/\//i) // javascript: is not allowed, i for case insensitive (so Javascript: is also not allowed) - .or(z.string().trim().includes("[homarr_base]")) - .or(z.string().trim().includes("[homarr_hostname]")) - .or(z.string().trim().includes("[homarr_domain]")) - .or(z.string().trim().includes("[homarr_protocol]")) .or(z.literal("")) .transform((value) => (value.length === 0 ? null : value)) .nullable();