mirror of
https://github.com/BerriAI/litellm.git
synced 2025-04-25 10:44:24 +00:00
Virtual Keys: Filter by User ID
This commit is contained in:
parent
b82af5b826
commit
e38b0cab8b
7 changed files with 111 additions and 14 deletions
|
@ -32,6 +32,8 @@ interface AllKeysTableProps {
|
||||||
setSelectedTeam: (team: Team | null) => void;
|
setSelectedTeam: (team: Team | null) => void;
|
||||||
selectedKeyAlias: string | null;
|
selectedKeyAlias: string | null;
|
||||||
setSelectedKeyAlias: Setter<string | null>;
|
setSelectedKeyAlias: Setter<string | null>;
|
||||||
|
selectedUserId: string | null;
|
||||||
|
setSelectedUserId: Setter<string | null>;
|
||||||
accessToken: string | null;
|
accessToken: string | null;
|
||||||
userID: string | null;
|
userID: string | null;
|
||||||
userRole: string | null;
|
userRole: string | null;
|
||||||
|
@ -98,10 +100,9 @@ export function AllKeysTable({
|
||||||
onPageChange,
|
onPageChange,
|
||||||
pageSize = 50,
|
pageSize = 50,
|
||||||
teams,
|
teams,
|
||||||
selectedTeam,
|
|
||||||
setSelectedTeam,
|
setSelectedTeam,
|
||||||
selectedKeyAlias,
|
|
||||||
setSelectedKeyAlias,
|
setSelectedKeyAlias,
|
||||||
|
setSelectedUserId,
|
||||||
accessToken,
|
accessToken,
|
||||||
userID,
|
userID,
|
||||||
userRole,
|
userRole,
|
||||||
|
@ -117,6 +118,7 @@ export function AllKeysTable({
|
||||||
filters,
|
filters,
|
||||||
filteredKeys,
|
filteredKeys,
|
||||||
allKeyAliases,
|
allKeyAliases,
|
||||||
|
allUsers,
|
||||||
allTeams,
|
allTeams,
|
||||||
allOrganizations,
|
allOrganizations,
|
||||||
handleFilterChange,
|
handleFilterChange,
|
||||||
|
@ -128,7 +130,8 @@ export function AllKeysTable({
|
||||||
accessToken,
|
accessToken,
|
||||||
setSelectedTeam,
|
setSelectedTeam,
|
||||||
setCurrentOrg,
|
setCurrentOrg,
|
||||||
setSelectedKeyAlias
|
setSelectedKeyAlias,
|
||||||
|
setSelectedUserId
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -363,6 +366,23 @@ export function AllKeysTable({
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "User ID",
|
||||||
|
label: "User ID",
|
||||||
|
isSearchable: true,
|
||||||
|
searchFn: async (searchText) => {
|
||||||
|
const filteredUserIds = allUsers.filter(user => {
|
||||||
|
return user.user_id.toLowerCase().includes(searchText.toLowerCase())
|
||||||
|
});
|
||||||
|
|
||||||
|
return filteredUserIds.map((user) => {
|
||||||
|
return {
|
||||||
|
label: user.user_id,
|
||||||
|
value: user.user_id
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "Key Alias",
|
name: "Key Alias",
|
||||||
label: "Key Alias",
|
label: "Key Alias",
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { keyListCall, teamListCall, organizationListCall } from '../networking';
|
import { keyListCall, teamListCall, organizationListCall, userListCall } from '../networking';
|
||||||
import { Team } from './key_list';
|
import { Team } from './key_list';
|
||||||
import { Organization } from '../networking';
|
import { Organization } from '../networking';
|
||||||
|
import { UserInfo } from '../view_users/types';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetches all key aliases across all pages
|
* Fetches all key aliases across all pages
|
||||||
|
@ -22,6 +23,7 @@ export const fetchAllKeyAliases = async (accessToken: string | null): Promise<st
|
||||||
null, // organization_id
|
null, // organization_id
|
||||||
"", // team_id
|
"", // team_id
|
||||||
null, // selectedKeyAlias
|
null, // selectedKeyAlias
|
||||||
|
null, // selectedUserId
|
||||||
currentPage,
|
currentPage,
|
||||||
100 // larger page size to reduce number of requests
|
100 // larger page size to reduce number of requests
|
||||||
);
|
);
|
||||||
|
@ -123,3 +125,37 @@ export const fetchAllOrganizations = async (accessToken: string | null): Promise
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const fetchAllUsers = async (accessToken: string | null): Promise<UserInfo[]> => {
|
||||||
|
if (!accessToken) return [];
|
||||||
|
|
||||||
|
try {
|
||||||
|
let allUsers: any[] = [];
|
||||||
|
let currentPage = 1;
|
||||||
|
let hasMorePages = true;
|
||||||
|
|
||||||
|
while (hasMorePages) {
|
||||||
|
const response = await userListCall(
|
||||||
|
accessToken,
|
||||||
|
null, // userIDs
|
||||||
|
currentPage,
|
||||||
|
100, // page size
|
||||||
|
);
|
||||||
|
|
||||||
|
// Add users from the response
|
||||||
|
allUsers = [...allUsers, ...response.users];
|
||||||
|
|
||||||
|
// Check if there are more pages
|
||||||
|
if (currentPage < response.total_pages) {
|
||||||
|
currentPage++;
|
||||||
|
} else {
|
||||||
|
hasMorePages = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return allUsers;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching all users:", error);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
};
|
|
@ -3,13 +3,14 @@ import { KeyResponse } from "../key_team_helpers/key_list";
|
||||||
import { Organization } from "../networking";
|
import { Organization } from "../networking";
|
||||||
import { Team } from "../key_team_helpers/key_list";
|
import { Team } from "../key_team_helpers/key_list";
|
||||||
import { useQuery } from "@tanstack/react-query";
|
import { useQuery } from "@tanstack/react-query";
|
||||||
import { fetchAllKeyAliases, fetchAllOrganizations, fetchAllTeams } from "./filter_helpers";
|
import { fetchAllKeyAliases, fetchAllOrganizations, fetchAllTeams, fetchAllUsers } from "./filter_helpers";
|
||||||
import { Setter } from "@/types";
|
import { Setter } from "@/types";
|
||||||
|
|
||||||
|
|
||||||
export interface FilterState {
|
export interface FilterState {
|
||||||
'Team ID': string;
|
'Team ID': string;
|
||||||
'Organization ID': string;
|
'Organization ID': string;
|
||||||
|
'User ID': string;
|
||||||
'Key Alias': string;
|
'Key Alias': string;
|
||||||
[key: string]: string;
|
[key: string]: string;
|
||||||
}
|
}
|
||||||
|
@ -21,7 +22,8 @@ export function useFilterLogic({
|
||||||
accessToken,
|
accessToken,
|
||||||
setSelectedTeam,
|
setSelectedTeam,
|
||||||
setCurrentOrg,
|
setCurrentOrg,
|
||||||
setSelectedKeyAlias
|
setSelectedKeyAlias,
|
||||||
|
setSelectedUserId
|
||||||
}: {
|
}: {
|
||||||
keys: KeyResponse[];
|
keys: KeyResponse[];
|
||||||
teams: Team[] | null;
|
teams: Team[] | null;
|
||||||
|
@ -30,10 +32,12 @@ export function useFilterLogic({
|
||||||
setSelectedTeam: (team: Team | null) => void;
|
setSelectedTeam: (team: Team | null) => void;
|
||||||
setCurrentOrg: React.Dispatch<React.SetStateAction<Organization | null>>;
|
setCurrentOrg: React.Dispatch<React.SetStateAction<Organization | null>>;
|
||||||
setSelectedKeyAlias: Setter<string | null>
|
setSelectedKeyAlias: Setter<string | null>
|
||||||
|
setSelectedUserId: Setter<string | null>
|
||||||
}) {
|
}) {
|
||||||
const [filters, setFilters] = useState<FilterState>({
|
const [filters, setFilters] = useState<FilterState>({
|
||||||
'Team ID': '',
|
'Team ID': '',
|
||||||
'Organization ID': '',
|
'Organization ID': '',
|
||||||
|
'User ID': '',
|
||||||
'Key Alias': ''
|
'Key Alias': ''
|
||||||
});
|
});
|
||||||
const [allTeams, setAllTeams] = useState<Team[]>(teams || []);
|
const [allTeams, setAllTeams] = useState<Team[]>(teams || []);
|
||||||
|
@ -86,7 +90,7 @@ export function useFilterLogic({
|
||||||
}
|
}
|
||||||
}, [accessToken]);
|
}, [accessToken]);
|
||||||
|
|
||||||
const queryAllKeysQuery = useQuery({
|
const allKeyAliasesQuery = useQuery({
|
||||||
queryKey: ['allKeys'],
|
queryKey: ['allKeys'],
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
if (!accessToken) throw new Error('Access token required');
|
if (!accessToken) throw new Error('Access token required');
|
||||||
|
@ -94,7 +98,18 @@ export function useFilterLogic({
|
||||||
},
|
},
|
||||||
enabled: !!accessToken
|
enabled: !!accessToken
|
||||||
});
|
});
|
||||||
const allKeyAliases = queryAllKeysQuery.data || []
|
const allKeyAliases = allKeyAliasesQuery.data || []
|
||||||
|
|
||||||
|
const allUsersQuery = useQuery({
|
||||||
|
queryKey: ['allUsers'],
|
||||||
|
enabled: !!accessToken,
|
||||||
|
queryFn: async () => {
|
||||||
|
if (!accessToken) throw new Error('Access token required');
|
||||||
|
return await fetchAllUsers(accessToken);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const allUsers = allUsersQuery.data || [];
|
||||||
|
|
||||||
|
|
||||||
// Update teams and organizations when props change
|
// Update teams and organizations when props change
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -120,6 +135,7 @@ export function useFilterLogic({
|
||||||
setFilters({
|
setFilters({
|
||||||
'Team ID': newFilters['Team ID'] || '',
|
'Team ID': newFilters['Team ID'] || '',
|
||||||
'Organization ID': newFilters['Organization ID'] || '',
|
'Organization ID': newFilters['Organization ID'] || '',
|
||||||
|
'User ID': newFilters['User ID'] || '',
|
||||||
'Key Alias': newFilters['Key Alias'] || ''
|
'Key Alias': newFilters['Key Alias'] || ''
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -144,6 +160,8 @@ export function useFilterLogic({
|
||||||
? allKeyAliases.find((k) => k === keyAlias) || null
|
? allKeyAliases.find((k) => k === keyAlias) || null
|
||||||
: null;
|
: null;
|
||||||
setSelectedKeyAlias(selectedKeyAlias)
|
setSelectedKeyAlias(selectedKeyAlias)
|
||||||
|
|
||||||
|
setSelectedUserId(newFilters['User ID'] || null)
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleFilterReset = () => {
|
const handleFilterReset = () => {
|
||||||
|
@ -151,6 +169,7 @@ export function useFilterLogic({
|
||||||
setFilters({
|
setFilters({
|
||||||
'Team ID': '',
|
'Team ID': '',
|
||||||
'Organization ID': '',
|
'Organization ID': '',
|
||||||
|
'User ID': '',
|
||||||
'Key Alias': ''
|
'Key Alias': ''
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -158,6 +177,7 @@ export function useFilterLogic({
|
||||||
setSelectedTeam(null);
|
setSelectedTeam(null);
|
||||||
setCurrentOrg(null);
|
setCurrentOrg(null);
|
||||||
setSelectedKeyAlias(null);
|
setSelectedKeyAlias(null);
|
||||||
|
setSelectedUserId(null)
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -165,6 +185,7 @@ export function useFilterLogic({
|
||||||
filteredKeys,
|
filteredKeys,
|
||||||
allKeyAliases,
|
allKeyAliases,
|
||||||
allTeams,
|
allTeams,
|
||||||
|
allUsers,
|
||||||
allOrganizations,
|
allOrganizations,
|
||||||
handleFilterChange,
|
handleFilterChange,
|
||||||
handleFilterReset
|
handleFilterReset
|
||||||
|
|
|
@ -86,6 +86,7 @@ interface UseKeyListProps {
|
||||||
selectedTeam?: Team;
|
selectedTeam?: Team;
|
||||||
currentOrg: Organization | null;
|
currentOrg: Organization | null;
|
||||||
selectedKeyAlias: string | null;
|
selectedKeyAlias: string | null;
|
||||||
|
selectedUserId: string | null;
|
||||||
accessToken: string;
|
accessToken: string;
|
||||||
currentPage?: number;
|
currentPage?: number;
|
||||||
}
|
}
|
||||||
|
@ -110,6 +111,7 @@ const useKeyList = ({
|
||||||
selectedTeam,
|
selectedTeam,
|
||||||
currentOrg,
|
currentOrg,
|
||||||
selectedKeyAlias,
|
selectedKeyAlias,
|
||||||
|
selectedUserId,
|
||||||
accessToken,
|
accessToken,
|
||||||
currentPage = 1,
|
currentPage = 1,
|
||||||
}: UseKeyListProps): UseKeyListReturn => {
|
}: UseKeyListProps): UseKeyListReturn => {
|
||||||
|
@ -136,6 +138,7 @@ const useKeyList = ({
|
||||||
currentOrg?.organization_id || null,
|
currentOrg?.organization_id || null,
|
||||||
selectedTeam?.team_id || "",
|
selectedTeam?.team_id || "",
|
||||||
selectedKeyAlias,
|
selectedKeyAlias,
|
||||||
|
selectedUserId,
|
||||||
params.page as number || 1,
|
params.page as number || 1,
|
||||||
50,
|
50,
|
||||||
);
|
);
|
||||||
|
@ -159,9 +162,11 @@ const useKeyList = ({
|
||||||
'accessToken',
|
'accessToken',
|
||||||
accessToken,
|
accessToken,
|
||||||
'selectedKeyAlias',
|
'selectedKeyAlias',
|
||||||
selectedKeyAlias
|
selectedKeyAlias,
|
||||||
|
'selectedUserId',
|
||||||
|
selectedUserId
|
||||||
);
|
);
|
||||||
}, [selectedTeam, currentOrg, accessToken, selectedKeyAlias]);
|
}, [selectedTeam, currentOrg, accessToken, selectedKeyAlias, selectedUserId]);
|
||||||
|
|
||||||
const setKeys = (newKeysOrUpdater: KeyResponse[] | ((prevKeys: KeyResponse[]) => KeyResponse[])) => {
|
const setKeys = (newKeysOrUpdater: KeyResponse[] | ((prevKeys: KeyResponse[]) => KeyResponse[])) => {
|
||||||
setKeyData(prevData => {
|
setKeyData(prevData => {
|
||||||
|
|
|
@ -2605,7 +2605,8 @@ export const keyListCall = async (
|
||||||
accessToken: String,
|
accessToken: String,
|
||||||
organizationID: string | null,
|
organizationID: string | null,
|
||||||
teamID: string | null,
|
teamID: string | null,
|
||||||
selectedKeyAlias: string | null,
|
keyAlias: string | null,
|
||||||
|
userId: string | null,
|
||||||
page: number,
|
page: number,
|
||||||
pageSize: number,
|
pageSize: number,
|
||||||
) => {
|
) => {
|
||||||
|
@ -2625,8 +2626,12 @@ export const keyListCall = async (
|
||||||
queryParams.append('organization_id', organizationID.toString());
|
queryParams.append('organization_id', organizationID.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (selectedKeyAlias) {
|
if (keyAlias) {
|
||||||
queryParams.append('key_alias', selectedKeyAlias)
|
queryParams.append('key_alias', keyAlias)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (userId) {
|
||||||
|
queryParams.append('user_id', userId)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (page) {
|
if (page) {
|
||||||
|
|
|
@ -110,6 +110,7 @@ const UserDashboard: React.FC<UserDashboardProps> = ({
|
||||||
};
|
};
|
||||||
const [selectedTeam, setSelectedTeam] = useState<any | null>(null);
|
const [selectedTeam, setSelectedTeam] = useState<any | null>(null);
|
||||||
const [selectedKeyAlias, setSelectedKeyAlias] = useState<string | null>(null);
|
const [selectedKeyAlias, setSelectedKeyAlias] = useState<string | null>(null);
|
||||||
|
const [selectedUserId, setSelectedUserId] = useState<string | null>(null);
|
||||||
// check if window is not undefined
|
// check if window is not undefined
|
||||||
if (typeof window !== "undefined") {
|
if (typeof window !== "undefined") {
|
||||||
window.addEventListener("beforeunload", function () {
|
window.addEventListener("beforeunload", function () {
|
||||||
|
@ -428,6 +429,8 @@ const UserDashboard: React.FC<UserDashboardProps> = ({
|
||||||
setSelectedTeam={setSelectedTeam}
|
setSelectedTeam={setSelectedTeam}
|
||||||
selectedKeyAlias={selectedKeyAlias}
|
selectedKeyAlias={selectedKeyAlias}
|
||||||
setSelectedKeyAlias={setSelectedKeyAlias}
|
setSelectedKeyAlias={setSelectedKeyAlias}
|
||||||
|
selectedUserId={selectedUserId}
|
||||||
|
setSelectedUserId={setSelectedUserId}
|
||||||
data={keys}
|
data={keys}
|
||||||
setData={setKeys}
|
setData={setKeys}
|
||||||
premiumUser={premiumUser}
|
premiumUser={premiumUser}
|
||||||
|
|
|
@ -110,6 +110,8 @@ interface ViewKeyTableProps {
|
||||||
setCurrentOrg: React.Dispatch<React.SetStateAction<Organization | null>>;
|
setCurrentOrg: React.Dispatch<React.SetStateAction<Organization | null>>;
|
||||||
selectedKeyAlias: string | null;
|
selectedKeyAlias: string | null;
|
||||||
setSelectedKeyAlias: Setter<string | null>;
|
setSelectedKeyAlias: Setter<string | null>;
|
||||||
|
selectedUserId: string | null;
|
||||||
|
setSelectedUserId: Setter<string | null>;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ItemData {
|
interface ItemData {
|
||||||
|
@ -159,7 +161,9 @@ const ViewKeyTable: React.FC<ViewKeyTableProps> = ({
|
||||||
organizations,
|
organizations,
|
||||||
setCurrentOrg,
|
setCurrentOrg,
|
||||||
selectedKeyAlias,
|
selectedKeyAlias,
|
||||||
setSelectedKeyAlias
|
setSelectedKeyAlias,
|
||||||
|
selectedUserId,
|
||||||
|
setSelectedUserId
|
||||||
}) => {
|
}) => {
|
||||||
const [isButtonClicked, setIsButtonClicked] = useState(false);
|
const [isButtonClicked, setIsButtonClicked] = useState(false);
|
||||||
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
|
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
|
||||||
|
@ -185,6 +189,7 @@ const ViewKeyTable: React.FC<ViewKeyTableProps> = ({
|
||||||
selectedTeam,
|
selectedTeam,
|
||||||
currentOrg,
|
currentOrg,
|
||||||
selectedKeyAlias,
|
selectedKeyAlias,
|
||||||
|
selectedUserId,
|
||||||
accessToken,
|
accessToken,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -440,6 +445,8 @@ const ViewKeyTable: React.FC<ViewKeyTableProps> = ({
|
||||||
refresh={refresh}
|
refresh={refresh}
|
||||||
selectedKeyAlias={selectedKeyAlias}
|
selectedKeyAlias={selectedKeyAlias}
|
||||||
setSelectedKeyAlias={setSelectedKeyAlias}
|
setSelectedKeyAlias={setSelectedKeyAlias}
|
||||||
|
selectedUserId={selectedUserId}
|
||||||
|
setSelectedUserId={setSelectedUserId}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{isDeleteModalOpen && (
|
{isDeleteModalOpen && (
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue