diff --git a/src/base/contexts/AppContext.tsx b/src/base/contexts/AppContext.tsx index 9fe978fd..0475f449 100644 --- a/src/base/contexts/AppContext.tsx +++ b/src/base/contexts/AppContext.tsx @@ -19,7 +19,7 @@ import { AppPageHistoryContextProvider } from '@/base/contexts/AppPageHistoryCon import { AppThemeContextProvider } from '@/features/theme/AppThemeContext.tsx'; import { NavBarContextProvider } from '@/features/navigation-bar/NavbarContext.tsx'; import { SessionContextProvider } from '@/features/authentication/SessionContext.tsx'; -import { SubpathConfig } from '@/lib/utils/SubpathConfig.ts'; +import { SubpathUtil } from '@/lib/utils/SubpathUtil.ts'; interface Props { children: React.ReactNode; @@ -27,7 +27,7 @@ interface Props { export const AppContext: React.FC = ({ children }) => ( - + diff --git a/src/i18n/index.ts b/src/i18n/index.ts index 0120eefb..d76f1410 100644 --- a/src/i18n/index.ts +++ b/src/i18n/index.ts @@ -10,7 +10,7 @@ import { use } from 'i18next'; import { initReactI18next } from 'react-i18next'; import LanguageDetector from 'i18next-browser-languagedetector'; import HttpBackend from 'i18next-http-backend'; -import { SubpathConfig } from '@/lib/utils/SubpathConfig.ts'; +import { SubpathUtil } from '@/lib/utils/SubpathUtil.ts'; /** * Keys have to match {@link IsoLanguages} codes, they're used for showing the language name in the dropdown in the {@link Settings}.
@@ -46,7 +46,7 @@ export const i18n = use(initReactI18next) fallbackLng: 'en', backend: { - loadPath: `${SubpathConfig.getSubpath()}/locales/{{lng}}.json`, + loadPath: `${SubpathUtil.getSubpath()}/locales/{{lng}}.json`, allowMultiLoading: true, }, diff --git a/src/lib/requests/client/BaseClient.ts b/src/lib/requests/client/BaseClient.ts index 99d64836..e7a87502 100644 --- a/src/lib/requests/client/BaseClient.ts +++ b/src/lib/requests/client/BaseClient.ts @@ -10,7 +10,7 @@ import { AppStorage } from '@/lib/storage/AppStorage.ts'; import { UserRefreshMutation } from '@/lib/graphql/generated/graphql.ts'; import { AuthManager } from '@/features/authentication/AuthManager.ts'; import { AbortableApolloMutationResponse } from '@/lib/requests/RequestManager.ts'; -import { SubpathConfig } from '@/lib/utils/SubpathConfig.ts'; +import { SubpathUtil } from '@/lib/utils/SubpathUtil.ts'; export abstract class BaseClient { protected abstract client: Client; @@ -72,7 +72,7 @@ export abstract class BaseClient { const serverBaseURL = AppStorage.local.getItemParsed('serverBaseURL', defaultUrl); // Apply subpath configuration to the base URL - return SubpathConfig.getApiBaseUrl(serverBaseURL); + return SubpathUtil.getApiBaseUrl(serverBaseURL); } public abstract updateConfig(config: Partial): void; diff --git a/src/lib/utils/SubpathConfig.ts b/src/lib/utils/SubpathConfig.ts deleted file mode 100644 index 81e26afc..00000000 --- a/src/lib/utils/SubpathConfig.ts +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (C) Contributors to the Suwayomi project - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ - -interface SuwayomiConfig { - webUISubpath?: string; -} - -declare global { - interface Window { - __SUWAYOMI_CONFIG__?: SuwayomiConfig; - } -} - -/** - * Utility for handling dynamic subpath configuration for the WebUI - * Server-side configuration only - WebUI automatically detects server subpath - */ -export class SubpathConfig { - private static cachedSubpath: string | null = null; - - /** - * Get the current subpath from server config, environment variables, or fallback - */ - public static getSubpath(): string { - // Return cached result if available - if (this.cachedSubpath !== null) { - return this.cachedSubpath; - } - - // Priority 1: Development environment variable (VITE_SUBPATH) - const envSubpath = import.meta.env.VITE_SUBPATH; - if (envSubpath && envSubpath !== '/') { - this.cachedSubpath = envSubpath; - return this.cachedSubpath; - } - - // Priority 2: Server-side configuration (injected by server) - const serverSubpath = this.getServerSubpath(); - if (serverSubpath && serverSubpath !== '/') { - this.cachedSubpath = serverSubpath; - return this.cachedSubpath; - } - - // Priority 3: No subpath found - this.cachedSubpath = ''; - return this.cachedSubpath; - } - - /** - * Get server-configured subpath from `window.__SUWAYOMI_CONFIG__` - */ - private static getServerSubpath(): string | null { - if (typeof window === 'undefined') { - return null; - } - - // eslint-disable-next-line no-underscore-dangle - return window.__SUWAYOMI_CONFIG__?.webUISubpath || null; - } - - /** - * Get subpath for React Router basename prop - * @returns Subpath string or undefined (React Router expects undefined when no basename) - */ - public static getRouterBasename(): string | undefined { - const subpath = this.getSubpath(); - return subpath || undefined; - } - - /** - * Get the API base URL with subpath consideration - * This ensures API calls work correctly when the app is served under a subpath - */ - public static getApiBaseUrl(serverUrl: string): string { - const baseUrl = serverUrl; - const subpath = this.getSubpath(); - - if (!subpath) { - return baseUrl; - } - - return `${baseUrl}${subpath}`; - } -} diff --git a/src/lib/utils/SubpathUtil.ts b/src/lib/utils/SubpathUtil.ts new file mode 100644 index 00000000..7db3423d --- /dev/null +++ b/src/lib/utils/SubpathUtil.ts @@ -0,0 +1,47 @@ +/* + * Copyright (C) Contributors to the Suwayomi project + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +export class SubpathUtil { + public static getSubpath(): string | null { + const envSubpath = import.meta.env.VITE_SUBPATH; + if (envSubpath && envSubpath !== '/') { + return envSubpath; + } + + const serverSubpath = this.getServerSubpath(); + if (serverSubpath && serverSubpath !== '/') { + return serverSubpath; + } + + return null; + } + + private static getServerSubpath(): string | null { + const baseTag = document.querySelector('base'); + if (!baseTag) { + return null; + } + + return baseTag.href.replace(window.location.origin, '').slice(0, -1); + } + + public static getRouterBasename(): string | undefined { + return this.getSubpath() ?? undefined; + } + + public static getApiBaseUrl(serverUrl: string): string { + const baseUrl = serverUrl; + const subpath = this.getSubpath(); + + if (!subpath) { + return baseUrl; + } + + return `${baseUrl}${subpath}`; + } +} diff --git a/versionToServerVersionMapping.json b/versionToServerVersionMapping.json index 1420f395..aca7a2b0 100644 --- a/versionToServerVersionMapping.json +++ b/versionToServerVersionMapping.json @@ -1,7 +1,6 @@ [ { - "tag": "PREVIEW", - "uiVersion": "r2817", + "uiVersion": "PREVIEW", "serverVersion": "r1910" }, {