From 4b463f96916044d7890a800a0182462473c63642 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Fri, 22 May 2026 16:38:55 +0200 Subject: [PATCH] fix: prefix hand-written URLs with basePath for subpath deployments --- app/(main)/[locale]/auth/callback/page.tsx | 4 ++-- app/(main)/[locale]/pro/page.tsx | 3 ++- app/(main)/setup/page.tsx | 12 ++++++------ components/layout/navigation-rail.tsx | 6 +++--- components/settings/about-data-settings.tsx | 5 +++-- lib/plugin-sandbox/bundle-signing.ts | 4 +++- 6 files changed, 19 insertions(+), 15 deletions(-) diff --git a/app/(main)/[locale]/auth/callback/page.tsx b/app/(main)/[locale]/auth/callback/page.tsx index a469c194..a0845972 100644 --- a/app/(main)/[locale]/auth/callback/page.tsx +++ b/app/(main)/[locale]/auth/callback/page.tsx @@ -4,7 +4,7 @@ import { Suspense, useEffect, useState } from "react"; import { useRouter, useSearchParams } from "next/navigation"; import { useTranslations } from "next-intl"; import { useAuthStore } from "@/stores/auth-store"; -import { getPathPrefix } from "@/lib/browser-navigation"; +import { apiFetch, getPathPrefix } from "@/lib/browser-navigation"; import { Loader2, AlertCircle } from "lucide-react"; import { Button } from "@/components/ui/button"; import { useParams } from "next/navigation"; @@ -96,7 +96,7 @@ function OAuthCallbackInner() { // the refresh-token cookie write for the same reason. (async () => { try { - const res = await fetch("/api/auth/sso/complete", { + const res = await apiFetch("/api/auth/sso/complete", { method: "POST", headers: { "Content-Type": "application/json" }, credentials: "include", diff --git a/app/(main)/[locale]/pro/page.tsx b/app/(main)/[locale]/pro/page.tsx index c8b00f14..1d1005f0 100644 --- a/app/(main)/[locale]/pro/page.tsx +++ b/app/(main)/[locale]/pro/page.tsx @@ -16,6 +16,7 @@ import { PaneSizeContext } from "@/hooks/use-pane-size"; import { ProTabBar, PRO_TAB_DRAG_MIME } from "@/components/pro/pro-tab-bar"; import { useProTabStore, type ProTab, type ProTabKind, type ProPaneId } from "@/stores/pro-tab-store"; import { cn } from "@/lib/utils"; +import { getPathPrefix } from "@/lib/browser-navigation"; import MailPage from "@/app/(main)/[locale]/page"; import CalendarPage from "@/app/(main)/[locale]/calendar/page"; @@ -172,7 +173,7 @@ export default function ProHome() { // enabled it. If either precondition stops holding, hand the user back // to the standard shell. if (isMobile || isTablet || !proInterface) { - window.location.replace("/"); + window.location.replace(`${getPathPrefix()}/`); } }, [initialCheckDone, isMobile, isTablet, proInterface]); diff --git a/app/(main)/setup/page.tsx b/app/(main)/setup/page.tsx index 64e9cad6..686bb202 100644 --- a/app/(main)/setup/page.tsx +++ b/app/(main)/setup/page.tsx @@ -3,7 +3,7 @@ import { useEffect, useState, type FormEvent, type ReactNode } from 'react'; import { useRouter, useSearchParams } from 'next/navigation'; import { CheckCircle2, AlertTriangle, AlertCircle, Server, ShieldCheck, KeyRound, FileText, Palette, Lock, ShieldAlert } from 'lucide-react'; -import { apiFetch } from '@/lib/browser-navigation'; +import { apiFetch, getPathPrefix } from '@/lib/browser-navigation'; type State = 'bootstrap' | 'configured' | 'env-managed'; @@ -258,7 +258,7 @@ export default function SetupWizardPage() { // edge cases that swallow client-side replaces after the // setupComplete flag flips. setTimeout(() => { - window.location.assign('/admin/login'); + window.location.assign(`${getPathPrefix()}/admin/login`); }, 1500); }} /> @@ -340,13 +340,13 @@ function CompletedScreen() {
Sign in to admin dashboard Open webmail login @@ -422,13 +422,13 @@ function AlreadyConfiguredScreen() {
Sign in to admin dashboard Open webmail login diff --git a/components/layout/navigation-rail.tsx b/components/layout/navigation-rail.tsx index 8a8b58f9..56a721d9 100644 --- a/components/layout/navigation-rail.tsx +++ b/components/layout/navigation-rail.tsx @@ -21,7 +21,7 @@ import { getMaxAccounts } from "@/lib/account-utils"; import { cn, formatFileSize } from "@/lib/utils"; import { PluginSlot } from "@/components/plugins/plugin-slot"; import { KeyboardShortcutsModal } from "@/components/keyboard-shortcuts-modal"; -import { apiFetch } from "@/lib/browser-navigation"; +import { apiFetch, getPathPrefix } from "@/lib/browser-navigation"; import { Avatar } from "@/components/ui/avatar"; interface NavItem { @@ -385,7 +385,7 @@ export function NavigationRail({ {/* Admin (Stalwart admins) - hard nav because /admin lives outside the [locale] tree */} {isStalwartAdmin && ( {isStalwartAdmin && ( diff --git a/components/settings/about-data-settings.tsx b/components/settings/about-data-settings.tsx index 6488d1cb..26be560c 100644 --- a/components/settings/about-data-settings.tsx +++ b/components/settings/about-data-settings.tsx @@ -10,6 +10,7 @@ import { usePolicyStore } from '@/stores/policy-store'; import { useUpdateStore } from '@/stores/update-store'; import { ExternalLink } from 'lucide-react'; import { cn } from '@/lib/utils'; +import { getPathPrefix } from '@/lib/browser-navigation'; import { SpamSiegeGame } from './spam-siege-game'; const APP_VERSION = process.env.NEXT_PUBLIC_APP_VERSION || "0.0.0"; @@ -123,12 +124,12 @@ export function AboutDataSettings() {