mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-09 22:31:41 +00:00
next routing, feature flagged via sidebar
This commit is contained in:
parent
7ed4715a9b
commit
2b0df22dc5
29 changed files with 1012 additions and 399 deletions
|
|
@ -0,0 +1,17 @@
|
|||
"use client";
|
||||
|
||||
import APIRef from "@/components/api_ref";
|
||||
import { useState } from "react";
|
||||
|
||||
interface ProxySettings {
|
||||
PROXY_BASE_URL: string;
|
||||
PROXY_LOGOUT_URL: string;
|
||||
}
|
||||
|
||||
const APIReferencePage = () => {
|
||||
const [proxySettings, setProxySettings] = useState<ProxySettings>({ PROXY_BASE_URL: "", PROXY_LOGOUT_URL: "" });
|
||||
|
||||
return <APIRef proxySettings={proxySettings} />;
|
||||
};
|
||||
|
||||
export default APIReferencePage;
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
import { Layout, Menu } from "antd";
|
||||
import { usePathname } from "next/navigation";
|
||||
"use client";
|
||||
|
||||
import { Layout, Menu, ConfigProvider } from "antd";
|
||||
import {
|
||||
KeyOutlined,
|
||||
PlayCircleOutlined,
|
||||
|
|
@ -18,126 +19,191 @@ import {
|
|||
ExperimentOutlined,
|
||||
ToolOutlined,
|
||||
TagsOutlined,
|
||||
BgColorsOutlined,
|
||||
} from "@ant-design/icons";
|
||||
import { ConfigProvider } from "antd";
|
||||
import useFeatureFlags from "@/hooks/useFeatureFlags";
|
||||
// import {
|
||||
// all_admin_roles,
|
||||
// rolesWithWriteAccess,
|
||||
// internalUserRoles,
|
||||
// isAdminRole,
|
||||
// } from "../utils/roles";
|
||||
// import UsageIndicator from "./usage_indicator";
|
||||
import * as React from "react";
|
||||
import { useRouter, usePathname } from "next/navigation";
|
||||
import { all_admin_roles, internalUserRoles, isAdminRole, rolesWithWriteAccess } from "@/utils/roles";
|
||||
import UsageIndicator from "@/components/usage_indicator";
|
||||
import React from "react";
|
||||
|
||||
const { Sider } = Layout;
|
||||
|
||||
// -------- Types --------
|
||||
interface SidebarProps {
|
||||
accessToken: string | null;
|
||||
setPage: (page: string) => void;
|
||||
userRole: string;
|
||||
/** Fallback selection id (legacy), used if path can't be matched */
|
||||
defaultSelectedKey: string;
|
||||
collapsed?: boolean;
|
||||
}
|
||||
|
||||
interface MenuItem {
|
||||
interface MenuItemCfg {
|
||||
key: string;
|
||||
page: string;
|
||||
page: string; // legacy id; we map this to a path below
|
||||
label: string;
|
||||
roles?: string[];
|
||||
children?: MenuItem[];
|
||||
children?: MenuItemCfg[];
|
||||
icon?: React.ReactNode;
|
||||
}
|
||||
|
||||
/** ---- BASE URL HELPERS ---- */
|
||||
function normalizeBasePrefix(raw: string | undefined | null): string {
|
||||
const trimmed = (raw ?? "").trim();
|
||||
if (!trimmed) return ""; // no base
|
||||
const core = trimmed.replace(/^\/+/, "").replace(/\/+$/, "");
|
||||
return core ? `/${core}` : "";
|
||||
}
|
||||
const BASE_PREFIX = normalizeBasePrefix(process.env.NEXT_PUBLIC_BASE_URL);
|
||||
/** ---------- Base URL helpers ---------- */
|
||||
/**
|
||||
* Normalizes NEXT_PUBLIC_BASE_URL to either "/" or "/ui/" (always with a trailing slash).
|
||||
* Supported env values: "" or "ui/".
|
||||
*/
|
||||
const getBasePath = () => {
|
||||
const raw = process.env.NEXT_PUBLIC_BASE_URL ?? "";
|
||||
const trimmed = raw.replace(/^\/+|\/+$/g, ""); // strip leading/trailing slashes
|
||||
return trimmed ? `/${trimmed}/` : "/"; // ensure trailing slash
|
||||
};
|
||||
|
||||
/** Build an absolute path under the configured base. */
|
||||
function withBase(path: string): string {
|
||||
// path can be "/virtual-keys" or "/?page=..."
|
||||
const p = path.startsWith("/") ? path : `/${path}`;
|
||||
return `${BASE_PREFIX}${p}` || p;
|
||||
}
|
||||
/** Map legacy `page` ids to real app routes (relative, no leading slash). */
|
||||
const routeFor = (slug: string): string => {
|
||||
switch (slug) {
|
||||
// top level
|
||||
case "api-keys":
|
||||
return "virtual-keys";
|
||||
case "llm-playground":
|
||||
return "test-key";
|
||||
case "models":
|
||||
return "models-and-endpoints";
|
||||
case "new_usage":
|
||||
return "usage";
|
||||
case "teams":
|
||||
return "teams";
|
||||
case "organizations":
|
||||
return "organizations";
|
||||
case "users":
|
||||
return "users";
|
||||
case "api_ref":
|
||||
return "api-reference";
|
||||
case "model-hub-table":
|
||||
// If you intend the newer in-dashboard page, use "model-hub".
|
||||
return "model-hub";
|
||||
case "logs":
|
||||
return "logs";
|
||||
case "guardrails":
|
||||
return "guardrails";
|
||||
|
||||
/** History-based navigation to prevent hard reloads / suspense flashes */
|
||||
function softNavigate(url: string, replace = false) {
|
||||
if (typeof window === "undefined") return;
|
||||
if (replace) window.history.replaceState(null, "", url);
|
||||
else window.history.pushState(null, "", url);
|
||||
}
|
||||
/** -------------------------------- */
|
||||
// tools
|
||||
case "mcp-servers":
|
||||
return "tools/mcp-servers";
|
||||
case "vector-stores":
|
||||
return "tools/vector-stores";
|
||||
|
||||
const Sidebar2: React.FC<SidebarProps> = ({
|
||||
accessToken,
|
||||
setPage,
|
||||
userRole,
|
||||
defaultSelectedKey,
|
||||
collapsed = false,
|
||||
}) => {
|
||||
const pathname = usePathname();
|
||||
const { refactoredUIFlag } = useFeatureFlags();
|
||||
// experimental
|
||||
case "caching":
|
||||
return "experimental/caching";
|
||||
case "prompts":
|
||||
return "experimental/prompts";
|
||||
case "budgets":
|
||||
return "experimental/budgets";
|
||||
case "transform-request":
|
||||
return "experimental/api-playground";
|
||||
case "tag-management":
|
||||
return "experimental/tag-management";
|
||||
case "usage": // "Old Usage"
|
||||
return "experimental/old-usage";
|
||||
|
||||
const menuItems: MenuItem[] = [
|
||||
{ key: "1", page: "api-keys", label: "Virtual Keys", icon: <KeyOutlined style={{ fontSize: "18px" }} /> },
|
||||
// settings
|
||||
case "general-settings":
|
||||
return "settings/router-settings";
|
||||
case "settings": // "Logging & Alerts"
|
||||
return "settings/logging-and-alerts";
|
||||
case "admin-panel":
|
||||
return "settings/admin-settings";
|
||||
case "ui-theme":
|
||||
return "settings/ui-theme";
|
||||
|
||||
default:
|
||||
// treat as already a relative path
|
||||
return slug.replace(/^\/+/, "");
|
||||
}
|
||||
};
|
||||
|
||||
/** Prefix base path ("/" or "/ui/") */
|
||||
const toHref = (slugOrPath: string) => {
|
||||
const base = getBasePath(); // "/" or "/ui/"
|
||||
const rel = routeFor(slugOrPath).replace(/^\/+|\/+$/g, "");
|
||||
return `${base}${rel}`;
|
||||
};
|
||||
|
||||
const Sidebar2: React.FC<SidebarProps> = ({ accessToken, userRole, defaultSelectedKey, collapsed = false }) => {
|
||||
const router = useRouter();
|
||||
const pathname = usePathname() || "/";
|
||||
|
||||
// ----- Menu config (unchanged labels/icons; same appearance) -----
|
||||
const menuItems: MenuItemCfg[] = [
|
||||
{ key: "1", page: "api-keys", label: "Virtual Keys", icon: <KeyOutlined style={{ fontSize: 18 }} /> },
|
||||
{
|
||||
key: "3",
|
||||
page: "llm-playground",
|
||||
label: "Test Key",
|
||||
icon: <PlayCircleOutlined style={{ fontSize: "18px" }} />,
|
||||
icon: <PlayCircleOutlined style={{ fontSize: 18 }} />,
|
||||
roles: rolesWithWriteAccess,
|
||||
},
|
||||
{
|
||||
key: "2",
|
||||
page: "models",
|
||||
label: "Models + Endpoints",
|
||||
icon: <BlockOutlined style={{ fontSize: "18px" }} />,
|
||||
icon: <BlockOutlined style={{ fontSize: 18 }} />,
|
||||
roles: rolesWithWriteAccess,
|
||||
},
|
||||
{
|
||||
key: "12",
|
||||
page: "new_usage",
|
||||
label: "Usage",
|
||||
icon: <BarChartOutlined style={{ fontSize: "18px" }} />,
|
||||
icon: <BarChartOutlined style={{ fontSize: 18 }} />,
|
||||
roles: [...all_admin_roles, ...internalUserRoles],
|
||||
},
|
||||
{ key: "6", page: "teams", label: "Teams", icon: <TeamOutlined style={{ fontSize: "18px" }} /> },
|
||||
{ key: "6", page: "teams", label: "Teams", icon: <TeamOutlined style={{ fontSize: 18 }} /> },
|
||||
{
|
||||
key: "17",
|
||||
page: "organizations",
|
||||
label: "Organizations",
|
||||
icon: <BankOutlined style={{ fontSize: "18px" }} />,
|
||||
icon: <BankOutlined style={{ fontSize: 18 }} />,
|
||||
roles: all_admin_roles,
|
||||
},
|
||||
{
|
||||
key: "5",
|
||||
page: "users",
|
||||
label: "Internal Users",
|
||||
icon: <UserOutlined style={{ fontSize: "18px" }} />,
|
||||
icon: <UserOutlined style={{ fontSize: 18 }} />,
|
||||
roles: all_admin_roles,
|
||||
},
|
||||
{ key: "14", page: "api_ref", label: "API Reference", icon: <ApiOutlined style={{ fontSize: "18px" }} /> },
|
||||
{ key: "16", page: "model-hub-table", label: "Model Hub", icon: <AppstoreOutlined style={{ fontSize: "18px" }} /> },
|
||||
{ key: "15", page: "logs", label: "Logs", icon: <LineChartOutlined style={{ fontSize: "18px" }} /> },
|
||||
{ key: "14", page: "api_ref", label: "API Reference", icon: <ApiOutlined style={{ fontSize: 18 }} /> },
|
||||
{
|
||||
key: "16",
|
||||
page: "model-hub-table",
|
||||
label: "Model Hub",
|
||||
icon: <AppstoreOutlined style={{ fontSize: 18 }} />,
|
||||
},
|
||||
{ key: "15", page: "logs", label: "Logs", icon: <LineChartOutlined style={{ fontSize: 18 }} /> },
|
||||
{
|
||||
key: "11",
|
||||
page: "guardrails",
|
||||
label: "Guardrails",
|
||||
icon: <SafetyOutlined style={{ fontSize: "18px" }} />,
|
||||
icon: <SafetyOutlined style={{ fontSize: 18 }} />,
|
||||
roles: all_admin_roles,
|
||||
},
|
||||
{
|
||||
key: "26",
|
||||
page: "tools",
|
||||
label: "Tools",
|
||||
icon: <ToolOutlined style={{ fontSize: "18px" }} />,
|
||||
icon: <ToolOutlined style={{ fontSize: 18 }} />,
|
||||
children: [
|
||||
{ key: "18", page: "mcp-servers", label: "MCP Servers", icon: <ToolOutlined style={{ fontSize: "18px" }} /> },
|
||||
{ key: "18", page: "mcp-servers", label: "MCP Servers", icon: <ToolOutlined style={{ fontSize: 18 }} /> },
|
||||
{
|
||||
key: "21",
|
||||
page: "vector-stores",
|
||||
label: "Vector Stores",
|
||||
icon: <DatabaseOutlined style={{ fontSize: "18px" }} />,
|
||||
icon: <DatabaseOutlined style={{ fontSize: 18 }} />,
|
||||
roles: all_admin_roles,
|
||||
},
|
||||
],
|
||||
|
|
@ -146,136 +212,135 @@ const Sidebar2: React.FC<SidebarProps> = ({
|
|||
key: "experimental",
|
||||
page: "experimental",
|
||||
label: "Experimental",
|
||||
icon: <ExperimentOutlined style={{ fontSize: "18px" }} />,
|
||||
icon: <ExperimentOutlined style={{ fontSize: 18 }} />,
|
||||
children: [
|
||||
{
|
||||
key: "9",
|
||||
page: "caching",
|
||||
label: "Caching",
|
||||
icon: <DatabaseOutlined style={{ fontSize: "18px" }} />,
|
||||
icon: <DatabaseOutlined style={{ fontSize: 18 }} />,
|
||||
roles: all_admin_roles,
|
||||
},
|
||||
{
|
||||
key: "25",
|
||||
page: "prompts",
|
||||
label: "Prompts",
|
||||
icon: <FileTextOutlined style={{ fontSize: "18px" }} />,
|
||||
icon: <FileTextOutlined style={{ fontSize: 18 }} />,
|
||||
roles: all_admin_roles,
|
||||
},
|
||||
{
|
||||
key: "10",
|
||||
page: "budgets",
|
||||
label: "Budgets",
|
||||
icon: <BankOutlined style={{ fontSize: "18px" }} />,
|
||||
icon: <BankOutlined style={{ fontSize: 18 }} />,
|
||||
roles: all_admin_roles,
|
||||
},
|
||||
{
|
||||
key: "20",
|
||||
page: "transform-request",
|
||||
label: "API Playground",
|
||||
icon: <ApiOutlined style={{ fontSize: "18px" }} />,
|
||||
icon: <ApiOutlined style={{ fontSize: 18 }} />,
|
||||
roles: [...all_admin_roles, ...internalUserRoles],
|
||||
},
|
||||
{
|
||||
key: "19",
|
||||
page: "tag-management",
|
||||
label: "Tag Management",
|
||||
icon: <TagsOutlined style={{ fontSize: "18px" }} />,
|
||||
icon: <TagsOutlined style={{ fontSize: 18 }} />,
|
||||
roles: all_admin_roles,
|
||||
},
|
||||
{ key: "4", page: "usage", label: "Old Usage", icon: <BarChartOutlined style={{ fontSize: "18px" }} /> },
|
||||
{ key: "4", page: "usage", label: "Old Usage", icon: <BarChartOutlined style={{ fontSize: 18 }} /> },
|
||||
],
|
||||
},
|
||||
{
|
||||
key: "settings",
|
||||
page: "settings",
|
||||
label: "Settings",
|
||||
icon: <SettingOutlined style={{ fontSize: "18px" }} />,
|
||||
icon: <SettingOutlined style={{ fontSize: 18 }} />,
|
||||
roles: all_admin_roles,
|
||||
children: [
|
||||
{
|
||||
key: "11",
|
||||
page: "general-settings",
|
||||
label: "Router Settings",
|
||||
icon: <SettingOutlined style={{ fontSize: "18px" }} />,
|
||||
icon: <SettingOutlined style={{ fontSize: 18 }} />,
|
||||
roles: all_admin_roles,
|
||||
},
|
||||
{
|
||||
key: "8",
|
||||
page: "settings",
|
||||
label: "Logging & Alerts",
|
||||
icon: <SettingOutlined style={{ fontSize: "18px" }} />,
|
||||
icon: <SettingOutlined style={{ fontSize: 18 }} />,
|
||||
roles: all_admin_roles,
|
||||
},
|
||||
{
|
||||
key: "13",
|
||||
page: "admin-panel",
|
||||
label: "Admin Settings",
|
||||
icon: <SettingOutlined style={{ fontSize: "18px" }} />,
|
||||
icon: <SettingOutlined style={{ fontSize: 18 }} />,
|
||||
roles: all_admin_roles,
|
||||
},
|
||||
{
|
||||
key: "14",
|
||||
page: "ui-theme",
|
||||
label: "UI Theme",
|
||||
icon: <BgColorsOutlined style={{ fontSize: "18px" }} />,
|
||||
icon: <SettingOutlined style={{ fontSize: 18 }} />,
|
||||
roles: all_admin_roles,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const findMenuItemKey = (page: string): string => {
|
||||
const topLevelItem = menuItems.find((item) => item.page === page);
|
||||
if (topLevelItem) return topLevelItem.key;
|
||||
for (const item of menuItems) {
|
||||
// ----- Filter by role without mutating originals -----
|
||||
const filteredMenuItems = React.useMemo<MenuItemCfg[]>(() => {
|
||||
return menuItems
|
||||
.filter((item) => !item.roles || item.roles.includes(userRole))
|
||||
.map((item) => ({
|
||||
...item,
|
||||
children: item.children ? item.children.filter((c) => !c.roles || c.roles.includes(userRole)) : undefined,
|
||||
}));
|
||||
}, [userRole]);
|
||||
|
||||
// ----- Compute selected key from current path -----
|
||||
const selectedMenuKey = React.useMemo(() => {
|
||||
const base = getBasePath();
|
||||
// strip base prefix and leading slash -> "virtual-keys", "tools/mcp-servers", etc.
|
||||
const rel = pathname.startsWith(base) ? pathname.slice(base.length) : pathname.replace(/^\/+/, "");
|
||||
const relLower = rel.toLowerCase();
|
||||
|
||||
const matchesPath = (slug: string) => {
|
||||
const route = routeFor(slug).toLowerCase();
|
||||
return relLower === route || relLower.startsWith(`${route}/`);
|
||||
};
|
||||
|
||||
// search top-level
|
||||
for (const item of filteredMenuItems) {
|
||||
if (!item.children && matchesPath(item.page)) return item.key;
|
||||
if (item.children) {
|
||||
const childItem = item.children.find((child) => child.page === page);
|
||||
if (childItem) return childItem.key;
|
||||
for (const child of item.children) {
|
||||
if (matchesPath(child.page)) return child.key;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// fallback to legacy defaultSelectedKey mapping
|
||||
const fallback = filteredMenuItems.find((i) => i.page === defaultSelectedKey)?.key;
|
||||
if (fallback) return fallback;
|
||||
|
||||
for (const item of filteredMenuItems) {
|
||||
if (item.children?.some((c) => c.page === defaultSelectedKey)) {
|
||||
const child = item.children.find((c) => c.page === defaultSelectedKey)!;
|
||||
return child.key;
|
||||
}
|
||||
}
|
||||
|
||||
return "1";
|
||||
};
|
||||
}, [pathname, filteredMenuItems, defaultSelectedKey]);
|
||||
|
||||
const selectedMenuKey = findMenuItemKey(defaultSelectedKey);
|
||||
|
||||
const filteredMenuItems = menuItems.filter((item) => {
|
||||
const hasParentAccess = !item.roles || item.roles.includes(userRole);
|
||||
if (!hasParentAccess) return false;
|
||||
if (item.children) {
|
||||
item.children = item.children.filter((child) => !child.roles || child.roles.includes(userRole));
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
// Helper: update /?page=... under the configured base, WITHOUT triggering App Router nav
|
||||
const pushToRootWithPage = (page: string, useReplace = false) => {
|
||||
const params = new URLSearchParams();
|
||||
params.set("page", page);
|
||||
const url = withBase(`/?${params.toString()}`);
|
||||
softNavigate(url, useReplace);
|
||||
};
|
||||
|
||||
const navigateToPage = (page: string) => {
|
||||
if (page === "api-keys") {
|
||||
if (refactoredUIFlag) {
|
||||
// vanity URL, keep SPA alive
|
||||
softNavigate(withBase("/virtual-keys"));
|
||||
return; // don't call setPage to keep parity, UI already shows api-keys by default
|
||||
}
|
||||
pushToRootWithPage(page);
|
||||
setPage(page);
|
||||
return;
|
||||
}
|
||||
|
||||
if (refactoredUIFlag) {
|
||||
const onVirtualKeys =
|
||||
typeof window !== "undefined" && window.location.pathname.startsWith(withBase("/virtual-keys"));
|
||||
pushToRootWithPage(page, onVirtualKeys);
|
||||
} else {
|
||||
pushToRootWithPage(page);
|
||||
}
|
||||
setPage(page);
|
||||
// ----- Navigation -----
|
||||
const goTo = (slug: string) => {
|
||||
const href = toHref(slug);
|
||||
router.push(href);
|
||||
};
|
||||
|
||||
return (
|
||||
|
|
@ -287,16 +352,32 @@ const Sidebar2: React.FC<SidebarProps> = ({
|
|||
collapsedWidth={80}
|
||||
collapsible
|
||||
trigger={null}
|
||||
style={{ transition: "all 0.3s cubic-bezier(0.4, 0, 0.2, 1)", position: "relative" }}
|
||||
style={{
|
||||
transition: "all 0.3s cubic-bezier(0.4, 0, 0.2, 1)",
|
||||
position: "relative",
|
||||
}}
|
||||
>
|
||||
<ConfigProvider theme={{ components: { Menu: { iconSize: 18, fontSize: 14 } } }}>
|
||||
<ConfigProvider
|
||||
theme={{
|
||||
components: {
|
||||
Menu: {
|
||||
iconSize: 18,
|
||||
fontSize: 14,
|
||||
},
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Menu
|
||||
mode="inline"
|
||||
selectedKeys={[selectedMenuKey]}
|
||||
defaultOpenKeys={collapsed ? [] : ["llm-tools"]}
|
||||
defaultOpenKeys={collapsed ? [] : ["llm-tools"]} // kept to preserve original appearance
|
||||
inlineCollapsed={collapsed}
|
||||
className="custom-sidebar-menu"
|
||||
style={{ borderRight: 0, backgroundColor: "transparent", fontSize: "14px" }}
|
||||
style={{
|
||||
borderRight: 0,
|
||||
backgroundColor: "transparent",
|
||||
fontSize: "14px",
|
||||
}}
|
||||
items={filteredMenuItems.map((item) => ({
|
||||
key: item.key,
|
||||
icon: item.icon,
|
||||
|
|
@ -305,9 +386,9 @@ const Sidebar2: React.FC<SidebarProps> = ({
|
|||
key: child.key,
|
||||
icon: child.icon,
|
||||
label: child.label,
|
||||
onClick: () => navigateToPage(child.page),
|
||||
onClick: () => goTo(child.page),
|
||||
})),
|
||||
onClick: !item.children ? () => navigateToPage(item.page) : undefined,
|
||||
onClick: !item.children ? () => goTo(item.page) : undefined,
|
||||
}))}
|
||||
/>
|
||||
</ConfigProvider>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,29 @@
|
|||
import useFeatureFlags from "@/hooks/useFeatureFlags";
|
||||
import Sidebar from "@/components/leftnav";
|
||||
import Sidebar2 from "@/app/(dashboard)/components/Sidebar2";
|
||||
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
|
||||
|
||||
interface SidebarProviderProps {
|
||||
defaultSelectedKey: string;
|
||||
setPage: (newPage: string) => void;
|
||||
sidebarCollapsed: boolean;
|
||||
}
|
||||
|
||||
const SidebarProvider = ({ setPage, defaultSelectedKey, sidebarCollapsed }: SidebarProviderProps) => {
|
||||
const { refactoredUIFlag } = useFeatureFlags();
|
||||
const { accessToken, userRole } = useAuthorized();
|
||||
|
||||
return refactoredUIFlag ? (
|
||||
<Sidebar2 accessToken={accessToken} defaultSelectedKey={defaultSelectedKey} userRole={userRole} />
|
||||
) : (
|
||||
<Sidebar
|
||||
accessToken={accessToken}
|
||||
setPage={setPage}
|
||||
userRole={userRole}
|
||||
defaultSelectedKey={defaultSelectedKey}
|
||||
collapsed={sidebarCollapsed}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default SidebarProvider;
|
||||
|
|
@ -70,7 +70,7 @@ const CreateUserModal: React.FC<CreateUserModalProps> = ({ possibleUIRoles, onUs
|
|||
const [isInvitationLinkModalVisible, setIsInvitationLinkModalVisible] = useState(false);
|
||||
const [invitationLinkData, setInvitationLinkData] = useState<InvitationLink | null>(null);
|
||||
const [baseUrl, setBaseUrl] = useState<string | null>(null);
|
||||
const teams = useTeams();
|
||||
const { teams } = useTeams();
|
||||
|
||||
// get all models
|
||||
useEffect(() => {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,12 @@
|
|||
"use client";
|
||||
|
||||
import TransformRequestPanel from "@/components/transform_request";
|
||||
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
|
||||
|
||||
const APIPlaygroundPage = () => {
|
||||
const { accessToken } = useAuthorized();
|
||||
|
||||
return <TransformRequestPanel accessToken={accessToken} />;
|
||||
};
|
||||
|
||||
export default APIPlaygroundPage;
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
"use client";
|
||||
|
||||
import BudgetPanel from "@/components/budgets/budget_panel";
|
||||
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
|
||||
|
||||
const BudgetsPage = () => {
|
||||
const { accessToken } = useAuthorized();
|
||||
|
||||
return <BudgetPanel accessToken={accessToken} />;
|
||||
};
|
||||
|
||||
export default BudgetsPage;
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
"use client";
|
||||
|
||||
import CacheDashboard from "@/components/cache_dashboard";
|
||||
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
|
||||
|
||||
const CachingPage = () => {
|
||||
const { token, accessToken, userRole, userId, premiumUser } = useAuthorized();
|
||||
|
||||
return (
|
||||
<CacheDashboard
|
||||
accessToken={accessToken}
|
||||
token={token}
|
||||
userRole={userRole}
|
||||
userID={userId}
|
||||
premiumUser={premiumUser}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default CachingPage;
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
"use client";
|
||||
|
||||
import Usage from "@/components/usage";
|
||||
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
|
||||
import { useState } from "react";
|
||||
|
||||
const OldUsagePage = () => {
|
||||
const { accessToken, token, userRole, userId, premiumUser } = useAuthorized();
|
||||
const [keys, setKeys] = useState<null | any[]>([]);
|
||||
|
||||
return (
|
||||
<Usage
|
||||
accessToken={accessToken}
|
||||
token={token}
|
||||
userRole={userRole}
|
||||
userID={userId}
|
||||
keys={keys}
|
||||
premiumUser={premiumUser}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default OldUsagePage;
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
"use client";
|
||||
|
||||
import PromptsPanel from "@/components/prompts";
|
||||
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
|
||||
|
||||
const PromptsPage = () => {
|
||||
const { accessToken } = useAuthorized();
|
||||
|
||||
return <PromptsPanel accessToken={accessToken} />;
|
||||
};
|
||||
|
||||
export default PromptsPage;
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
"use client";
|
||||
|
||||
import TagManagement from "@/components/tag_management";
|
||||
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
|
||||
|
||||
const TagManagementPage = () => {
|
||||
const { accessToken, userId, userRole } = useAuthorized();
|
||||
|
||||
return <TagManagement accessToken={accessToken} userID={userId} userRole={userRole} />;
|
||||
};
|
||||
|
||||
export default TagManagementPage;
|
||||
12
ui/litellm-dashboard/src/app/(dashboard)/guardrails/page.tsx
Normal file
12
ui/litellm-dashboard/src/app/(dashboard)/guardrails/page.tsx
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
"use client";
|
||||
|
||||
import GuardrailsPanel from "@/components/guardrails";
|
||||
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
|
||||
|
||||
const GuardrailsPage = () => {
|
||||
const { accessToken } = useAuthorized();
|
||||
|
||||
return <GuardrailsPanel accessToken={accessToken} />;
|
||||
};
|
||||
|
||||
export default GuardrailsPage;
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
"use client";
|
||||
|
||||
import { useEffect, useMemo } from "react";
|
||||
import { getCookie, clearTokenCookies } from "@/utils/cookieUtils";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { jwtDecode } from "jwt-decode";
|
||||
import { clearTokenCookies, getCookie } from "@/utils/cookieUtils";
|
||||
|
||||
const useAuthorized = () => {
|
||||
const router = useRouter();
|
||||
|
|
@ -31,10 +31,13 @@ const useAuthorized = () => {
|
|||
}, [token, router]);
|
||||
|
||||
return {
|
||||
token: token,
|
||||
accessToken: decoded?.key ?? null,
|
||||
userId: decoded?.user_id ?? null,
|
||||
userRole: decoded?.user_role ?? null,
|
||||
premiumUser: decoded?.premium_user ?? null,
|
||||
disabledPersonalKeyCreation: decoded?.disabled_non_admin_personal_key_creation ?? null,
|
||||
showSSOBanner: decoded?.login_method === "username_password" ?? false,
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
|||
28
ui/litellm-dashboard/src/app/(dashboard)/logs/page.tsx
Normal file
28
ui/litellm-dashboard/src/app/(dashboard)/logs/page.tsx
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
"use client";
|
||||
|
||||
import SpendLogsTable from "@/components/view_logs";
|
||||
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
|
||||
import useTeams from "@/app/(dashboard)/virtual-keys/hooks/useTeams";
|
||||
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||||
|
||||
const LogsPage = () => {
|
||||
const { accessToken, token, userRole, userId, premiumUser } = useAuthorized();
|
||||
const { teams } = useTeams();
|
||||
|
||||
const queryClient = new QueryClient();
|
||||
|
||||
return (
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<SpendLogsTable
|
||||
accessToken={accessToken}
|
||||
token={token}
|
||||
userRole={userRole}
|
||||
userID={userId}
|
||||
allTeams={teams || []}
|
||||
premiumUser={premiumUser}
|
||||
/>
|
||||
</QueryClientProvider>
|
||||
);
|
||||
};
|
||||
|
||||
export default LogsPage;
|
||||
12
ui/litellm-dashboard/src/app/(dashboard)/model-hub/page.tsx
Normal file
12
ui/litellm-dashboard/src/app/(dashboard)/model-hub/page.tsx
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
"use client";
|
||||
|
||||
import ModelHubTable from "@/components/model_hub_table";
|
||||
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
|
||||
|
||||
const ModelHubPage = () => {
|
||||
const { accessToken, premiumUser, userRole } = useAuthorized();
|
||||
|
||||
return <ModelHubTable accessToken={accessToken} publicPage={false} premiumUser={premiumUser} userRole={userRole} />;
|
||||
};
|
||||
|
||||
export default ModelHubPage;
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
"use client";
|
||||
|
||||
import ModelDashboard from "@/components/templates/model_dashboard";
|
||||
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
|
||||
import useTeams from "@/app/(dashboard)/virtual-keys/hooks/useTeams";
|
||||
import { useState } from "react";
|
||||
|
||||
const ModelsAndEndpointsPage = () => {
|
||||
const { token, accessToken, userRole, userId, premiumUser } = useAuthorized();
|
||||
const [keys, setKeys] = useState<null | any[]>([]);
|
||||
|
||||
const { teams } = useTeams();
|
||||
|
||||
return (
|
||||
<ModelDashboard
|
||||
accessToken={accessToken}
|
||||
token={token}
|
||||
userRole={userRole}
|
||||
userID={userId}
|
||||
modelData={{ data: [] }}
|
||||
keys={keys}
|
||||
setModelData={() => {}}
|
||||
premiumUser={premiumUser}
|
||||
teams={teams}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default ModelsAndEndpointsPage;
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
"use client";
|
||||
|
||||
import Organizations, { fetchOrganizations } from "@/components/organizations";
|
||||
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
|
||||
import { useEffect, useState } from "react";
|
||||
import { Organization } from "@/components/networking";
|
||||
import { fetchUserModels } from "@/components/organisms/create_key_button";
|
||||
|
||||
const OrganizationsPage = () => {
|
||||
const { userId: userID, accessToken, userRole, premiumUser } = useAuthorized();
|
||||
const [organizations, setOrganizations] = useState<Organization[]>([]);
|
||||
const [userModels, setUserModels] = useState<string[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
fetchOrganizations(accessToken, setOrganizations).then(() => {});
|
||||
}, [accessToken]);
|
||||
|
||||
useEffect(() => {
|
||||
fetchUserModels(userID, userRole, accessToken, setUserModels).then(() => {});
|
||||
}, [userID, userRole, accessToken]);
|
||||
|
||||
return (
|
||||
<Organizations
|
||||
organizations={organizations}
|
||||
userRole={userRole}
|
||||
userModels={userModels}
|
||||
accessToken={accessToken}
|
||||
setOrganizations={setOrganizations}
|
||||
premiumUser={premiumUser}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default OrganizationsPage;
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
"use client";
|
||||
|
||||
import AdminPanel from "@/components/admins";
|
||||
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
|
||||
import { useState } from "react";
|
||||
import { Team } from "@/components/key_team_helpers/key_list";
|
||||
import useTeams from "@/app/(dashboard)/virtual-keys/hooks/useTeams";
|
||||
|
||||
const AdminSettings = () => {
|
||||
const { teams, setTeams } = useTeams();
|
||||
|
||||
const [searchParams, setSearchParams] = useState<URLSearchParams>(() =>
|
||||
typeof window === "undefined" ? new URLSearchParams() : new URLSearchParams(window.location.search),
|
||||
);
|
||||
const { accessToken, userId, premiumUser, showSSOBanner } = useAuthorized();
|
||||
|
||||
return (
|
||||
<AdminPanel
|
||||
searchParams={searchParams}
|
||||
accessToken={accessToken}
|
||||
userID={userId}
|
||||
setTeams={setTeams}
|
||||
showSSOBanner={showSSOBanner}
|
||||
premiumUser={premiumUser}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default AdminSettings;
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
"use client";
|
||||
|
||||
import Settings from "@/components/settings";
|
||||
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
|
||||
|
||||
const LoggingAndAlertsPage = () => {
|
||||
const { accessToken, userRole, userId, premiumUser } = useAuthorized();
|
||||
|
||||
return <Settings accessToken={accessToken} userRole={userRole} userID={userId} premiumUser={premiumUser} />;
|
||||
};
|
||||
|
||||
export default LoggingAndAlertsPage;
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
"use client";
|
||||
|
||||
import GeneralSettings from "@/components/general_settings";
|
||||
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
|
||||
|
||||
const RouterSettingsPage = () => {
|
||||
const { accessToken, userRole, userId } = useAuthorized();
|
||||
|
||||
return <GeneralSettings accessToken={accessToken} userRole={userRole} userID={userId} modelData={{}} />;
|
||||
};
|
||||
|
||||
export default RouterSettingsPage;
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
"use client";
|
||||
|
||||
import UIThemeSettings from "@/components/ui_theme_settings";
|
||||
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
|
||||
|
||||
const UIThemePage = () => {
|
||||
const { userId, userRole, accessToken } = useAuthorized();
|
||||
|
||||
return <UIThemeSettings userID={userId} userRole={userRole} accessToken={accessToken} />;
|
||||
};
|
||||
|
||||
export default UIThemePage;
|
||||
35
ui/litellm-dashboard/src/app/(dashboard)/teams/page.tsx
Normal file
35
ui/litellm-dashboard/src/app/(dashboard)/teams/page.tsx
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
"use client";
|
||||
|
||||
import Teams from "@/components/teams";
|
||||
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
|
||||
import useTeams from "@/app/(dashboard)/virtual-keys/hooks/useTeams";
|
||||
import { useEffect, useState } from "react";
|
||||
import { Organization } from "@/components/networking";
|
||||
import { fetchOrganizations } from "@/components/organizations";
|
||||
|
||||
const TeamsPage = () => {
|
||||
const { accessToken, userId, userRole } = useAuthorized();
|
||||
const { teams, setTeams } = useTeams();
|
||||
const [searchParams, setSearchParams] = useState<URLSearchParams>(() =>
|
||||
typeof window === "undefined" ? new URLSearchParams() : new URLSearchParams(window.location.search),
|
||||
);
|
||||
const [organizations, setOrganizations] = useState<Organization[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
fetchOrganizations(accessToken, setOrganizations).then(() => {});
|
||||
}, [accessToken]);
|
||||
|
||||
return (
|
||||
<Teams
|
||||
teams={teams}
|
||||
searchParams={searchParams}
|
||||
accessToken={accessToken}
|
||||
setTeams={setTeams}
|
||||
userID={userId}
|
||||
userRole={userRole}
|
||||
organizations={organizations}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default TeamsPage;
|
||||
20
ui/litellm-dashboard/src/app/(dashboard)/test-key/page.tsx
Normal file
20
ui/litellm-dashboard/src/app/(dashboard)/test-key/page.tsx
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
"use client";
|
||||
|
||||
import ChatUI from "@/components/chat_ui/ChatUI";
|
||||
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
|
||||
|
||||
const TestKeyPage = () => {
|
||||
const { token, accessToken, userRole, userId, disabledPersonalKeyCreation } = useAuthorized();
|
||||
|
||||
return (
|
||||
<ChatUI
|
||||
accessToken={accessToken}
|
||||
token={token}
|
||||
userRole={userRole}
|
||||
userID={userId}
|
||||
disabledPersonalKeyCreation={disabledPersonalKeyCreation}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default TestKeyPage;
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
"use client";
|
||||
|
||||
import { MCPServers } from "@/components/mcp_tools";
|
||||
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
|
||||
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||||
|
||||
const MCPServersPage = () => {
|
||||
const { accessToken, userRole, userId } = useAuthorized();
|
||||
|
||||
const queryClient = new QueryClient();
|
||||
|
||||
return (
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<MCPServers accessToken={accessToken} userRole={userRole} userID={userId} />
|
||||
</QueryClientProvider>
|
||||
);
|
||||
};
|
||||
|
||||
export default MCPServersPage;
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
"use client";
|
||||
|
||||
import VectorStoreManagement from "@/components/vector_store_management";
|
||||
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
|
||||
|
||||
const VectorStoresPage = () => {
|
||||
const { accessToken, userId, userRole } = useAuthorized();
|
||||
|
||||
return <VectorStoreManagement accessToken={accessToken} userID={userId} userRole={userRole} />;
|
||||
};
|
||||
|
||||
export default VectorStoresPage;
|
||||
22
ui/litellm-dashboard/src/app/(dashboard)/usage/page.tsx
Normal file
22
ui/litellm-dashboard/src/app/(dashboard)/usage/page.tsx
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
"use client";
|
||||
|
||||
import NewUsagePage from "@/components/new_usage";
|
||||
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
|
||||
import useTeams from "@/app/(dashboard)/virtual-keys/hooks/useTeams";
|
||||
|
||||
const UsagePage = () => {
|
||||
const { accessToken, userRole, userId, premiumUser } = useAuthorized();
|
||||
const { teams } = useTeams();
|
||||
|
||||
return (
|
||||
<NewUsagePage
|
||||
accessToken={accessToken}
|
||||
userRole={userRole}
|
||||
userID={userId}
|
||||
teams={teams ?? []}
|
||||
premiumUser={premiumUser}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default UsagePage;
|
||||
31
ui/litellm-dashboard/src/app/(dashboard)/users/page.tsx
Normal file
31
ui/litellm-dashboard/src/app/(dashboard)/users/page.tsx
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
"use client";
|
||||
|
||||
import ViewUserDashboard from "@/components/view_users";
|
||||
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
|
||||
import useTeams from "@/app/(dashboard)/virtual-keys/hooks/useTeams";
|
||||
import { useState } from "react";
|
||||
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||||
|
||||
const UsersPage = () => {
|
||||
const { accessToken, userRole, userId, token } = useAuthorized();
|
||||
const [keys, setKeys] = useState<null | any[]>([]);
|
||||
|
||||
const { teams } = useTeams();
|
||||
const queryClient = new QueryClient();
|
||||
|
||||
return (
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<ViewUserDashboard
|
||||
accessToken={accessToken}
|
||||
token={token}
|
||||
keys={keys}
|
||||
userRole={userRole}
|
||||
userID={userId}
|
||||
teams={teams as any}
|
||||
setKeys={setKeys}
|
||||
/>
|
||||
</QueryClientProvider>
|
||||
);
|
||||
};
|
||||
|
||||
export default UsersPage;
|
||||
|
|
@ -61,7 +61,7 @@ const VirtualKeysTable = ({
|
|||
setAccessToken,
|
||||
}: AllKeysTableProps) => {
|
||||
const { userId: userID, userRole, accessToken, premiumUser } = useAuthorized();
|
||||
const teams = useTeams();
|
||||
const { teams } = useTeams();
|
||||
const [selectedKeyId, setSelectedKeyId] = useState<string | null>(null);
|
||||
const [userList, setUserList] = useState<UserResponse[]>([]);
|
||||
const [sorting, setSorting] = React.useState<SortingState>(() => {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { Team } from "@/components/key_team_helpers/key_list";
|
|||
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
|
||||
|
||||
const useTeams = () => {
|
||||
const [teams, setTeams] = useState<Team[]>([]);
|
||||
const [teams, setTeams] = useState<Team[] | null>([]);
|
||||
const { accessToken, userId: userID, userRole } = useAuthorized();
|
||||
|
||||
useEffect(() => {
|
||||
|
|
@ -14,7 +14,7 @@ const useTeams = () => {
|
|||
})();
|
||||
}, [accessToken, userID, userRole]);
|
||||
|
||||
return teams;
|
||||
return { teams, setTeams };
|
||||
};
|
||||
|
||||
export default useTeams;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
"use client";
|
||||
|
||||
import React, { useEffect, useMemo, useState } from "react";
|
||||
import React, { Suspense, useEffect, useState } from "react";
|
||||
import { useSearchParams } from "next/navigation";
|
||||
import { jwtDecode } from "jwt-decode";
|
||||
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||||
import { Team } from "@/components/key_team_helpers/key_list";
|
||||
|
|
@ -22,6 +23,7 @@ import ModelHubTable from "@/components/model_hub_table";
|
|||
import NewUsagePage from "@/components/new_usage";
|
||||
import APIRef from "@/components/api_ref";
|
||||
import ChatUI from "@/components/chat_ui/ChatUI";
|
||||
import Sidebar from "@/components/leftnav";
|
||||
import Usage from "@/components/usage";
|
||||
import CacheDashboard from "@/components/cache_dashboard";
|
||||
import { getUiConfig, proxyBaseUrl, setGlobalLitellmHeaderName } from "@/components/networking";
|
||||
|
|
@ -37,35 +39,50 @@ import VectorStoreManagement from "@/components/vector_store_management";
|
|||
import UIThemeSettings from "@/components/ui_theme_settings";
|
||||
import { UiLoadingSpinner } from "@/components/ui/ui-loading-spinner";
|
||||
import { cx } from "@/lib/cva.config";
|
||||
import Sidebar2 from "@/app/(dashboard)/components/Sidebar2";
|
||||
|
||||
/** ---- BASE URL HELPERS ---- */
|
||||
function normalizeBasePrefix(raw: string | undefined | null): string {
|
||||
const trimmed = (raw ?? "").trim();
|
||||
if (!trimmed) return "";
|
||||
const core = trimmed.replace(/^\/+/, "").replace(/\/+$/, "");
|
||||
return core ? `/${core}` : "";
|
||||
}
|
||||
const BASE_PREFIX = normalizeBasePrefix(process.env.NEXT_PUBLIC_BASE_URL);
|
||||
function withBase(path: string): string {
|
||||
const p = path.startsWith("/") ? path : `/${path}`;
|
||||
return `${BASE_PREFIX}${p}` || p;
|
||||
}
|
||||
/** -------------------------------- */
|
||||
import useFeatureFlags from "@/hooks/useFeatureFlags";
|
||||
import SidebarProvider from "@/app/(dashboard)/components/SidebarProvider";
|
||||
|
||||
function getCookie(name: string) {
|
||||
if (typeof document === "undefined") return null;
|
||||
const cookieValue = document.cookie.split("; ").find((row) => row.startsWith(name + "="));
|
||||
return cookieValue ? cookieValue.split("=")[1] : null;
|
||||
// Safer cookie read + decoding; handles '=' inside values
|
||||
const match = document.cookie.split("; ").find((row) => row.startsWith(name + "="));
|
||||
if (!match) return null;
|
||||
const value = match.slice(name.length + 1);
|
||||
try {
|
||||
return decodeURIComponent(value);
|
||||
} catch {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
function deleteCookie(name: string, path = "/") {
|
||||
// Best-effort client-side clear (works for non-HttpOnly cookies without Domain)
|
||||
document.cookie = `${name}=; Max-Age=0; Path=${path}`;
|
||||
}
|
||||
|
||||
function isJwtExpired(token: string): boolean {
|
||||
try {
|
||||
const decoded: any = jwtDecode(token);
|
||||
if (decoded && typeof decoded.exp === "number") {
|
||||
return decoded.exp * 1000 <= Date.now();
|
||||
}
|
||||
return false;
|
||||
} catch {
|
||||
// If we can't decode, treat as invalid/expired
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
function formatUserRole(userRole: string) {
|
||||
if (!userRole) return "Undefined Role";
|
||||
if (!userRole) {
|
||||
return "Undefined Role";
|
||||
}
|
||||
switch (userRole.toLowerCase()) {
|
||||
case "app_owner":
|
||||
return "App Owner";
|
||||
case "demo_app_owner":
|
||||
return "App Owner";
|
||||
case "app_admin":
|
||||
return "Admin";
|
||||
case "proxy_admin":
|
||||
return "Admin";
|
||||
case "proxy_admin_viewer":
|
||||
|
|
@ -75,7 +92,7 @@ function formatUserRole(userRole: string) {
|
|||
case "internal_user":
|
||||
return "Internal User";
|
||||
case "internal_user_viewer":
|
||||
case "internal_viewer":
|
||||
case "internal_viewer": // TODO:remove if deprecated
|
||||
return "Internal Viewer";
|
||||
case "app_user":
|
||||
return "App User";
|
||||
|
|
@ -95,6 +112,7 @@ function LoadingScreen() {
|
|||
return (
|
||||
<div className={cx("h-screen", "flex items-center justify-center gap-4")}>
|
||||
<div className="text-lg font-medium py-2 pr-4 border-r border-r-gray-200">🚅 LiteLLM</div>
|
||||
|
||||
<div className="flex items-center justify-center gap-2">
|
||||
<UiLoadingSpinner className="size-4" />
|
||||
<span className="text-gray-600 text-sm">Loading...</span>
|
||||
|
|
@ -103,16 +121,6 @@ function LoadingScreen() {
|
|||
);
|
||||
}
|
||||
|
||||
/** Derive the app "page" from the URL without triggering App Router navigations */
|
||||
function getPageFromLocation(loc: Location): string {
|
||||
const sp = new URLSearchParams(loc.search);
|
||||
const p = sp.get("page");
|
||||
if (p) return p;
|
||||
// vanity route for refactored UI
|
||||
if (loc.pathname.endsWith("/virtual-keys")) return "api-keys";
|
||||
return "api-keys";
|
||||
}
|
||||
|
||||
export default function CreateKeyPage() {
|
||||
const [userRole, setUserRole] = useState("");
|
||||
const [premiumUser, setPremiumUser] = useState(false);
|
||||
|
|
@ -122,98 +130,156 @@ export default function CreateKeyPage() {
|
|||
const [keys, setKeys] = useState<null | any[]>([]);
|
||||
const [organizations, setOrganizations] = useState<Organization[]>([]);
|
||||
const [userModels, setUserModels] = useState<string[]>([]);
|
||||
const [proxySettings, setProxySettings] = useState<ProxySettings>({ PROXY_BASE_URL: "", PROXY_LOGOUT_URL: "" });
|
||||
const [proxySettings, setProxySettings] = useState<ProxySettings>({
|
||||
PROXY_BASE_URL: "",
|
||||
PROXY_LOGOUT_URL: "",
|
||||
});
|
||||
|
||||
const [showSSOBanner, setShowSSOBanner] = useState<boolean>(true);
|
||||
|
||||
// Stable local mirror of URLSearchParams (no suspense, no flashes)
|
||||
const [searchParams, setSearchParams] = useState<URLSearchParams>(() =>
|
||||
typeof window === "undefined" ? new URLSearchParams() : new URLSearchParams(window.location.search),
|
||||
);
|
||||
const invitation_id = useMemo(() => searchParams.get("invitation_id"), [searchParams]);
|
||||
|
||||
// Local page state (drives UI)
|
||||
const [page, setPage] = useState<string>(() =>
|
||||
typeof window === "undefined" ? "api-keys" : getPageFromLocation(window.location),
|
||||
);
|
||||
|
||||
// Keep history/back/forward in sync with UI
|
||||
useEffect(() => {
|
||||
const onPop = () => {
|
||||
if (typeof window === "undefined") return;
|
||||
setSearchParams(new URLSearchParams(window.location.search));
|
||||
setPage(getPageFromLocation(window.location));
|
||||
};
|
||||
window.addEventListener("popstate", onPop);
|
||||
return () => window.removeEventListener("popstate", onPop);
|
||||
}, []);
|
||||
|
||||
// Update URL without triggering App Router reload/suspense
|
||||
const updatePage = (newPage: string) => {
|
||||
if (typeof window === "undefined") return;
|
||||
// 1) instant UI update
|
||||
setPage(newPage);
|
||||
// 2) URL update under base prefix
|
||||
const sp = new URLSearchParams(window.location.search);
|
||||
sp.set("page", newPage);
|
||||
const url = withBase(`/?${sp.toString()}`);
|
||||
window.history.pushState(null, "", url);
|
||||
// 3) keep our local mirror in sync
|
||||
setSearchParams(new URLSearchParams(sp));
|
||||
};
|
||||
|
||||
const searchParams = useSearchParams()!;
|
||||
const [modelData, setModelData] = useState<any>({ data: [] });
|
||||
const [token, setToken] = useState<string | null>(null);
|
||||
const [createClicked, setCreateClicked] = useState<boolean>(false);
|
||||
const [authLoading, setAuthLoading] = useState(true);
|
||||
const [userID, setUserID] = useState<string | null>(null);
|
||||
const { refactoredUIFlag } = useFeatureFlags();
|
||||
|
||||
const invitation_id = searchParams.get("invitation_id");
|
||||
|
||||
// Get page from URL, default to 'api-keys' if not present
|
||||
const [page, setPage] = useState(() => {
|
||||
return searchParams.get("page") || "api-keys";
|
||||
});
|
||||
|
||||
// Custom setPage function that updates URL
|
||||
const updatePage = (newPage: string) => {
|
||||
// Update URL without full page reload
|
||||
const newSearchParams = new URLSearchParams(searchParams);
|
||||
newSearchParams.set("page", newPage);
|
||||
|
||||
// Use Next.js router to update URL
|
||||
window.history.pushState(null, "", `?${newSearchParams.toString()}`);
|
||||
|
||||
setPage(newPage);
|
||||
};
|
||||
|
||||
const [accessToken, setAccessToken] = useState<string | null>(null);
|
||||
const [sidebarCollapsed, setSidebarCollapsed] = useState(false);
|
||||
|
||||
const toggleSidebar = () => setSidebarCollapsed((v) => !v);
|
||||
const toggleSidebar = () => {
|
||||
setSidebarCollapsed(!sidebarCollapsed);
|
||||
};
|
||||
|
||||
const addKey = (data: any) => {
|
||||
setKeys((prevData) => (prevData ? [...prevData, data] : [data]));
|
||||
setCreateClicked((v) => !v);
|
||||
setCreateClicked(() => !createClicked);
|
||||
};
|
||||
const redirectToLogin = authLoading === false && token === null && invitation_id === null;
|
||||
|
||||
useEffect(() => {
|
||||
const token = getCookie("token");
|
||||
getUiConfig().then(() => {
|
||||
setToken(token);
|
||||
setAuthLoading(false);
|
||||
});
|
||||
let cancelled = false;
|
||||
|
||||
(async () => {
|
||||
try {
|
||||
await getUiConfig(); // ensures proxyBaseUrl etc. are ready
|
||||
} catch {
|
||||
// proceed regardless; we still need to decide auth state
|
||||
}
|
||||
|
||||
if (cancelled) return;
|
||||
|
||||
const raw = getCookie("token");
|
||||
const valid = raw && !isJwtExpired(raw) ? raw : null;
|
||||
|
||||
// If token exists but is invalid/expired, clear it so downstream code
|
||||
// doesn't keep trying to use it and cause redirect spasms.
|
||||
if (raw && !valid) {
|
||||
deleteCookie("token", "/");
|
||||
}
|
||||
|
||||
if (!cancelled) {
|
||||
setToken(valid);
|
||||
setAuthLoading(false);
|
||||
}
|
||||
})();
|
||||
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (redirectToLogin) {
|
||||
window.location.href = (proxyBaseUrl || "") + "/sso/key/generate";
|
||||
// Replace instead of assigning to avoid back-button loops
|
||||
const dest = (proxyBaseUrl || "") + "/sso/key/generate";
|
||||
window.location.replace(dest);
|
||||
}
|
||||
}, [redirectToLogin]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!token) return;
|
||||
const decoded = jwtDecode(token) as { [key: string]: any };
|
||||
if (!decoded) return;
|
||||
|
||||
setAccessToken(decoded.key);
|
||||
setDisabledPersonalKeyCreation(decoded.disabled_non_admin_personal_key_creation);
|
||||
|
||||
if (decoded.user_role) {
|
||||
const formattedUserRole = formatUserRole(decoded.user_role);
|
||||
setUserRole(formattedUserRole);
|
||||
if (formattedUserRole === "Admin Viewer") setPage("usage");
|
||||
if (!token) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Defensive: re-check expiry in case cookie changed after mount
|
||||
if (isJwtExpired(token)) {
|
||||
deleteCookie("token", "/");
|
||||
setToken(null);
|
||||
return;
|
||||
}
|
||||
|
||||
let decoded: any = null;
|
||||
try {
|
||||
decoded = jwtDecode(token);
|
||||
} catch {
|
||||
// Malformed token → treat as unauthenticated
|
||||
deleteCookie("token", "/");
|
||||
setToken(null);
|
||||
return;
|
||||
}
|
||||
|
||||
if (decoded) {
|
||||
// set accessToken
|
||||
setAccessToken(decoded.key);
|
||||
|
||||
setDisabledPersonalKeyCreation(decoded.disabled_non_admin_personal_key_creation);
|
||||
|
||||
// check if userRole is defined
|
||||
if (decoded.user_role) {
|
||||
const formattedUserRole = formatUserRole(decoded.user_role);
|
||||
setUserRole(formattedUserRole);
|
||||
if (formattedUserRole == "Admin Viewer") {
|
||||
setPage("usage");
|
||||
}
|
||||
}
|
||||
|
||||
if (decoded.user_email) {
|
||||
setUserEmail(decoded.user_email);
|
||||
}
|
||||
|
||||
if (decoded.login_method) {
|
||||
setShowSSOBanner(decoded.login_method == "username_password" ? true : false);
|
||||
}
|
||||
|
||||
if (decoded.premium_user) {
|
||||
setPremiumUser(decoded.premium_user);
|
||||
}
|
||||
|
||||
if (decoded.auth_header_name) {
|
||||
setGlobalLitellmHeaderName(decoded.auth_header_name);
|
||||
}
|
||||
|
||||
if (decoded.user_id) {
|
||||
setUserID(decoded.user_id);
|
||||
}
|
||||
}
|
||||
if (decoded.user_email) setUserEmail(decoded.user_email);
|
||||
if (decoded.login_method) setShowSSOBanner(decoded.login_method === "username_password");
|
||||
if (decoded.premium_user) setPremiumUser(decoded.premium_user);
|
||||
if (decoded.auth_header_name) setGlobalLitellmHeaderName(decoded.auth_header_name);
|
||||
if (decoded.user_id) setUserID(decoded.user_id);
|
||||
}, [token]);
|
||||
|
||||
useEffect(() => {
|
||||
if (accessToken && userID && userRole) {
|
||||
fetchUserModels(userID, userRole, accessToken, setUserModels);
|
||||
}
|
||||
if (accessToken && userID && userRole) {
|
||||
fetchTeams(accessToken, userID, userRole, null, setTeams);
|
||||
}
|
||||
if (accessToken) {
|
||||
|
|
@ -226,194 +292,201 @@ export default function CreateKeyPage() {
|
|||
}
|
||||
|
||||
return (
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<ThemeProvider accessToken={accessToken}>
|
||||
{invitation_id ? (
|
||||
<UserDashboard
|
||||
userID={userID}
|
||||
userRole={userRole}
|
||||
userEmail={userEmail}
|
||||
premiumUser={premiumUser}
|
||||
teams={teams}
|
||||
keys={keys}
|
||||
setUserRole={setUserRole}
|
||||
setUserEmail={setUserEmail}
|
||||
setTeams={setTeams}
|
||||
setKeys={setKeys}
|
||||
organizations={organizations}
|
||||
addKey={addKey}
|
||||
createClicked={createClicked}
|
||||
/>
|
||||
) : (
|
||||
<div className="flex flex-col min-h-screen">
|
||||
<Navbar
|
||||
<Suspense fallback={<LoadingScreen />}>
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<ThemeProvider accessToken={accessToken}>
|
||||
{invitation_id ? (
|
||||
<UserDashboard
|
||||
userID={userID}
|
||||
userRole={userRole}
|
||||
premiumUser={premiumUser}
|
||||
teams={teams}
|
||||
keys={keys}
|
||||
setUserRole={setUserRole}
|
||||
userEmail={userEmail}
|
||||
setProxySettings={setProxySettings}
|
||||
proxySettings={proxySettings}
|
||||
accessToken={accessToken}
|
||||
isPublicPage={false}
|
||||
sidebarCollapsed={sidebarCollapsed}
|
||||
onToggleSidebar={toggleSidebar}
|
||||
setUserEmail={setUserEmail}
|
||||
setTeams={setTeams}
|
||||
setKeys={setKeys}
|
||||
organizations={organizations}
|
||||
addKey={addKey}
|
||||
createClicked={createClicked}
|
||||
/>
|
||||
<div className="flex flex-1 overflow-auto">
|
||||
<div className="mt-2">
|
||||
<Sidebar2
|
||||
defaultSelectedKey={page}
|
||||
setPage={updatePage}
|
||||
accessToken={accessToken}
|
||||
userRole={userRole}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex flex-col min-h-screen">
|
||||
<Navbar
|
||||
userID={userID}
|
||||
userRole={userRole}
|
||||
premiumUser={premiumUser}
|
||||
userEmail={userEmail}
|
||||
setProxySettings={setProxySettings}
|
||||
proxySettings={proxySettings}
|
||||
accessToken={accessToken}
|
||||
isPublicPage={false}
|
||||
sidebarCollapsed={sidebarCollapsed}
|
||||
onToggleSidebar={toggleSidebar}
|
||||
/>
|
||||
<div className="flex flex-1 overflow-auto">
|
||||
<div className="mt-2">
|
||||
<SidebarProvider setPage={updatePage} defaultSelectedKey={page} sidebarCollapsed={sidebarCollapsed} />
|
||||
</div>
|
||||
|
||||
{page === "api-keys" ? (
|
||||
<UserDashboard
|
||||
userID={userID}
|
||||
userRole={userRole}
|
||||
userEmail={userEmail}
|
||||
premiumUser={premiumUser}
|
||||
teams={teams}
|
||||
keys={keys}
|
||||
setUserRole={setUserRole}
|
||||
setUserEmail={setUserEmail}
|
||||
setTeams={setTeams}
|
||||
setKeys={setKeys}
|
||||
organizations={organizations}
|
||||
addKey={addKey}
|
||||
createClicked={createClicked}
|
||||
/>
|
||||
) : page === "models" ? (
|
||||
<ModelDashboard
|
||||
userID={userID}
|
||||
userRole={userRole}
|
||||
token={token}
|
||||
keys={keys}
|
||||
accessToken={accessToken}
|
||||
modelData={{ data: [] }}
|
||||
setModelData={() => {}}
|
||||
premiumUser={premiumUser}
|
||||
teams={teams}
|
||||
/>
|
||||
) : page === "llm-playground" ? (
|
||||
<ChatUI
|
||||
userID={userID}
|
||||
userRole={userRole}
|
||||
token={token}
|
||||
accessToken={accessToken}
|
||||
disabledPersonalKeyCreation={disabledPersonalKeyCreation}
|
||||
/>
|
||||
) : page === "users" ? (
|
||||
<ViewUserDashboard
|
||||
userID={userID}
|
||||
userRole={userRole}
|
||||
token={token}
|
||||
keys={keys}
|
||||
teams={teams}
|
||||
accessToken={accessToken}
|
||||
setKeys={setKeys}
|
||||
/>
|
||||
) : page === "teams" ? (
|
||||
<Teams
|
||||
teams={teams}
|
||||
setTeams={setTeams}
|
||||
searchParams={searchParams}
|
||||
accessToken={accessToken}
|
||||
userID={userID}
|
||||
userRole={userRole}
|
||||
organizations={organizations}
|
||||
premiumUser={premiumUser}
|
||||
/>
|
||||
) : page === "organizations" ? (
|
||||
<Organizations
|
||||
organizations={organizations}
|
||||
setOrganizations={setOrganizations}
|
||||
userModels={userModels}
|
||||
accessToken={accessToken}
|
||||
userRole={userRole}
|
||||
premiumUser={premiumUser}
|
||||
/>
|
||||
) : page === "admin-panel" ? (
|
||||
<AdminPanel
|
||||
setTeams={setTeams}
|
||||
searchParams={searchParams}
|
||||
accessToken={accessToken}
|
||||
userID={userID}
|
||||
showSSOBanner={showSSOBanner}
|
||||
premiumUser={premiumUser}
|
||||
proxySettings={proxySettings}
|
||||
/>
|
||||
) : page === "api_ref" ? (
|
||||
<APIRef proxySettings={proxySettings} />
|
||||
) : page === "settings" ? (
|
||||
<Settings userID={userID} userRole={userRole} accessToken={accessToken} premiumUser={premiumUser} />
|
||||
) : page === "budgets" ? (
|
||||
<BudgetPanel accessToken={accessToken} />
|
||||
) : page === "guardrails" ? (
|
||||
<GuardrailsPanel accessToken={accessToken} userRole={userRole} />
|
||||
) : page === "prompts" ? (
|
||||
<PromptsPanel accessToken={accessToken} userRole={userRole} />
|
||||
) : page === "transform-request" ? (
|
||||
<TransformRequestPanel accessToken={accessToken} />
|
||||
) : page === "general-settings" ? (
|
||||
<GeneralSettings userID={userID} userRole={userRole} accessToken={accessToken} modelData={{}} />
|
||||
) : page === "ui-theme" ? (
|
||||
<UIThemeSettings userID={userID} userRole={userRole} accessToken={accessToken} />
|
||||
) : page === "model-hub-table" ? (
|
||||
<ModelHubTable
|
||||
accessToken={accessToken}
|
||||
publicPage={false}
|
||||
premiumUser={premiumUser}
|
||||
userRole={userRole}
|
||||
/>
|
||||
) : page === "caching" ? (
|
||||
<CacheDashboard
|
||||
userID={userID}
|
||||
userRole={userRole}
|
||||
token={token}
|
||||
accessToken={accessToken}
|
||||
premiumUser={premiumUser}
|
||||
/>
|
||||
) : page === "pass-through-settings" ? (
|
||||
<PassThroughSettings userID={userID} userRole={userRole} accessToken={accessToken} modelData={{}} />
|
||||
) : page === "logs" ? (
|
||||
<SpendLogsTable
|
||||
userID={userID}
|
||||
userRole={userRole}
|
||||
token={token}
|
||||
accessToken={accessToken}
|
||||
allTeams={(teams as Team[]) ?? []}
|
||||
premiumUser={premiumUser}
|
||||
/>
|
||||
) : page === "mcp-servers" ? (
|
||||
<MCPServers accessToken={accessToken} userRole={userRole} userID={userID} />
|
||||
) : page === "tag-management" ? (
|
||||
<TagManagement accessToken={accessToken} userRole={userRole} userID={userID} />
|
||||
) : page === "vector-stores" ? (
|
||||
<VectorStoreManagement accessToken={accessToken} userRole={userRole} userID={userID} />
|
||||
) : page === "new_usage" ? (
|
||||
<NewUsagePage
|
||||
userID={userID}
|
||||
userRole={userRole}
|
||||
accessToken={accessToken}
|
||||
teams={(teams as Team[]) ?? []}
|
||||
premiumUser={premiumUser}
|
||||
/>
|
||||
) : (
|
||||
<Usage
|
||||
userID={userID}
|
||||
userRole={userRole}
|
||||
token={token}
|
||||
accessToken={accessToken}
|
||||
keys={keys}
|
||||
premiumUser={premiumUser}
|
||||
/>
|
||||
)}
|
||||
{page == "api-keys" ? (
|
||||
<UserDashboard
|
||||
userID={userID}
|
||||
userRole={userRole}
|
||||
premiumUser={premiumUser}
|
||||
teams={teams}
|
||||
keys={keys}
|
||||
setUserRole={setUserRole}
|
||||
userEmail={userEmail}
|
||||
setUserEmail={setUserEmail}
|
||||
setTeams={setTeams}
|
||||
setKeys={setKeys}
|
||||
organizations={organizations}
|
||||
addKey={addKey}
|
||||
createClicked={createClicked}
|
||||
/>
|
||||
) : page == "models" ? (
|
||||
<ModelDashboard
|
||||
userID={userID}
|
||||
userRole={userRole}
|
||||
token={token}
|
||||
keys={keys}
|
||||
accessToken={accessToken}
|
||||
modelData={modelData}
|
||||
setModelData={setModelData}
|
||||
premiumUser={premiumUser}
|
||||
teams={teams}
|
||||
/>
|
||||
) : page == "llm-playground" ? (
|
||||
<ChatUI
|
||||
userID={userID}
|
||||
userRole={userRole}
|
||||
token={token}
|
||||
accessToken={accessToken}
|
||||
disabledPersonalKeyCreation={disabledPersonalKeyCreation}
|
||||
/>
|
||||
) : page == "users" ? (
|
||||
<ViewUserDashboard
|
||||
userID={userID}
|
||||
userRole={userRole}
|
||||
token={token}
|
||||
keys={keys}
|
||||
teams={teams}
|
||||
accessToken={accessToken}
|
||||
setKeys={setKeys}
|
||||
/>
|
||||
) : page == "teams" ? (
|
||||
<Teams
|
||||
teams={teams}
|
||||
setTeams={setTeams}
|
||||
searchParams={searchParams}
|
||||
accessToken={accessToken}
|
||||
userID={userID}
|
||||
userRole={userRole}
|
||||
organizations={organizations}
|
||||
premiumUser={premiumUser}
|
||||
/>
|
||||
) : page == "organizations" ? (
|
||||
<Organizations
|
||||
organizations={organizations}
|
||||
setOrganizations={setOrganizations}
|
||||
userModels={userModels}
|
||||
accessToken={accessToken}
|
||||
userRole={userRole}
|
||||
premiumUser={premiumUser}
|
||||
/>
|
||||
) : page == "admin-panel" ? (
|
||||
<AdminPanel
|
||||
setTeams={setTeams}
|
||||
searchParams={searchParams}
|
||||
accessToken={accessToken}
|
||||
userID={userID}
|
||||
showSSOBanner={showSSOBanner}
|
||||
premiumUser={premiumUser}
|
||||
proxySettings={proxySettings}
|
||||
/>
|
||||
) : page == "api_ref" ? (
|
||||
<APIRef proxySettings={proxySettings} />
|
||||
) : page == "settings" ? (
|
||||
<Settings userID={userID} userRole={userRole} accessToken={accessToken} premiumUser={premiumUser} />
|
||||
) : page == "budgets" ? (
|
||||
<BudgetPanel accessToken={accessToken} />
|
||||
) : page == "guardrails" ? (
|
||||
<GuardrailsPanel accessToken={accessToken} userRole={userRole} />
|
||||
) : page == "prompts" ? (
|
||||
<PromptsPanel accessToken={accessToken} userRole={userRole} />
|
||||
) : page == "transform-request" ? (
|
||||
<TransformRequestPanel accessToken={accessToken} />
|
||||
) : page == "general-settings" ? (
|
||||
<GeneralSettings
|
||||
userID={userID}
|
||||
userRole={userRole}
|
||||
accessToken={accessToken}
|
||||
modelData={modelData}
|
||||
/>
|
||||
) : page == "ui-theme" ? (
|
||||
<UIThemeSettings userID={userID} userRole={userRole} accessToken={accessToken} />
|
||||
) : page == "model-hub-table" ? (
|
||||
<ModelHubTable
|
||||
accessToken={accessToken}
|
||||
publicPage={false}
|
||||
premiumUser={premiumUser}
|
||||
userRole={userRole}
|
||||
/>
|
||||
) : page == "caching" ? (
|
||||
<CacheDashboard
|
||||
userID={userID}
|
||||
userRole={userRole}
|
||||
token={token}
|
||||
accessToken={accessToken}
|
||||
premiumUser={premiumUser}
|
||||
/>
|
||||
) : page == "pass-through-settings" ? (
|
||||
<PassThroughSettings
|
||||
userID={userID}
|
||||
userRole={userRole}
|
||||
accessToken={accessToken}
|
||||
modelData={modelData}
|
||||
/>
|
||||
) : page == "logs" ? (
|
||||
<SpendLogsTable
|
||||
userID={userID}
|
||||
userRole={userRole}
|
||||
token={token}
|
||||
accessToken={accessToken}
|
||||
allTeams={(teams as Team[]) ?? []}
|
||||
premiumUser={premiumUser}
|
||||
/>
|
||||
) : page == "mcp-servers" ? (
|
||||
<MCPServers accessToken={accessToken} userRole={userRole} userID={userID} />
|
||||
) : page == "tag-management" ? (
|
||||
<TagManagement accessToken={accessToken} userRole={userRole} userID={userID} />
|
||||
) : page == "vector-stores" ? (
|
||||
<VectorStoreManagement accessToken={accessToken} userRole={userRole} userID={userID} />
|
||||
) : page == "new_usage" ? (
|
||||
<NewUsagePage
|
||||
userID={userID}
|
||||
userRole={userRole}
|
||||
accessToken={accessToken}
|
||||
teams={(teams as Team[]) ?? []}
|
||||
premiumUser={premiumUser}
|
||||
/>
|
||||
) : (
|
||||
<Usage
|
||||
userID={userID}
|
||||
userRole={userRole}
|
||||
token={token}
|
||||
accessToken={accessToken}
|
||||
keys={keys}
|
||||
premiumUser={premiumUser}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</ThemeProvider>
|
||||
</QueryClientProvider>
|
||||
)}
|
||||
</ThemeProvider>
|
||||
</QueryClientProvider>
|
||||
</Suspense>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue