diff --git a/web/src/pages/admin/forms/user-form.tsx b/web/src/pages/admin/forms/user-form.tsx index 2ffe16a26..144709f3f 100644 --- a/web/src/pages/admin/forms/user-form.tsx +++ b/web/src/pages/admin/forms/user-form.tsx @@ -24,7 +24,9 @@ import { SelectValue, } from '@/components/ui/select'; import { listRoles } from '@/services/admin-service'; + import EnterpriseFeature from '../components/enterprise-feature'; +import { IS_ENTERPRISE } from '../utils'; interface CreateUserFormData { email: string; @@ -49,6 +51,8 @@ export const CreateUserForm = ({ const { data: roleList } = useQuery({ queryKey: ['admin/listRoles'], queryFn: async () => (await listRoles()).data.data.roles, + enabled: IS_ENTERPRISE, + retry: false, }); return ( diff --git a/web/src/pages/admin/index.tsx b/web/src/pages/admin/index.tsx index 9009de635..3b33cec15 100644 --- a/web/src/pages/admin/index.tsx +++ b/web/src/pages/admin/index.tsx @@ -1,3 +1,16 @@ +import { type AxiosResponseHeaders } from 'axios'; +import { useEffect, useId, useState } from 'react'; +import { useForm } from 'react-hook-form'; +import { useTranslation } from 'react-i18next'; +import { useNavigate } from 'umi'; + +import { LucideEye, LucideEyeOff } from 'lucide-react'; + +import { useMutation } from '@tanstack/react-query'; + +import { zodResolver } from '@hookform/resolvers/zod'; +import { z } from 'zod'; + import Spotlight from '@/components/spotlight'; import { ButtonLoading } from '@/components/ui/button'; import { Card, CardContent, CardFooter } from '@/components/ui/card'; @@ -11,22 +24,17 @@ import { FormMessage, } from '@/components/ui/form'; import { Input } from '@/components/ui/input'; +import { ScrollArea } from '@/components/ui/scroll-area'; import { Authorization } from '@/constants/authorization'; + import { useAuth } from '@/hooks/auth-hooks'; import { cn } from '@/lib/utils'; import { Routes } from '@/routes'; -import adminService from '@/services/admin-service'; import { rsaPsw } from '@/utils'; import authorizationUtil from '@/utils/authorization-util'; -import { zodResolver } from '@hookform/resolvers/zod'; -import { useMutation } from '@tanstack/react-query'; -import { AxiosResponseHeaders } from 'axios'; -import { LucideEye, LucideEyeOff } from 'lucide-react'; -import { useEffect, useId, useState } from 'react'; -import { SubmitHandler, useForm } from 'react-hook-form'; -import { useTranslation } from 'react-i18next'; -import { useNavigate } from 'umi'; -import { z } from 'zod'; + +import { login } from '@/services/admin-service'; + import { BgSvg } from '../login-next/bg'; import ThemeSwitch from './components/theme-switch'; @@ -37,13 +45,19 @@ function AdminLogin() { const [showPassword, setShowPassword] = useState(false); - const { isPending: signLoading, mutateAsync: login } = useMutation({ + const loginMutation = useMutation({ mutationKey: ['adminLogin'], mutationFn: async (params: { email: string; password: string }) => { - const request = await adminService.login(params); + const rsaPassWord = rsaPsw(params.password) as string; + return await login({ + email: params.email, + password: rsaPassWord, + }); + }, + onSuccess: (request) => { const { data: req, headers } = request; - if (req.code === 0) { + if (req?.code === 0) { const authorization = (headers as AxiosResponseHeaders)?.get( Authorization, ); @@ -60,13 +74,17 @@ function AdminLogin() { Token: token, userInfo: JSON.stringify(userInfo), }); - } - return req; + navigate('/admin/services'); + } }, + onError: (error) => { + console.log('Failed:', error); + }, + retry: false, }); - const loading = signLoading; + const loading = loginMutation.isPending; useEffect(() => { if (isLogin) { @@ -93,172 +111,159 @@ function AdminLogin() { resolver: zodResolver(FormSchema), }); - const onCheck: SubmitHandler> = async (params) => { - try { - const rsaPassWord = rsaPsw(params.password) as string; - - const { code } = await login({ - email: `${params.email}`.trim(), - password: rsaPassWord, - }); - - if (code === 0) { - navigate('/admin/services'); - } - } catch (errorInfo) { - console.log('Failed:', errorInfo); - } - }; - return ( -
- - - + +
+ + + - + -
-
- logo - RAGFlow +
+
+ logo + RAGFlow +
+ +

+ {t('loginTitle', { keyPrefix: 'admin' })} +

-

- {t('loginTitle', { keyPrefix: 'admin' })} -

-
- -
-
- - -
- - ( - - {t('emailLabel')} - - - - - - - +
+
+ + + + + loginMutation.mutate(data), )} - /> + > + ( + + {t('emailLabel')} - ( - - {t('passwordLabel')} - - -
+ - -
-
- - -
- )} - /> - - ( - - - - - {t('rememberMe')} - - - )} - /> - - -
+ + + )} + /> - - - {t('login')} - - -
+ ( + + {t('passwordLabel')} -
- + +
+ + +
+
+ + + + )} + /> + + ( + + + + + + + {t('rememberMe')} + + + )} + /> + + + + + + + {t('login')} + + + + +
+ +
-
+ ); } diff --git a/web/src/pages/admin/layout.tsx b/web/src/pages/admin/layout.tsx index 5ee2cf895..f019fa22c 100644 --- a/web/src/pages/admin/layout.tsx +++ b/web/src/pages/admin/layout.tsx @@ -14,13 +14,12 @@ import { } from 'lucide-react'; import { useMemo } from 'react'; import { useTranslation } from 'react-i18next'; -import { NavLink, Outlet, useLocation, useNavigate } from 'umi'; +import { NavLink, Outlet, useNavigate } from 'umi'; import ThemeSwitch from './components/theme-switch'; import { IS_ENTERPRISE } from './utils'; const AdminLayout = () => { const { t } = useTranslation(); - const { pathname } = useLocation(); const navigate = useNavigate(); const navItems = useMemo( @@ -58,11 +57,7 @@ const AdminLayout = () => { [t], ); - const { - data, - isPending, - mutateAsync: logout, - } = useMutation({ + const logoutMutation = useMutation({ mutationKey: ['adminLogout'], mutationFn: async () => { await adminService.logout(); @@ -71,11 +66,12 @@ const AdminLayout = () => { authorizationUtil.removeAll(); navigate(Routes.Admin); }, + retry: false, }); return (
-
diff --git a/web/src/pages/admin/user-detail.tsx b/web/src/pages/admin/user-detail.tsx index f92e478cf..656cf2c1d 100644 --- a/web/src/pages/admin/user-detail.tsx +++ b/web/src/pages/admin/user-detail.tsx @@ -307,6 +307,7 @@ function AdminUserDetail() { }; }, enabled: !!id, + retry: false, }); return ( diff --git a/web/src/pages/admin/users.tsx b/web/src/pages/admin/users.tsx index ebaf91b35..dc0d9090d 100644 --- a/web/src/pages/admin/users.tsx +++ b/web/src/pages/admin/users.tsx @@ -126,11 +126,14 @@ function AdminUserManagement() { const { data: roleList } = useQuery({ queryKey: ['admin/listRoles'], queryFn: async () => (await listRoles()).data.data.roles, + enabled: IS_ENTERPRISE, + retry: false, }); const { data: usersList, isPending } = useQuery({ queryKey: ['admin/listUsers'], queryFn: async () => (await listUsers()).data.data, + retry: false, }); // Delete user mutation @@ -142,17 +145,19 @@ function AdminUserManagement() { setDeleteModalOpen(false); setUserToMakeAction(null); }, + retry: false, }); // Change password mutation const changePasswordMutation = useMutation({ mutationFn: ({ email, password }: { email: string; password: string }) => - updateUserPassword(email, password), + updateUserPassword(email, rsaPsw(password) as string), onSuccess: () => { // message.success(t('admin.passwordChangedSuccessfully')); setPasswordModalOpen(false); setUserToMakeAction(null); }, + retry: false, }); // Update user role mutation @@ -162,6 +167,7 @@ function AdminUserManagement() { onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['admin/listUsers'] }); }, + retry: false, }); // Create user mutation @@ -175,7 +181,7 @@ function AdminUserManagement() { password: string; role?: string; }) => { - await createUser(email, password); + await createUser(email, rsaPsw(password) as string); if (IS_ENTERPRISE && role) { await updateUserRoleMutation.mutateAsync({ email, role }); @@ -187,6 +193,7 @@ function AdminUserManagement() { setCreateUserModalOpen(false); createUserForm.form.reset(); }, + retry: false, }); // Update user status mutation @@ -196,6 +203,7 @@ function AdminUserManagement() { onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['admin/listUsers'] }); }, + retry: false, }); const columnDefs = useMemo( @@ -573,7 +581,8 @@ function AdminUserManagement() { className="px-4 h-10" variant="destructive" onClick={() => - deleteUserMutation.mutate(userToMakeAction?.email || '') + userToMakeAction && + deleteUserMutation.mutate(userToMakeAction?.email) } disabled={deleteUserMutation.isPending} loading={deleteUserMutation.isPending} @@ -586,7 +595,14 @@ function AdminUserManagement() { {/* Change Password Modal */} - + { + if (!passwordModalOpen) { + changePasswordForm.form.reset(); + } + }} + > {t('admin.changePassword')} @@ -599,7 +615,7 @@ function AdminUserManagement() { if (userToMakeAction) { changePasswordMutation.mutate({ email: userToMakeAction.email, - password: rsaPsw(newPassword) as string, + password: newPassword, }); } }} @@ -649,12 +665,7 @@ function AdminUserManagement() {
{ - createUserMutation.mutate({ - email: email, - password: rsaPsw(password) as string, - }); - }} + onSubmit={createUserMutation.mutate} />
diff --git a/web/src/services/admin-service.ts b/web/src/services/admin-service.ts index 329ac9e31..246172756 100644 --- a/web/src/services/admin-service.ts +++ b/web/src/services/admin-service.ts @@ -1,6 +1,6 @@ import { message, notification } from 'antd'; import axios from 'axios'; -import { Navigate } from 'umi'; +import { history } from 'umi'; import { Authorization } from '@/constants/authorization'; import i18n from '@/locales/config'; @@ -48,7 +48,7 @@ request.interceptors.response.use( }); authorizationUtil.removeAll(); - Navigate({ to: Routes.Admin }); + history.push(Routes.Admin); } else if (data?.code && data.code !== 0) { notification.error({ message: `${i18n.t('message.hint')}: ${data?.code}`, @@ -70,15 +70,16 @@ request.interceptors.response.use( }); } else if (data?.code === 100) { message.error(data?.message); - } else if (data?.code === 401) { + } else if (response.status === 401 || data?.code === 401) { notification.error({ - message: data?.message, - description: data?.message, + message: data?.message || response.statusText, + description: + data?.message || RetcodeMessage[response?.status as ResultCode], duration: 3, }); authorizationUtil.removeAll(); - Navigate({ to: Routes.Admin }); + history.push(Routes.Admin); } else if (data?.code && data.code !== 0) { notification.error({ message: `${i18n.t('message.hint')}: ${data?.code}`, @@ -93,17 +94,9 @@ request.interceptors.response.use( }); } else if (response.status === 413 || response?.status === 504) { message.error(RetcodeMessage[response?.status as ResultCode]); - } else if (response.status === 401) { - notification.error({ - message: response.data.message, - description: response.data.message, - duration: 3, - }); - authorizationUtil.removeAll(); - window.location.href = location.origin + '/admin'; } - return error; + throw error; }, ); @@ -112,7 +105,7 @@ const { adminLogout, adminListUsers, adminCreateUser, - adminGetUserDetails: adminShowUserDetails, + adminGetUserDetails, adminUpdateUserStatus, adminUpdateUserPassword, adminDeleteUser, @@ -260,11 +253,11 @@ export namespace AdminService { update_date: string; }; - export type AssignRolePermissionInput = { - permissions: Record>; - }; - - export type RevokeRolePermissionInput = AssignRolePermissionInput; + export type AssignRolePermissionsInput = Record< + string, + Partial + >; + export type RevokeRolePermissionInput = AssignRolePermissionsInput; export type UserDetailWithPermission = { user: { @@ -293,7 +286,7 @@ export const createUser = (email: string, password: string) => }); export const getUserDetails = (email: string) => request.get>( - adminShowUserDetails(email), + adminGetUserDetails(email), ); export const listUserDatasets = (email: string) => request.get>( @@ -317,7 +310,10 @@ export const showServiceDetails = (serviceId: number) => adminShowServiceDetails(String(serviceId)), ); -export const createRole = (params: { roleName: string; description: string }) => +export const createRole = (params: { + roleName: string; + description?: string; +}) => request.post>(adminCreateRole, params); export const updateRoleDescription = (role: string, description: string) => request.put>( @@ -343,15 +339,17 @@ export const getRolePermissions = (role: string) => ); export const assignRolePermissions = ( role: string, - params: AdminService.AssignRolePermissionInput, + permissions: Partial, ) => - request.post>(adminAssignRolePermissions(role), params); + request.post>(adminAssignRolePermissions(role), { + new_permissions: permissions, + }); export const revokeRolePermissions = ( role: string, - params: AdminService.RevokeRolePermissionInput, + permissions: Partial, ) => request.delete>(adminRevokeRolePermissions(role), { - data: params, + data: { revoke_permissions: permissions }, }); export const updateUserRole = (username: string, role: string) => @@ -365,12 +363,23 @@ export const getUserPermissions = (username: string) => export const listResources = () => request.get>(adminListResources); +export const whitelistImportFromExcel = (file: File) => { + const fd = new FormData(); + + fd.append('file', file); + + return request.post>( + '/api/v1/admin/whitelist/import', + fd, + ); +}; + export default { login, logout, listUsers, createUser, - showUserDetails: getUserDetails, + getUserDetails, updateUserStatus, updateUserPassword, deleteUser,