diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index 4c837471c..0ca9bc740 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -4020,7 +4020,11 @@ async def user_auth(request: Request): async def user_info( user_id: Optional[str] = fastapi.Query( default=None, description="User ID in the request parameters" - ) + ), + view_all: bool = fastapi.Query( + default=False, + description="set to true to View all users. When using view_all, don't pass user_id", + ), ): """ Use this to get user information. (user row + all user key info) @@ -4040,6 +4044,11 @@ async def user_info( ## GET USER ROW ## if user_id is not None: user_info = await prisma_client.get_data(user_id=user_id) + elif view_all == True: + user_info = await prisma_client.get_data( + table_name="user", query_type="find_all" + ) + return user_info else: user_info = None ## GET ALL TEAMS ## diff --git a/ui/litellm-dashboard/src/app/page.tsx b/ui/litellm-dashboard/src/app/page.tsx index d49581bec..841880bc8 100644 --- a/ui/litellm-dashboard/src/app/page.tsx +++ b/ui/litellm-dashboard/src/app/page.tsx @@ -4,6 +4,7 @@ import { useSearchParams } from "next/navigation"; import Navbar from "../components/navbar"; import UserDashboard from "../components/user_dashboard"; import ModelDashboard from "@/components/model_dashboard"; +import ViewUserDashboard from "@/components/view_users"; import ChatUI from "@/components/chat_ui"; import Sidebar from "../components/leftnav"; import Usage from "../components/usage"; @@ -68,12 +69,13 @@ const CreateKeyPage = () => { } } + return ( Loading...}>
- + {page == "api-keys" ? ( { accessToken={accessToken} /> ) + : page == "users" ? ( + + ) : ( >; + userRole: string; } -const Sidebar: React.FC = ({ setPage }) => { +const Sidebar: React.FC = ({ setPage, userRole }) => { return ( @@ -29,6 +30,13 @@ const Sidebar: React.FC = ({ setPage }) => { setPage("usage")}> Usage + { + userRole == "Admin" ? + setPage("users")}> + Users + + : null + } diff --git a/ui/litellm-dashboard/src/components/networking.tsx b/ui/litellm-dashboard/src/components/networking.tsx index df0f62427..4c4fb72d9 100644 --- a/ui/litellm-dashboard/src/components/networking.tsx +++ b/ui/litellm-dashboard/src/components/networking.tsx @@ -104,14 +104,19 @@ export const keyDeleteCall = async (accessToken: String, user_key: String) => { export const userInfoCall = async ( accessToken: String, - userID: String, - userRole: String + userID: String | null, + userRole: String, + viewAll: Boolean = false ) => { try { let url = proxyBaseUrl ? `${proxyBaseUrl}/user/info` : `/user/info`; - if (userRole == "App Owner") { + if (userRole == "App Owner" && userID) { url = `${url}/?user_id=${userID}`; } + console.log("in userInfoCall viewAll=", viewAll); + if (viewAll) { + url = `${url}/?view_all=true`; + } message.info("Requesting user data"); const response = await fetch(url, { method: "GET", diff --git a/ui/litellm-dashboard/src/components/view_users.tsx b/ui/litellm-dashboard/src/components/view_users.tsx new file mode 100644 index 000000000..9db7b878e --- /dev/null +++ b/ui/litellm-dashboard/src/components/view_users.tsx @@ -0,0 +1,88 @@ +import React, { useState, useEffect } from "react"; +import { Card, Title, Subtitle, Table, TableHead, TableRow, TableCell, TableBody, Metric, Grid } from "@tremor/react"; +import { userInfoCall } from "./networking"; +import { Badge, BadgeDelta, Button } from '@tremor/react'; +import RequestAccess from "./request_model_access"; + +interface ViewUserDashboardProps { + accessToken: string | null; + token: string | null; + userRole: string | null; + userID: string | null; +} + +const ViewUserDashboard: React.FC = ({ + accessToken, + token, + userRole, + userID, +}) => { + const [userData, setuserData] = useState(null); + const [pendingRequests, setPendingRequests] = useState([]); + + + useEffect(() => { + if (!accessToken || !token || !userRole || !userID) { + return; + } + const fetchData = async () => { + try { + // Replace with your actual API call for model data + const userDataResponse = await userInfoCall(accessToken, null, userRole, true); + console.log("user data response:", userDataResponse); + setuserData(userDataResponse); + + } catch (error) { + console.error("There was an error fetching the model data", error); + } + }; + + if (accessToken && token && userRole && userID) { + fetchData(); + } + }, [accessToken, token, userRole, userID]); + + if (!userData) { + return
Loading...
; + } + + if (!accessToken || !token || !userRole || !userID) { + return
Loading...
; + } + + // when users click request access show pop up to allow them to request access + + return ( +
+ + + + + + User ID + User Role + User Models + User Spend ($ USD) + User Max Budget ($ USD) + + + + {userData.map((user: any) => ( + + {user.user_id} + {user.user_role ? user.user_role : "app_user"} + {user.models && user.models.length > 0 ? user.models : "All Models"} + {user.spend ? user.spend : 0} + {user.max_budget ? user.max_budget : "Unlimited"} + + + ))} + +
+
+
+
+ ); +}; + +export default ViewUserDashboard;