### What problem does this PR solve? feat: Bind data to TenantTable #2846 feat: Add TenantTable ### Type of change - [x] New Feature (non-breaking change which adds functionality)tags/v0.13.0
| import { ResponseGetType } from '@/interfaces/database/base'; | import { ResponseGetType } from '@/interfaces/database/base'; | ||||
| import { IToken } from '@/interfaces/database/chat'; | import { IToken } from '@/interfaces/database/chat'; | ||||
| import { ITenantInfo } from '@/interfaces/database/knowledge'; | import { ITenantInfo } from '@/interfaces/database/knowledge'; | ||||
| import { ISystemStatus, IUserInfo } from '@/interfaces/database/user-setting'; | |||||
| import userService from '@/services/user-service'; | |||||
| import { | |||||
| ISystemStatus, | |||||
| ITenant, | |||||
| ITenantUser, | |||||
| IUserInfo, | |||||
| } from '@/interfaces/database/user-setting'; | |||||
| import userService, { | |||||
| addTenantUser, | |||||
| agreeTenant, | |||||
| deleteTenantUser, | |||||
| listTenant, | |||||
| listTenantUser, | |||||
| } from '@/services/user-service'; | |||||
| import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; | import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; | ||||
| import { Modal, message } from 'antd'; | import { Modal, message } from 'antd'; | ||||
| import DOMPurify from 'dompurify'; | import DOMPurify from 'dompurify'; | ||||
| return { data, loading, createToken: mutateAsync }; | return { data, loading, createToken: mutateAsync }; | ||||
| }; | }; | ||||
| export const useListTenantUser = () => { | |||||
| const { data: tenantInfo } = useFetchTenantInfo(); | |||||
| const tenantId = tenantInfo.tenant_id; | |||||
| const { | |||||
| data, | |||||
| isFetching: loading, | |||||
| refetch, | |||||
| } = useQuery<ITenantUser[]>({ | |||||
| queryKey: ['listTenantUser', tenantId], | |||||
| initialData: [], | |||||
| gcTime: 0, | |||||
| enabled: !!tenantId, | |||||
| queryFn: async () => { | |||||
| const { data } = await listTenantUser(tenantId); | |||||
| return data?.data ?? []; | |||||
| }, | |||||
| }); | |||||
| return { data, loading, refetch }; | |||||
| }; | |||||
| export const useAddTenantUser = () => { | |||||
| const { data: tenantInfo } = useFetchTenantInfo(); | |||||
| const queryClient = useQueryClient(); | |||||
| const { | |||||
| data, | |||||
| isPending: loading, | |||||
| mutateAsync, | |||||
| } = useMutation({ | |||||
| mutationKey: ['addTenantUser'], | |||||
| mutationFn: async (email: string) => { | |||||
| const { data } = await addTenantUser(tenantInfo.tenant_id, email); | |||||
| if (data.retcode === 0) { | |||||
| queryClient.invalidateQueries({ queryKey: ['listTenantUser'] }); | |||||
| } | |||||
| return data?.retcode; | |||||
| }, | |||||
| }); | |||||
| return { data, loading, addTenantUser: mutateAsync }; | |||||
| }; | |||||
| export const useDeleteTenantUser = () => { | |||||
| const { data: tenantInfo } = useFetchTenantInfo(); | |||||
| const queryClient = useQueryClient(); | |||||
| const { t } = useTranslation(); | |||||
| const { | |||||
| data, | |||||
| isPending: loading, | |||||
| mutateAsync, | |||||
| } = useMutation({ | |||||
| mutationKey: ['deleteTenantUser'], | |||||
| mutationFn: async ({ | |||||
| userId, | |||||
| tenantId, | |||||
| }: { | |||||
| userId: string; | |||||
| tenantId?: string; | |||||
| }) => { | |||||
| const { data } = await deleteTenantUser({ | |||||
| tenantId: tenantId ?? tenantInfo.tenant_id, | |||||
| userId, | |||||
| }); | |||||
| if (data.retcode === 0) { | |||||
| message.success(t('message.deleted')); | |||||
| queryClient.invalidateQueries({ queryKey: ['listTenantUser'] }); | |||||
| queryClient.invalidateQueries({ queryKey: ['listTenant'] }); | |||||
| } | |||||
| return data?.data ?? []; | |||||
| }, | |||||
| }); | |||||
| return { data, loading, deleteTenantUser: mutateAsync }; | |||||
| }; | |||||
| export const useListTenant = () => { | |||||
| const { data: tenantInfo } = useFetchTenantInfo(); | |||||
| const tenantId = tenantInfo.tenant_id; | |||||
| const { | |||||
| data, | |||||
| isFetching: loading, | |||||
| refetch, | |||||
| } = useQuery<ITenant[]>({ | |||||
| queryKey: ['listTenant', tenantId], | |||||
| initialData: [], | |||||
| gcTime: 0, | |||||
| enabled: !!tenantId, | |||||
| queryFn: async () => { | |||||
| const { data } = await listTenant(); | |||||
| return data?.data ?? []; | |||||
| }, | |||||
| }); | |||||
| return { data, loading, refetch }; | |||||
| }; | |||||
| export const useAgreeTenant = () => { | |||||
| const queryClient = useQueryClient(); | |||||
| const { t } = useTranslation(); | |||||
| const { | |||||
| data, | |||||
| isPending: loading, | |||||
| mutateAsync, | |||||
| } = useMutation({ | |||||
| mutationKey: ['agreeTenant'], | |||||
| mutationFn: async (tenantId: string) => { | |||||
| const { data } = await agreeTenant(tenantId); | |||||
| if (data.retcode === 0) { | |||||
| message.success(t('message.operated')); | |||||
| queryClient.invalidateQueries({ queryKey: ['listTenant'] }); | |||||
| } | |||||
| return data?.data ?? []; | |||||
| }, | |||||
| }); | |||||
| return { data, loading, agreeTenant: mutateAsync }; | |||||
| }; | 
| number_of_nodes: number; | number_of_nodes: number; | ||||
| active_shards: number; | active_shards: number; | ||||
| } | } | ||||
| export interface ITenantUser { | |||||
| avatar: null; | |||||
| delta_seconds: number; | |||||
| email: string; | |||||
| is_active: string; | |||||
| is_anonymous: string; | |||||
| is_authenticated: string; | |||||
| is_superuser: boolean; | |||||
| nickname: string; | |||||
| role: string; | |||||
| status: string; | |||||
| update_date: string; | |||||
| user_id: string; | |||||
| } | |||||
| export interface ITenant { | |||||
| avatar: string; | |||||
| delta_seconds: number; | |||||
| email: string; | |||||
| nickname: string; | |||||
| role: string; | |||||
| tenant_id: string; | |||||
| update_date: string; | |||||
| } | 
| close: 'Close', | close: 'Close', | ||||
| preview: 'Preview', | preview: 'Preview', | ||||
| move: 'Move', | move: 'Move', | ||||
| warn: '提醒', | |||||
| warn: 'Warn', | |||||
| action: 'Action', | |||||
| }, | }, | ||||
| login: { | login: { | ||||
| login: 'Sign in', | login: 'Sign in', | ||||
| 'Please add both embedding model and LLM in <b>Settings > Model providers</b> firstly.', | 'Please add both embedding model and LLM in <b>Settings > Model providers</b> firstly.', | ||||
| apiVersion: 'API-Version', | apiVersion: 'API-Version', | ||||
| apiVersionMessage: 'Please input API version', | apiVersionMessage: 'Please input API version', | ||||
| add: 'Add', | |||||
| updateDate: 'Update Date', | |||||
| role: 'Role', | |||||
| invite: 'Invite', | |||||
| agree: 'Agree', | |||||
| refuse: 'Refuse', | |||||
| teamMembers: 'Team Members', | |||||
| joinedTeams: 'Joined Teams', | |||||
| }, | }, | ||||
| message: { | message: { | ||||
| registered: 'Registered!', | registered: 'Registered!', | 
| preview: '預覽', | preview: '預覽', | ||||
| move: '移動', | move: '移動', | ||||
| warn: '提醒', | warn: '提醒', | ||||
| action: '操作', | |||||
| }, | }, | ||||
| login: { | login: { | ||||
| login: '登入', | login: '登入', | ||||
| GoogleRegionMessage: '請輸入 Google Cloud 區域', | GoogleRegionMessage: '請輸入 Google Cloud 區域', | ||||
| modelProvidersWarn: | modelProvidersWarn: | ||||
| '請先在 <b>「設定」>「模型提供者」</b> 中新增嵌入模型和LLM。', | '請先在 <b>「設定」>「模型提供者」</b> 中新增嵌入模型和LLM。', | ||||
| add: '添加', | |||||
| updateDate: '更新日期', | |||||
| role: '角色', | |||||
| invite: '邀請', | |||||
| agree: '同意', | |||||
| refuse: '拒絕', | |||||
| teamMembers: '團隊成員', | |||||
| joinedTeams: '加入的團隊', | |||||
| }, | }, | ||||
| message: { | message: { | ||||
| registered: '註冊成功', | registered: '註冊成功', | 
| preview: '预览', | preview: '预览', | ||||
| move: '移动', | move: '移动', | ||||
| warn: '提醒', | warn: '提醒', | ||||
| action: '操作', | |||||
| }, | }, | ||||
| login: { | login: { | ||||
| login: '登录', | login: '登录', | ||||
| '请首先在 <b>设置 > 模型提供商</b> 中添加嵌入模型和 LLM。', | '请首先在 <b>设置 > 模型提供商</b> 中添加嵌入模型和 LLM。', | ||||
| apiVersion: 'API版本', | apiVersion: 'API版本', | ||||
| apiVersionMessage: '请输入API版本!', | apiVersionMessage: '请输入API版本!', | ||||
| add: '添加', | |||||
| updateDate: '更新日期', | |||||
| role: '角色', | |||||
| invite: '邀请', | |||||
| agree: '同意', | |||||
| refuse: '拒绝', | |||||
| teamMembers: '团队成员', | |||||
| joinedTeams: '加入的团队', | |||||
| }, | }, | ||||
| message: { | message: { | ||||
| registered: '注册成功', | registered: '注册成功', | 
| 'OpenRouter', | 'OpenRouter', | ||||
| 'HuggingFace', | 'HuggingFace', | ||||
| ]; | ]; | ||||
| export enum TenantRole { | |||||
| Owner = 'owner', | |||||
| Invite = 'invite', | |||||
| Normal = 'normal', | |||||
| } | 
| import { IModalProps } from '@/interfaces/common'; | |||||
| import { Form, Input, Modal } from 'antd'; | |||||
| import { useTranslation } from 'react-i18next'; | |||||
| const AddingUserModal = ({ | |||||
| visible, | |||||
| hideModal, | |||||
| loading, | |||||
| onOk, | |||||
| }: IModalProps<string>) => { | |||||
| const [form] = Form.useForm(); | |||||
| const { t } = useTranslation(); | |||||
| type FieldType = { | |||||
| email?: string; | |||||
| }; | |||||
| const handleOk = async () => { | |||||
| const ret = await form.validateFields(); | |||||
| return onOk?.(ret.email); | |||||
| }; | |||||
| return ( | |||||
| <Modal | |||||
| title={t('setting.add')} | |||||
| open={visible} | |||||
| onOk={handleOk} | |||||
| onCancel={hideModal} | |||||
| okButtonProps={{ loading }} | |||||
| confirmLoading={loading} | |||||
| > | |||||
| <Form | |||||
| name="basic" | |||||
| labelCol={{ span: 6 }} | |||||
| wrapperCol={{ span: 18 }} | |||||
| autoComplete="off" | |||||
| form={form} | |||||
| > | |||||
| <Form.Item<FieldType> | |||||
| label={t('setting.email')} | |||||
| name="email" | |||||
| rules={[{ required: true, message: t('namePlaceholder') }]} | |||||
| > | |||||
| <Input /> | |||||
| </Form.Item> | |||||
| </Form> | |||||
| </Modal> | |||||
| ); | |||||
| }; | |||||
| export default AddingUserModal; | 
| import { useSetModalState, useShowDeleteConfirm } from '@/hooks/common-hooks'; | |||||
| import { | |||||
| useAddTenantUser, | |||||
| useAgreeTenant, | |||||
| useDeleteTenantUser, | |||||
| useFetchUserInfo, | |||||
| } from '@/hooks/user-setting-hooks'; | |||||
| import { useCallback } from 'react'; | |||||
| export const useAddUser = () => { | |||||
| const { addTenantUser } = useAddTenantUser(); | |||||
| const { | |||||
| visible: addingTenantModalVisible, | |||||
| hideModal: hideAddingTenantModal, | |||||
| showModal: showAddingTenantModal, | |||||
| } = useSetModalState(); | |||||
| const handleAddUserOk = useCallback( | |||||
| async (email: string) => { | |||||
| const retcode = await addTenantUser(email); | |||||
| if (retcode === 0) { | |||||
| hideAddingTenantModal(); | |||||
| } | |||||
| }, | |||||
| [addTenantUser, hideAddingTenantModal], | |||||
| ); | |||||
| return { | |||||
| addingTenantModalVisible, | |||||
| hideAddingTenantModal, | |||||
| showAddingTenantModal, | |||||
| handleAddUserOk, | |||||
| }; | |||||
| }; | |||||
| export const useHandleDeleteUser = () => { | |||||
| const { deleteTenantUser, loading } = useDeleteTenantUser(); | |||||
| const showDeleteConfirm = useShowDeleteConfirm(); | |||||
| const handleDeleteTenantUser = (userId: string) => () => { | |||||
| showDeleteConfirm({ | |||||
| onOk: async () => { | |||||
| const retcode = await deleteTenantUser({ userId }); | |||||
| if (retcode === 0) { | |||||
| } | |||||
| return; | |||||
| }, | |||||
| }); | |||||
| }; | |||||
| return { handleDeleteTenantUser, loading }; | |||||
| }; | |||||
| export const useHandleAgreeTenant = () => { | |||||
| const { agreeTenant } = useAgreeTenant(); | |||||
| const { deleteTenantUser } = useDeleteTenantUser(); | |||||
| const { data: user } = useFetchUserInfo(); | |||||
| const handleAgree = (tenantId: string, isAgree: boolean) => () => { | |||||
| if (isAgree) { | |||||
| agreeTenant(tenantId); | |||||
| } else { | |||||
| deleteTenantUser({ tenantId, userId: user.id }); | |||||
| } | |||||
| }; | |||||
| return { handleAgree }; | |||||
| }; | 
| .teamWrapper { | .teamWrapper { | ||||
| width: 100%; | width: 100%; | ||||
| display: flex; | |||||
| flex-direction: column; | |||||
| gap: 20px; | |||||
| .teamCard { | .teamCard { | ||||
| // width: 100%; | // width: 100%; | ||||
| } | } | 
| import { Button, Card, Flex } from 'antd'; | |||||
| import { | |||||
| useFetchUserInfo, | |||||
| useListTenantUser, | |||||
| } from '@/hooks/user-setting-hooks'; | |||||
| import { Button, Card, Flex, Space } from 'antd'; | |||||
| import { useTranslation } from 'react-i18next'; | |||||
| import { useTranslate } from '@/hooks/common-hooks'; | |||||
| import { useFetchUserInfo } from '@/hooks/user-setting-hooks'; | |||||
| import { TeamOutlined, UserAddOutlined, UserOutlined } from '@ant-design/icons'; | |||||
| import AddingUserModal from './add-user-modal'; | |||||
| import { useAddUser } from './hooks'; | |||||
| import styles from './index.less'; | import styles from './index.less'; | ||||
| import TenantTable from './tenant-table'; | |||||
| import UserTable from './user-table'; | |||||
| const iconStyle = { fontSize: 20, color: '#1677ff' }; | |||||
| const UserSettingTeam = () => { | const UserSettingTeam = () => { | ||||
| const { data: userInfo } = useFetchUserInfo(); | const { data: userInfo } = useFetchUserInfo(); | ||||
| const { t } = useTranslate('setting'); | |||||
| const { t } = useTranslation(); | |||||
| useListTenantUser(); | |||||
| const { | |||||
| addingTenantModalVisible, | |||||
| hideAddingTenantModal, | |||||
| showAddingTenantModal, | |||||
| handleAddUserOk, | |||||
| } = useAddUser(); | |||||
| return ( | return ( | ||||
| <div className={styles.teamWrapper}> | <div className={styles.teamWrapper}> | ||||
| <Card className={styles.teamCard}> | <Card className={styles.teamCard}> | ||||
| <Flex align="center" justify={'space-between'}> | <Flex align="center" justify={'space-between'}> | ||||
| <span> | <span> | ||||
| {userInfo.nickname} {t('workspace')} | |||||
| {userInfo.nickname} {t('setting.workspace')} | |||||
| </span> | </span> | ||||
| <Button type="primary" disabled> | |||||
| {t('upgrade')} | |||||
| <Button type="primary" onClick={showAddingTenantModal}> | |||||
| <UserAddOutlined /> | |||||
| {t('setting.invite')} | |||||
| </Button> | </Button> | ||||
| </Flex> | </Flex> | ||||
| </Card> | </Card> | ||||
| <Card | |||||
| title={ | |||||
| <Space> | |||||
| <UserOutlined style={iconStyle} /> {t('setting.teamMembers')} | |||||
| </Space> | |||||
| } | |||||
| bordered={false} | |||||
| > | |||||
| <UserTable></UserTable> | |||||
| </Card> | |||||
| <Card | |||||
| title={ | |||||
| <Space> | |||||
| <TeamOutlined style={iconStyle} /> {t('setting.joinedTeams')} | |||||
| </Space> | |||||
| } | |||||
| bordered={false} | |||||
| > | |||||
| <TenantTable></TenantTable> | |||||
| </Card> | |||||
| {addingTenantModalVisible && ( | |||||
| <AddingUserModal | |||||
| visible | |||||
| hideModal={hideAddingTenantModal} | |||||
| onOk={handleAddUserOk} | |||||
| ></AddingUserModal> | |||||
| )} | |||||
| </div> | </div> | ||||
| ); | ); | ||||
| }; | }; | 
| import { useListTenant } from '@/hooks/user-setting-hooks'; | |||||
| import { ITenant } from '@/interfaces/database/user-setting'; | |||||
| import { formatDate } from '@/utils/date'; | |||||
| import type { TableProps } from 'antd'; | |||||
| import { Button, Space, Table } from 'antd'; | |||||
| import { useTranslation } from 'react-i18next'; | |||||
| import { TenantRole } from '../constants'; | |||||
| import { useHandleAgreeTenant } from './hooks'; | |||||
| const TenantTable = () => { | |||||
| const { t } = useTranslation(); | |||||
| const { data, loading } = useListTenant(); | |||||
| const { handleAgree } = useHandleAgreeTenant(); | |||||
| const columns: TableProps<ITenant>['columns'] = [ | |||||
| { | |||||
| title: t('common.name'), | |||||
| dataIndex: 'nickname', | |||||
| key: 'nickname', | |||||
| }, | |||||
| { | |||||
| title: t('setting.email'), | |||||
| dataIndex: 'email', | |||||
| key: 'email', | |||||
| }, | |||||
| { | |||||
| title: t('setting.updateDate'), | |||||
| dataIndex: 'update_date', | |||||
| key: 'update_date', | |||||
| render(value) { | |||||
| return formatDate(value); | |||||
| }, | |||||
| }, | |||||
| { | |||||
| title: t('common.action'), | |||||
| key: 'action', | |||||
| render: (_, { role, tenant_id }) => { | |||||
| if (role === TenantRole.Invite) { | |||||
| return ( | |||||
| <Space> | |||||
| <Button type="link" onClick={handleAgree(tenant_id, true)}> | |||||
| {t(`setting.agree`)} | |||||
| </Button> | |||||
| <Button type="link" onClick={handleAgree(tenant_id, false)}> | |||||
| {t(`setting.refuse`)} | |||||
| </Button> | |||||
| </Space> | |||||
| ); | |||||
| } | |||||
| }, | |||||
| }, | |||||
| ]; | |||||
| return ( | |||||
| <Table<ITenant> | |||||
| columns={columns} | |||||
| dataSource={data} | |||||
| rowKey={'tenant_id'} | |||||
| loading={loading} | |||||
| pagination={false} | |||||
| /> | |||||
| ); | |||||
| }; | |||||
| export default TenantTable; | 
| import { useListTenantUser } from '@/hooks/user-setting-hooks'; | |||||
| import { ITenantUser } from '@/interfaces/database/user-setting'; | |||||
| import { formatDate } from '@/utils/date'; | |||||
| import { DeleteOutlined } from '@ant-design/icons'; | |||||
| import type { TableProps } from 'antd'; | |||||
| import { Button, Table, Tag } from 'antd'; | |||||
| import { useTranslation } from 'react-i18next'; | |||||
| import { TenantRole } from '../constants'; | |||||
| import { useHandleDeleteUser } from './hooks'; | |||||
| const ColorMap = { | |||||
| [TenantRole.Normal]: 'green', | |||||
| [TenantRole.Invite]: 'orange', | |||||
| [TenantRole.Owner]: 'red', | |||||
| }; | |||||
| const UserTable = () => { | |||||
| const { data, loading } = useListTenantUser(); | |||||
| const { handleDeleteTenantUser } = useHandleDeleteUser(); | |||||
| const { t } = useTranslation(); | |||||
| const columns: TableProps<ITenantUser>['columns'] = [ | |||||
| { | |||||
| title: t('common.name'), | |||||
| dataIndex: 'nickname', | |||||
| key: 'nickname', | |||||
| }, | |||||
| { | |||||
| title: t('setting.email'), | |||||
| dataIndex: 'email', | |||||
| key: 'email', | |||||
| }, | |||||
| { | |||||
| title: t('setting.role'), | |||||
| dataIndex: 'role', | |||||
| key: 'role', | |||||
| render(value, { role }) { | |||||
| return ( | |||||
| <Tag color={ColorMap[role as keyof typeof ColorMap]}>{role}</Tag> | |||||
| ); | |||||
| }, | |||||
| }, | |||||
| { | |||||
| title: t('setting.updateDate'), | |||||
| dataIndex: 'update_date', | |||||
| key: 'update_date', | |||||
| render(value) { | |||||
| return formatDate(value); | |||||
| }, | |||||
| }, | |||||
| { | |||||
| title: t('common.action'), | |||||
| key: 'action', | |||||
| render: (_, record) => ( | |||||
| <Button type="text" onClick={handleDeleteTenantUser(record.user_id)}> | |||||
| <DeleteOutlined size={20} /> | |||||
| </Button> | |||||
| ), | |||||
| }, | |||||
| ]; | |||||
| return ( | |||||
| <Table<ITenantUser> | |||||
| rowKey={'user_id'} | |||||
| columns={columns} | |||||
| dataSource={data} | |||||
| loading={loading} | |||||
| pagination={false} | |||||
| /> | |||||
| ); | |||||
| }; | |||||
| export default UserTable; | 
| import api from '@/utils/api'; | import api from '@/utils/api'; | ||||
| import registerServer from '@/utils/register-server'; | import registerServer from '@/utils/register-server'; | ||||
| import request from '@/utils/request'; | |||||
| import request, { post } from '@/utils/request'; | |||||
| const { | const { | ||||
| login, | login, | ||||
| const userService = registerServer<keyof typeof methods>(methods, request); | const userService = registerServer<keyof typeof methods>(methods, request); | ||||
| export const listTenantUser = (tenantId: string) => | |||||
| request.get(api.listTenantUser(tenantId)); | |||||
| export const addTenantUser = (tenantId: string, email: string) => | |||||
| post(api.addTenantUser(tenantId), { email }); | |||||
| export const deleteTenantUser = ({ | |||||
| tenantId, | |||||
| userId, | |||||
| }: { | |||||
| tenantId: string; | |||||
| userId: string; | |||||
| }) => request.delete(api.deleteTenantUser(tenantId, userId)); | |||||
| export const listTenant = () => request.get(api.listTenant); | |||||
| export const agreeTenant = (tenantId: string) => | |||||
| request.put(api.agreeTenant(tenantId)); | |||||
| export default userService; | export default userService; | 
| tenant_info: `${api_host}/user/tenant_info`, | tenant_info: `${api_host}/user/tenant_info`, | ||||
| set_tenant_info: `${api_host}/user/set_tenant_info`, | set_tenant_info: `${api_host}/user/set_tenant_info`, | ||||
| // team | |||||
| addTenantUser: (tenantId: string) => `${api_host}/tenant/${tenantId}/user`, | |||||
| listTenantUser: (tenantId: string) => | |||||
| `${api_host}/tenant/${tenantId}/user/list`, | |||||
| deleteTenantUser: (tenantId: string, userId: string) => | |||||
| `${api_host}/tenant/${tenantId}/user/${userId}`, | |||||
| listTenant: `${api_host}/tenant/list`, | |||||
| agreeTenant: (tenantId: string) => `${api_host}/tenant/agree/${tenantId}`, | |||||
| // llm model | // llm model | ||||
| factories_list: `${api_host}/llm/factories`, | factories_list: `${api_host}/llm/factories`, | ||||
| llm_list: `${api_host}/llm/list`, | llm_list: `${api_host}/llm/list`, | 
| }); | }); | ||||
| export default request; | export default request; | ||||
| export const get = (url: string) => { | |||||
| return request.get(url); | |||||
| }; | |||||
| export const post = (url: string, body: any) => { | |||||
| return request.post(url, { data: body }); | |||||
| }; | |||||
| export const drop = () => {}; | |||||
| export const put = () => {}; |