Fix wrong URL for internal user invitation (#7762)

* format

* done
This commit is contained in:
yujonglee 2025-01-16 08:10:32 +09:00 committed by GitHub
parent 6fff77d131
commit 503b38e609
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 224 additions and 240 deletions

View file

@ -20,17 +20,15 @@ import Usage from "../components/usage";
import CacheDashboard from "@/components/cache_dashboard";
import { jwtDecode } from "jwt-decode";
import { Typography } from "antd";
import { setGlobalLitellmHeaderName } from "../components/networking"
import { setGlobalLitellmHeaderName } from "../components/networking";
function getCookie(name: string) {
console.log("COOKIES", document.cookie)
const cookieValue = document.cookie
.split('; ')
.find(row => row.startsWith(name + '='));
return cookieValue ? cookieValue.split('=')[1] : null;
.split("; ")
.find((row) => row.startsWith(name + "="));
return cookieValue ? cookieValue.split("=")[1] : null;
}
function formatUserRole(userRole: string) {
if (!userRole) {
return "Undefined Role";
@ -68,7 +66,8 @@ const CreateKeyPage = () => {
const { Title, Paragraph } = Typography;
const [userRole, setUserRole] = useState("");
const [premiumUser, setPremiumUser] = useState(false);
const [disabledPersonalKeyCreation, setDisabledPersonalKeyCreation] = useState(false);
const [disabledPersonalKeyCreation, setDisabledPersonalKeyCreation] =
useState(false);
const [userEmail, setUserEmail] = useState<null | string>(null);
const [teams, setTeams] = useState<null | any[]>(null);
const [keys, setKeys] = useState<null | any[]>(null);
@ -80,244 +79,244 @@ const CreateKeyPage = () => {
const [showSSOBanner, setShowSSOBanner] = useState<boolean>(true);
const searchParams = useSearchParams()!;
const [modelData, setModelData] = useState<any>({ data: [] });
const [token, setToken] = useState<string | null>(null);
const userID = searchParams.get("userID");
const invitation_id = searchParams.get("invitation_id");
const token = getCookie('token');
// Get page from URL, default to 'api-keys' if not present
const [page, setPage] = useState(() => {
return searchParams.get('page') || 'api-keys';
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);
newSearchParams.set("page", newPage);
// Use Next.js router to update URL
window.history.pushState(
null,
'',
`?${newSearchParams.toString()}`
);
window.history.pushState(null, "", `?${newSearchParams.toString()}`);
setPage(newPage);
};
const [accessToken, setAccessToken] = useState<string | null>(null);
useEffect(() => {
if (token) {
const decoded = jwtDecode(token) as { [key: string]: any };
if (decoded) {
// cast decoded to dictionary
console.log("Decoded token:", decoded);
const token = getCookie("token");
setToken(token);
}, []);
console.log("Decoded key:", decoded.key);
// set accessToken
setAccessToken(decoded.key);
useEffect(() => {
if (!token) {
return;
}
setDisabledPersonalKeyCreation(decoded.disabled_non_admin_personal_key_creation);
const decoded = jwtDecode(token) as { [key: string]: any };
if (decoded) {
// cast decoded to dictionary
console.log("Decoded token:", decoded);
// check if userRole is defined
if (decoded.user_role) {
const formattedUserRole = formatUserRole(decoded.user_role);
console.log("Decoded user_role:", formattedUserRole);
setUserRole(formattedUserRole);
if (formattedUserRole == "Admin Viewer") {
setPage("usage");
}
} else {
console.log("User role not defined");
console.log("Decoded key:", decoded.key);
// 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);
console.log("Decoded user_role:", formattedUserRole);
setUserRole(formattedUserRole);
if (formattedUserRole == "Admin Viewer") {
setPage("usage");
}
} else {
console.log("User role not defined");
}
if (decoded.user_email) {
setUserEmail(decoded.user_email);
} else {
console.log(`User Email is not set ${decoded}`);
}
if (decoded.user_email) {
setUserEmail(decoded.user_email);
} else {
console.log(`User Email is not set ${decoded}`);
}
if (decoded.login_method) {
setShowSSOBanner(
decoded.login_method == "username_password" ? true : false
);
} else {
console.log(`User Email is not set ${decoded}`);
}
if (decoded.login_method) {
setShowSSOBanner(
decoded.login_method == "username_password" ? true : false,
);
} else {
console.log(`User Email is not set ${decoded}`);
}
if (decoded.premium_user) {
setPremiumUser(decoded.premium_user);
}
if (decoded.premium_user) {
setPremiumUser(decoded.premium_user);
}
if (decoded.auth_header_name) {
setGlobalLitellmHeaderName(decoded.auth_header_name);
}
if (decoded.auth_header_name) {
setGlobalLitellmHeaderName(decoded.auth_header_name);
}
}
}, [token]);
return (
<Suspense fallback={<div>Loading...</div>}>
{
invitation_id ? (
<UserDashboard
userID={userID}
userRole={userRole}
premiumUser={premiumUser}
teams={teams}
keys={keys}
setUserRole={setUserRole}
userEmail={userEmail}
setUserEmail={setUserEmail}
setTeams={setTeams}
setKeys={setKeys}
/>
) : (
<div className="flex flex-col min-h-screen">
<Navbar
{invitation_id ? (
<UserDashboard
userID={userID}
userRole={userRole}
userEmail={userEmail}
premiumUser={premiumUser}
setProxySettings={setProxySettings}
proxySettings={proxySettings}
teams={teams}
keys={keys}
setUserRole={setUserRole}
userEmail={userEmail}
setUserEmail={setUserEmail}
setTeams={setTeams}
setKeys={setKeys}
/>
<div className="flex flex-1 overflow-auto">
<div className="mt-8">
) : (
<div className="flex flex-col min-h-screen">
<Navbar
userID={userID}
userRole={userRole}
userEmail={userEmail}
premiumUser={premiumUser}
setProxySettings={setProxySettings}
proxySettings={proxySettings}
/>
<div className="flex flex-1 overflow-auto">
<div className="mt-8">
<Sidebar
setPage={updatePage}
userRole={userRole}
defaultSelectedKey={page}
/>
</div>
/>
</div>
{page == "api-keys" ? (
<UserDashboard
userID={userID}
userRole={userRole}
premiumUser={premiumUser}
teams={teams}
keys={keys}
setUserRole={setUserRole}
userEmail={userEmail}
setUserEmail={setUserEmail}
setTeams={setTeams}
setKeys={setKeys}
/>
) : page == "models" ? (
<ModelDashboard
userID={userID}
userRole={userRole}
token={token}
keys={keys}
accessToken={accessToken}
modelData={modelData}
setModelData={setModelData}
premiumUser={premiumUser}
/>
) : 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}
/>
) : page == "organizations" ? (
<Organizations
teams={teams}
setTeams={setTeams}
searchParams={searchParams}
accessToken={accessToken}
userID={userID}
userRole={userRole}
premiumUser={premiumUser}
/>
) : page == "admin-panel" ? (
<AdminPanel
setTeams={setTeams}
searchParams={searchParams}
accessToken={accessToken}
showSSOBanner={showSSOBanner}
premiumUser={premiumUser}
/>
) : page == "api_ref" ? (
<APIRef
proxySettings={proxySettings}
/>
) : page == "settings" ? (
<Settings
userID={userID}
userRole={userRole}
accessToken={accessToken}
premiumUser={premiumUser}
/>
) : page == "budgets" ? (
<BudgetPanel accessToken={accessToken} />
) : page == "general-settings" ? (
<GeneralSettings
userID={userID}
userRole={userRole}
accessToken={accessToken}
modelData={modelData}
/>
) : page == "model-hub" ? (
<ModelHub
accessToken={accessToken}
publicPage={false}
premiumUser={premiumUser}
/>
) : 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}
/>
) : (
<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}
/>
) : page == "models" ? (
<ModelDashboard
userID={userID}
userRole={userRole}
token={token}
keys={keys}
accessToken={accessToken}
modelData={modelData}
setModelData={setModelData}
premiumUser={premiumUser}
/>
) : 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}
/>
) : page == "organizations" ? (
<Organizations
teams={teams}
setTeams={setTeams}
searchParams={searchParams}
accessToken={accessToken}
userID={userID}
userRole={userRole}
premiumUser={premiumUser}
/>
) : page == "admin-panel" ? (
<AdminPanel
setTeams={setTeams}
searchParams={searchParams}
accessToken={accessToken}
showSSOBanner={showSSOBanner}
premiumUser={premiumUser}
/>
) : page == "api_ref" ? (
<APIRef proxySettings={proxySettings} />
) : page == "settings" ? (
<Settings
userID={userID}
userRole={userRole}
accessToken={accessToken}
premiumUser={premiumUser}
/>
) : page == "budgets" ? (
<BudgetPanel accessToken={accessToken} />
) : page == "general-settings" ? (
<GeneralSettings
userID={userID}
userRole={userRole}
accessToken={accessToken}
modelData={modelData}
/>
) : page == "model-hub" ? (
<ModelHub
accessToken={accessToken}
publicPage={false}
premiumUser={premiumUser}
/>
) : 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}
/>
) : (
<Usage
userID={userID}
userRole={userRole}
token={token}
accessToken={accessToken}
keys={keys}
premiumUser={premiumUser}
/>
)}
</div>
</div>
</div>
)
}
)}
</Suspense>
);
};

View file

@ -53,11 +53,9 @@ const Createuser: React.FC<CreateuserProps> = ({
useState<InvitationLink | null>(null);
const router = useRouter();
const isLocal = process.env.NODE_ENV === "development";
if (isLocal != true) {
console.log = function() {};
}
const [baseUrl, setBaseUrl] = useState(
isLocal ? "http://localhost:4000" : ""
isLocal ? "http://localhost:4000" : "",
);
// get all models
useEffect(() => {
@ -67,7 +65,7 @@ const Createuser: React.FC<CreateuserProps> = ({
const modelDataResponse = await modelAvailableCall(
accessToken,
userID,
userRole
userRole,
);
// Assuming modelDataResponse.data contains an array of model objects with a 'model_name' property
const availableModels = [];
@ -81,27 +79,26 @@ const Createuser: React.FC<CreateuserProps> = ({
// Assuming modelDataResponse.data contains an array of model names
setUserModels(availableModels);
// get ui settings
// get ui settings
const uiSettingsResponse = await getProxyUISettings(accessToken);
console.log("uiSettingsResponse:", uiSettingsResponse);
setUISettings(uiSettingsResponse);
} catch (error) {
console.error("Error fetching model data:", error);
}
};
fetchData(); // Call the function to fetch model data when the component mounts
}, []); // Empty dependency array to run only once
useEffect(() => {
if (router) {
const { protocol, host } = window.location;
const baseUrl = `${protocol}/${host}`;
setBaseUrl(baseUrl);
if (!router) {
return;
}
const base = new URL("/", window.location.href);
setBaseUrl(base.toString());
}, [router]);
const handleOk = () => {
setIsModalVisible(false);
@ -123,7 +120,7 @@ const Createuser: React.FC<CreateuserProps> = ({
console.log("user create Response:", response);
setApiuser(response["key"]);
const user_id = response.data?.user_id || response.user_id;
// only do invite link flow if sso is not enabled
if (!uiSettings?.SSO_ENABLED) {
invitationCreateCall(accessToken, user_id).then((data) => {
@ -132,7 +129,7 @@ const Createuser: React.FC<CreateuserProps> = ({
setIsInvitationLinkModalVisible(true);
});
} else {
// create an InvitationLink Object for this user for the SSO flow
// create an InvitationLink Object for this user for the SSO flow
// for SSO the invite link is the proxy base url since the User just needs to login
const invitationLink: InvitationLink = {
id: crypto.randomUUID(), // Generate a unique ID
@ -198,7 +195,7 @@ const Createuser: React.FC<CreateuserProps> = ({
</p>
</div>
</SelectItem>
)
),
)}
</Select2>
</Form.Item>

View file

@ -1,16 +1,8 @@
import React, { useState, useEffect } from "react";
import {
Button as Button2,
Modal,
Form,
Input,
Select as Select2,
InputNumber,
message,
Typography,
} from "antd";
import React from "react";
import { Modal, message, Typography } from "antd";
import { CopyToClipboard } from "react-copy-to-clipboard";
import { Text, Button } from "@tremor/react";
export interface InvitationLink {
id: string;
user_id: string;
@ -33,12 +25,12 @@ interface OnboardingProps {
invitationLinkData: InvitationLink | null;
}
const OnboardingModal: React.FC<OnboardingProps> = ({
export default function OnboardingModal({
isInvitationLinkModalVisible,
setIsInvitationLinkModalVisible,
baseUrl,
invitationLinkData,
}) => {
}: OnboardingProps) {
const { Title, Paragraph } = Typography;
const handleInvitationOk = () => {
setIsInvitationLinkModalVisible(false);
@ -50,9 +42,9 @@ const OnboardingModal: React.FC<OnboardingProps> = ({
const getInvitationUrl = () => {
if (invitationLinkData?.has_user_setup_sso) {
return `${baseUrl}/ui`;
return new URL("/ui", baseUrl).toString();
}
return `${baseUrl}/ui?invitation_id=${invitationLinkData?.id}`;
return new URL(`/ui?invitation_id=${invitationLinkData?.id}`, baseUrl).toString();
};
return (
@ -64,7 +56,6 @@ const OnboardingModal: React.FC<OnboardingProps> = ({
onOk={handleInvitationOk}
onCancel={handleInvitationCancel}
>
{/* {JSON.stringify(invitationLinkData)} */}
<Paragraph>
Copy and send the generated link to onboard this user to the proxy.
</Paragraph>
@ -75,11 +66,10 @@ const OnboardingModal: React.FC<OnboardingProps> = ({
<div className="flex justify-between pt-5 pb-2">
<Text>Invitation Link</Text>
<Text>
<Text>{getInvitationUrl()}</Text>
<Text>{getInvitationUrl()}</Text>
</Text>
</div>
<div className="flex justify-end mt-5">
<div></div>
<CopyToClipboard
text={getInvitationUrl()}
onCopy={() => message.success("Copied!")}
@ -89,6 +79,4 @@ const OnboardingModal: React.FC<OnboardingProps> = ({
</div>
</Modal>
);
};
export default OnboardingModal;
}