forked from phoenix/litellm-mirror
Merge pull request #3929 from BerriAI/litellm_edit_user_role_admin_ui
[UI] edit user role admin UI
This commit is contained in:
commit
b6ec312f60
4 changed files with 85 additions and 8 deletions
|
@ -7791,7 +7791,6 @@ async def user_auth(request: Request):
|
|||
|
||||
params = {
|
||||
"sender_name": "LiteLLM Proxy",
|
||||
"sender_email": os.getenv("SMTP_SENDER_EMAIL"),
|
||||
"receiver_email": user_email,
|
||||
"subject": "Your Magic Link",
|
||||
"html": f"<strong> Follow this link, to login:\n\n{base_url}user/?token={response['token']}&user_id={response['user_id']}&page={page_params}</strong>",
|
||||
|
@ -7801,6 +7800,42 @@ async def user_auth(request: Request):
|
|||
return "Email sent!"
|
||||
|
||||
|
||||
@router.get(
|
||||
"/user/available_roles",
|
||||
tags=["Internal User management"],
|
||||
include_in_schema=False,
|
||||
dependencies=[Depends(user_api_key_auth)],
|
||||
)
|
||||
async def ui_get_available_role(
|
||||
user_api_key_dict: UserAPIKeyAuth = Depends(user_api_key_auth),
|
||||
):
|
||||
"""
|
||||
Endpoint used by Admin UI to show all available roles to assign a user
|
||||
return {
|
||||
"proxy_admin": {
|
||||
"description": "Proxy Admin role",
|
||||
"ui_label": "Admin"
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
_data_to_return = {}
|
||||
for role in LitellmUserRoles:
|
||||
|
||||
# We only show a subset of roles on UI
|
||||
if role in [
|
||||
LitellmUserRoles.PROXY_ADMIN,
|
||||
LitellmUserRoles.PROXY_ADMIN_VIEW_ONLY,
|
||||
LitellmUserRoles.INTERNAL_USER,
|
||||
LitellmUserRoles.INTERNAL_USER_VIEW_ONLY,
|
||||
]:
|
||||
_data_to_return[role.value] = {
|
||||
"description": role.description,
|
||||
"ui_label": role.ui_label,
|
||||
}
|
||||
return _data_to_return
|
||||
|
||||
|
||||
@router.get(
|
||||
"/user/info",
|
||||
tags=["Internal User management"],
|
||||
|
|
|
@ -23,12 +23,13 @@ import {
|
|||
|
||||
interface EditUserModalProps {
|
||||
visible: boolean;
|
||||
possibleUIRoles: null | Record<string, Record<string, string>>;
|
||||
onCancel: () => void;
|
||||
user: any;
|
||||
onSubmit: (data: any) => void;
|
||||
}
|
||||
|
||||
const EditUserModal: React.FC<EditUserModalProps> = ({ visible, onCancel, user, onSubmit }) => {
|
||||
const EditUserModal: React.FC<EditUserModalProps> = ({ visible, possibleUIRoles, onCancel, user, onSubmit }) => {
|
||||
const [editedUser, setEditedUser] = useState(user);
|
||||
const [form] = Form.useForm();
|
||||
|
||||
|
@ -43,9 +44,9 @@ const EditUserModal: React.FC<EditUserModalProps> = ({ visible, onCancel, user,
|
|||
|
||||
const handleEditSubmit = async (formValues: Record<string, any>) => {
|
||||
// Call API to update team with teamId and values
|
||||
form.resetFields();
|
||||
|
||||
onSubmit(formValues);
|
||||
|
||||
form.resetFields();
|
||||
onCancel();
|
||||
};
|
||||
|
||||
|
@ -94,8 +95,14 @@ const EditUserModal: React.FC<EditUserModalProps> = ({ visible, onCancel, user,
|
|||
name="user_role"
|
||||
>
|
||||
<Select2>
|
||||
<Select2.Option value="proxy_admin">Proxy Admin (Can create, edit, delete keys, teams)</Select2.Option>
|
||||
<Select2.Option value="proxy_admin_viewer">Proxy Viewer (Can just view spend, cannot created keys, teams)</Select2.Option>
|
||||
{possibleUIRoles &&
|
||||
Object.entries(possibleUIRoles).map(([role, { ui_label, description }]) => (
|
||||
<SelectItem key={role} value={role} title={ui_label}>
|
||||
<div className='flex'>
|
||||
{ui_label} <p className="ml-2" style={{ color: "gray", fontSize: "12px" }}>{description}</p>
|
||||
</div>
|
||||
</SelectItem>
|
||||
))}
|
||||
</Select2>
|
||||
|
||||
</Form.Item>
|
||||
|
|
|
@ -1390,6 +1390,34 @@ export const userGetAllUsersCall = async (
|
|||
}
|
||||
};
|
||||
|
||||
export const getPossibleUserRoles = async (
|
||||
accessToken: String,
|
||||
) => {
|
||||
try {
|
||||
const url = proxyBaseUrl
|
||||
? `${proxyBaseUrl}/user/available_roles`
|
||||
: `/user/available_roles`;
|
||||
const response = await fetch(url, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.text();
|
||||
throw new Error("Network response was not ok");
|
||||
}
|
||||
const data = await response.json();
|
||||
console.log("response from user/available_role", data);
|
||||
return data;
|
||||
// Handle success - you might want to update some state or UI based on the created key
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
export const teamCreateCall = async (
|
||||
accessToken: string,
|
||||
formValues: Record<string, any> // Assuming formValues is an object
|
||||
|
|
|
@ -24,7 +24,7 @@ import {
|
|||
Icon,
|
||||
TextInput,
|
||||
} from "@tremor/react";
|
||||
import { userInfoCall, userUpdateUserCall } from "./networking";
|
||||
import { userInfoCall, userUpdateUserCall, getPossibleUserRoles } from "./networking";
|
||||
import { Badge, BadgeDelta, Button } from "@tremor/react";
|
||||
import RequestAccess from "./request_model_access";
|
||||
import CreateUser from "./create_user_button";
|
||||
|
@ -62,6 +62,7 @@ const ViewUserDashboard: React.FC<ViewUserDashboardProps> = ({
|
|||
const [selectedItem, setSelectedItem] = useState<null | any>(null);
|
||||
const [editModalVisible, setEditModalVisible] = useState(false);
|
||||
const [selectedUser, setSelectedUser] = useState(null);
|
||||
const [possibleUIRoles, setPossibleUIRoles] = useState<Record<string, Record<string, string>>>({});
|
||||
const defaultPageSize = 25;
|
||||
|
||||
const handleEditCancel = async () => {
|
||||
|
@ -76,7 +77,7 @@ const ViewUserDashboard: React.FC<ViewUserDashboardProps> = ({
|
|||
return;
|
||||
}
|
||||
|
||||
userUpdateUserCall(accessToken, editedUser, userRole);
|
||||
userUpdateUserCall(accessToken, editedUser, null);
|
||||
|
||||
if (userData) {
|
||||
const updatedUserData = userData.map((user) =>
|
||||
|
@ -106,11 +107,16 @@ const ViewUserDashboard: React.FC<ViewUserDashboardProps> = ({
|
|||
);
|
||||
console.log("user data response:", userDataResponse);
|
||||
setUserData(userDataResponse);
|
||||
|
||||
const availableUserRoles = await getPossibleUserRoles(accessToken);
|
||||
setPossibleUIRoles(availableUserRoles);
|
||||
|
||||
} catch (error) {
|
||||
console.error("There was an error fetching the model data", error);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
if (accessToken && token && userRole && userID) {
|
||||
fetchData();
|
||||
}
|
||||
|
@ -273,6 +279,7 @@ const ViewUserDashboard: React.FC<ViewUserDashboardProps> = ({
|
|||
</TabGroup>
|
||||
<EditUserModal
|
||||
visible={editModalVisible}
|
||||
possibleUIRoles={possibleUIRoles}
|
||||
onCancel={handleEditCancel}
|
||||
user={selectedUser}
|
||||
onSubmit={handleEditSubmit}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue