feat(frontend): support embedded API prefixes

This commit is contained in:
User
2026-08-22 16:29:56 +02:00
parent 3d02c5c1ef
commit 5c505d4c85
2 changed files with 22 additions and 4 deletions
+4
View File
@@ -15,6 +15,10 @@ describe("resolveBackendUrl", () => {
expect(backendBaseUrl).toBe("/api");
});
it("accepts a same-origin prefixed API base for embedded deployments", () => {
expect(resolveBackendUrl({ backendBaseUrl: "/embedded/api" })).toBe("/embedded/api");
});
it.each(["/backend", "api", "//evil.test", "ftp://example.test", "https://user:pass@example.test"])(
"rejects any browser-facing backend URL %j",
(backendBaseUrl) => {
+18 -4
View File
@@ -1,6 +1,18 @@
export function resolveBackendUrl(value?: string): string {
if (value === undefined || value === "/api") return "/api";
throw new Error("Invalid backend URL: the browser must use the same-origin /api route");
export interface RuntimeConfig {
backendBaseUrl?: string;
}
declare global {
interface Window {
__THOTHII_CONFIG__?: RuntimeConfig;
}
}
export function resolveBackendUrl(value?: string | RuntimeConfig): string {
const candidate = typeof value === "string" ? value : value?.backendBaseUrl;
if (candidate === undefined || candidate === "/api") return "/api";
if (/^\/(?:[A-Za-z0-9._~-]+\/)+api$/.test(candidate)) return candidate;
throw new Error("Invalid backend URL: the browser must use a same-origin API route");
}
export function joinBackendPath(base: string, path: string): string {
@@ -9,4 +21,6 @@ export function joinBackendPath(base: string, path: string): string {
return `${normalizedBase}/${normalizedPath}`;
}
export const backendBaseUrl = resolveBackendUrl();
export const backendBaseUrl = resolveBackendUrl(
typeof window === "undefined" ? undefined : window.__THOTHII_CONFIG__,
);