Update server injected subpath detection
This commit is contained in:
@@ -19,7 +19,7 @@ import { AppPageHistoryContextProvider } from '@/base/contexts/AppPageHistoryCon
|
|||||||
import { AppThemeContextProvider } from '@/features/theme/AppThemeContext.tsx';
|
import { AppThemeContextProvider } from '@/features/theme/AppThemeContext.tsx';
|
||||||
import { NavBarContextProvider } from '@/features/navigation-bar/NavbarContext.tsx';
|
import { NavBarContextProvider } from '@/features/navigation-bar/NavbarContext.tsx';
|
||||||
import { SessionContextProvider } from '@/features/authentication/SessionContext.tsx';
|
import { SessionContextProvider } from '@/features/authentication/SessionContext.tsx';
|
||||||
import { SubpathConfig } from '@/lib/utils/SubpathConfig.ts';
|
import { SubpathUtil } from '@/lib/utils/SubpathUtil.ts';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
@@ -27,7 +27,7 @@ interface Props {
|
|||||||
|
|
||||||
export const AppContext: React.FC<Props> = ({ children }) => (
|
export const AppContext: React.FC<Props> = ({ children }) => (
|
||||||
<SessionContextProvider>
|
<SessionContextProvider>
|
||||||
<Router basename={SubpathConfig.getRouterBasename()}>
|
<Router basename={SubpathUtil.getRouterBasename()}>
|
||||||
<StyledEngineProvider injectFirst>
|
<StyledEngineProvider injectFirst>
|
||||||
<AppThemeContextProvider>
|
<AppThemeContextProvider>
|
||||||
<QueryParamProvider adapter={ReactRouter6Adapter}>
|
<QueryParamProvider adapter={ReactRouter6Adapter}>
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import { use } from 'i18next';
|
|||||||
import { initReactI18next } from 'react-i18next';
|
import { initReactI18next } from 'react-i18next';
|
||||||
import LanguageDetector from 'i18next-browser-languagedetector';
|
import LanguageDetector from 'i18next-browser-languagedetector';
|
||||||
import HttpBackend from 'i18next-http-backend';
|
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}.<br/>
|
* Keys have to match {@link IsoLanguages} codes, they're used for showing the language name in the dropdown in the {@link Settings}.<br/>
|
||||||
@@ -46,7 +46,7 @@ export const i18n = use(initReactI18next)
|
|||||||
fallbackLng: 'en',
|
fallbackLng: 'en',
|
||||||
|
|
||||||
backend: {
|
backend: {
|
||||||
loadPath: `${SubpathConfig.getSubpath()}/locales/{{lng}}.json`,
|
loadPath: `${SubpathUtil.getSubpath()}/locales/{{lng}}.json`,
|
||||||
allowMultiLoading: true,
|
allowMultiLoading: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import { AppStorage } from '@/lib/storage/AppStorage.ts';
|
|||||||
import { UserRefreshMutation } from '@/lib/graphql/generated/graphql.ts';
|
import { UserRefreshMutation } from '@/lib/graphql/generated/graphql.ts';
|
||||||
import { AuthManager } from '@/features/authentication/AuthManager.ts';
|
import { AuthManager } from '@/features/authentication/AuthManager.ts';
|
||||||
import { AbortableApolloMutationResponse } from '@/lib/requests/RequestManager.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<Client, ClientConfig, Fetcher> {
|
export abstract class BaseClient<Client, ClientConfig, Fetcher> {
|
||||||
protected abstract client: Client;
|
protected abstract client: Client;
|
||||||
@@ -72,7 +72,7 @@ export abstract class BaseClient<Client, ClientConfig, Fetcher> {
|
|||||||
const serverBaseURL = AppStorage.local.getItemParsed('serverBaseURL', defaultUrl);
|
const serverBaseURL = AppStorage.local.getItemParsed('serverBaseURL', defaultUrl);
|
||||||
|
|
||||||
// Apply subpath configuration to the base URL
|
// Apply subpath configuration to the base URL
|
||||||
return SubpathConfig.getApiBaseUrl(serverBaseURL);
|
return SubpathUtil.getApiBaseUrl(serverBaseURL);
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract updateConfig(config: Partial<ClientConfig>): void;
|
public abstract updateConfig(config: Partial<ClientConfig>): void;
|
||||||
|
|||||||
@@ -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}`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
47
src/lib/utils/SubpathUtil.ts
Normal file
47
src/lib/utils/SubpathUtil.ts
Normal file
@@ -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}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"tag": "PREVIEW",
|
"uiVersion": "PREVIEW",
|
||||||
"uiVersion": "r2817",
|
|
||||||
"serverVersion": "r1910"
|
"serverVersion": "r1910"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user