Browse Source

support list, create ,delete user

Signed-off-by: nameczz <zizhao.chen@zilliz.com>
nameczz 3 years ago
parent
commit
d93190cacb

+ 4 - 3
client/src/components/grid/Grid.tsx

@@ -123,12 +123,13 @@ const AttuGrid: FC<AttuGridType> = props => {
     headEditable = false,
     headEditable = false,
     editHeads = [],
     editHeads = [],
     selected = [],
     selected = [],
-    setSelected = () => { },
-    setRowsPerPage = () => { },
+    setSelected = () => {},
+    setRowsPerPage = () => {},
     tableCellMaxWidth,
     tableCellMaxWidth,
     handleSort,
     handleSort,
     order,
     order,
     orderBy,
     orderBy,
+    showPagination = true,
   } = props;
   } = props;
 
 
   const _isSelected = (row: { [x: string]: any }) => {
   const _isSelected = (row: { [x: string]: any }) => {
@@ -225,7 +226,7 @@ const AttuGrid: FC<AttuGridType> = props => {
           order={order}
           order={order}
           orderBy={orderBy}
           orderBy={orderBy}
         ></Table>
         ></Table>
-        {rowCount ? (
+        {rowCount && showPagination ? (
           <TablePagination
           <TablePagination
             component="div"
             component="div"
             colSpan={3}
             colSpan={3}

+ 1 - 0
client/src/components/grid/Types.ts

@@ -115,6 +115,7 @@ export type ColDefinitionsType = {
 };
 };
 
 
 export type AttuGridType = ToolBarType & {
 export type AttuGridType = ToolBarType & {
+  showPagination?: boolean;
   rowCount: number;
   rowCount: number;
   rowsPerPage?: number;
   rowsPerPage?: number;
   // used to dynamic set page size by table container and row height
   // used to dynamic set page size by table container and row height

+ 10 - 0
client/src/components/icons/Icons.tsx

@@ -24,6 +24,8 @@ import FilterListIcon from '@material-ui/icons/FilterList';
 import AlternateEmailIcon from '@material-ui/icons/AlternateEmail';
 import AlternateEmailIcon from '@material-ui/icons/AlternateEmail';
 import DatePicker from '@material-ui/icons/Event';
 import DatePicker from '@material-ui/icons/Event';
 import GetAppIcon from '@material-ui/icons/GetApp';
 import GetAppIcon from '@material-ui/icons/GetApp';
+// import PersonOutlineIcon from '@material-ui/icons/PersonOutline';
+import PersonOutlineIcon from '@material-ui/icons/Person';
 import { SvgIcon } from '@material-ui/core';
 import { SvgIcon } from '@material-ui/core';
 import { ReactComponent as ZillizIcon } from '../../assets/icons/zilliz.svg';
 import { ReactComponent as ZillizIcon } from '../../assets/icons/zilliz.svg';
 import { ReactComponent as OverviewIcon } from '../../assets/icons/overview.svg';
 import { ReactComponent as OverviewIcon } from '../../assets/icons/overview.svg';
@@ -68,6 +70,14 @@ const icons: { [x in IconsType]: (props?: any) => React.ReactElement } = {
   zilliz: (props = {}) => (
   zilliz: (props = {}) => (
     <SvgIcon viewBox="0 0 30 30" component={ZillizIcon} {...props} />
     <SvgIcon viewBox="0 0 30 30" component={ZillizIcon} {...props} />
   ),
   ),
+  navPerson: (props = {}) => (
+    <SvgIcon
+      viewBox="0 0 24 24"
+      component={PersonOutlineIcon}
+      strokeWidth="2"
+      {...props}
+    />
+  ),
   navOverview: (props = {}) => (
   navOverview: (props = {}) => (
     <SvgIcon viewBox="0 0 20 20" component={OverviewIcon} {...props} />
     <SvgIcon viewBox="0 0 20 20" component={OverviewIcon} {...props} />
   ),
   ),

+ 1 - 0
client/src/components/icons/Types.ts

@@ -17,6 +17,7 @@ export type IconsType =
   | 'navConsole'
   | 'navConsole'
   | 'navSearch'
   | 'navSearch'
   | 'navSystem'
   | 'navSystem'
+  | 'navPerson'
   | 'expandLess'
   | 'expandLess'
   | 'expandMore'
   | 'expandMore'
   | 'back'
   | 'back'

+ 9 - 5
client/src/components/layout/Layout.tsx

@@ -66,6 +66,10 @@ const Layout = (props: any) => {
       return navTrans('system');
       return navTrans('system');
     }
     }
 
 
+    if (location.pathname.includes('users')) {
+      return navTrans('user');
+    }
+
     return navTrans('overview');
     return navTrans('overview');
   }, [location, navTrans]);
   }, [location, navTrans]);
 
 
@@ -75,11 +79,11 @@ const Layout = (props: any) => {
       label: navTrans('overview'),
       label: navTrans('overview'),
       onClick: () => history.push('/'),
       onClick: () => history.push('/'),
     },
     },
-    // {
-    //   icon: icons.navSystem,
-    //   label: navTrans('system'),
-    //   onClick: () => history.push('/system'),
-    // },
+    {
+      icon: icons.navPerson,
+      label: navTrans('user'),
+      onClick: () => history.push('/users'),
+    },
     {
     {
       icon: icons.navCollection,
       icon: icons.navCollection,
       label: navTrans('collection'),
       label: navTrans('collection'),

+ 1 - 1
client/src/context/Root.tsx

@@ -11,7 +11,7 @@ import {
 import CustomSnackBar from '../components/customSnackBar/CustomSnackBar';
 import CustomSnackBar from '../components/customSnackBar/CustomSnackBar';
 import CustomDialog from '../components/customDialog/CustomDialog';
 import CustomDialog from '../components/customDialog/CustomDialog';
 import { theme } from '../styles/theme';
 import { theme } from '../styles/theme';
-import { MilvusHttp } from 'insight_src/http/Milvus';
+import { MilvusHttp } from '../http/Milvus';
 
 
 const DefaultDialogConfigs: DialogType = {
 const DefaultDialogConfigs: DialogType = {
   open: false,
   open: false,

+ 37 - 0
client/src/http/User.ts

@@ -0,0 +1,37 @@
+import {
+  CreateUserParams,
+  DeleteUserParams,
+  UpdateUserParams,
+} from 'insight_src/pages/user/Types';
+import BaseModel from './BaseModel';
+
+export class UserHttp extends BaseModel {
+  private names!: string[];
+
+  constructor(props: {}) {
+    super(props);
+    Object.assign(this, props);
+  }
+
+  static USER_URL = `/users`;
+
+  static getUsers() {
+    return super.search({ path: this.USER_URL, params: {} });
+  }
+
+  static createUser(data: CreateUserParams) {
+    return super.create({ path: this.USER_URL, data });
+  }
+
+  static updateUser(data: UpdateUserParams) {
+    return super.update({ path: this.USER_URL, data });
+  }
+
+  static deleteUser(data: DeleteUserParams) {
+    return super.delete({ path: `${this.USER_URL}/${data.username}` });
+  }
+
+  get _names() {
+    return this.names;
+  }
+}

+ 1 - 0
client/src/i18n/cn/nav.ts

@@ -4,6 +4,7 @@ const navTrans = {
   console: 'Search Console',
   console: 'Search Console',
   search: 'Vector Search',
   search: 'Vector Search',
   system: 'System View',
   system: 'System View',
+  user: 'User',
 };
 };
 
 
 export default navTrans;
 export default navTrans;

+ 7 - 0
client/src/i18n/cn/user.ts

@@ -0,0 +1,7 @@
+const userTrans = {
+  createTitle: 'Create user',
+  user: 'User',
+  deleteWarning: 'You are trying to delete user. This action cannot be undone.',
+};
+
+export default userTrans;

+ 1 - 0
client/src/i18n/en/nav.ts

@@ -4,6 +4,7 @@ const navTrans = {
   console: 'Search Console',
   console: 'Search Console',
   search: 'Vector Search',
   search: 'Vector Search',
   system: 'System View',
   system: 'System View',
+  user: 'User',
 };
 };
 
 
 export default navTrans;
 export default navTrans;

+ 7 - 0
client/src/i18n/en/user.ts

@@ -0,0 +1,7 @@
+const userTrans = {
+  createTitle: 'Create user',
+  user: 'User',
+  deleteWarning: 'You are trying to delete user. This action cannot be undone.',
+};
+
+export default userTrans;

+ 5 - 1
client/src/i18n/index.ts

@@ -27,6 +27,8 @@ import searchEn from './en/search';
 import searchCn from './cn/search';
 import searchCn from './cn/search';
 import systemViewTransEn from './en/systemView';
 import systemViewTransEn from './en/systemView';
 import systemViewTransCn from './cn/systemView';
 import systemViewTransCn from './cn/systemView';
+import userTransEn from './en/user';
+import userTransCn from './cn/user';
 
 
 export const resources = {
 export const resources = {
   cn: {
   cn: {
@@ -43,6 +45,7 @@ export const resources = {
     insert: insertCn,
     insert: insertCn,
     search: searchCn,
     search: searchCn,
     systemView: systemViewTransCn,
     systemView: systemViewTransCn,
+    user: userTransCn,
   },
   },
   en: {
   en: {
     translation: commonEn,
     translation: commonEn,
@@ -57,7 +60,8 @@ export const resources = {
     index: indexEn,
     index: indexEn,
     insert: insertEn,
     insert: insertEn,
     search: searchEn,
     search: searchEn,
-    systemView: systemViewTransEn
+    systemView: systemViewTransEn,
+    user: userTransEn,
   },
   },
 };
 };
 
 

+ 5 - 5
client/src/pages/connect/AuthForm.tsx

@@ -9,14 +9,14 @@ import { ITextfieldConfig } from '../../components/customInput/Types';
 import { useFormValidation } from '../../hooks/Form';
 import { useFormValidation } from '../../hooks/Form';
 import { formatForm } from '../../utils/Form';
 import { formatForm } from '../../utils/Form';
 import { MilvusHttp } from '../../http/Milvus';
 import { MilvusHttp } from '../../http/Milvus';
-import { formatAddress } from 'insight_src/utils/Format';
+import { formatAddress } from '../../utils/Format';
 import { useHistory } from 'react-router-dom';
 import { useHistory } from 'react-router-dom';
 import { rootContext } from '../../context/Root';
 import { rootContext } from '../../context/Root';
 import { authContext } from '../../context/Auth';
 import { authContext } from '../../context/Auth';
-import { MILVUS_ADDRESS } from 'insight_src/consts/Localstorage';
-import { CODE_STATUS } from 'insight_src/consts/Http';
-import { MILVUS_URL } from 'insight_src/consts/Milvus';
-import { CustomRadio } from 'insight_src/components/customRadio/CustomRadio';
+import { MILVUS_ADDRESS } from '../../consts/Localstorage';
+import { CODE_STATUS } from '../../consts/Http';
+import { MILVUS_URL } from '../../consts/Milvus';
+import { CustomRadio } from '../../components/customRadio/CustomRadio';
 
 
 const useStyles = makeStyles((theme: Theme) => ({
 const useStyles = makeStyles((theme: Theme) => ({
   wrapper: {
   wrapper: {

+ 0 - 25
client/src/pages/partitions/Partitions.tsx

@@ -58,10 +58,7 @@ const Partitions: FC<{
   const InfoIcon = icons.info;
   const InfoIcon = icons.info;
 
 
   const { handleInsertDialog } = useInsertDialogHook();
   const { handleInsertDialog } = useInsertDialogHook();
-  // const LoadIcon = icons.load;
-  // const ReleaseIcon = icons.release;
 
 
-  // const { handleAction } = useDialogHook({ type: 'partition' });
   const [selectedPartitions, setSelectedPartitions] = useState<PartitionView[]>(
   const [selectedPartitions, setSelectedPartitions] = useState<PartitionView[]>(
     []
     []
   );
   );
@@ -138,28 +135,6 @@ const Partitions: FC<{
     handleCloseDialog();
     handleCloseDialog();
   };
   };
 
 
-  // const handleRelease = async (data: PartitionView) => {
-  //   const param: PartitionParam = {
-  //     collectionName,
-  //     partitionNames: [data._name],
-  //   };
-  //   const res = await PartitionHttp.releasePartition(param);
-  //   openSnackBar(successTrans('release', { name: t('partition') }));
-  //   fetchPartitions(collectionName);
-  //   return res;
-  // };
-
-  // const handleLoad = async (data: PartitionView) => {
-  //   const param: PartitionParam = {
-  //     collectionName,
-  //     partitionNames: [data._name!],
-  //   };
-  //   const res = await PartitionHttp.loadPartition(param);
-  //   openSnackBar(successTrans('load', { name: t('partition') }));
-  //   fetchPartitions(collectionName);
-  //   return res;
-  // };
-
   const handleSearch = (value: string) => {
   const handleSearch = (value: string) => {
     if (timer) {
     if (timer) {
       clearTimeout(timer);
       clearTimeout(timer);

+ 106 - 0
client/src/pages/user/Create.tsx

@@ -0,0 +1,106 @@
+import { makeStyles, Theme } from '@material-ui/core';
+import { FC, useMemo, useState } from 'react';
+import { useTranslation } from 'react-i18next';
+import DialogTemplate from '../../components/customDialog/DialogTemplate';
+import CustomInput from '../../components/customInput/CustomInput';
+import { ITextfieldConfig } from '../../components/customInput/Types';
+import { useFormValidation } from '../../hooks/Form';
+import { formatForm } from '../../utils/Form';
+import { CreateUserProps, CreateUserParams } from './Types';
+
+const useStyles = makeStyles((theme: Theme) => ({
+  input: {
+    margin: theme.spacing(3, 0, 0.5),
+  },
+}));
+
+const CreateUser: FC<CreateUserProps> = ({ handleCreate, handleClose }) => {
+  const { t: commonTrans } = useTranslation();
+  const { t: userTrans } = useTranslation('user');
+  const { t: btnTrans } = useTranslation('btn');
+  const { t: warningTrans } = useTranslation('warning');
+  const attuTrans = commonTrans('attu');
+
+  const [form, setForm] = useState<CreateUserParams>({
+    username: '',
+    password: '',
+  });
+  const checkedForm = useMemo(() => {
+    return formatForm(form);
+  }, [form]);
+  const { validation, checkIsValid, disabled } = useFormValidation(checkedForm);
+
+  const classes = useStyles();
+
+  const handleInputChange = (key: 'username' | 'password', value: string) => {
+    setForm(v => ({ ...v, [key]: value }));
+  };
+
+  const createConfigs: ITextfieldConfig[] = [
+    {
+      label: attuTrans.username,
+      key: 'username',
+      onChange: (value: string) => handleInputChange('username', value),
+      variant: 'filled',
+      className: classes.input,
+      placeholder: attuTrans.username,
+      fullWidth: true,
+      validations: [
+        {
+          rule: 'require',
+          errorText: warningTrans('required', {
+            name: attuTrans.username,
+          }),
+        },
+      ],
+      defaultValue: form.username,
+    },
+    {
+      label: attuTrans.password,
+      key: 'password',
+      onChange: (value: string) => handleInputChange('password', value),
+      variant: 'filled',
+      className: classes.input,
+      placeholder: attuTrans.password,
+      fullWidth: true,
+      type: 'password',
+      validations: [
+        {
+          rule: 'require',
+          errorText: warningTrans('required', {
+            name: attuTrans.password,
+          }),
+        },
+      ],
+      defaultValue: form.username,
+    },
+  ];
+
+  const handleCreateUser = () => {
+    handleCreate(form);
+  };
+
+  return (
+    <DialogTemplate
+      title={userTrans('createTitle')}
+      handleClose={handleClose}
+      confirmLabel={btnTrans('create')}
+      handleConfirm={handleCreateUser}
+      confirmDisabled={disabled}
+    >
+      <form>
+        {createConfigs.map(v => (
+          <CustomInput
+            type="text"
+            textConfig={v}
+            checkValid={checkIsValid}
+            validInfo={validation}
+            key={v.label}
+          />
+        ))}
+      </form>
+    </DialogTemplate>
+  );
+};
+
+export default CreateUser;

+ 22 - 0
client/src/pages/user/Types.ts

@@ -0,0 +1,22 @@
+export interface UserData {
+  name: string;
+}
+export interface CreateUserParams {
+  username: string;
+  password: string;
+}
+
+export interface CreateUserProps {
+  handleCreate: (data: CreateUserParams) => void;
+  handleClose: () => void;
+}
+
+export interface UpdateUserParams {
+  username: string;
+  oldPassword: string;
+  newPassword: string;
+}
+
+export interface DeleteUserParams {
+  username: string;
+}

+ 150 - 0
client/src/pages/user/User.tsx

@@ -0,0 +1,150 @@
+import { UserHttp } from '../../http/User';
+import React, { useContext, useEffect, useState } from 'react';
+import AttuGrid from 'insight_src/components/grid/Grid';
+import {
+  ColDefinitionsType,
+  ToolBarConfig,
+} from 'insight_src/components/grid/Types';
+import { makeStyles, Theme } from '@material-ui/core';
+import { CreateUserParams, DeleteUserParams, UserData } from './Types';
+import { rootContext } from 'insight_src/context/Root';
+import CreateUser from './Create';
+import { useTranslation } from 'react-i18next';
+import DeleteTemplate from 'insight_src/components/customDialog/DeleteDialogTemplate';
+
+const useStyles = makeStyles((theme: Theme) => ({
+  wrapper: {
+    height: '100%',
+  },
+  icon: {
+    fontSize: '20px',
+    marginLeft: theme.spacing(0.5),
+  },
+  highlight: {
+    color: theme.palette.primary.main,
+    backgroundColor: 'transparent',
+  },
+}));
+
+const Users = () => {
+  const classes = useStyles();
+  const [users, setUsers] = useState<UserData[]>([]);
+  const [selectedUser, setSelectedUser] = useState<UserData[]>([]);
+  const { setDialog, handleCloseDialog, openSnackBar } =
+    useContext(rootContext);
+  const { t: successTrans } = useTranslation('success');
+  const { t: userTrans } = useTranslation('user');
+  const { t: btnTrans } = useTranslation('btn');
+  const { t: dialogTrans } = useTranslation('dialog');
+
+  const fetchUsers = async () => {
+    const res = await UserHttp.getUsers();
+
+    setUsers(res.usernames.map((v: string) => ({ name: v })));
+  };
+
+  const handleCreate = async (data: CreateUserParams) => {
+    await UserHttp.createUser(data);
+    fetchUsers();
+    openSnackBar(successTrans('create', { name: userTrans('user') }));
+    handleCloseDialog();
+  };
+
+  const handleDelete = async () => {
+    for (const user of selectedUser) {
+      const param: DeleteUserParams = {
+        username: user.name,
+      };
+      await UserHttp.deleteUser(param);
+    }
+
+    openSnackBar(successTrans('delete', { name: userTrans('user') }));
+    fetchUsers();
+    handleCloseDialog();
+  };
+
+  const toolbarConfigs: ToolBarConfig[] = [
+    {
+      label: 'Create user',
+      onClick: () => {
+        setDialog({
+          open: true,
+          type: 'custom',
+          params: {
+            component: (
+              <CreateUser
+                handleCreate={handleCreate}
+                handleClose={handleCloseDialog}
+              />
+            ),
+          },
+        });
+      },
+      icon: 'add',
+    },
+
+    {
+      type: 'iconBtn',
+      onClick: () => {
+        setDialog({
+          open: true,
+          type: 'custom',
+          params: {
+            component: (
+              <DeleteTemplate
+                label={btnTrans('delete')}
+                title={dialogTrans('deleteTitle', { type: userTrans('user') })}
+                text={userTrans('deleteWarning')}
+                handleDelete={handleDelete}
+              />
+            ),
+          },
+        });
+      },
+      label: '',
+      icon: 'delete',
+    },
+  ];
+
+  const colDefinitions: ColDefinitionsType[] = [
+    {
+      id: 'name',
+      align: 'left',
+      disablePadding: false,
+      label: 'Name',
+    },
+  ];
+
+  const handleSelectChange = (value: UserData[]) => {
+    setSelectedUser(value);
+  };
+
+  useEffect(() => {
+    fetchUsers();
+  }, []);
+
+  return (
+    <div className="page-wrapper">
+      <AttuGrid
+        toolbarConfigs={toolbarConfigs}
+        colDefinitions={colDefinitions}
+        rows={users}
+        rowCount={users.length}
+        primaryKey="name"
+        showPagination={false}
+        selected={selectedUser}
+        setSelected={handleSelectChange}
+        // page={currentPage}
+        // onChangePage={handlePageChange}
+        // rowsPerPage={pageSize}
+        // setRowsPerPage={handlePageSize}
+        // isLoading={loading}
+        // order={order}
+        // orderBy={orderBy}
+        // handleSort={handleGridSort}
+      />
+    </div>
+  );
+};
+
+export default Users;

+ 2 - 2
client/src/plugins/search/Types.ts

@@ -3,8 +3,8 @@ import { searchKeywordsType } from '../../consts/Milvus';
 import {
 import {
   DataTypeEnum,
   DataTypeEnum,
   DataTypeStringEnum,
   DataTypeStringEnum,
-} from 'insight_src/pages/collections/Types';
-import { IndexView } from 'insight_src/pages/schema/Types';
+} from '../../pages/collections/Types';
+import { IndexView } from '../../pages/schema/Types';
 
 
 export interface SearchParamsProps {
 export interface SearchParamsProps {
   // if user created index, pass metric type choosed when creating
   // if user created index, pass metric type choosed when creating

+ 3 - 6
client/src/plugins/search/VectorSearch.tsx

@@ -16,10 +16,7 @@ import SimpleMenu from '../../components/menu/SimpleMenu';
 import { TOP_K_OPTIONS } from './Constants';
 import { TOP_K_OPTIONS } from './Constants';
 import { Option } from '../../components/customSelector/Types';
 import { Option } from '../../components/customSelector/Types';
 import { CollectionHttp } from '../../http/Collection';
 import { CollectionHttp } from '../../http/Collection';
-import {
-  CollectionData,
-  DataTypeEnum,
-} from 'insight_src/pages/collections/Types';
+import { CollectionData, DataTypeEnum } from '../../pages/collections/Types';
 import { IndexHttp } from '../../http/Index';
 import { IndexHttp } from '../../http/Index';
 import { getVectorSearchStyles } from './Styles';
 import { getVectorSearchStyles } from './Styles';
 import { parseValue } from '../../utils/Insert';
 import { parseValue } from '../../utils/Insert';
@@ -36,8 +33,8 @@ import Filter from '../../components/advancedSearch';
 import { Field } from '../../components/advancedSearch/Types';
 import { Field } from '../../components/advancedSearch/Types';
 import { useLocation } from 'react-router-dom';
 import { useLocation } from 'react-router-dom';
 import { parseLocationSearch } from '../../utils/Format';
 import { parseLocationSearch } from '../../utils/Format';
-import { CustomDatePicker } from 'insight_src/components/customDatePicker/CustomDatePicker';
-import { useTimeTravelHook } from 'insight_src/hooks/TimeTravel';
+import { CustomDatePicker } from '../../components/customDatePicker/CustomDatePicker';
+import { useTimeTravelHook } from '../../hooks/TimeTravel';
 
 
 const VectorSearch = () => {
 const VectorSearch = () => {
   useNavigationHook(ALL_ROUTER_TYPES.SEARCH);
   useNavigationHook(ALL_ROUTER_TYPES.SEARCH);

+ 2 - 5
client/src/router/Config.ts

@@ -5,6 +5,7 @@ import Overview from '../pages/overview/Overview';
 // import VectorSearch from '../pages/seach/VectorSearch';
 // import VectorSearch from '../pages/seach/VectorSearch';
 import { RouterConfigType } from './Types';
 import { RouterConfigType } from './Types';
 import loadable from '@loadable/component';
 import loadable from '@loadable/component';
+import Users from '../pages/user/User';
 
 
 const PLUGIN_DEV = process.env.REACT_APP_PLUGIN_DEV;
 const PLUGIN_DEV = process.env.REACT_APP_PLUGIN_DEV;
 
 
@@ -29,11 +30,7 @@ const RouterConfig: RouterConfigType[] = [
     component: Collection,
     component: Collection,
     auth: true,
     auth: true,
   },
   },
-  // {
-  //   path: '/search',
-  //   component: VectorSearch,
-  //   auth: true,
-  // },
+  { path: '/users', component: Users, auth: true },
 ];
 ];
 
 
 function importAll(r: any, outOfRoot = false) {
 function importAll(r: any, outOfRoot = false) {

+ 1 - 0
server/package.json

@@ -81,6 +81,7 @@
     "start": "nodemon src/app.ts",
     "start": "nodemon src/app.ts",
     "start:plugin": "yarn build && cross-env PLUGIN_DEV=1 node dist/attu/express/src/app.js",
     "start:plugin": "yarn build && cross-env PLUGIN_DEV=1 node dist/attu/express/src/app.js",
     "start:prod": "node dist/src/app.js",
     "start:prod": "node dist/src/app.js",
+    "start:debug": "DEBUG=express:* nodemon src/app.ts",
     "test": "cross-env NODE_ENV=test jest --passWithNoTests",
     "test": "cross-env NODE_ENV=test jest --passWithNoTests",
     "test:watch": "jest --watch",
     "test:watch": "jest --watch",
     "test:cov": "cross-env NODE_ENV=test jest --passWithNoTests --coverage",
     "test:cov": "cross-env NODE_ENV=test jest --passWithNoTests --coverage",

+ 1 - 0
server/src/milvus/milvus.service.ts

@@ -50,6 +50,7 @@ export class MilvusService {
         HTTP_STATUS_CODE.FORBIDDEN,
         HTTP_STATUS_CODE.FORBIDDEN,
         'Can not find your connection, please connect Milvus again'
         'Can not find your connection, please connect Milvus again'
       );
       );
+
       // throw new Error('Please connect milvus first');
       // throw new Error('Please connect milvus first');
     }
     }
   }
   }

+ 0 - 5
server/src/users/dto.ts

@@ -18,8 +18,3 @@ export class UpdateUserDto {
   @IsString()
   @IsString()
   readonly newPassword: string;
   readonly newPassword: string;
 }
 }
-
-export class DeleteUserDto {
-  @IsString()
-  readonly username: string;
-}

+ 7 - 11
server/src/users/users.controller.ts

@@ -3,7 +3,7 @@ import { dtoValidationMiddleware } from '../middlewares/validation';
 import { UserService } from './users.service';
 import { UserService } from './users.service';
 import { milvusService } from '../milvus';
 import { milvusService } from '../milvus';
 
 
-import { CreateUserDto, UpdateUserDto, DeleteUserDto } from './dto';
+import { CreateUserDto, UpdateUserDto } from './dto';
 
 
 export class UserController {
 export class UserController {
   private router: Router;
   private router: Router;
@@ -29,11 +29,7 @@ export class UserController {
       this.updateUsers.bind(this)
       this.updateUsers.bind(this)
     );
     );
 
 
-    this.router.delete(
-      '/',
-      dtoValidationMiddleware(DeleteUserDto),
-      this.deleteUsers.bind(this)
-    );
+    this.router.delete('/:username', this.deleteUser.bind(this));
 
 
     return this.router;
     return this.router;
   }
   }
@@ -50,7 +46,7 @@ export class UserController {
   async createUsers(req: Request, res: Response, next: NextFunction) {
   async createUsers(req: Request, res: Response, next: NextFunction) {
     const { username, password } = req.body;
     const { username, password } = req.body;
     try {
     try {
-      const result = this.userService.createUser({ username, password });
+      const result = await this.userService.createUser({ username, password });
       res.send(result);
       res.send(result);
     } catch (error) {
     } catch (error) {
       next(error);
       next(error);
@@ -60,7 +56,7 @@ export class UserController {
   async updateUsers(req: Request, res: Response, next: NextFunction) {
   async updateUsers(req: Request, res: Response, next: NextFunction) {
     const { username, oldPassword, newPassword } = req.body;
     const { username, oldPassword, newPassword } = req.body;
     try {
     try {
-      const result = this.userService.updateUser({
+      const result = await this.userService.updateUser({
         username,
         username,
         oldPassword,
         oldPassword,
         newPassword,
         newPassword,
@@ -71,10 +67,10 @@ export class UserController {
     }
     }
   }
   }
 
 
-  async deleteUsers(req: Request, res: Response, next: NextFunction) {
-    const { username } = req.body;
+  async deleteUser(req: Request, res: Response, next: NextFunction) {
+    const { username } = req.params;
     try {
     try {
-      const result = this.userService.deleteUser({ username });
+      const result = await this.userService.deleteUser({ username });
       res.send(result);
       res.send(result);
     } catch (error) {
     } catch (error) {
       next(error);
       next(error);

+ 16 - 8
server/src/users/users.service.ts

@@ -4,6 +4,7 @@ import {
   UpdateUserReq,
   UpdateUserReq,
   DeleteUserReq,
   DeleteUserReq,
 } from '@zilliz/milvus2-sdk-node/dist/milvus/types/User';
 } from '@zilliz/milvus2-sdk-node/dist/milvus/types/User';
+import { throwErrorFromSDK } from '../utils/Error';
 
 
 export class UserService {
 export class UserService {
   constructor(private milvusService: MilvusService) {}
   constructor(private milvusService: MilvusService) {}
@@ -13,22 +14,29 @@ export class UserService {
   }
   }
 
 
   async getUsers() {
   async getUsers() {
-    const result = await this.userManager.listUsers();
-    return result;
+    const res = await this.userManager.listUsers();
+    throwErrorFromSDK(res.status);
+
+    return res;
   }
   }
 
 
   async createUser(data: CreateUserReq) {
   async createUser(data: CreateUserReq) {
-    const result = await this.userManager.createUser(data);
-    return result;
+    const res = await this.userManager.createUser(data);
+    throwErrorFromSDK(res);
+
+    return res;
   }
   }
 
 
   async updateUser(data: UpdateUserReq) {
   async updateUser(data: UpdateUserReq) {
-    const result = await this.userManager.updateUser(data);
-    return result;
+    const res = await this.userManager.updateUser(data);
+    throwErrorFromSDK(res);
+
+    return res;
   }
   }
 
 
   async deleteUser(data: DeleteUserReq) {
   async deleteUser(data: DeleteUserReq) {
-    const result = await this.userManager.deleteUser(data);
-    return result;
+    const res = await this.userManager.deleteUser(data);
+    throwErrorFromSDK(res);
+    return res;
   }
   }
 }
 }