feat: prevent the user from entering the knowledge base if he is not logged in (#45)

This commit is contained in:
balibabu 2024-01-29 19:28:39 +08:00 committed by GitHub
parent e1bc1d46e6
commit 04aba1bb65
20 changed files with 700 additions and 591 deletions

View File

@ -1,22 +1,20 @@
import { defineConfig } from "umi";
import routes from './routes'
import { defineConfig } from 'umi';
import routes from './src/routes';
export default defineConfig({
outputPath: 'dist',
// alias: { '@': './src' },
routes,
npmClient: 'npm',
base: '/',
routes,
publicPath: '/web/dist/',
esbuildMinifyIIFE: true,
icons: {
},
icons: {},
hash: true,
history: {
type: 'browser',
},
plugins: ['@react-dev-inspector/umi4-plugin','@umijs/plugins/dist/dva',],
plugins: ['@react-dev-inspector/umi4-plugin', '@umijs/plugins/dist/dva'],
dva: {},
// proxy: {
// '/v1': {
@ -26,4 +24,3 @@ export default defineConfig({
// },
// },
});

View File

@ -1,89 +0,0 @@
const routes = [
{
path: '/login',
component: '@/pages/login',
layout: false
},
{
path: '/',
component: '@/layouts', // 默认页面
redirect: '/knowledge',
// wrappers: [
// '@/wrappers/auth',
// ]
},
{
id: 2,
name: '知识库',
icon: 'home',
auth: [3, 4, 100],
path: '/knowledge',
component: '@/pages/knowledge',
pathname: 'knowledge'
},
{
id: 2,
name: '知识库',
icon: 'home',
auth: [3, 4, 100],
path: '/knowledge/add/*',
component: '@/pages/add-knowledge',
pathname: 'knowledge',
// routes: [{
// id: 3,
// name: '设置',
// icon: 'home',
// auth: [3, 4, 100],
// path: '/knowledge/add/setting',
// component: '@/pages/setting',
// pathname: "setting"
// }, {
// id: 1,
// name: '文件',
// icon: 'file',
// auth: [3, 4, 100],
// path: '/knowledge/add/file',
// component: '@/pages/file',
// pathname: 'file'
// },]
},
{
id: 3,
name: '聊天',
icon: 'home',
auth: [3, 4, 100],
path: '/chat',
component: '@/pages/chat',
pathname: "chat"
},
{
id: 3,
name: '设置',
icon: 'home',
auth: [3, 4, 100],
path: '/setting',
component: '@/pages/setting',
pathname: "setting"
},
{
id: 1,
name: '文件',
icon: 'file',
auth: [3, 4, 100],
path: '/file',
component: '@/pages/file',
pathname: 'file'
},
{
path: '/*',
component: '@/pages/404',
layout: false
}
];
module.exports = routes;

View File

@ -0,0 +1,3 @@
export const Authorization = 'Authorization';
export const Token = 'token';
export const UserInfo = 'userInfo';

View File

10
web/src/hooks/authHook.ts Normal file
View File

@ -0,0 +1,10 @@
import authorizationUtil from '@/utils/authorizationUtil';
import { useState } from 'react';
export const useAuth = () => {
const [isLogin, setIsLogin] = useState(
() => !!authorizationUtil.getAuthorization(),
);
return { isLogin };
};

View File

@ -1,38 +1,53 @@
import React, { useMemo } from 'react';
import authorizationUtil from '@/utils/authorizationUtil';
import type { MenuProps } from 'antd';
import { Button, Dropdown, } from 'antd';
import { history } from 'umi'
import { useTranslation, Trans } from 'react-i18next'
import { Button, Dropdown } from 'antd';
import React, { useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { history } from 'umi';
const App: React.FC = () => {
const { t } = useTranslation()
const logout = () => { history.push('/login') }
const toSetting = () => { history.push('/setting') }
const items: MenuProps['items'] = useMemo(() => {
return [
{
key: '1',
label: (
<Button type="text" onClick={logout}>{t('header.logout')}</Button>
),
},
{
key: '2',
label: (
<Button type="text" onClick={toSetting}>{t('header.setting')}</Button>
),
},
]
}, []);
const { t } = useTranslation();
return (<>
<Dropdown menu={{ items }} placement="bottomLeft" arrow>
<img
style={{ width: '50px', height: '50px', borderRadius: '25px' }}
src="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png"
/>
</Dropdown>
</>)
}
const logout = () => {
authorizationUtil.removeAll();
history.push('/login');
};
export default App;
const toSetting = () => {
history.push('/setting');
};
const items: MenuProps['items'] = useMemo(() => {
return [
{
key: '1',
label: (
<Button type="text" onClick={logout}>
{t('header.logout')}
</Button>
),
},
{
key: '2',
label: (
<Button type="text" onClick={toSetting}>
{t('header.setting')}
</Button>
),
},
];
}, []);
return (
<>
<Dropdown menu={{ items }} placement="bottomLeft" arrow>
<img
style={{ width: '50px', height: '50px', borderRadius: '25px' }}
src="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png"
/>
</Dropdown>
</>
);
};
export default App;

View File

@ -1,22 +1,18 @@
import logo from '@/assets/logo.png';
import { Layout, Space, theme } from 'antd';
import classnames from 'classnames';
import React, { useEffect, useState } from 'react';
import { history, Outlet, useLocation, useNavigate } from 'umi';
import { useTranslation, Trans } from 'react-i18next'
import classnames from 'classnames'
import { useTranslation } from 'react-i18next';
import { Outlet, useLocation, useNavigate } from 'umi';
import '../locales/config';
import logo from '@/assets/logo.png'
import {
RedditOutlined
} from '@ant-design/icons';
import { Layout, Button, theme, Space, } from 'antd';
import styles from './index.less'
import User from './components/user'
import { head } from 'lodash';
import User from './components/user';
import styles from './index.less';
const { Header, Content } = Layout;
const App: React.FC = (props) => {
const { t } = useTranslation()
const navigate = useNavigate()
const { t } = useTranslation();
const navigate = useNavigate();
const {
token: { colorBgContainer, borderRadiusLG },
} = theme.useToken();
@ -25,34 +21,49 @@ const App: React.FC = (props) => {
const location = useLocation();
useEffect(() => {
if (location.pathname !== '/') {
const path = location.pathname.split('/')
setCurrent(path[1]);
const path = location.pathname.split('/');
// setCurrent(path[1]);
}
console.log(location.pathname.split('/'))
}, [location.pathname])
console.log(location.pathname.split('/'));
}, [location.pathname]);
const handleChange = (path: string) => {
setCurrent(path)
// setCurrent(path)
navigate(path);
};
const tagsData = [{ path: '/knowledge', name: 'knowledge' }, { path: '/chat', name: 'chat' }, { path: '/file', name: 'file' }];
const tagsData = [
{ path: '/knowledge', name: 'knowledge' },
{ path: '/chat', name: 'chat' },
{ path: '/file', name: 'file' },
];
return (
<Layout className={styles.layout} >
<Layout className={styles.layout}>
<Layout>
<Header style={{ padding: '0 8px', background: colorBgContainer, display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<Header
style={{
padding: '0 8px',
background: colorBgContainer,
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
}}
>
<img src={logo} alt="" style={{ height: 30, width: 30 }} />
<Space size={[0, 8]} wrap>
{tagsData.map((item) =>
(<span key={item.name} className={classnames(styles['tag'], {
[styles['checked']]: current === item.name
})} onClick={() => handleChange(item.path)}>
{item.name}
</span>)
)}
{tagsData.map((item) => (
<span
key={item.name}
className={classnames(styles['tag'], {
[styles['checked']]: current === item.name,
})}
onClick={() => handleChange(item.path)}
>
{item.name}
</span>
))}
</Space>
<User ></User>
<User></User>
</Header>
<Content
style={{
@ -61,14 +72,14 @@ const App: React.FC = (props) => {
minHeight: 280,
background: colorBgContainer,
borderRadius: borderRadiusLG,
overflow: 'auto'
overflow: 'auto',
}}
>
<Outlet />
</Content>
</Layout>
</Layout >
</Layout>
);
};
export default App;
export default App;

View File

@ -1,6 +1,5 @@
import { Effect, Reducer, Subscription } from 'umi'
import { message } from 'antd';
import kbService from '@/services/kbService';
import { Effect, Reducer } from 'umi';
export interface chunkModelState {
loading: boolean;
@ -9,7 +8,7 @@ export interface chunkModelState {
isShowCreateModal: boolean;
chunk_id: string;
doc_id: string;
chunkInfo: any
chunkInfo: any;
}
export interface chunkgModelType {
namespace: 'chunkModel';
@ -24,7 +23,7 @@ export interface chunkgModelType {
reducers: {
updateState: Reducer<chunkModelState>;
};
subscriptions: { setup: Subscription };
// subscriptions: { setup: Subscription };
}
const Model: chunkgModelType = {
namespace: 'chunkModel',
@ -35,91 +34,86 @@ const Model: chunkgModelType = {
isShowCreateModal: false,
chunk_id: '',
doc_id: '',
chunkInfo: {}
},
subscriptions: {
setup({ dispatch, history }) {
history.listen(location => {
console.log(location)
});
}
chunkInfo: {},
},
// subscriptions: {
// setup({ dispatch, history }) {
// history.listen(location => {
// console.log(location)
// });
// }
// },
effects: {
* chunk_list({ payload = {}, callback }, { call, put }) {
*chunk_list({ payload = {}, callback }, { call, put }) {
const { data, response } = yield call(kbService.chunk_list, payload);
const { retcode, data: res, retmsg } = data
const { retcode, data: res, retmsg } = data;
if (retcode === 0) {
console.log(res)
console.log(res);
yield put({
type: 'updateState',
payload: {
data: res.chunks,
total: res.total,
loading: false
}
loading: false,
},
});
callback && callback()
callback && callback();
}
},
*switch_chunk({ payload = {}, callback }, { call, put }) {
const { data, response } = yield call(kbService.switch_chunk, payload);
const { retcode, data: res, retmsg } = data
const { retcode, data: res, retmsg } = data;
if (retcode === 0) {
callback && callback()
callback && callback();
}
},
*rm_chunk({ payload = {}, callback }, { call, put }) {
console.log('shanchu')
console.log('shanchu');
const { data, response } = yield call(kbService.rm_chunk, payload);
const { retcode, data: res, retmsg } = data
const { retcode, data: res, retmsg } = data;
if (retcode === 0) {
callback && callback()
callback && callback();
}
},
* get_chunk({ payload = {}, callback }, { call, put }) {
*get_chunk({ payload = {}, callback }, { call, put }) {
const { data, response } = yield call(kbService.get_chunk, payload);
const { retcode, data: res, retmsg } = data
const { retcode, data: res, retmsg } = data;
if (retcode === 0) {
yield put({
type: 'updateState',
payload: {
chunkInfo: res
}
chunkInfo: res,
},
});
callback && callback(res)
callback && callback(res);
}
},
*create_hunk({ payload = {} }, { call, put }) {
yield put({
type: 'updateState',
payload: {
loading: true
}
loading: true,
},
});
let service = kbService.create_chunk
let service = kbService.create_chunk;
if (payload.chunk_id) {
service = kbService.set_chunk
service = kbService.set_chunk;
}
const { data, response } = yield call(service, payload);
const { retcode, data: res, retmsg } = data
const { retcode, data: res, retmsg } = data;
yield put({
type: 'updateState',
payload: {
loading: false
}
loading: false,
},
});
if (retcode === 0) {
yield put({
type: 'updateState',
payload: {
isShowCreateModal: false
}
isShowCreateModal: false,
},
});
}
},
@ -128,9 +122,9 @@ const Model: chunkgModelType = {
updateState(state, { payload }) {
return {
...state,
...payload
...payload,
};
}
}
},
},
};
export default Model;

View File

@ -1,6 +1,6 @@
import { message } from 'antd';
import { Effect, Reducer, Subscription } from 'umi'
import kbService from '@/services/kbService';
import { message } from 'antd';
import { Effect, Reducer, Subscription } from 'umi';
export interface kFModelState {
isShowCEFwModal: boolean;
@ -8,7 +8,7 @@ export interface kFModelState {
isShowSegmentSetModal: boolean;
loading: boolean;
tenantIfo: any;
data: any[]
data: any[];
}
export interface kFModelType {
namespace: 'kFModel';
@ -36,60 +36,60 @@ const Model: kFModelType = {
isShowSegmentSetModal: false,
loading: false,
tenantIfo: {},
data: []
data: [],
},
subscriptions: {
setup({ dispatch, history }) {
history.listen(location => {
});
}
history.listen((location) => {});
},
},
effects: {
* createKf({ payload = {}, callback }, { call, put }) {
*createKf({ payload = {}, callback }, { call, put }) {
const { data, response } = yield call(kbService.createKb, payload);
const { retcode, data: res, retmsg } = data
const { retcode, data: res, retmsg } = data;
if (retcode === 0) {
message.success('创建成功!');
}
},
* updateKf({ payload = {}, callback }, { call, put }) {
*updateKf({ payload = {}, callback }, { call, put }) {
const { data, response } = yield call(kbService.updateKb, payload);
const { retcode, data: res, retmsg } = data
const { retcode, data: res, retmsg } = data;
if (retcode === 0) {
message.success('修改成功!');
}
},
*getKfDetail({ payload = {}, callback }, { call, put }) {
const { data, response } = yield call(kbService.get_kb_detail, payload);
const { retcode, data: res, retmsg } = data
const { retcode, data: res, retmsg } = data;
if (retcode === 0) {
// localStorage.setItem('userInfo',res.)
callback && callback(res)
callback && callback(res);
}
},
*getKfList({ payload = {} }, { call, put }) {
yield put({
type: 'updateState',
payload: {
loading: true
}
loading: true,
},
});
const { data, response } = yield call(kbService.get_document_list, payload);
const { retcode, data: res, retmsg } = data
const { data, response } = yield call(
kbService.get_document_list,
payload,
);
const { retcode, data: res, retmsg } = data;
yield put({
type: 'updateState',
payload: {
loading: false
}
loading: false,
},
});
if (retcode === 0) {
yield put({
type: 'updateState',
payload: {
data: res
}
data: res,
},
});
}
},
@ -97,58 +97,60 @@ const Model: kFModelType = {
yield put({
type: 'updateState',
payload: {
loading: true
}
loading: true,
},
});
const { data, response } = yield call(kbService.document_change_status, payload);
const { retcode, data: res, retmsg } = data
const { data, response } = yield call(
kbService.document_change_status,
payload,
);
const { retcode, data: res, retmsg } = data;
if (retcode === 0) {
message.success('修改成功!');
yield put({
type: 'updateState',
payload: {
loading: false
}
loading: false,
},
});
callback && callback()
callback && callback();
}
},
*document_rm({ payload = {}, callback }, { call, put }) {
const { data, response } = yield call(kbService.document_rm, payload);
const { retcode, data: res, retmsg } = data
const { retcode, data: res, retmsg } = data;
if (retcode === 0) {
message.success('删除成功!');
callback && callback()
callback && callback();
}
},
*document_create({ payload = {}, callback }, { call, put }) {
const { data, response } = yield call(kbService.document_create, payload);
const { retcode, data: res, retmsg } = data
const { retcode, data: res, retmsg } = data;
if (retcode === 0) {
message.success('创建成功!');
callback && callback()
callback && callback();
}
},
*document_change_parser({ payload = {}, callback }, { call, put }) {
const { data, response } = yield call(kbService.document_change_parser, payload);
const { retcode, data: res, retmsg } = data
const { data, response } = yield call(
kbService.document_change_parser,
payload,
);
const { retcode, data: res, retmsg } = data;
if (retcode === 0) {
message.success('修改成功!');
callback && callback()
callback && callback();
}
},
},
reducers: {
updateState(state, { payload }) {
return {
...state,
...payload
...payload,
};
}
}
},
},
};
export default Model;

View File

@ -1,109 +1,131 @@
import React, { useEffect, useState, } from 'react';
import { useNavigate, connect, Dispatch } from 'umi'
import { Card, List, Popconfirm, message, FloatButton, Row, Col } from 'antd';
import { MinusSquareOutlined, DeleteOutlined, PlusOutlined } from '@ant-design/icons';
import styles from './index.less'
import { formatDate } from '@/utils/date'
import type { knowledgeModelState } from './model'
import { formatDate } from '@/utils/date';
import {
DeleteOutlined,
MinusSquareOutlined,
PlusOutlined,
} from '@ant-design/icons';
import { Card, Col, FloatButton, Popconfirm, Row } from 'antd';
import React, { useEffect } from 'react';
import { Dispatch, connect, useNavigate } from 'umi';
import styles from './index.less';
import type { knowledgeModelState } from './model';
interface KnowledgeProps {
dispatch: Dispatch;
knowledgeModel: knowledgeModelState
knowledgeModel: knowledgeModelState;
}
const Index: React.FC<KnowledgeProps> = ({ knowledgeModel, dispatch }) => {
const navigate = useNavigate()
const navigate = useNavigate();
// const [datas, setDatas] = useState(data)
const { data = [] } = knowledgeModel
console.log(knowledgeModel)
const { data = [] } = knowledgeModel;
console.log(knowledgeModel);
// const x = useSelector((state) => state.knowledgeModel);
const confirm = (id: string) => {
dispatch({
type: 'knowledgeModel/rmKb',
payload: {
kb_id: id
kb_id: id,
},
callback: () => {
dispatch({
type: 'knowledgeModel/getList',
payload: {
}
payload: {},
});
}
},
});
};
const handleAddKnowledge = () => {
navigate(`add/setting?activeKey=setting`);
}
};
const handleEditKnowledge = (id: string) => {
navigate(`add/setting?activeKey=file&id=${id}`);
}
};
useEffect(() => {
dispatch({
type: 'knowledgeModel/getList',
payload: {
}
payload: {},
});
}, [])
return (<>
<div className={styles.knowledge}>
<FloatButton onClick={handleAddKnowledge} icon={<PlusOutlined />} type="primary" style={{ right: 24, top: 100 }} />
<Row gutter={{ xs: 8, sm: 16, md: 24, lg: 32 }}>
{
data.map((item: any) => {
return (<Col className="gutter-row" key={item.name} xs={24} sm={12} md={8} lg={6}>
<Card className={styles.card}
onClick={() => { handleEditKnowledge(item.id) }}
}, []);
return (
<>
<div className={styles.knowledge}>
<FloatButton
onClick={handleAddKnowledge}
icon={<PlusOutlined />}
type="primary"
style={{ right: 24, top: 100 }}
/>
<Row gutter={{ xs: 8, sm: 16, md: 24, lg: 32 }}>
{data.map((item: any) => {
return (
<Col
className="gutter-row"
key={item.name}
xs={24}
sm={12}
md={8}
lg={6}
>
<div className={styles.container}>
<div className={styles.content}>
<span className={styles.context}>
{item.name}
</span>
<span className={styles.delete}>
<Popconfirm
title="Delete the task"
description="Are you sure to delete this task?"
onConfirm={(e: any) => {
e.stopPropagation();
e.nativeEvent.stopImmediatePropagation()
confirm(item.id)
}}
okText="Yes"
cancelText="No"
>
<DeleteOutlined onClick={(e) => {
e.stopPropagation();
e.nativeEvent.stopImmediatePropagation()
}} />
</Popconfirm>
</span>
<Card
className={styles.card}
onClick={() => {
handleEditKnowledge(item.id);
}}
>
<div className={styles.container}>
<div className={styles.content}>
<span className={styles.context}>{item.name}</span>
<span className={styles.delete}>
<Popconfirm
title="Delete the task"
description="Are you sure to delete this task?"
onConfirm={(e: any) => {
e.stopPropagation();
e.nativeEvent.stopImmediatePropagation();
confirm(item.id);
}}
okText="Yes"
cancelText="No"
>
<DeleteOutlined
onClick={(e) => {
e.stopPropagation();
e.nativeEvent.stopImmediatePropagation();
}}
/>
</Popconfirm>
</span>
</div>
<div className={styles.footer}>
<span className={styles.text}>
<MinusSquareOutlined />
{item.doc_num}
</span>
<span className={styles.text}>
<MinusSquareOutlined />
{item.chunk_num}
</span>
<span className={styles.text}>
<MinusSquareOutlined />
{item.token_num}
</span>
<span style={{ float: 'right' }}>
{formatDate(item.update_date)}
</span>
</div>
</div>
<div className={styles.footer}>
<span className={styles.text}>
<MinusSquareOutlined />{item.doc_num}
</span>
<span className={styles.text}>
<MinusSquareOutlined />{item.chunk_num}
</span>
<span className={styles.text}>
<MinusSquareOutlined />{item.token_num}
</span>
<span style={{ float: 'right' }}>
{formatDate(item.update_date)}
</span>
</div>
</div>
</Card>
</Col>)
})
}
</Row>
</div>
</>
)
</Card>
</Col>
);
})}
</Row>
</div>
</>
);
};
export default connect(({ knowledgeModel, loading }) => ({ knowledgeModel, loading }))(Index);
export default connect(({ knowledgeModel, loading }) => ({
knowledgeModel,
loading,
}))(Index);

View File

@ -1,5 +1,5 @@
import kbService from '@/services/kbService';
import { Effect, Reducer, Subscription } from 'umi';
import { Effect, Reducer } from 'umi';
export interface knowledgeModelState {
loading: boolean;
@ -15,7 +15,7 @@ export interface knowledgegModelType {
reducers: {
updateState: Reducer<knowledgeModelState>;
};
subscriptions: { setup: Subscription };
// subscriptions: { setup: Subscription };
}
const Model: knowledgegModelType = {
namespace: 'knowledgeModel',
@ -23,13 +23,13 @@ const Model: knowledgegModelType = {
loading: false,
data: [],
},
subscriptions: {
setup({ dispatch, history }) {
history.listen((location) => {
console.log(location);
});
},
},
// subscriptions: {
// setup({ dispatch, history }) {
// history.listen((location) => {
// console.log(location);
// });
// },
// },
effects: {
*rmKb({ payload = {}, callback }, { call, put }) {
const { data, response } = yield call(kbService.rmKb, payload);

View File

@ -1,19 +1,18 @@
import { connect, Icon, Dispatch } from 'umi';
import { Input, Form, Button, Checkbox } from 'antd';
import { rsaPsw } from '@/utils';
import { Button, Checkbox, Form, Input } from 'antd';
import { FC, useEffect, useState } from 'react';
import { Dispatch, Icon, connect, useNavigate } from 'umi';
import styles from './index.less';
import { rsaPsw } from '@/utils'
import { useState, useEffect, FC } from 'react';
interface LoginProps {
dispatch: Dispatch;
}
const View: FC<LoginProps> = ({
dispatch,
}) => {
const [title, setTitle] = useState('login')
const View: FC<LoginProps> = ({ dispatch }) => {
let navigate = useNavigate();
const [title, setTitle] = useState('login');
const changeTitle = () => {
setTitle((title) => title === 'login' ? 'register' : 'login')
}
setTitle((title) => (title === 'login' ? 'register' : 'login'));
};
const [form] = Form.useForm();
const [checkNick, setCheckNick] = useState(false);
@ -25,15 +24,17 @@ const View: FC<LoginProps> = ({
try {
const params = await form.validateFields();
var rsaPassWord = rsaPsw(params.password)
var rsaPassWord = rsaPsw(params.password);
if (title === 'login') {
dispatch({
const ret = await dispatch({
type: 'loginModel/login',
payload: {
email: params.email,
password: rsaPassWord
}
password: rsaPassWord,
},
});
console.info(ret);
navigate('/knowledge');
} else {
dispatch({
type: 'loginModel/register',
@ -43,8 +44,8 @@ const View: FC<LoginProps> = ({
password: rsaPassWord,
},
callback() {
setTitle('login')
}
setTitle('login');
},
});
}
} catch (errorInfo) {
@ -56,103 +57,124 @@ const View: FC<LoginProps> = ({
// wrapperCol: { span: 8 },
};
const toGoogle = () => {
window.location.href = "https://github.com/login/oauth/authorize?scope=user:email&client_id=302129228f0d96055bee"
}
window.location.href =
'https://github.com/login/oauth/authorize?scope=user:email&client_id=302129228f0d96055bee';
};
return (
<div className={styles.loginPage}>
<div className={styles.loginLeft}>
<div className={styles.modal}>
<div className={styles.loginTitle}>
<div>
{title === 'login' ? 'Sign in' : 'Create an account'}
</div>
<span >
{title === 'login' ? 'Were so excited to see you again!' : 'Glad to have you on board!'}
<div>{title === 'login' ? 'Sign in' : 'Create an account'}</div>
<span>
{title === 'login'
? 'Were so excited to see you again!'
: 'Glad to have you on board!'}
</span>
</div>
<Form form={form} layout="vertical" name="dynamic_rule" style={{ maxWidth: 600 }}>
<Form
form={form}
layout="vertical"
name="dynamic_rule"
style={{ maxWidth: 600 }}
>
<Form.Item
{...formItemLayout}
name="email"
label="Email"
rules={[{ required: true, message: 'Please input value' }]}
>
<Input size='large' placeholder="Please input value" />
<Input size="large" placeholder="Please input value" />
</Form.Item>
{
title === 'register' && <Form.Item
{title === 'register' && (
<Form.Item
{...formItemLayout}
name="nickname"
label="Nickname"
rules={[{ required: true, message: 'Please input your nickname' }]}
rules={[
{ required: true, message: 'Please input your nickname' },
]}
>
<Input size='large' placeholder="Please input your nickname" />
<Input size="large" placeholder="Please input your nickname" />
</Form.Item>
}
)}
<Form.Item
{...formItemLayout}
name="password"
label="Password"
rules={[{ required: true, message: 'Please input value' }]}
>
<Input size='large' placeholder="Please input value" />
<Input size="large" placeholder="Please input value" />
</Form.Item>
{
title === 'login' && <Form.Item
name="remember"
valuePropName="checked"
>
{title === 'login' && (
<Form.Item name="remember" valuePropName="checked">
<Checkbox> Remember me</Checkbox>
</Form.Item>
}
<div> {
title === 'login' && (<div>
Dont have an account?
<Button type="link" onClick={changeTitle}>
Sign up
</Button>
</div>)
}
{
title === 'register' && (<div>
)}
<div>
{' '}
{title === 'login' && (
<div>
Dont have an account?
<Button type="link" onClick={changeTitle}>
Sign up
</Button>
</div>
)}
{title === 'register' && (
<div>
Already have an account?
<Button type="link" onClick={changeTitle}>
Sign in
</Button>
</div>)
}
</div>
)}
</div>
<Button type="primary" block size='large' onClick={onCheck}>
<Button type="primary" block size="large" onClick={onCheck}>
{title === 'login' ? 'Sign in' : 'Continue'}
</Button>
{
title === 'login' && (<><Button block size='large' onClick={toGoogle} style={{ marginTop: 15 }}>
<div >
<Icon icon="local:google" style={{ verticalAlign: 'middle', marginRight: 5 }} />
Sign in with Google
</div>
</Button>
<Button block size='large' onClick={toGoogle} style={{ marginTop: 15 }}>
<div >
<Icon icon="local:github" style={{ verticalAlign: 'middle', marginRight: 5 }} />
{title === 'login' && (
<>
<Button
block
size="large"
onClick={toGoogle}
style={{ marginTop: 15 }}
>
<div>
<Icon
icon="local:google"
style={{ verticalAlign: 'middle', marginRight: 5 }}
/>
Sign in with Google
</div>
</Button>
<Button
block
size="large"
onClick={toGoogle}
style={{ marginTop: 15 }}
>
<div>
<Icon
icon="local:github"
style={{ verticalAlign: 'middle', marginRight: 5 }}
/>
Sign in with Github
</div>
</Button></>)
}
</Button>
</>
)}
</Form>
</div>
</div>
<div className={styles.loginRight}>
</div>
<div className={styles.loginRight}></div>
</div>
);
};
export default connect(({ loginModel, loading }) => ({ loginModel, loading }))(View);
export default connect(({ loginModel, loading }) => ({ loginModel, loading }))(
View,
);

View File

@ -1,6 +1,8 @@
import { Effect, Reducer, Subscription } from 'umi'
import { message } from 'antd';
import { Authorization } from '@/constants/authorization';
import userService from '@/services/userService';
import authorizationUtil from '@/utils/authorizationUtil';
import { message } from 'antd';
import { Effect, Reducer, Subscription } from 'umi';
export interface loginModelState {
list: any[];
@ -28,49 +30,52 @@ const Model: logingModelType = {
},
subscriptions: {
setup({ dispatch, history }) {
history.listen(location => { });
}
history.listen((location) => {});
},
},
effects: {
*login({ payload = {} }, { call, put }) {
console.log(111, payload)
console.log(111, payload);
const { data, response } = yield call(userService.login, payload);
const { retcode, data: res, retmsg } = data
console.log()
const Authorization = response.headers.get('Authorization')
const { retcode, data: res, retmsg } = data;
console.log();
const authorization = response.headers.get(Authorization);
if (retcode === 0) {
message.success('登录成功!');
const token = res.access_token;
const userInfo = {
avatar: res.avatar,
name: res.nickname,
email: res.email
email: res.email,
};
localStorage.setItem('token', token)
localStorage.setItem('userInfo', JSON.stringify(userInfo))
localStorage.setItem('Authorization', Authorization)
setTimeout(() => {
window.location.href = '/file';
}, 300);
authorizationUtil.setItems({
Authorization: authorization,
userInfo: JSON.stringify(userInfo),
Token: token,
});
// setTimeout(() => {
// window.location.href = '/file';
// }, 300);
}
return data;
},
*register({ payload = {}, callback }, { call, put }) {
const { data, response } = yield call(userService.register, payload);
console.log()
const { retcode, data: res, retmsg } = data
console.log();
const { retcode, data: res, retmsg } = data;
if (retcode === 0) {
message.success('注册成功!');
callback && callback()
callback && callback();
}
}
},
},
reducers: {
updateState(state, { payload }) {
return {
...state,
...payload
...payload,
};
}
}
},
},
};
export default Model;

View File

@ -1,90 +1,105 @@
import { connect, Dispatch } from 'umi';
import { Button, FloatButton } from 'antd';
import i18n from 'i18next';
import { useTranslation, Trans } from 'react-i18next'
import { Button, FloatButton } from 'antd'
import { useTranslation } from 'react-i18next';
import { Dispatch, connect } from 'umi';
import authorizationUtil from '@/utils/authorizationUtil';
import { FC, useEffect } from 'react';
import CPwModal from './CPwModal';
import List from './List';
import SAKModal from './SAKModal';
import SSModal from './SSModal';
import TntModal from './TntModal';
import styles from './index.less';
import CPwModal from './CPwModal'
import SAKModal from './SAKModal'
import TntModal from './TntModal'
import SSModal from './SSModal'
import List from './List'
import { useEffect, useState, FC } from 'react';
interface CPwModalProps {
dispatch: Dispatch;
settingModel: any
dispatch: Dispatch;
settingModel: any;
}
const Index: FC<CPwModalProps> = ({ settingModel, dispatch }) => {
// const [llm_factory, set_llm_factory] = useState('')
const { t } = useTranslation()
const userInfo = JSON.parse(localStorage.getItem('userInfo') || '')
const changeLang = (val: string) => { // 改变状态里的 语言 进行切换
i18n.changeLanguage(val);
}
useEffect(() => {
dispatch({
type: 'settingModel/getTenantInfo',
payload: {
}
});
}, [])
const showCPwModal = () => {
dispatch({
type: 'settingModel/updateState',
payload: {
isShowPSwModal: true
}
});
};
const showTntModal = () => {
dispatch({
type: 'settingModel/updateState',
payload: {
isShowTntModal: true
}
});
};
const showSSModal = () => {
dispatch({
type: 'settingModel/updateState',
payload: {
isShowSSModal: true
}
});
// dispatch({
// type: 'settingModel/getTenantInfo',
// payload: {
// }
// });
};
return (
<div className={styles.settingPage}>
<div className={styles.avatar}>
<img style={{ width: 50, marginRight: 5 }} src="https://os.alipayobjects.com/rmsportal/QBnOOoLaAfKPirc.png" alt="" />
<div>
<div>{userInfo.name}</div>
<div><span>******</span><Button type='link' onClick={showCPwModal}></Button></div>
</div>
</div >
<div>
<Button type="link" onClick={showTntModal}>
</Button>
<Button type="link" onClick={showSSModal}>
</Button>
<List />
</div>
<CPwModal />
<SAKModal />
<SSModal />
<TntModal />
<FloatButton shape='square' description={t('setting.btn')} onClick={() => i18n.changeLanguage(i18n.language == 'en' ? 'zh' : 'en')} type="default" style={{ right: 94, fontSize: 14 }} />
</div >
);
}
export default connect(({ settingModel, loading }) => ({ settingModel, loading }))(Index);
// const [llm_factory, set_llm_factory] = useState('')
const { t } = useTranslation();
const userInfo = authorizationUtil.getUserInfoObject();
const changeLang = (val: string) => {
// 改变状态里的 语言 进行切换
i18n.changeLanguage(val);
};
useEffect(() => {
dispatch({
type: 'settingModel/getTenantInfo',
payload: {},
});
}, []);
const showCPwModal = () => {
dispatch({
type: 'settingModel/updateState',
payload: {
isShowPSwModal: true,
},
});
};
const showTntModal = () => {
dispatch({
type: 'settingModel/updateState',
payload: {
isShowTntModal: true,
},
});
};
const showSSModal = () => {
dispatch({
type: 'settingModel/updateState',
payload: {
isShowSSModal: true,
},
});
// dispatch({
// type: 'settingModel/getTenantInfo',
// payload: {
// }
// });
};
return (
<div className={styles.settingPage}>
<div className={styles.avatar}>
<img
style={{ width: 50, marginRight: 5 }}
src="https://os.alipayobjects.com/rmsportal/QBnOOoLaAfKPirc.png"
alt=""
/>
<div>
<div>{userInfo.name}</div>
<div>
<span>******</span>
<Button type="link" onClick={showCPwModal}>
</Button>
</div>
</div>
</div>
<div>
<Button type="link" onClick={showTntModal}>
</Button>
<Button type="link" onClick={showSSModal}>
</Button>
<List />
</div>
<CPwModal />
<SAKModal />
<SSModal />
<TntModal />
<FloatButton
shape="square"
description={t('setting.btn')}
onClick={() => i18n.changeLanguage(i18n.language == 'en' ? 'zh' : 'en')}
type="default"
style={{ right: 94, fontSize: 14 }}
/>
</div>
);
};
export default connect(({ settingModel, loading }) => ({
settingModel,
loading,
}))(Index);

View File

@ -1,6 +1,7 @@
import { Effect, Reducer, Subscription } from 'umi';
import { message } from 'antd';
import userService from '@/services/userService';
import authorizationUtil from '@/utils/authorizationUtil';
import { message } from 'antd';
import { Effect, Reducer, Subscription } from 'umi';
export interface settingModelState {
isShowPSwModal: boolean;
@ -9,10 +10,10 @@ export interface settingModelState {
isShowSSModal: boolean;
llm_factory: string;
loading: boolean;
tenantIfo: any,
llmInfo: any,
myLlm: any[],
factoriesList: any[]
tenantIfo: any;
llmInfo: any;
myLlm: any[];
factoriesList: any[];
}
export interface settingModelType {
@ -45,32 +46,31 @@ const Model: settingModelType = {
tenantIfo: {},
llmInfo: {},
myLlm: [],
factoriesList: []
factoriesList: [],
},
subscriptions: {
setup({ dispatch, history }) {
history.listen(location => {
});
}
history.listen((location) => {});
},
},
effects: {
*setting({ payload = {}, callback }, { call, put }) {
const { data, response } = yield call(userService.setting, payload);
const { retcode, data: res, retmsg } = data
const { retcode, data: res, retmsg } = data;
if (retcode === 0) {
message.success('密码修改成功!');
callback && callback()
callback && callback();
}
},
*getUserInfo({ payload = {} }, { call, put }) {
const { data, response } = yield call(userService.user_info, payload);
const { retcode, data: res, retmsg } = data
const { retcode, data: res, retmsg } = data;
const userInfo = {
avatar: res.avatar,
name: res.nickname,
email: res.email
email: res.email,
};
localStorage.setItem('userInfo', JSON.stringify(userInfo))
authorizationUtil.setUserInfo(userInfo);
if (retcode === 0) {
// localStorage.setItem('userInfo',res.)
}
@ -79,91 +79,100 @@ const Model: settingModelType = {
yield put({
type: 'updateState',
payload: {
loading: true
}
loading: true,
},
});
const { data, response } = yield call(userService.get_tenant_info, payload);
const { retcode, data: res, retmsg } = data
const { data, response } = yield call(
userService.get_tenant_info,
payload,
);
const { retcode, data: res, retmsg } = data;
// llm_id 对应chat_id
// asr_id 对应speech2txt
yield put({
type: 'updateState',
payload: {
loading: false
}
loading: false,
},
});
if (retcode === 0) {
res.chat_id = res.llm_id
res.speech2text_id = res.asr_id
res.chat_id = res.llm_id;
res.speech2text_id = res.asr_id;
yield put({
type: 'updateState',
payload: {
tenantIfo: res
}
tenantIfo: res,
},
});
}
},
*set_tenant_info({ payload = {} }, { call, put }) {
const { data, response } = yield call(userService.set_tenant_info, payload);
const { retcode, data: res, retmsg } = data
const { data, response } = yield call(
userService.set_tenant_info,
payload,
);
const { retcode, data: res, retmsg } = data;
// llm_id 对应chat_id
// asr_id 对应speech2txt
if (retcode === 0) {
yield put({
type: 'updateState',
payload: {
isShowSSModal: false
}
isShowSSModal: false,
},
});
yield put({
type: 'getTenantInfo'
})
type: 'getTenantInfo',
});
}
},
*factories_list({ payload = {} }, { call, put }) {
const { data, response } = yield call(userService.factories_list, payload);
const { retcode, data: res, retmsg } = data
const { data, response } = yield call(
userService.factories_list,
payload,
);
const { retcode, data: res, retmsg } = data;
if (retcode === 0) {
yield put({
type: 'updateState',
payload: {
factoriesList: res
}
factoriesList: res,
},
});
}
},
*llm_list({ payload = {} }, { call, put }) {
const { data, response } = yield call(userService.llm_list, payload);
const { retcode, data: res, retmsg } = data
const { retcode, data: res, retmsg } = data;
if (retcode === 0) {
yield put({
type: 'updateState',
payload: {
llmInfo: res
}
llmInfo: res,
},
});
}
},
*my_llm({ payload = {} }, { call, put }) {
const { data, response } = yield call(userService.my_llm, payload);
const { retcode, data: res, retmsg } = data
const { retcode, data: res, retmsg } = data;
if (retcode === 0) {
yield put({
type: 'updateState',
payload: {
myLlm: res
}
myLlm: res,
},
});
}
},
*set_api_key({ payload = {}, callback }, { call, put }) {
const { data, response } = yield call(userService.set_api_key, payload);
const { retcode, data: res, retmsg } = data
const { retcode, data: res, retmsg } = data;
if (retcode === 0) {
message.success('设置API KEY成功');
callback && callback()
callback && callback();
}
},
},
@ -171,9 +180,9 @@ const Model: settingModelType = {
updateState(state, { payload }) {
return {
...state,
...payload
...payload,
};
}
}
},
},
};
export default Model;

43
web/src/routes.ts Normal file
View File

@ -0,0 +1,43 @@
const routes = [
{
path: '/login',
component: '@/pages/login',
layout: false,
},
{
path: '/',
component: '@/layouts',
layout: false,
wrappers: ['@/wrappers/auth'],
routes: [
{ path: '/', redirect: '/knowledge' },
{
path: '/knowledge',
component: '@/pages/knowledge',
},
{
path: '/knowledge/add/*',
component: '@/pages/add-knowledge',
},
{
path: '/chat',
component: '@/pages/chat',
},
{
path: '/setting',
component: '@/pages/setting',
},
{
path: '/file',
component: '@/pages/file',
},
],
},
{
path: '/*',
component: '@/pages/404',
layout: false,
},
];
export default routes;

View File

@ -0,0 +1,43 @@
import { Authorization, Token, UserInfo } from '@/constants/authorization';
const KeySet = [Authorization, Token, UserInfo];
const storage = {
getAuthorization: () => {
return localStorage.getItem(Authorization);
},
getToken: () => {
return localStorage.getItem(Token);
},
getUserInfo: () => {
return localStorage.getItem(UserInfo);
},
getUserInfoObject: () => {
return JSON.parse(localStorage.getItem('userInfo') || '');
},
setAuthorization: (value: string) => {
localStorage.setItem(Authorization, value);
},
setToken: (value: string) => {
localStorage.setItem(Token, value);
},
setUserInfo: (value: string | Object) => {
let valueStr = typeof value !== 'string' ? JSON.stringify(value) : value;
localStorage.setItem(UserInfo, valueStr);
},
setItems: (pairs: Record<string, string>) => {
Object.entries(pairs).forEach(([key, value]) => {
localStorage.setItem(key, value);
});
},
removeAuthorization: () => {
localStorage.removeItem(Authorization);
},
removeAll: () => {
KeySet.forEach((x) => {
localStorage.removeItem(x);
});
},
};
export default storage;

3
web/src/utils/history.ts Normal file
View File

@ -0,0 +1,3 @@
import { createBrowserHistory } from 'history';
export const history = createBrowserHistory();

View File

@ -1,14 +1,14 @@
import { message, notification } from 'antd';
import { extend } from 'umi-request';
import { notification, message } from 'antd';
import { Authorization } from '@/constants/authorization';
import api from '@/utils/api';
import authorizationUtil from '@/utils/authorizationUtil';
const { login } = api;
const ABORT_REQUEST_ERR_MESSAGE = 'The user aborted a request.'; // 手动中断请求。errorHandler 抛出的error message
const retcodeMessage = {
200: '服务器成功返回请求的数据。',
201: '新建或修改数据成功。',
@ -24,7 +24,7 @@ const retcodeMessage = {
500: '服务器发生错误,请检查服务器。',
502: '网关错误。',
503: '服务不可用,服务器暂时过载或维护。',
504: '网关超时。'
504: '网关超时。',
};
type retcode =
| 200
@ -49,16 +49,20 @@ interface responseType {
retcode: number;
data: any;
retmsg: string;
status: number
status: number;
}
const errorHandler = (error: { response: Response, message: string }): Response => {
const errorHandler = (error: {
response: Response;
message: string;
}): Response => {
const { response } = error;
// 手动中断请求 abort
if (error.message === ABORT_REQUEST_ERR_MESSAGE) {
console.log('user abort request');
} else {
if (response && response.status) {
const errorText = retcodeMessage[response.status as retcode] || response.statusText;
const errorText =
retcodeMessage[response.status as retcode] || response.statusText;
const { status, url } = response;
notification.error({
message: `请求错误 ${status}: ${url}`,
@ -80,21 +84,21 @@ const errorHandler = (error: { response: Response, message: string }): Response
const request = extend({
errorHandler, // 默认错误处理
timeout: 3000000,
getResponse: true
getResponse: true,
});
request.interceptors.request.use((url: string, options: any) => {
const Authorization = localStorage.getItem('Authorization')
const authorization = authorizationUtil.getAuthorization();
return {
url,
options: {
...options,
headers: {
...(options.skipToken ? undefined : { Authorization }),
...options.headers
...(options.skipToken ? undefined : { [Authorization]: authorization }),
...options.headers,
},
interceptors: true
}
interceptors: true,
},
};
});
@ -103,7 +107,7 @@ request.interceptors.request.use((url: string, options: any) => {
* */
request.interceptors.response.use(async (response: any, request) => {
console.log(response, request)
console.log(response, request);
const data: responseType = await response.clone().json();
// response 拦截
@ -113,6 +117,8 @@ request.interceptors.response.use(async (response: any, request) => {
description: data.retmsg,
duration: 3,
});
authorizationUtil.removeAll();
// history.push('/login'); // Will not jump to the login page
} else if (data.retcode !== 0) {
if (data.retcode === 100) {
message.error(data.retmsg);
@ -126,7 +132,6 @@ request.interceptors.response.use(async (response: any, request) => {
return response;
} else {
return response;
}
});

View File

@ -1,12 +1,11 @@
import { Navigate, Outlet } from 'umi'
import { useAuth } from '@/hooks/authHook';
import { Navigate, Outlet } from 'umi';
export default (props) => {
// const { isLogin } = useAuth();
console.log(props)
const isLogin = false
if (isLogin) {
return <Outlet />;
} else {
return <Navigate to="/login" />;
}
}
export default () => {
const { isLogin } = useAuth();
if (isLogin) {
return <Outlet />;
} else {
return <Navigate to="/login" />;
}
};