mirror of
https://github.com/arsvendg/Stirling-PDF.git
synced 2026-07-14 10:34:06 +02:00
fix: respect BASE_PATH in redirects, comparisons, and cookie consent paths
This commit is contained in:
@@ -34,6 +34,7 @@ import CloudUploadIcon from "@mui/icons-material/CloudUpload";
|
||||
import KeyboardArrowRightIcon from "@mui/icons-material/KeyboardArrowRight";
|
||||
import RefreshIcon from "@mui/icons-material/Refresh";
|
||||
|
||||
import { stripBasePath } from "@app/constants/app";
|
||||
import { useSharingEnabled } from "@app/hooks/useSharingEnabled";
|
||||
import { useFolders } from "@app/contexts/FolderContext";
|
||||
import { useFileActions } from "@app/contexts/file/fileHooks";
|
||||
@@ -190,10 +191,11 @@ export default function FileManagerView() {
|
||||
|
||||
// Push folder selection into the URL while still on /files.
|
||||
useEffect(() => {
|
||||
if (!window.location.pathname.startsWith("/files")) return;
|
||||
const stripped = stripBasePath(window.location.pathname);
|
||||
if (!stripped.startsWith("/files")) return;
|
||||
const target =
|
||||
currentFolderId === null ? "/files" : `/files/${currentFolderId}`;
|
||||
if (window.location.pathname !== target) {
|
||||
if (stripped !== target) {
|
||||
navigate(target, { replace: true });
|
||||
}
|
||||
}, [currentFolderId, navigate]);
|
||||
|
||||
@@ -23,6 +23,7 @@ import {
|
||||
useUnsavedChanges,
|
||||
} from "@app/contexts/UnsavedChangesContext";
|
||||
import { SettingsSearchBar } from "@app/components/shared/config/SettingsSearchBar";
|
||||
import { stripBasePath } from "@app/constants/app";
|
||||
|
||||
interface AppConfigModalProps {
|
||||
opened: boolean;
|
||||
@@ -82,7 +83,7 @@ const AppConfigModalInner: React.FC<AppConfigModalProps> = ({
|
||||
const detail = (ev as CustomEvent).detail as { key?: NavKey } | undefined;
|
||||
if (detail?.key) {
|
||||
const alreadyInSettings =
|
||||
window.location.pathname.startsWith("/settings");
|
||||
stripBasePath(window.location.pathname).startsWith("/settings");
|
||||
navigate(`/settings/${detail.key}`, {
|
||||
replace: alreadyInSettings,
|
||||
});
|
||||
|
||||
@@ -18,7 +18,7 @@ import {
|
||||
useNavigationState,
|
||||
useNavigationGuard,
|
||||
} from "@app/contexts/NavigationContext";
|
||||
import { BASE_PATH, withBasePath } from "@app/constants/app";
|
||||
import { stripBasePath, withBasePath } from "@app/constants/app";
|
||||
import { useRedaction, useRedactionMode } from "@app/contexts/RedactionContext";
|
||||
import TextFieldsIcon from "@mui/icons-material/TextFields";
|
||||
import StraightenIcon from "@mui/icons-material/Straighten";
|
||||
@@ -73,17 +73,10 @@ export function useViewerWorkbenchBarButtons(
|
||||
});
|
||||
}, [registerImmediatePanUpdate]);
|
||||
|
||||
const stripBasePath = useCallback((path: string) => {
|
||||
if (BASE_PATH && path.startsWith(BASE_PATH)) {
|
||||
return path.slice(BASE_PATH.length) || "/";
|
||||
}
|
||||
return path;
|
||||
}, []);
|
||||
|
||||
const isAnnotationsPath = useCallback(() => {
|
||||
const cleanPath = stripBasePath(window.location.pathname).toLowerCase();
|
||||
return cleanPath === "/annotations" || cleanPath.endsWith("/annotations");
|
||||
}, [stripBasePath]);
|
||||
}, []);
|
||||
|
||||
const [isAnnotationsActive, setIsAnnotationsActive] = useState<boolean>(() =>
|
||||
isAnnotationsPath(),
|
||||
|
||||
@@ -38,3 +38,16 @@ export const absoluteWithBasePath = (path: string): string => {
|
||||
const clean = path.startsWith("/") ? path : `/${path}`;
|
||||
return `${window.location.origin}${BASE_PATH}${clean}`;
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove the BASE_PATH prefix from a pathname so comparisons against
|
||||
* bare app routes ("/login", "/files", …) work under any subpath deploy.
|
||||
*/
|
||||
export const stripBasePath = (pathname: string): string => {
|
||||
if (!BASE_PATH) return pathname;
|
||||
if (pathname === BASE_PATH) return "/";
|
||||
if (pathname.startsWith(`${BASE_PATH}/`)) {
|
||||
return pathname.slice(BASE_PATH.length);
|
||||
}
|
||||
return pathname;
|
||||
};
|
||||
|
||||
@@ -36,14 +36,14 @@ export const useCookieConsent = ({
|
||||
|
||||
const mainCSS = document.createElement("link");
|
||||
mainCSS.rel = "stylesheet";
|
||||
mainCSS.href = `${BASE_PATH}css/cookieconsent.css`;
|
||||
mainCSS.href = `${BASE_PATH}/css/cookieconsent.css`;
|
||||
if (!document.querySelector(`link[href="${mainCSS.href}"]`)) {
|
||||
document.head.appendChild(mainCSS);
|
||||
}
|
||||
|
||||
const customCSS = document.createElement("link");
|
||||
customCSS.rel = "stylesheet";
|
||||
customCSS.href = `${BASE_PATH}css/cookieconsentCustomisation.css`;
|
||||
customCSS.href = `${BASE_PATH}/css/cookieconsentCustomisation.css`;
|
||||
if (!document.querySelector(`link[href="${customCSS.href}"]`)) {
|
||||
document.head.appendChild(customCSS);
|
||||
}
|
||||
@@ -57,7 +57,7 @@ export const useCookieConsent = ({
|
||||
}
|
||||
|
||||
const script = document.createElement("script");
|
||||
script.src = `${BASE_PATH}js/thirdParty/cookieconsent.umd.js`;
|
||||
script.src = `${BASE_PATH}/js/thirdParty/cookieconsent.umd.js`;
|
||||
script.onload = () => {
|
||||
setTimeout(() => {
|
||||
const detectTheme = () => {
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
clampText,
|
||||
extractAxiosErrorMessage,
|
||||
} from "@app/services/httpErrorUtils";
|
||||
import { withBasePath } from "@app/constants/app";
|
||||
|
||||
// Module-scoped state to reduce global variable usage
|
||||
const recentSpecialByEndpoint: Record<string, number> = {};
|
||||
@@ -82,7 +83,7 @@ export async function handleHttpError(error: any): Promise<boolean> {
|
||||
// ignore storage access failures
|
||||
}
|
||||
const expiredPrefix = hadStoredJwt ? "expired=true&" : "";
|
||||
window.location.href = `/login?${expiredPrefix}from=${encodeURIComponent(currentLocation)}`;
|
||||
window.location.href = `${withBasePath("/login")}?${expiredPrefix}from=${encodeURIComponent(currentLocation)}`;
|
||||
return true; // Suppress toast since we're redirecting
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { NavKey } from "@app/components/shared/config/types";
|
||||
import { stripBasePath, withBasePath } from "@app/constants/app";
|
||||
|
||||
/**
|
||||
* Navigate to a specific settings section
|
||||
@@ -13,8 +14,7 @@ import { NavKey } from "@app/components/shared/config/types";
|
||||
* navigateToSettings('adminPremium');
|
||||
*/
|
||||
export function navigateToSettings(section: NavKey) {
|
||||
const basePath = window.location.pathname.split("/settings")[0] || "";
|
||||
const newPath = `${basePath}/settings/${section}`;
|
||||
const newPath = withBasePath(`/settings/${section}`);
|
||||
window.history.pushState({}, "", newPath);
|
||||
|
||||
// Trigger a popstate event to notify components
|
||||
@@ -30,10 +30,10 @@ export function navigateToSettings(section: NavKey) {
|
||||
*
|
||||
* @example
|
||||
* <a href={getSettingsUrl('people')}>Go to People Settings</a>
|
||||
* // Returns: "/settings/people"
|
||||
* // Returns: "/settings/people" (or "/bpp/settings/people" under subpath)
|
||||
*/
|
||||
export function getSettingsUrl(section: NavKey): string {
|
||||
return `/settings/${section}`;
|
||||
return withBasePath(`/settings/${section}`);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -43,7 +43,7 @@ export function getSettingsUrl(section: NavKey): string {
|
||||
* @returns True if in settings (and matching specific section if provided)
|
||||
*/
|
||||
export function isInSettings(section?: NavKey): boolean {
|
||||
const pathname = window.location.pathname;
|
||||
const pathname = stripBasePath(window.location.pathname);
|
||||
|
||||
if (!section) {
|
||||
return pathname.startsWith("/settings");
|
||||
|
||||
@@ -10,6 +10,7 @@ import { useTranslation } from "react-i18next";
|
||||
import type { TFunction } from "i18next";
|
||||
import { springAuth } from "@app/auth/springAuthClient";
|
||||
import { clearPlatformAuthOnLoginInit } from "@app/extensions/authSessionCleanup";
|
||||
import { stripBasePath } from "@app/constants/app";
|
||||
import type {
|
||||
Session,
|
||||
User,
|
||||
@@ -167,7 +168,7 @@ export function AuthProvider({ children }: { children: ReactNode }) {
|
||||
// Clear any platform-specific cached auth on login page init.
|
||||
if (
|
||||
typeof window !== "undefined" &&
|
||||
window.location.pathname.startsWith("/login")
|
||||
stripBasePath(window.location.pathname).startsWith("/login")
|
||||
) {
|
||||
await clearPlatformAuthOnLoginInit();
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { AxiosInstance, AxiosError, InternalAxiosRequestConfig } from "axios";
|
||||
import { withBasePath } from "@app/constants/app";
|
||||
|
||||
let isRefreshing = false;
|
||||
let failedQueue: Array<{
|
||||
@@ -89,9 +90,10 @@ async function refreshAuthToken(client: AxiosInstance): Promise<string> {
|
||||
clearJwtTokenFromStorage();
|
||||
|
||||
// Redirect to login
|
||||
if (window.location.pathname !== "/login") {
|
||||
const loginPath = withBasePath("/login");
|
||||
if (window.location.pathname !== loginPath) {
|
||||
console.log("[API Client] Redirecting to login page...");
|
||||
window.location.href = "/login";
|
||||
window.location.href = loginPath;
|
||||
}
|
||||
|
||||
throw error;
|
||||
|
||||
@@ -3,6 +3,7 @@ import { supabase } from "@app/auth/supabase";
|
||||
import { handleHttpError } from "@app/services/httpErrorHandler";
|
||||
import { alert } from "@app/components/toast";
|
||||
import { openPlanSettings } from "@app/utils/appSettings";
|
||||
import { withBasePath } from "@app/constants/app";
|
||||
|
||||
// Global credit update callback - will be set by the AuthProvider
|
||||
let globalCreditUpdateCallback: ((credits: number) => void) | null = null;
|
||||
@@ -189,7 +190,7 @@ apiClient.interceptors.response.use(
|
||||
|
||||
if (!isPublicEndpoint) {
|
||||
// Redirect to login only for protected endpoints
|
||||
window.location.href = "/login";
|
||||
window.location.href = withBasePath("/login");
|
||||
}
|
||||
|
||||
return Promise.reject(error);
|
||||
@@ -209,8 +210,9 @@ apiClient.interceptors.response.use(
|
||||
console.debug(
|
||||
"[API Client] No session to refresh, 401 on protected endpoint",
|
||||
);
|
||||
if (window.location.pathname !== "/login") {
|
||||
window.location.href = "/login";
|
||||
const loginPath = withBasePath("/login");
|
||||
if (window.location.pathname !== loginPath) {
|
||||
window.location.href = loginPath;
|
||||
}
|
||||
}
|
||||
} catch (refreshError) {
|
||||
|
||||
Reference in New Issue
Block a user