mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-05 02:41:56 +00:00
The App Router migration moved pages to deeper path segments and the proxy can be mounted under a sub-path (e.g. /litellm behind a reverse proxy). Local logo asset paths were emitted without the server root prefix, so they resolved off the origin root and 404'd. Route every local logo src through a single resolver that prefixes the live server root path and leaves external URLs untouched, fixing provider, guardrail, vector store, callback, MCP and audit-log logos at any route depth and root path.
60 lines
2.5 KiB
TypeScript
60 lines
2.5 KiB
TypeScript
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
|
|
import { withServerRoot } from "./assetPaths";
|
|
|
|
describe("withServerRoot", () => {
|
|
it("leaves a root-relative path unchanged at the default root", () => {
|
|
expect(withServerRoot("/ui/assets/logos/openai.svg", "/")).toBe("/ui/assets/logos/openai.svg");
|
|
expect(withServerRoot("/ui/assets/logos/openai.svg", "")).toBe("/ui/assets/logos/openai.svg");
|
|
});
|
|
|
|
it("prefixes a custom server root path", () => {
|
|
expect(withServerRoot("/ui/assets/logos/openai.svg", "/litellm")).toBe("/litellm/ui/assets/logos/openai.svg");
|
|
});
|
|
|
|
it("normalizes a trailing or missing slash in the root", () => {
|
|
expect(withServerRoot("/ui/assets/logos/x.svg", "/litellm/")).toBe("/litellm/ui/assets/logos/x.svg");
|
|
expect(withServerRoot("/ui/assets/logos/x.svg", "team-x")).toBe("/team-x/ui/assets/logos/x.svg");
|
|
});
|
|
|
|
it("guarantees a single leading slash on the path", () => {
|
|
expect(withServerRoot("ui/assets/logos/x.svg", "/litellm")).toBe("/litellm/ui/assets/logos/x.svg");
|
|
});
|
|
});
|
|
|
|
describe("resolveLogoSrc", () => {
|
|
afterEach(() => {
|
|
vi.resetModules();
|
|
vi.doUnmock("@/lib/serverRootPath");
|
|
});
|
|
|
|
const importWithRoot = async (root: string) => {
|
|
vi.resetModules();
|
|
vi.doMock("@/lib/serverRootPath", () => ({ serverRootPath: root }));
|
|
return import("./assetPaths");
|
|
};
|
|
|
|
it("returns undefined for empty values", async () => {
|
|
const { resolveLogoSrc } = await importWithRoot("/litellm");
|
|
expect(resolveLogoSrc(null)).toBeUndefined();
|
|
expect(resolveLogoSrc(undefined)).toBeUndefined();
|
|
expect(resolveLogoSrc("")).toBeUndefined();
|
|
});
|
|
|
|
it("passes external and inline sources through untouched", async () => {
|
|
const { resolveLogoSrc } = await importWithRoot("/litellm");
|
|
expect(resolveLogoSrc("https://www.zapier.com/logo.png")).toBe("https://www.zapier.com/logo.png");
|
|
expect(resolveLogoSrc("data:image/png;base64,AAAA")).toBe("data:image/png;base64,AAAA");
|
|
expect(resolveLogoSrc("//cdn.example.com/x.svg")).toBe("//cdn.example.com/x.svg");
|
|
});
|
|
|
|
it("roots a local asset path using the live server root path", async () => {
|
|
const { resolveLogoSrc } = await importWithRoot("/litellm");
|
|
expect(resolveLogoSrc("/ui/assets/logos/github.svg")).toBe("/litellm/ui/assets/logos/github.svg");
|
|
});
|
|
|
|
it("leaves a local asset path unchanged at the default root", async () => {
|
|
const { resolveLogoSrc } = await importWithRoot("/");
|
|
expect(resolveLogoSrc("/ui/assets/logos/github.svg")).toBe("/ui/assets/logos/github.svg");
|
|
});
|
|
});
|