mirror of https://github.com/fantasticit/think.git
refactor: support org
This commit is contained in:
parent
3e0eb5dc92
commit
808f7877eb
|
@ -23,6 +23,7 @@ server:
|
||||||
enableRateLimit: true # 是否限流
|
enableRateLimit: true # 是否限流
|
||||||
rateLimitWindowMs: 60000 # 限流时间
|
rateLimitWindowMs: 60000 # 限流时间
|
||||||
rateLimitMax: 1000 # 单位限流时间内单个 ip 最大访问数量
|
rateLimitMax: 1000 # 单位限流时间内单个 ip 最大访问数量
|
||||||
|
enableEmailVerify: false
|
||||||
email: # 邮箱服务,参考 http://help.163.com/09/1223/14/5R7P6CJ600753VB8.html?servCode=6010376 获取 SMTP 配置
|
email: # 邮箱服务,参考 http://help.163.com/09/1223/14/5R7P6CJ600753VB8.html?servCode=6010376 获取 SMTP 配置
|
||||||
host: ''
|
host: ''
|
||||||
port: 465
|
port: 465
|
||||||
|
|
|
@ -30,6 +30,7 @@ export const System = () => {
|
||||||
<Banner type="warning" description="系统锁定后,除系统管理员外均不可登录,谨慎修改!" closeIcon={null} />
|
<Banner type="warning" description="系统锁定后,除系统管理员外均不可登录,谨慎修改!" closeIcon={null} />
|
||||||
<Form labelPosition="left" initValues={data} onChange={onFormChange} onSubmit={onFinish}>
|
<Form labelPosition="left" initValues={data} onChange={onFormChange} onSubmit={onFinish}>
|
||||||
<Form.Switch field="isSystemLocked" label="系统锁定" />
|
<Form.Switch field="isSystemLocked" label="系统锁定" />
|
||||||
|
<Form.Switch field="enableEmailVerify" label="邮箱检验" />
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
htmlType="submit"
|
htmlType="submit"
|
||||||
|
|
|
@ -20,7 +20,18 @@ export const DocumentCollaboration: React.FC<IProps> = ({ wikiId, documentId, di
|
||||||
const { user: currentUser } = useUser();
|
const { user: currentUser } = useUser();
|
||||||
const [visible, toggleVisible] = useToggle(false);
|
const [visible, toggleVisible] = useToggle(false);
|
||||||
const [collaborationUsers, setCollaborationUsers] = useState([]);
|
const [collaborationUsers, setCollaborationUsers] = useState([]);
|
||||||
const content = useMemo(() => <Members id={documentId} hook={useDoumentMembers} />, [documentId]);
|
const content = useMemo(
|
||||||
|
() => (
|
||||||
|
<div style={{ padding: '24px 0' }}>
|
||||||
|
<Members
|
||||||
|
id={documentId}
|
||||||
|
hook={useDoumentMembers}
|
||||||
|
descriptions={['权限继承:默认继承知识库成员权限', '超级管理员:知识库超级管理员和文档创建者']}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
[documentId]
|
||||||
|
);
|
||||||
const btn = useMemo(
|
const btn = useMemo(
|
||||||
() => (
|
() => (
|
||||||
<Button theme="borderless" type="tertiary" disabled={disabled} icon={<IconUserAdd />} onClick={toggleVisible} />
|
<Button theme="borderless" type="tertiary" disabled={disabled} icon={<IconUserAdd />} onClick={toggleVisible} />
|
||||||
|
@ -79,7 +90,7 @@ export const DocumentCollaboration: React.FC<IProps> = ({ wikiId, documentId, di
|
||||||
visible={visible}
|
visible={visible}
|
||||||
footer={null}
|
footer={null}
|
||||||
onCancel={toggleVisible}
|
onCancel={toggleVisible}
|
||||||
style={{ maxWidth: '96vw' }}
|
style={{ maxWidth: '96vw', maxHeight: '60vh', overflow: 'auto' }}
|
||||||
>
|
>
|
||||||
{content}
|
{content}
|
||||||
</Modal>
|
</Modal>
|
||||||
|
@ -97,6 +108,8 @@ export const DocumentCollaboration: React.FC<IProps> = ({ wikiId, documentId, di
|
||||||
width: 412,
|
width: 412,
|
||||||
maxWidth: '96vw',
|
maxWidth: '96vw',
|
||||||
padding: '0 24px',
|
padding: '0 24px',
|
||||||
|
maxHeight: '60vh',
|
||||||
|
overflow: 'auto',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{content}
|
{content}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { IconDelete } from '@douyinfe/semi-icons';
|
import { IconDelete } from '@douyinfe/semi-icons';
|
||||||
import { Modal, Popconfirm, Space, Typography } from '@douyinfe/semi-ui';
|
import { Popconfirm, Space, Typography } from '@douyinfe/semi-ui';
|
||||||
import { useDeleteDocument } from 'data/document';
|
import { useDeleteDocument } from 'data/document';
|
||||||
import { useRouterQuery } from 'hooks/use-router-query';
|
import { useRouterQuery } from 'hooks/use-router-query';
|
||||||
import Router from 'next/router';
|
import Router from 'next/router';
|
||||||
|
@ -15,8 +15,11 @@ interface IProps {
|
||||||
const { Text } = Typography;
|
const { Text } = Typography;
|
||||||
|
|
||||||
export const DocumentDeletor: React.FC<IProps> = ({ wikiId, documentId, render, onDelete }) => {
|
export const DocumentDeletor: React.FC<IProps> = ({ wikiId, documentId, render, onDelete }) => {
|
||||||
const { wikiId: currentWikiId, documentId: currentDocumentId } =
|
const {
|
||||||
useRouterQuery<{ wikiId?: string; documentId?: string }>();
|
organizationId,
|
||||||
|
wikiId: currentWikiId,
|
||||||
|
documentId: currentDocumentId,
|
||||||
|
} = useRouterQuery<{ organizationId: string; wikiId?: string; documentId?: string }>();
|
||||||
const { deleteDocument: api, loading } = useDeleteDocument(documentId);
|
const { deleteDocument: api, loading } = useDeleteDocument(documentId);
|
||||||
|
|
||||||
const deleteAction = useCallback(() => {
|
const deleteAction = useCallback(() => {
|
||||||
|
@ -26,14 +29,15 @@ export const DocumentDeletor: React.FC<IProps> = ({ wikiId, documentId, render,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Router.push({
|
Router.push({
|
||||||
pathname: `/wiki/${wikiId}`,
|
pathname: `/app/org/[organizationId]/wiki/[wikiId]`,
|
||||||
|
query: { organizationId, wikiId },
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
navigate();
|
navigate();
|
||||||
onDelete && onDelete();
|
onDelete && onDelete();
|
||||||
});
|
});
|
||||||
}, [wikiId, documentId, api, onDelete, currentWikiId, currentDocumentId]);
|
}, [organizationId, wikiId, documentId, api, onDelete, currentWikiId, currentDocumentId]);
|
||||||
|
|
||||||
const content = useMemo(
|
const content = useMemo(
|
||||||
() => (
|
() => (
|
||||||
|
|
|
@ -1,16 +1,11 @@
|
||||||
import { IAuthority, ILoginUser } from '@think/domains';
|
import { IAuthority, ILoginUser } from '@think/domains';
|
||||||
import cls from 'classnames';
|
import cls from 'classnames';
|
||||||
import { useDoumentMembers } from 'data/document';
|
|
||||||
import { event, triggerChangeDocumentTitle, triggerJoinUser, USE_DOCUMENT_VERSION } from 'event';
|
import { event, triggerChangeDocumentTitle, triggerJoinUser, USE_DOCUMENT_VERSION } from 'event';
|
||||||
import { useMount } from 'hooks/use-mount';
|
import { useMount } from 'hooks/use-mount';
|
||||||
import { useToggle } from 'hooks/use-toggle';
|
import React, { useEffect, useRef } from 'react';
|
||||||
import Router from 'next/router';
|
|
||||||
import React, { useEffect, useRef, useState } from 'react';
|
|
||||||
import { CollaborationEditor, ICollaborationRefProps } from 'tiptap/editor';
|
import { CollaborationEditor, ICollaborationRefProps } from 'tiptap/editor';
|
||||||
import { findMentions } from 'tiptap/prose-utils';
|
|
||||||
|
|
||||||
import styles from './index.module.scss';
|
import styles from './index.module.scss';
|
||||||
import { DocumentUserSetting } from './users';
|
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
user: ILoginUser;
|
user: ILoginUser;
|
||||||
|
@ -19,12 +14,8 @@ interface IProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Editor: React.FC<IProps> = ({ user: currentUser, documentId, authority }) => {
|
export const Editor: React.FC<IProps> = ({ user: currentUser, documentId, authority }) => {
|
||||||
const $hasShowUserSettingModal = useRef(false);
|
|
||||||
const $editor = useRef<ICollaborationRefProps>();
|
const $editor = useRef<ICollaborationRefProps>();
|
||||||
const mounted = useMount();
|
const mounted = useMount();
|
||||||
const { users, addUser, updateUser } = useDoumentMembers(documentId);
|
|
||||||
const [mentionUsersSettingVisible, toggleMentionUsersSettingVisible] = useToggle(false);
|
|
||||||
const [mentionUsers, setMentionUsers] = useState([]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handler = (data) => {
|
const handler = (data) => {
|
||||||
|
@ -39,56 +30,6 @@ export const Editor: React.FC<IProps> = ({ user: currentUser, documentId, author
|
||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const handler = () => {
|
|
||||||
const editor = $editor.current && $editor.current.getEditor();
|
|
||||||
if (!editor) return;
|
|
||||||
|
|
||||||
// 已经拦截过一次,不再拦截
|
|
||||||
if ($hasShowUserSettingModal.current) return;
|
|
||||||
|
|
||||||
const mentionUsers = findMentions(editor);
|
|
||||||
if (!mentionUsers || !mentionUsers.length) return;
|
|
||||||
|
|
||||||
const currentUserAuth = users.find((user) => {
|
|
||||||
return user.user.name === currentUser.name;
|
|
||||||
});
|
|
||||||
const isCurrentUserCreateUser = currentUserAuth.auth.createUserId === currentUser.id;
|
|
||||||
|
|
||||||
if (!isCurrentUserCreateUser) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const data = Array.from(new Set(mentionUsers))
|
|
||||||
.filter((userName) => {
|
|
||||||
const exist = users.find((user) => {
|
|
||||||
return user.user.name === userName;
|
|
||||||
});
|
|
||||||
if (!exist || !exist.auth.readable) return true;
|
|
||||||
return false;
|
|
||||||
})
|
|
||||||
.filter(Boolean);
|
|
||||||
|
|
||||||
if (!data.length) return;
|
|
||||||
|
|
||||||
setMentionUsers(data);
|
|
||||||
toggleMentionUsersSettingVisible(true);
|
|
||||||
$hasShowUserSettingModal.current = true;
|
|
||||||
// ignore-me
|
|
||||||
const newErr = new Error('请完成权限操作后关闭页面');
|
|
||||||
throw newErr;
|
|
||||||
};
|
|
||||||
|
|
||||||
Router.events.on('routeChangeStart', handler);
|
|
||||||
window.addEventListener('unload', handler);
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
$hasShowUserSettingModal.current = false;
|
|
||||||
Router.events.off('routeChangeStart', handler);
|
|
||||||
window.removeEventListener('unload', handler);
|
|
||||||
};
|
|
||||||
}, [users, currentUser, toggleMentionUsersSettingVisible]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={cls(styles.editorWrap)}>
|
<div className={cls(styles.editorWrap)}>
|
||||||
{mounted && (
|
{mounted && (
|
||||||
|
@ -103,14 +44,6 @@ export const Editor: React.FC<IProps> = ({ user: currentUser, documentId, author
|
||||||
onAwarenessUpdate={triggerJoinUser}
|
onAwarenessUpdate={triggerJoinUser}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<DocumentUserSetting
|
|
||||||
visible={mentionUsersSettingVisible}
|
|
||||||
toggleVisible={toggleMentionUsersSettingVisible}
|
|
||||||
mentionUsers={mentionUsers}
|
|
||||||
users={users}
|
|
||||||
addUser={addUser}
|
|
||||||
updateUser={updateUser}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,94 +0,0 @@
|
||||||
import { Checkbox, Modal, Table, Typography } from '@douyinfe/semi-ui';
|
|
||||||
import { IAuthority, IUser } from '@think/domains';
|
|
||||||
import { DocAuth } from 'data/document';
|
|
||||||
import React, { useMemo } from 'react';
|
|
||||||
|
|
||||||
interface IProps {
|
|
||||||
visible: boolean;
|
|
||||||
toggleVisible: (arg: boolean) => void;
|
|
||||||
mentionUsers: string[];
|
|
||||||
users: Array<{ user: IUser; auth: IAuthority }>;
|
|
||||||
addUser: (auth: DocAuth) => Promise<unknown>;
|
|
||||||
updateUser: (auth: DocAuth) => Promise<unknown>;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { Text } = Typography;
|
|
||||||
const { Column } = Table;
|
|
||||||
|
|
||||||
// eslint-disable-next-line react/display-name
|
|
||||||
const renderChecked = (onChange, authKey: 'readable' | 'editable') => (checked, data) => {
|
|
||||||
const handle = (evt) => {
|
|
||||||
const ret = {
|
|
||||||
...data,
|
|
||||||
};
|
|
||||||
ret[authKey] = evt.target.checked;
|
|
||||||
onChange(ret);
|
|
||||||
};
|
|
||||||
return <Checkbox style={{ display: 'inline-block' }} checked={checked} onChange={handle} />;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const DocumentUserSetting: React.FC<IProps> = ({
|
|
||||||
visible,
|
|
||||||
toggleVisible,
|
|
||||||
mentionUsers,
|
|
||||||
users,
|
|
||||||
addUser,
|
|
||||||
updateUser,
|
|
||||||
}) => {
|
|
||||||
const renderUsers = useMemo(() => {
|
|
||||||
return mentionUsers
|
|
||||||
.map((mentionUser) => {
|
|
||||||
const exist = users.find((user) => {
|
|
||||||
return user.user.name === mentionUser;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!exist) return { userName: mentionUser, readable: false, editable: false, shouldAddToDocument: true };
|
|
||||||
|
|
||||||
return {
|
|
||||||
userName: mentionUser,
|
|
||||||
readable: exist.auth.readable,
|
|
||||||
editable: exist.auth.editable,
|
|
||||||
shouldAddToDocument: false,
|
|
||||||
};
|
|
||||||
})
|
|
||||||
.filter(Boolean);
|
|
||||||
}, [users, mentionUsers]);
|
|
||||||
|
|
||||||
const handler = async (data) => {
|
|
||||||
if (data.shouldAddToDocument) {
|
|
||||||
await addUser(data.userName);
|
|
||||||
}
|
|
||||||
|
|
||||||
await updateUser(data);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Modal
|
|
||||||
title={'权限操作'}
|
|
||||||
visible={visible}
|
|
||||||
onCancel={() => toggleVisible(false)}
|
|
||||||
maskClosable={false}
|
|
||||||
style={{ maxWidth: '96vw' }}
|
|
||||||
footer={null}
|
|
||||||
>
|
|
||||||
<Text>您在该文档中 @ 了以下用户,请为他们操作权限,否则他们无法阅读该文档。</Text>
|
|
||||||
<Table style={{ margin: '24px 0' }} dataSource={renderUsers} size="small" pagination>
|
|
||||||
<Column title="用户名" dataIndex="userName" key="name" />
|
|
||||||
<Column
|
|
||||||
title="是否可读"
|
|
||||||
dataIndex="readable"
|
|
||||||
key="readable"
|
|
||||||
render={renderChecked(handler, 'readable')}
|
|
||||||
align="center"
|
|
||||||
/>
|
|
||||||
<Column
|
|
||||||
title="是否可编辑"
|
|
||||||
dataIndex="editable"
|
|
||||||
key="editable"
|
|
||||||
render={renderChecked(handler, 'editable')}
|
|
||||||
align="center"
|
|
||||||
/>
|
|
||||||
</Table>
|
|
||||||
</Modal>
|
|
||||||
);
|
|
||||||
};
|
|
|
@ -1,14 +1,12 @@
|
||||||
import { Banner, Button, Input, Modal, Select, Space } from '@douyinfe/semi-ui';
|
import { Banner, Input, Popconfirm, Select, Space } from '@douyinfe/semi-ui';
|
||||||
import { AuthEnum, AuthEnumArray } from '@think/domains';
|
import { AuthEnum, AuthEnumArray } from '@think/domains';
|
||||||
import React, { useCallback, useState } from 'react';
|
import React, { useCallback, useState } from 'react';
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
visible: boolean;
|
|
||||||
toggleVisible: (arg) => void;
|
|
||||||
onOk: (arg) => any;
|
onOk: (arg) => any;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const AddUser: React.FC<IProps> = ({ visible, toggleVisible, onOk }) => {
|
export const AddUser: React.FC<IProps> = ({ onOk, children }) => {
|
||||||
const [userAuth, setUserAuth] = useState(AuthEnum.noAccess);
|
const [userAuth, setUserAuth] = useState(AuthEnum.noAccess);
|
||||||
const [userName, setUserName] = useState('');
|
const [userName, setUserName] = useState('');
|
||||||
|
|
||||||
|
@ -16,27 +14,26 @@ export const AddUser: React.FC<IProps> = ({ visible, toggleVisible, onOk }) => {
|
||||||
onOk({ userName, userAuth }).then(() => {
|
onOk({ userName, userAuth }).then(() => {
|
||||||
setUserAuth(AuthEnum.noAccess);
|
setUserAuth(AuthEnum.noAccess);
|
||||||
setUserName('');
|
setUserName('');
|
||||||
toggleVisible(false);
|
|
||||||
});
|
});
|
||||||
}, [onOk, userName, userAuth, toggleVisible]);
|
}, [onOk, userName, userAuth]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Popconfirm
|
||||||
|
zIndex={1070}
|
||||||
title={'添加成员'}
|
title={'添加成员'}
|
||||||
okText={'邀请对方'}
|
okText={'邀请对方'}
|
||||||
visible={visible}
|
style={{ maxWidth: '96vw', width: 380 }}
|
||||||
onOk={handleOk}
|
onConfirm={handleOk}
|
||||||
onCancel={() => toggleVisible(false)}
|
okButtonProps={{
|
||||||
maskClosable={false}
|
disabled: !userName,
|
||||||
style={{ maxWidth: '96vw' }}
|
}}
|
||||||
footer={null}
|
content={
|
||||||
>
|
<div style={{ margin: '16px -68px 0 0' }}>
|
||||||
<div style={{ marginTop: 16 }}>
|
|
||||||
{[AuthEnum.creator, AuthEnum.admin].includes(userAuth) ? (
|
{[AuthEnum.creator, AuthEnum.admin].includes(userAuth) ? (
|
||||||
<Banner style={{ marginBottom: 16 }} type="warning" description="请谨慎操作管理员权限!" />
|
<Banner style={{ marginBottom: 16 }} type="warning" description="请谨慎操作管理员权限!" />
|
||||||
) : null}
|
) : null}
|
||||||
<Space>
|
<Space>
|
||||||
<Select value={userAuth} onChange={setUserAuth} style={{ width: 120 }}>
|
<Select value={userAuth} onChange={setUserAuth} style={{ width: 120 }} zIndex={1080}>
|
||||||
{AuthEnumArray.map((wikiStatus) => {
|
{AuthEnumArray.map((wikiStatus) => {
|
||||||
return (
|
return (
|
||||||
<Select.Option key={wikiStatus.value} value={wikiStatus.value}>
|
<Select.Option key={wikiStatus.value} value={wikiStatus.value}>
|
||||||
|
@ -50,13 +47,13 @@ export const AddUser: React.FC<IProps> = ({ visible, toggleVisible, onOk }) => {
|
||||||
placeholder="输入对方用户名"
|
placeholder="输入对方用户名"
|
||||||
value={userName}
|
value={userName}
|
||||||
onChange={setUserName}
|
onChange={setUserName}
|
||||||
style={{ width: 270 }}
|
style={{ width: 160 }}
|
||||||
></Input>
|
></Input>
|
||||||
</Space>
|
</Space>
|
||||||
<Button theme="solid" block style={{ margin: '24px 0' }} onClick={handleOk} disabled={!userName}>
|
|
||||||
添加成员
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
</Modal>
|
}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</Popconfirm>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,41 +1,26 @@
|
||||||
import { Banner, Button, Modal, Select } from '@douyinfe/semi-ui';
|
import { Banner, Popconfirm, Select, Toast } from '@douyinfe/semi-ui';
|
||||||
import { AuthEnum, AuthEnumArray, IAuth, IUser } from '@think/domains';
|
import { AuthEnum, AuthEnumArray, IAuth, IUser } from '@think/domains';
|
||||||
import React, { useCallback, useEffect, useState } from 'react';
|
import React, { useCallback, useState } from 'react';
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
visible: boolean;
|
userWithAuth: { user: IUser; auth: IAuth };
|
||||||
toggleVisible: (arg) => void;
|
updateUser: (arg) => any;
|
||||||
currentUser: { user: IUser; auth: IAuth };
|
|
||||||
onOk: (arg) => any;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const EditUser: React.FC<IProps> = ({ visible, toggleVisible, currentUser, onOk }) => {
|
export const EditUser: React.FC<IProps> = ({ userWithAuth, updateUser, children }) => {
|
||||||
const [userAuth, setUserAuth] = useState(AuthEnum.noAccess);
|
const [userAuth, setUserAuth] = useState(AuthEnum.noAccess);
|
||||||
|
|
||||||
const handleOk = useCallback(() => {
|
const handleOk = useCallback(() => {
|
||||||
onOk(userAuth).then(() => {
|
return updateUser({ userName: userWithAuth.user.name, userAuth }).then(() => {
|
||||||
setUserAuth(AuthEnum.noAccess);
|
Toast.success('操作成功');
|
||||||
toggleVisible(false);
|
|
||||||
});
|
});
|
||||||
}, [onOk, userAuth, toggleVisible]);
|
}, [updateUser, userAuth, userWithAuth]);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!visible) {
|
|
||||||
setUserAuth(AuthEnum.noAccess);
|
|
||||||
}
|
|
||||||
}, [visible]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Popconfirm
|
||||||
title={`修改用户${currentUser && currentUser.user.name}权限`}
|
title={`修改用户${userWithAuth && userWithAuth.user.name}权限`}
|
||||||
visible={visible}
|
content={
|
||||||
onOk={handleOk}
|
<div style={{ margin: '16px -68px 0 0' }}>
|
||||||
onCancel={() => toggleVisible(false)}
|
|
||||||
maskClosable={false}
|
|
||||||
style={{ maxWidth: '96vw' }}
|
|
||||||
footer={null}
|
|
||||||
>
|
|
||||||
<div style={{ marginTop: 16 }}>
|
|
||||||
{[AuthEnum.creator, AuthEnum.admin].includes(userAuth) ? (
|
{[AuthEnum.creator, AuthEnum.admin].includes(userAuth) ? (
|
||||||
<Banner style={{ marginBottom: 16 }} type="warning" description="请谨慎操作管理员权限!" />
|
<Banner style={{ marginBottom: 16 }} type="warning" description="请谨慎操作管理员权限!" />
|
||||||
) : null}
|
) : null}
|
||||||
|
@ -49,10 +34,11 @@ export const EditUser: React.FC<IProps> = ({ visible, toggleVisible, currentUser
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</Select>
|
</Select>
|
||||||
<Button theme="solid" block style={{ margin: '24px 0' }} onClick={handleOk}>
|
|
||||||
提交修改
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
</Modal>
|
}
|
||||||
|
onConfirm={handleOk}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</Popconfirm>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,11 +1,9 @@
|
||||||
import { IconDelete, IconEdit } from '@douyinfe/semi-icons';
|
import { IconDelete, IconEdit } from '@douyinfe/semi-icons';
|
||||||
import { Banner, Button, Popconfirm, Table, Typography } from '@douyinfe/semi-ui';
|
import { Banner, Button, Popconfirm, Table, Typography } from '@douyinfe/semi-ui';
|
||||||
import { AuthEnumTextMap, IOrganization } from '@think/domains';
|
import { AuthEnumTextMap } from '@think/domains';
|
||||||
import { DataRender } from 'components/data-render';
|
import { DataRender } from 'components/data-render';
|
||||||
import { LocaleTime } from 'components/locale-time';
|
import { LocaleTime } from 'components/locale-time';
|
||||||
import { useOrganizationMembers } from 'data/organization';
|
import React from 'react';
|
||||||
import { useToggle } from 'hooks/use-toggle';
|
|
||||||
import React, { useState } from 'react';
|
|
||||||
|
|
||||||
import { AddUser } from './add';
|
import { AddUser } from './add';
|
||||||
import { EditUser } from './edit';
|
import { EditUser } from './edit';
|
||||||
|
@ -14,33 +12,17 @@ import styles from './index.module.scss';
|
||||||
interface IProps {
|
interface IProps {
|
||||||
id: string;
|
id: string;
|
||||||
hook: any;
|
hook: any;
|
||||||
|
descriptions?: Array<string>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { Title, Paragraph } = Typography;
|
const { Title, Paragraph } = Typography;
|
||||||
const { Column } = Table;
|
const { Column } = Table;
|
||||||
|
|
||||||
export const Members: React.FC<IProps> = ({ id, hook }) => {
|
export const Members: React.FC<IProps> = ({ id, hook, descriptions }) => {
|
||||||
const { data, loading, error, addUser, updateUser, deleteUser } = hook(id);
|
const { data, loading, error, page, pageSize, setPage, addUser, updateUser, deleteUser } = hook(id);
|
||||||
const [visible, toggleVisible] = useToggle(false);
|
|
||||||
const [editVisible, toggleEditVisible] = useToggle(false);
|
|
||||||
const [currentUser, setCurrentUser] = useState(null);
|
|
||||||
|
|
||||||
const editUser = (user) => {
|
|
||||||
setCurrentUser(user);
|
|
||||||
toggleEditVisible(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleEdit = (userAuth) => {
|
|
||||||
return updateUser({ userName: currentUser.user.name, userAuth }).then(() => {
|
|
||||||
setCurrentUser(null);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
console.log(data);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.wrap}>
|
<div className={styles.wrap}>
|
||||||
<header>{/* <MemberAdder /> */}</header>
|
|
||||||
<DataRender
|
<DataRender
|
||||||
loading={loading}
|
loading={loading}
|
||||||
error={error}
|
error={error}
|
||||||
|
@ -55,19 +37,36 @@ export const Members: React.FC<IProps> = ({ id, hook }) => {
|
||||||
title={<Title heading={6}>权限说明</Title>}
|
title={<Title heading={6}>权限说明</Title>}
|
||||||
description={
|
description={
|
||||||
<div>
|
<div>
|
||||||
|
{descriptions && descriptions.length ? (
|
||||||
|
descriptions.map((desc) => {
|
||||||
|
return <Paragraph key={desc}>{desc}</Paragraph>;
|
||||||
|
})
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
<Paragraph>创建者:管理组织内所有知识库、文档,可删除组织</Paragraph>
|
<Paragraph>创建者:管理组织内所有知识库、文档,可删除组织</Paragraph>
|
||||||
<Paragraph>管理员:管理组织内所有知识库、文档,不可删除组织</Paragraph>
|
<Paragraph>管理员:管理组织内所有知识库、文档,不可删除组织</Paragraph>
|
||||||
<Paragraph>成员:可访问组织内所有知识库、文档,不可删除组织</Paragraph>
|
<Paragraph>成员:可访问组织内所有知识库、文档,不可删除组织</Paragraph>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div style={{ display: 'flex', justifyContent: 'flex-end' }}>
|
<div style={{ display: 'flex', justifyContent: 'flex-end' }}>
|
||||||
<Button theme="solid" onClick={toggleVisible}>
|
<AddUser onOk={addUser}>
|
||||||
添加用户
|
<Button theme="solid">添加用户</Button>
|
||||||
</Button>
|
</AddUser>
|
||||||
</div>
|
</div>
|
||||||
<Table style={{ margin: '16px 0' }} dataSource={data.data} size="small" pagination={false}>
|
<Table
|
||||||
|
style={{ margin: '16px 0' }}
|
||||||
|
dataSource={data.data}
|
||||||
|
size="small"
|
||||||
|
pagination={{
|
||||||
|
currentPage: page,
|
||||||
|
pageSize,
|
||||||
|
total: data.total,
|
||||||
|
onPageChange: setPage,
|
||||||
|
}}
|
||||||
|
>
|
||||||
<Column title="用户名" dataIndex="user.name" key="user.name" />
|
<Column title="用户名" dataIndex="user.name" key="user.name" />
|
||||||
<Column
|
<Column
|
||||||
title="成员权限"
|
title="成员权限"
|
||||||
|
@ -90,7 +89,9 @@ export const Members: React.FC<IProps> = ({ id, hook }) => {
|
||||||
align="center"
|
align="center"
|
||||||
render={(_, data) => (
|
render={(_, data) => (
|
||||||
<>
|
<>
|
||||||
<Button type="tertiary" theme="borderless" icon={<IconEdit />} onClick={() => editUser(data)} />
|
<EditUser userWithAuth={data} updateUser={updateUser}>
|
||||||
|
<Button type="tertiary" theme="borderless" icon={<IconEdit />} />
|
||||||
|
</EditUser>
|
||||||
<Popconfirm
|
<Popconfirm
|
||||||
showArrow
|
showArrow
|
||||||
title="确认删除该成员?"
|
title="确认删除该成员?"
|
||||||
|
@ -105,9 +106,6 @@ export const Members: React.FC<IProps> = ({ id, hook }) => {
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<AddUser visible={visible} toggleVisible={toggleVisible} onOk={addUser} />
|
|
||||||
<EditUser visible={editVisible} toggleVisible={toggleEditVisible} currentUser={currentUser} onOk={handleEdit} />
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
import { IconDelete } from '@douyinfe/semi-icons';
|
||||||
|
import { Modal, Space, Typography } from '@douyinfe/semi-ui';
|
||||||
|
import { IOrganization } from '@think/domains';
|
||||||
|
import { useOrganizationDetail } from 'data/organization';
|
||||||
|
import { useRouterQuery } from 'hooks/use-router-query';
|
||||||
|
import Router from 'next/router';
|
||||||
|
import React, { useCallback } from 'react';
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
organizationId: IOrganization['id'];
|
||||||
|
onDelete?: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { Text } = Typography;
|
||||||
|
|
||||||
|
export const OrganizationDeletor: React.FC<IProps> = ({ organizationId, onDelete, children }) => {
|
||||||
|
const { deleteOrganization } = useOrganizationDetail(organizationId);
|
||||||
|
|
||||||
|
const deleteAction = useCallback(() => {
|
||||||
|
Modal.error({
|
||||||
|
title: '确定删除吗?',
|
||||||
|
content: <Text>删除后不可恢复!</Text>,
|
||||||
|
onOk: () => {
|
||||||
|
deleteOrganization().then(() => {
|
||||||
|
onDelete
|
||||||
|
? onDelete()
|
||||||
|
: Router.push({
|
||||||
|
pathname: `/`,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
okButtonProps: {
|
||||||
|
type: 'danger',
|
||||||
|
},
|
||||||
|
style: { maxWidth: '96vw' },
|
||||||
|
});
|
||||||
|
}, [deleteOrganization, onDelete]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Text type="danger" onClick={deleteAction}>
|
||||||
|
{children || (
|
||||||
|
<Space>
|
||||||
|
<IconDelete />
|
||||||
|
删除
|
||||||
|
</Space>
|
||||||
|
)}
|
||||||
|
</Text>
|
||||||
|
);
|
||||||
|
};
|
|
@ -1,33 +1,23 @@
|
||||||
import { IconSmallTriangleDown } from '@douyinfe/semi-icons';
|
import { IconAppCenter, IconApps, IconSmallTriangleDown } from '@douyinfe/semi-icons';
|
||||||
import { Avatar, Button, Dropdown, Space, Typography } from '@douyinfe/semi-ui';
|
import { Avatar, Button, Dropdown, Space, Typography } from '@douyinfe/semi-ui';
|
||||||
import { DataRender } from 'components/data-render';
|
import { DataRender } from 'components/data-render';
|
||||||
import { LogoImage, LogoText } from 'components/logo';
|
import { LogoImage, LogoText } from 'components/logo';
|
||||||
import { useUserOrganizations } from 'data/organization';
|
import { useUserOrganizations } from 'data/organization';
|
||||||
|
import { useUser } from 'data/user';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import Router from 'next/router';
|
|
||||||
import { useCallback } from 'react';
|
|
||||||
|
|
||||||
import styles from './index.module.scss';
|
import styles from './index.module.scss';
|
||||||
|
|
||||||
const { Text, Paragraph } = Typography;
|
const { Text, Paragraph } = Typography;
|
||||||
|
|
||||||
export const OrganizationPublicSwitcher = () => {
|
const Inner = () => {
|
||||||
const {
|
const {
|
||||||
data: userOrganizations,
|
data: userOrganizations,
|
||||||
loading: userOrganizationsLoading,
|
loading: userOrganizationsLoading,
|
||||||
error: userOrganizationsError,
|
error: userOrganizationsError,
|
||||||
} = useUserOrganizations();
|
} = useUserOrganizations();
|
||||||
|
|
||||||
const gotoCreate = useCallback(() => {
|
|
||||||
Router.push(`/app/org/create`);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<span className={styles.nameWrap}>
|
|
||||||
<Space>
|
|
||||||
<LogoImage />
|
|
||||||
<LogoText />
|
|
||||||
</Space>
|
|
||||||
<Dropdown
|
<Dropdown
|
||||||
trigger="click"
|
trigger="click"
|
||||||
render={
|
render={
|
||||||
|
@ -37,7 +27,9 @@ export const OrganizationPublicSwitcher = () => {
|
||||||
normalContent={() => {
|
normalContent={() => {
|
||||||
return (
|
return (
|
||||||
<Dropdown.Menu>
|
<Dropdown.Menu>
|
||||||
{(userOrganizations || []).map((org) => {
|
{userOrganizations.length ? (
|
||||||
|
<>
|
||||||
|
{userOrganizations.map((org) => {
|
||||||
return (
|
return (
|
||||||
<Dropdown.Item key={org.id}>
|
<Dropdown.Item key={org.id}>
|
||||||
<Link
|
<Link
|
||||||
|
@ -66,10 +58,44 @@ export const OrganizationPublicSwitcher = () => {
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
<Dropdown.Divider />
|
<Dropdown.Divider />
|
||||||
<Dropdown.Item onClick={gotoCreate}>
|
</>
|
||||||
|
) : null}
|
||||||
|
<Dropdown.Item>
|
||||||
|
<Link
|
||||||
|
href={{
|
||||||
|
pathname: '/',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<a style={{ display: 'flex', alignItems: 'center', width: '100%' }}>
|
||||||
<Text>
|
<Text>
|
||||||
<Space>新建组织</Space>
|
<Space>
|
||||||
|
<Avatar size="extra-small">
|
||||||
|
<IconApps />
|
||||||
|
</Avatar>
|
||||||
|
前往广场
|
||||||
|
</Space>
|
||||||
</Text>
|
</Text>
|
||||||
|
</a>
|
||||||
|
</Link>
|
||||||
|
</Dropdown.Item>
|
||||||
|
|
||||||
|
<Dropdown.Item>
|
||||||
|
<Link
|
||||||
|
href={{
|
||||||
|
pathname: '/app/org/create',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<a style={{ display: 'flex', alignItems: 'center', width: '100%' }}>
|
||||||
|
<Text>
|
||||||
|
<Space>
|
||||||
|
<Avatar size="extra-small">
|
||||||
|
<IconAppCenter />
|
||||||
|
</Avatar>
|
||||||
|
新建组织
|
||||||
|
</Space>
|
||||||
|
</Text>
|
||||||
|
</a>
|
||||||
|
</Link>
|
||||||
</Dropdown.Item>
|
</Dropdown.Item>
|
||||||
</Dropdown.Menu>
|
</Dropdown.Menu>
|
||||||
);
|
);
|
||||||
|
@ -79,6 +105,19 @@ export const OrganizationPublicSwitcher = () => {
|
||||||
>
|
>
|
||||||
<Button size="small" icon={<IconSmallTriangleDown />} style={{ marginLeft: 12 }} />
|
<Button size="small" icon={<IconSmallTriangleDown />} style={{ marginLeft: 12 }} />
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const OrganizationPublicSwitcher = () => {
|
||||||
|
const { user } = useUser();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<span className={styles.nameWrap}>
|
||||||
|
<Space>
|
||||||
|
<LogoImage />
|
||||||
|
<LogoText />
|
||||||
|
</Space>
|
||||||
|
{user && <Inner />}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -36,29 +36,9 @@ export const OrganizationSetting: React.FC<IProps> = ({ organizationId, tab, onN
|
||||||
<OrganizationMembers organizationId={organizationId} />
|
<OrganizationMembers organizationId={organizationId} />
|
||||||
</TabPane>
|
</TabPane>
|
||||||
|
|
||||||
{/* <TabPane tab={TitleMap['base']} itemKey="base">
|
|
||||||
<Base wiki={data} update={update as any} />
|
|
||||||
</TabPane>
|
|
||||||
|
|
||||||
<TabPane tab={TitleMap['users']} itemKey="users">
|
|
||||||
<Users wikiId={wikiId} />
|
|
||||||
</TabPane>
|
|
||||||
|
|
||||||
<TabPane tab={TitleMap['tocs']} itemKey="tocs">
|
|
||||||
<WikiTocsManager wikiId={wikiId} />
|
|
||||||
</TabPane>
|
|
||||||
|
|
||||||
<TabPane tab={TitleMap['privacy']} itemKey="privacy">
|
|
||||||
<Privacy wikiId={wikiId} />
|
|
||||||
</TabPane>
|
|
||||||
|
|
||||||
<TabPane tab={TitleMap['import']} itemKey="import">
|
|
||||||
<Import wikiId={wikiId} />
|
|
||||||
</TabPane>
|
|
||||||
|
|
||||||
<TabPane tab={TitleMap['more']} itemKey="more">
|
<TabPane tab={TitleMap['more']} itemKey="more">
|
||||||
<More wikiId={wikiId} />
|
<More organizationId={organizationId} />
|
||||||
</TabPane> */}
|
</TabPane>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,62 +0,0 @@
|
||||||
import { Banner, Button, Input, Modal, Select, Space } from '@douyinfe/semi-ui';
|
|
||||||
import { AuthEnum, AuthEnumArray } from '@think/domains';
|
|
||||||
import React, { useCallback, useState } from 'react';
|
|
||||||
|
|
||||||
interface IProps {
|
|
||||||
visible: boolean;
|
|
||||||
toggleVisible: (arg) => void;
|
|
||||||
onOk: (arg) => any;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const AddUser: React.FC<IProps> = ({ visible, toggleVisible, onOk }) => {
|
|
||||||
const [userAuth, setUserAuth] = useState(AuthEnum.noAccess);
|
|
||||||
const [userName, setUserName] = useState('');
|
|
||||||
|
|
||||||
const handleOk = useCallback(() => {
|
|
||||||
onOk({ userName, userAuth }).then(() => {
|
|
||||||
setUserAuth(AuthEnum.noAccess);
|
|
||||||
setUserName('');
|
|
||||||
toggleVisible(false);
|
|
||||||
});
|
|
||||||
}, [onOk, userName, userAuth, toggleVisible]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Modal
|
|
||||||
title={'添加成员'}
|
|
||||||
okText={'邀请对方'}
|
|
||||||
visible={visible}
|
|
||||||
onOk={handleOk}
|
|
||||||
onCancel={() => toggleVisible(false)}
|
|
||||||
maskClosable={false}
|
|
||||||
style={{ maxWidth: '96vw' }}
|
|
||||||
footer={null}
|
|
||||||
>
|
|
||||||
<div style={{ marginTop: 16 }}>
|
|
||||||
{[AuthEnum.creator, AuthEnum.admin].includes(userAuth) ? (
|
|
||||||
<Banner style={{ marginBottom: 16 }} type="warning" description="请谨慎操作管理员权限!" />
|
|
||||||
) : null}
|
|
||||||
<Space>
|
|
||||||
<Select value={userAuth} onChange={setUserAuth} style={{ width: 120 }}>
|
|
||||||
{AuthEnumArray.map((wikiStatus) => {
|
|
||||||
return (
|
|
||||||
<Select.Option key={wikiStatus.value} value={wikiStatus.value}>
|
|
||||||
{wikiStatus.label}
|
|
||||||
</Select.Option>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</Select>
|
|
||||||
<Input
|
|
||||||
autofocus
|
|
||||||
placeholder="输入对方用户名"
|
|
||||||
value={userName}
|
|
||||||
onChange={setUserName}
|
|
||||||
style={{ width: 270 }}
|
|
||||||
></Input>
|
|
||||||
</Space>
|
|
||||||
<Button theme="solid" block style={{ margin: '24px 0' }} onClick={handleOk} disabled={!userName}>
|
|
||||||
添加成员
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</Modal>
|
|
||||||
);
|
|
||||||
};
|
|
|
@ -1,58 +0,0 @@
|
||||||
import { Banner, Button, Modal, Select } from '@douyinfe/semi-ui';
|
|
||||||
import { AuthEnum, AuthEnumArray, IAuth, IUser } from '@think/domains';
|
|
||||||
import React, { useCallback, useEffect, useState } from 'react';
|
|
||||||
|
|
||||||
interface IProps {
|
|
||||||
visible: boolean;
|
|
||||||
toggleVisible: (arg) => void;
|
|
||||||
currentUser: { user: IUser; auth: IAuth };
|
|
||||||
onOk: (arg) => any;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const EditUser: React.FC<IProps> = ({ visible, toggleVisible, currentUser, onOk }) => {
|
|
||||||
const [userAuth, setUserAuth] = useState(AuthEnum.noAccess);
|
|
||||||
|
|
||||||
const handleOk = useCallback(() => {
|
|
||||||
onOk(userAuth).then(() => {
|
|
||||||
setUserAuth(AuthEnum.noAccess);
|
|
||||||
toggleVisible(false);
|
|
||||||
});
|
|
||||||
}, [onOk, userAuth, toggleVisible]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!visible) {
|
|
||||||
setUserAuth(AuthEnum.noAccess);
|
|
||||||
}
|
|
||||||
}, [visible]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Modal
|
|
||||||
title={`修改用户${currentUser && currentUser.user.name}权限`}
|
|
||||||
visible={visible}
|
|
||||||
onOk={handleOk}
|
|
||||||
onCancel={() => toggleVisible(false)}
|
|
||||||
maskClosable={false}
|
|
||||||
style={{ maxWidth: '96vw' }}
|
|
||||||
footer={null}
|
|
||||||
>
|
|
||||||
<div style={{ marginTop: 16 }}>
|
|
||||||
{[AuthEnum.creator, AuthEnum.admin].includes(userAuth) ? (
|
|
||||||
<Banner style={{ marginBottom: 16 }} type="warning" description="请谨慎操作管理员权限!" />
|
|
||||||
) : null}
|
|
||||||
{}
|
|
||||||
<Select value={userAuth} onChange={setUserAuth} style={{ width: '100%' }}>
|
|
||||||
{AuthEnumArray.map((wikiStatus) => {
|
|
||||||
return (
|
|
||||||
<Select.Option key={wikiStatus.value} value={wikiStatus.value}>
|
|
||||||
{wikiStatus.label}
|
|
||||||
</Select.Option>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</Select>
|
|
||||||
<Button theme="solid" block style={{ margin: '24px 0' }} onClick={handleOk}>
|
|
||||||
提交修改
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</Modal>
|
|
||||||
);
|
|
||||||
};
|
|
|
@ -1,6 +0,0 @@
|
||||||
.wrap {
|
|
||||||
> header {
|
|
||||||
display: flex;
|
|
||||||
justify-content: flex-end;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,110 +1,12 @@
|
||||||
import { IconDelete, IconEdit } from '@douyinfe/semi-icons';
|
import { IOrganization } from '@think/domains';
|
||||||
import { Banner, Button, Popconfirm, Table, Typography } from '@douyinfe/semi-ui';
|
import { Members } from 'components/members';
|
||||||
import { AuthEnumTextMap, IOrganization } from '@think/domains';
|
|
||||||
import { DataRender } from 'components/data-render';
|
|
||||||
import { LocaleTime } from 'components/locale-time';
|
|
||||||
import { useOrganizationMembers } from 'data/organization';
|
import { useOrganizationMembers } from 'data/organization';
|
||||||
import { useToggle } from 'hooks/use-toggle';
|
import React from 'react';
|
||||||
import React, { useState } from 'react';
|
|
||||||
|
|
||||||
import { AddUser } from './add';
|
|
||||||
import { EditUser } from './edit';
|
|
||||||
import styles from './index.module.scss';
|
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
organizationId: IOrganization['id'];
|
organizationId: IOrganization['id'];
|
||||||
}
|
}
|
||||||
|
|
||||||
const { Title, Paragraph } = Typography;
|
|
||||||
const { Column } = Table;
|
|
||||||
|
|
||||||
export const OrganizationMembers: React.FC<IProps> = ({ organizationId }) => {
|
export const OrganizationMembers: React.FC<IProps> = ({ organizationId }) => {
|
||||||
const { data, loading, error, refresh, addUser, updateUser, deleteUser } = useOrganizationMembers(organizationId);
|
return <Members id={organizationId} hook={useOrganizationMembers} />;
|
||||||
const [visible, toggleVisible] = useToggle(false);
|
|
||||||
const [editVisible, toggleEditVisible] = useToggle(false);
|
|
||||||
const [currentUser, setCurrentUser] = useState(null);
|
|
||||||
|
|
||||||
const editUser = (user) => {
|
|
||||||
setCurrentUser(user);
|
|
||||||
toggleEditVisible(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleEdit = (userAuth) => {
|
|
||||||
return updateUser({ userName: currentUser.user.name, userAuth }).then(() => {
|
|
||||||
setCurrentUser(null);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={styles.wrap}>
|
|
||||||
<header>{/* <MemberAdder /> */}</header>
|
|
||||||
<DataRender
|
|
||||||
loading={loading}
|
|
||||||
error={error}
|
|
||||||
normalContent={() => (
|
|
||||||
<div>
|
|
||||||
<Banner
|
|
||||||
fullMode={false}
|
|
||||||
type="info"
|
|
||||||
bordered
|
|
||||||
icon={null}
|
|
||||||
style={{ margin: '16px 0' }}
|
|
||||||
title={<Title heading={6}>权限说明</Title>}
|
|
||||||
description={
|
|
||||||
<div>
|
|
||||||
<Paragraph>创建者:管理组织内所有知识库、文档,可删除组织</Paragraph>
|
|
||||||
<Paragraph>管理员:管理组织内所有知识库、文档,不可删除组织</Paragraph>
|
|
||||||
<Paragraph>成员:可访问组织内所有知识库、文档,不可删除组织</Paragraph>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div style={{ display: 'flex', justifyContent: 'flex-end' }}>
|
|
||||||
<Button theme="solid" onClick={toggleVisible}>
|
|
||||||
添加用户
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
<Table style={{ margin: '16px 0' }} dataSource={data.data} size="small" pagination={false}>
|
|
||||||
<Column title="用户名" dataIndex="user.name" key="user.name" />
|
|
||||||
<Column
|
|
||||||
title="成员权限"
|
|
||||||
dataIndex="auth.auth"
|
|
||||||
key="auth.auth"
|
|
||||||
align="center"
|
|
||||||
render={(auth) => AuthEnumTextMap[auth]}
|
|
||||||
/>
|
|
||||||
<Column
|
|
||||||
title="加入时间"
|
|
||||||
dataIndex="auth.createdAt"
|
|
||||||
key="auth.createdAt"
|
|
||||||
align="center"
|
|
||||||
render={(d) => <LocaleTime date={d} />}
|
|
||||||
/>
|
|
||||||
<Column
|
|
||||||
title="操作"
|
|
||||||
dataIndex="operate"
|
|
||||||
key="operate"
|
|
||||||
align="center"
|
|
||||||
render={(_, data) => (
|
|
||||||
<>
|
|
||||||
<Button type="tertiary" theme="borderless" icon={<IconEdit />} onClick={() => editUser(data)} />
|
|
||||||
<Popconfirm
|
|
||||||
showArrow
|
|
||||||
title="确认删除该成员?"
|
|
||||||
onConfirm={() => deleteUser({ userName: data.user.name, userAuth: data.auth.auth })}
|
|
||||||
>
|
|
||||||
<Button type="tertiary" theme="borderless" icon={<IconDelete />} />
|
|
||||||
</Popconfirm>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</Table>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<AddUser visible={visible} toggleVisible={toggleVisible} onOk={addUser} />
|
|
||||||
<EditUser visible={editVisible} toggleVisible={toggleEditVisible} currentUser={currentUser} onOk={handleEdit} />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,27 +1,23 @@
|
||||||
import { Banner, Button, Typography } from '@douyinfe/semi-ui';
|
import { Banner, Button, Typography } from '@douyinfe/semi-ui';
|
||||||
import { WorkspaceDeletor } from 'components/wiki/delete';
|
import { OrganizationDeletor } from 'components/organization/delete';
|
||||||
|
|
||||||
interface IProps {
|
|
||||||
wikiId: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { Paragraph } = Typography;
|
const { Paragraph } = Typography;
|
||||||
|
|
||||||
export const More: React.FC<IProps> = ({ wikiId }) => {
|
export const More = ({ organizationId }) => {
|
||||||
return (
|
return (
|
||||||
<div style={{ marginTop: 16 }}>
|
<div style={{ marginTop: 16 }}>
|
||||||
<Banner
|
<Banner
|
||||||
fullMode={false}
|
fullMode={false}
|
||||||
type="danger"
|
type="danger"
|
||||||
closeIcon={null}
|
closeIcon={null}
|
||||||
description={<Paragraph>删除知识库及内部所有文档,不可恢复!</Paragraph>}
|
description={<Paragraph>删除组织及内部所有知识库以及文档,不可恢复!</Paragraph>}
|
||||||
style={{ marginBottom: 16 }}
|
style={{ marginBottom: 16 }}
|
||||||
/>
|
/>
|
||||||
<WorkspaceDeletor wikiId={wikiId}>
|
<OrganizationDeletor organizationId={organizationId}>
|
||||||
<Button type="danger" theme="solid">
|
<Button type="danger" theme="solid">
|
||||||
删除知识库
|
删除组织
|
||||||
</Button>
|
</Button>
|
||||||
</WorkspaceDeletor>
|
</OrganizationDeletor>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,12 +1,10 @@
|
||||||
import { IconAppCenter, IconSmallTriangleDown } from '@douyinfe/semi-icons';
|
import { IconAppCenter, IconApps, IconSmallTriangleDown } from '@douyinfe/semi-icons';
|
||||||
import { Button, Dropdown, Space, Typography } from '@douyinfe/semi-ui';
|
import { Button, Dropdown, Space, Typography } from '@douyinfe/semi-ui';
|
||||||
import { Avatar } from '@douyinfe/semi-ui';
|
import { Avatar } from '@douyinfe/semi-ui';
|
||||||
import { DataRender } from 'components/data-render';
|
import { DataRender } from 'components/data-render';
|
||||||
import { useOrganizationDetail, useUserOrganizations } from 'data/organization';
|
import { useOrganizationDetail, useUserOrganizations } from 'data/organization';
|
||||||
import { useRouterQuery } from 'hooks/use-router-query';
|
import { useRouterQuery } from 'hooks/use-router-query';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import Router from 'next/router';
|
|
||||||
import { useCallback } from 'react';
|
|
||||||
|
|
||||||
import styles from './index.module.scss';
|
import styles from './index.module.scss';
|
||||||
|
|
||||||
|
@ -22,10 +20,6 @@ export const OrganizationSwitcher = () => {
|
||||||
error: userOrganizationsError,
|
error: userOrganizationsError,
|
||||||
} = useUserOrganizations();
|
} = useUserOrganizations();
|
||||||
|
|
||||||
const gotoCreate = useCallback(() => {
|
|
||||||
Router.push(`/app/org/create`);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DataRender
|
<DataRender
|
||||||
loading={loading}
|
loading={loading}
|
||||||
|
@ -64,7 +58,9 @@ export const OrganizationSwitcher = () => {
|
||||||
normalContent={() => {
|
normalContent={() => {
|
||||||
return (
|
return (
|
||||||
<Dropdown.Menu>
|
<Dropdown.Menu>
|
||||||
{(userOrganizations || []).map((org) => {
|
{userOrganizations.length ? (
|
||||||
|
<>
|
||||||
|
{userOrganizations.map((org) => {
|
||||||
return (
|
return (
|
||||||
<Dropdown.Item key={org.id}>
|
<Dropdown.Item key={org.id}>
|
||||||
<Link
|
<Link
|
||||||
|
@ -93,7 +89,34 @@ export const OrganizationSwitcher = () => {
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
<Dropdown.Divider />
|
<Dropdown.Divider />
|
||||||
<Dropdown.Item onClick={gotoCreate}>
|
</>
|
||||||
|
) : null}
|
||||||
|
<Dropdown.Item>
|
||||||
|
<Link
|
||||||
|
href={{
|
||||||
|
pathname: '/',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<a style={{ display: 'flex', alignItems: 'center', width: '100%' }}>
|
||||||
|
<Text>
|
||||||
|
<Space>
|
||||||
|
<Avatar size="extra-small">
|
||||||
|
<IconApps />
|
||||||
|
</Avatar>
|
||||||
|
前往广场
|
||||||
|
</Space>
|
||||||
|
</Text>
|
||||||
|
</a>
|
||||||
|
</Link>
|
||||||
|
</Dropdown.Item>
|
||||||
|
|
||||||
|
<Dropdown.Item>
|
||||||
|
<Link
|
||||||
|
href={{
|
||||||
|
pathname: '/app/org/create',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<a style={{ display: 'flex', alignItems: 'center', width: '100%' }}>
|
||||||
<Text>
|
<Text>
|
||||||
<Space>
|
<Space>
|
||||||
<Avatar size="extra-small">
|
<Avatar size="extra-small">
|
||||||
|
@ -102,6 +125,8 @@ export const OrganizationSwitcher = () => {
|
||||||
新建组织
|
新建组织
|
||||||
</Space>
|
</Space>
|
||||||
</Text>
|
</Text>
|
||||||
|
</a>
|
||||||
|
</Link>
|
||||||
</Dropdown.Item>
|
</Dropdown.Item>
|
||||||
</Dropdown.Menu>
|
</Dropdown.Menu>
|
||||||
);
|
);
|
||||||
|
|
|
@ -27,6 +27,7 @@ export const WikiCreator: React.FC<IProps> = ({ visible, toggleVisible }) => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleCancel = () => {
|
const handleCancel = () => {
|
||||||
toggleVisible(false);
|
toggleVisible(false);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { IconDelete } from '@douyinfe/semi-icons';
|
import { IconDelete } from '@douyinfe/semi-icons';
|
||||||
import { Modal, Space, Typography } from '@douyinfe/semi-ui';
|
import { Modal, Space, Typography } from '@douyinfe/semi-ui';
|
||||||
import { useOwnWikis } from 'data/wiki';
|
import { useOwnWikis } from 'data/wiki';
|
||||||
|
import { useRouterQuery } from 'hooks/use-router-query';
|
||||||
import Router from 'next/router';
|
import Router from 'next/router';
|
||||||
import React, { useCallback } from 'react';
|
import React, { useCallback } from 'react';
|
||||||
|
|
||||||
|
@ -11,8 +12,9 @@ interface IProps {
|
||||||
|
|
||||||
const { Text } = Typography;
|
const { Text } = Typography;
|
||||||
|
|
||||||
export const WorkspaceDeletor: React.FC<IProps> = ({ wikiId, onDelete, children }) => {
|
export const WikiDeletor: React.FC<IProps> = ({ wikiId, onDelete, children }) => {
|
||||||
const { deletWiki } = useOwnWikis();
|
const { organizationId } = useRouterQuery<{ organizationId: string }>();
|
||||||
|
const { deletWiki } = useOwnWikis(organizationId);
|
||||||
|
|
||||||
const deleteAction = useCallback(() => {
|
const deleteAction = useCallback(() => {
|
||||||
Modal.error({
|
Modal.error({
|
||||||
|
@ -23,7 +25,8 @@ export const WorkspaceDeletor: React.FC<IProps> = ({ wikiId, onDelete, children
|
||||||
onDelete
|
onDelete
|
||||||
? onDelete()
|
? onDelete()
|
||||||
: Router.push({
|
: Router.push({
|
||||||
pathname: `/wiki`,
|
pathname: `/app/org/[organizationId]`,
|
||||||
|
query: { organizationId },
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -32,7 +35,7 @@ export const WorkspaceDeletor: React.FC<IProps> = ({ wikiId, onDelete, children
|
||||||
},
|
},
|
||||||
style: { maxWidth: '96vw' },
|
style: { maxWidth: '96vw' },
|
||||||
});
|
});
|
||||||
}, [wikiId, deletWiki, onDelete]);
|
}, [organizationId, wikiId, deletWiki, onDelete]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Text type="danger" onClick={deleteAction}>
|
<Text type="danger" onClick={deleteAction}>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { Banner, Button, Typography } from '@douyinfe/semi-ui';
|
import { Banner, Button, Typography } from '@douyinfe/semi-ui';
|
||||||
import { WorkspaceDeletor } from 'components/wiki/delete';
|
import { WikiDeletor } from 'components/wiki/delete';
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
wikiId: string;
|
wikiId: string;
|
||||||
|
@ -17,11 +17,11 @@ export const More: React.FC<IProps> = ({ wikiId }) => {
|
||||||
description={<Paragraph>删除知识库及内部所有文档,不可恢复!</Paragraph>}
|
description={<Paragraph>删除知识库及内部所有文档,不可恢复!</Paragraph>}
|
||||||
style={{ marginBottom: 16 }}
|
style={{ marginBottom: 16 }}
|
||||||
/>
|
/>
|
||||||
<WorkspaceDeletor wikiId={wikiId}>
|
<WikiDeletor wikiId={wikiId}>
|
||||||
<Button type="danger" theme="solid">
|
<Button type="danger" theme="solid">
|
||||||
删除知识库
|
删除知识库
|
||||||
</Button>
|
</Button>
|
||||||
</WorkspaceDeletor>
|
</WikiDeletor>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -7,5 +7,11 @@ interface IProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Users: React.FC<IProps> = ({ wikiId }) => {
|
export const Users: React.FC<IProps> = ({ wikiId }) => {
|
||||||
return <Members id={wikiId} hook={useWikiMembers} />;
|
return (
|
||||||
|
<Members
|
||||||
|
id={wikiId}
|
||||||
|
hook={useWikiMembers}
|
||||||
|
descriptions={['权限继承:默认继承组织成员权限', '超级管理员:组织超级管理员和知识库创建者']}
|
||||||
|
/>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -65,11 +65,20 @@ export type DocAuth = {
|
||||||
* @param cookie
|
* @param cookie
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const getDocumentMembers = (documentId, cookie = null): Promise<Array<{ user: IUser; auth: IAuthority }>> => {
|
export const getDocumentMembers = (
|
||||||
|
documentId,
|
||||||
|
page,
|
||||||
|
pageSize,
|
||||||
|
cookie = null
|
||||||
|
): Promise<Array<{ user: IUser; auth: IAuthority }>> => {
|
||||||
return HttpClient.request({
|
return HttpClient.request({
|
||||||
method: DocumentApiDefinition.getMemberById.method,
|
method: DocumentApiDefinition.getMemberById.method,
|
||||||
url: DocumentApiDefinition.getMemberById.client(documentId),
|
url: DocumentApiDefinition.getMemberById.client(documentId),
|
||||||
cookie,
|
cookie,
|
||||||
|
params: {
|
||||||
|
page,
|
||||||
|
pageSize,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -79,9 +88,11 @@ export const getDocumentMembers = (documentId, cookie = null): Promise<Array<{ u
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const useDoumentMembers = (documentId, options?: UseQueryOptions<Array<{ user: IUser; auth: IAuthority }>>) => {
|
export const useDoumentMembers = (documentId, options?: UseQueryOptions<Array<{ user: IUser; auth: IAuthority }>>) => {
|
||||||
|
const [pageSize] = useState(12);
|
||||||
|
const [page, setPage] = useState(1);
|
||||||
const { data, error, isLoading, refetch } = useQuery(
|
const { data, error, isLoading, refetch } = useQuery(
|
||||||
DocumentApiDefinition.getMemberById.client(documentId),
|
[DocumentApiDefinition.getMemberById.client(documentId), page],
|
||||||
() => getDocumentMembers(documentId),
|
() => getDocumentMembers(documentId, page, pageSize),
|
||||||
options
|
options
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -124,7 +135,7 @@ export const useDoumentMembers = (documentId, options?: UseQueryOptions<Array<{
|
||||||
[refetch, documentId]
|
[refetch, documentId]
|
||||||
);
|
);
|
||||||
|
|
||||||
return { data, loading: isLoading, error, addUser, updateUser, deleteUser };
|
return { data, loading: isLoading, error, page, pageSize, setPage, addUser, updateUser, deleteUser };
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -108,17 +108,32 @@ export const useOrganizationDetail = (id) => {
|
||||||
[refetch, id]
|
[refetch, id]
|
||||||
);
|
);
|
||||||
|
|
||||||
return { data, error, loading: isLoading, refresh: refetch, update };
|
const deleteOrganization = useCallback(async () => {
|
||||||
|
const res = await HttpClient.request({
|
||||||
|
method: OrganizationApiDefinition.deleteOrganization.method,
|
||||||
|
url: OrganizationApiDefinition.deleteOrganization.client(id),
|
||||||
|
});
|
||||||
|
refetch();
|
||||||
|
return res;
|
||||||
|
}, [refetch, id]);
|
||||||
|
|
||||||
|
return { data, error, loading: isLoading, refresh: refetch, update, deleteOrganization };
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getOrganizationMembers = (
|
export const getOrganizationMembers = (
|
||||||
id,
|
id,
|
||||||
|
page = 1,
|
||||||
|
pageSize,
|
||||||
cookie = null
|
cookie = null
|
||||||
): Promise<{ data: Array<{ auth: IAuth; user: IUser }>; total: number }> => {
|
): Promise<{ data: Array<{ auth: IAuth; user: IUser }>; total: number }> => {
|
||||||
return HttpClient.request({
|
return HttpClient.request({
|
||||||
method: OrganizationApiDefinition.getMembers.method,
|
method: OrganizationApiDefinition.getMembers.method,
|
||||||
url: OrganizationApiDefinition.getMembers.client(id),
|
url: OrganizationApiDefinition.getMembers.client(id),
|
||||||
cookie,
|
cookie,
|
||||||
|
params: {
|
||||||
|
page,
|
||||||
|
pageSize,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -127,8 +142,10 @@ export const getOrganizationMembers = (
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const useOrganizationMembers = (id) => {
|
export const useOrganizationMembers = (id) => {
|
||||||
const { data, error, isLoading, refetch } = useQuery(OrganizationApiDefinition.getMembers.client(id), () =>
|
const [pageSize] = useState(12);
|
||||||
getOrganizationMembers(id)
|
const [page, setPage] = useState(1);
|
||||||
|
const { data, error, isLoading, refetch } = useQuery([OrganizationApiDefinition.getMembers.client(id), page], () =>
|
||||||
|
getOrganizationMembers(id, page, pageSize)
|
||||||
);
|
);
|
||||||
|
|
||||||
const addUser = useCallback(
|
const addUser = useCallback(
|
||||||
|
@ -170,5 +187,16 @@ export const useOrganizationMembers = (id) => {
|
||||||
[refetch, id]
|
[refetch, id]
|
||||||
);
|
);
|
||||||
|
|
||||||
return { data, error, loading: isLoading, refresh: refetch, addUser, updateUser, deleteUser };
|
return {
|
||||||
|
data,
|
||||||
|
error,
|
||||||
|
loading: isLoading,
|
||||||
|
page,
|
||||||
|
pageSize,
|
||||||
|
setPage,
|
||||||
|
refresh: refetch,
|
||||||
|
addUser,
|
||||||
|
updateUser,
|
||||||
|
deleteUser,
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { Toast } from '@douyinfe/semi-ui';
|
import { Toast } from '@douyinfe/semi-ui';
|
||||||
import { ILoginUser, ISystemConfig, IUser, UserApiDefinition } from '@think/domains';
|
import { ILoginUser, ISystemConfig, IUser, SystemApiDefinition, UserApiDefinition } from '@think/domains';
|
||||||
import { getStorage, setStorage } from 'helpers/storage';
|
import { getStorage, setStorage } from 'helpers/storage';
|
||||||
import { useAsyncLoading } from 'hooks/use-async-loading';
|
import { useAsyncLoading } from 'hooks/use-async-loading';
|
||||||
import Router, { useRouter } from 'next/router';
|
import Router, { useRouter } from 'next/router';
|
||||||
|
@ -138,10 +138,17 @@ export const useUser = () => {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
export const useSystemPublicConfig = () => {
|
||||||
* 获取验证码
|
const { data, error, isLoading, refetch } = useQuery(SystemApiDefinition.getPublicConfig.client(), () =>
|
||||||
* @returns
|
HttpClient.request<ISystemConfig>({
|
||||||
*/
|
method: SystemApiDefinition.getPublicConfig.method,
|
||||||
|
url: SystemApiDefinition.getPublicConfig.client(),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
return { data, error, loading: isLoading, refresh: refetch };
|
||||||
|
};
|
||||||
|
|
||||||
export const useSystemConfig = () => {
|
export const useSystemConfig = () => {
|
||||||
const { data, error, isLoading, refetch } = useQuery(UserApiDefinition.getSystemConfig.client(), () =>
|
const { data, error, isLoading, refetch } = useQuery(UserApiDefinition.getSystemConfig.client(), () =>
|
||||||
HttpClient.request<ISystemConfig>({
|
HttpClient.request<ISystemConfig>({
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { IAuth, IDocument, IUser, IWiki, IWikiUser, WikiApiDefinition } from '@think/domains';
|
import { IAuth, IDocument, IUser, IWiki, WikiApiDefinition } from '@think/domains';
|
||||||
import { event, REFRESH_TOCS } from 'event';
|
import { event, REFRESH_TOCS } from 'event';
|
||||||
import { useCallback, useEffect, useState } from 'react';
|
import { useCallback, useEffect, useState } from 'react';
|
||||||
import { useQuery } from 'react-query';
|
import { useQuery } from 'react-query';
|
||||||
|
@ -97,7 +97,7 @@ export const useOwnWikis = (organizationId) => {
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 删除文档
|
* 删除知识库
|
||||||
* @param id
|
* @param id
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
|
@ -318,12 +318,18 @@ export const useWikiDocuments = (wikiId) => {
|
||||||
*/
|
*/
|
||||||
export const getWikiMembers = (
|
export const getWikiMembers = (
|
||||||
wikiId,
|
wikiId,
|
||||||
|
page,
|
||||||
|
pageSize,
|
||||||
cookie = null
|
cookie = null
|
||||||
): Promise<{ data: Array<{ auth: IAuth; user: IUser }>; total: number }> => {
|
): Promise<{ data: Array<{ auth: IAuth; user: IUser }>; total: number }> => {
|
||||||
return HttpClient.request({
|
return HttpClient.request({
|
||||||
method: WikiApiDefinition.getMemberById.method,
|
method: WikiApiDefinition.getMemberById.method,
|
||||||
url: WikiApiDefinition.getMemberById.client(wikiId),
|
url: WikiApiDefinition.getMemberById.client(wikiId),
|
||||||
cookie,
|
cookie,
|
||||||
|
params: {
|
||||||
|
page,
|
||||||
|
pageSize,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -333,8 +339,10 @@ export const getWikiMembers = (
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const useWikiMembers = (wikiId) => {
|
export const useWikiMembers = (wikiId) => {
|
||||||
const { data, error, isLoading, refetch } = useQuery(WikiApiDefinition.getMemberById.client(wikiId), () =>
|
const [pageSize] = useState(12);
|
||||||
getWikiMembers(wikiId)
|
const [page, setPage] = useState(1);
|
||||||
|
const { data, error, isLoading, refetch } = useQuery([WikiApiDefinition.getMemberById.client(wikiId), page], () =>
|
||||||
|
getWikiMembers(wikiId, page, pageSize)
|
||||||
);
|
);
|
||||||
|
|
||||||
const addUser = useCallback(
|
const addUser = useCallback(
|
||||||
|
@ -376,7 +384,7 @@ export const useWikiMembers = (wikiId) => {
|
||||||
[refetch, wikiId]
|
[refetch, wikiId]
|
||||||
);
|
);
|
||||||
|
|
||||||
return { data, loading: isLoading, error, addUser, updateUser, deleteUser };
|
return { data, loading: isLoading, error, page, pageSize, setPage, addUser, updateUser, deleteUser };
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
import { Layout as SemiLayout, Nav, Space } from '@douyinfe/semi-ui';
|
import { Button, Layout as SemiLayout, Nav, Space } from '@douyinfe/semi-ui';
|
||||||
import { Message } from 'components/message';
|
import { Message } from 'components/message';
|
||||||
import { OrganizationPublicSwitcher } from 'components/organization/public-switcher';
|
import { OrganizationPublicSwitcher } from 'components/organization/public-switcher';
|
||||||
import { Theme } from 'components/theme';
|
import { Theme } from 'components/theme';
|
||||||
import { User } from 'components/user';
|
import { User } from 'components/user';
|
||||||
|
import { useUser } from 'data/user';
|
||||||
import { IsOnMobile } from 'hooks/use-on-mobile';
|
import { IsOnMobile } from 'hooks/use-on-mobile';
|
||||||
import Router, { useRouter } from 'next/router';
|
import Router, { useRouter } from 'next/router';
|
||||||
import React from 'react';
|
import React, { useCallback } from 'react';
|
||||||
|
|
||||||
const { Header: SemiHeader } = SemiLayout;
|
const { Header: SemiHeader } = SemiLayout;
|
||||||
|
|
||||||
|
@ -28,12 +29,26 @@ const menus = [
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
itemKey: '/template',
|
||||||
|
text: '模板',
|
||||||
|
onClick: () => {
|
||||||
|
Router.push({
|
||||||
|
pathname: `/template`,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export const RouterHeader: React.FC = () => {
|
export const RouterHeader: React.FC = () => {
|
||||||
|
const { user } = useUser();
|
||||||
const { pathname } = useRouter();
|
const { pathname } = useRouter();
|
||||||
const { isMobile } = IsOnMobile.useHook();
|
const { isMobile } = IsOnMobile.useHook();
|
||||||
|
|
||||||
|
const gotoApp = useCallback(() => {
|
||||||
|
Router.push(`/app`);
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SemiHeader>
|
<SemiHeader>
|
||||||
{isMobile ? (
|
{isMobile ? (
|
||||||
|
@ -47,6 +62,11 @@ export const RouterHeader: React.FC = () => {
|
||||||
}
|
}
|
||||||
footer={
|
footer={
|
||||||
<Space>
|
<Space>
|
||||||
|
{user && (
|
||||||
|
<Button theme="solid" onClick={gotoApp}>
|
||||||
|
前往组织空间
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
<Theme />
|
<Theme />
|
||||||
<User />
|
<User />
|
||||||
</Space>
|
</Space>
|
||||||
|
@ -65,7 +85,12 @@ export const RouterHeader: React.FC = () => {
|
||||||
items={menus}
|
items={menus}
|
||||||
footer={
|
footer={
|
||||||
<Space>
|
<Space>
|
||||||
<Message />
|
{user && (
|
||||||
|
<Button theme="solid" onClick={gotoApp}>
|
||||||
|
前往组织空间
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
{user && <Message />}
|
||||||
<Theme />
|
<Theme />
|
||||||
<User />
|
<User />
|
||||||
</Space>
|
</Space>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Typography } from '@douyinfe/semi-ui';
|
import { Banner, Typography } from '@douyinfe/semi-ui';
|
||||||
import { SystemConfig } from 'components/admin/system-config';
|
import { SystemConfig } from 'components/admin/system-config';
|
||||||
import { Seo } from 'components/seo';
|
import { Seo } from 'components/seo';
|
||||||
import { useUser } from 'data/user';
|
import { useUser } from 'data/user';
|
||||||
|
@ -37,6 +37,7 @@ const Page: NextPage = () => {
|
||||||
管理后台
|
管理后台
|
||||||
</Title>
|
</Title>
|
||||||
</div>
|
</div>
|
||||||
|
<Banner type="info" description="该部分是全局的系统管理后台,用于系统配置管理等操作!" />
|
||||||
<SystemConfig tab={tab} onNavigate={navigate} />
|
<SystemConfig tab={tab} onNavigate={navigate} />
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
|
|
|
@ -1,32 +1,103 @@
|
||||||
import { Spin } from '@douyinfe/semi-ui';
|
import { Avatar, Button, Table, Typography } from '@douyinfe/semi-ui';
|
||||||
import { usePeronalOrganization } from 'data/organization';
|
import { IOrganization } from '@think/domains';
|
||||||
|
import { DataRender } from 'components/data-render';
|
||||||
|
import { LocaleTime } from 'components/locale-time';
|
||||||
|
import { usePeronalOrganization, useUserOrganizations } from 'data/organization';
|
||||||
import { SingleColumnLayout } from 'layouts/single-column';
|
import { SingleColumnLayout } from 'layouts/single-column';
|
||||||
|
import Link from 'next/link';
|
||||||
import Router from 'next/router';
|
import Router from 'next/router';
|
||||||
import { useEffect } from 'react';
|
import { useCallback, useEffect } from 'react';
|
||||||
|
|
||||||
|
const { Title, Paragraph } = Typography;
|
||||||
|
const { Column } = Table;
|
||||||
|
|
||||||
const Page = () => {
|
const Page = () => {
|
||||||
const { data: organization } = usePeronalOrganization();
|
const { data: organization } = usePeronalOrganization();
|
||||||
|
const {
|
||||||
|
data: userOrganizations,
|
||||||
|
loading: userOrganizationsLoading,
|
||||||
|
error: userOrganizationsError,
|
||||||
|
} = useUserOrganizations();
|
||||||
|
|
||||||
|
const gotoCreate = useCallback(() => {
|
||||||
|
Router.push({
|
||||||
|
pathname: '/app/org/create',
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (userOrganizations && userOrganizations.length) return;
|
||||||
if (!organization) return;
|
if (!organization) return;
|
||||||
|
|
||||||
Router.replace({
|
Router.replace({
|
||||||
pathname: `/app/org/[organizationId]`,
|
pathname: `/app/org/[organizationId]`,
|
||||||
query: { organizationId: organization.id },
|
query: { organizationId: organization.id },
|
||||||
});
|
});
|
||||||
}, [organization]);
|
}, [organization, userOrganizations]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SingleColumnLayout>
|
<SingleColumnLayout>
|
||||||
<div className="container">
|
<div className="container">
|
||||||
<div
|
<div style={{ marginBottom: 24, display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
|
||||||
style={{
|
<Title heading={3} style={{ margin: '8px 0' }}>
|
||||||
padding: '10vh',
|
组织列表
|
||||||
textAlign: 'center',
|
</Title>
|
||||||
|
<Button theme="solid" onClick={gotoCreate}>
|
||||||
|
新建组织
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<DataRender
|
||||||
|
loading={userOrganizationsLoading}
|
||||||
|
error={userOrganizationsError}
|
||||||
|
normalContent={() => (
|
||||||
|
<>
|
||||||
|
<Table style={{ margin: '16px 0' }} dataSource={userOrganizations} size="small" pagination={false}>
|
||||||
|
<Column
|
||||||
|
title="名称"
|
||||||
|
dataIndex="name"
|
||||||
|
key="name"
|
||||||
|
width={200}
|
||||||
|
render={(_, org: IOrganization) => {
|
||||||
|
return (
|
||||||
|
<Link
|
||||||
|
href={{
|
||||||
|
pathname: `/app/org/[organizationId]`,
|
||||||
|
query: {
|
||||||
|
organizationId: org.id,
|
||||||
|
},
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Spin></Spin>
|
<a style={{ color: 'inherit', textDecoration: 'none' }}>
|
||||||
</div>
|
<span style={{ display: 'flex', alignItems: 'center' }}>
|
||||||
|
<Avatar size="small" src={org.logo} style={{ marginRight: 8 }} />
|
||||||
|
<Paragraph
|
||||||
|
style={{
|
||||||
|
maxWidth: 100,
|
||||||
|
whiteSpace: 'nowrap',
|
||||||
|
textOverflow: 'ellipsis',
|
||||||
|
overflow: 'hidden',
|
||||||
|
}}
|
||||||
|
strong
|
||||||
|
>
|
||||||
|
{org.name}
|
||||||
|
</Paragraph>
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Column
|
||||||
|
width={120}
|
||||||
|
title="创建时间"
|
||||||
|
dataIndex="createdAt"
|
||||||
|
key="createdAt"
|
||||||
|
render={(date) => <LocaleTime date={date} />}
|
||||||
|
/>
|
||||||
|
</Table>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</SingleColumnLayout>
|
</SingleColumnLayout>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,12 +1,3 @@
|
||||||
.wikiItemWrap {
|
.wrap {
|
||||||
padding: 12px 16px !important;
|
|
||||||
margin: 8px 2px;
|
|
||||||
cursor: pointer;
|
|
||||||
background-color: var(--semi-color-bg-2);
|
|
||||||
border: 1px solid var(--semi-color-border) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.titleWrap {
|
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,35 +1,51 @@
|
||||||
import { Button, Typography } from '@douyinfe/semi-ui';
|
import { Button, Typography } from '@douyinfe/semi-ui';
|
||||||
import { Seo } from 'components/seo';
|
import { Seo } from 'components/seo';
|
||||||
|
import { toLogin, useUser } from 'data/user';
|
||||||
import { SingleColumnLayout } from 'layouts/single-column';
|
import { SingleColumnLayout } from 'layouts/single-column';
|
||||||
import type { NextPage } from 'next';
|
import type { NextPage } from 'next';
|
||||||
import Router from 'next/router';
|
import Router from 'next/router';
|
||||||
import React, { useCallback } from 'react';
|
import React, { useCallback } from 'react';
|
||||||
|
|
||||||
const { Title } = Typography;
|
import styles from './index.module.scss';
|
||||||
|
|
||||||
|
const { Title, Paragraph } = Typography;
|
||||||
|
|
||||||
const Page: NextPage = () => {
|
const Page: NextPage = () => {
|
||||||
const gotoApp = useCallback(() => {
|
const { user } = useUser();
|
||||||
|
|
||||||
|
const start = useCallback(() => {
|
||||||
|
if (user) {
|
||||||
Router.push(`/app`);
|
Router.push(`/app`);
|
||||||
|
} else {
|
||||||
|
toLogin();
|
||||||
|
}
|
||||||
|
}, [user]);
|
||||||
|
|
||||||
|
const toGithub = useCallback(() => {
|
||||||
|
window.open('https://github.com/fantasticit/think');
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SingleColumnLayout>
|
<SingleColumnLayout>
|
||||||
<Seo title="主页" />
|
<Seo title="主页" />
|
||||||
<div className="container">
|
<div className="container">
|
||||||
<div style={{ marginBottom: 24 }}>
|
<div className={styles.wrap}>
|
||||||
<Title heading={3} style={{ margin: '8px 0' }}>
|
<div>
|
||||||
主页
|
<div style={{ textAlign: 'center' }}>
|
||||||
</Title>
|
<Title style={{ margin: 24 }}>云策文档</Title>
|
||||||
|
<Paragraph type="tertiary">
|
||||||
|
云策文档是一款开源知识管理工具。通过独立的知识库空间,结构化地组织在线协作文档,实现知识的积累与沉淀,促进知识的复用与流通。
|
||||||
|
</Paragraph>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div style={{ margin: '48px 0', textAlign: 'center' }}>
|
||||||
style={{
|
<Button theme="solid" onClick={start}>
|
||||||
padding: '10vh',
|
开始使用
|
||||||
textAlign: 'center',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Button theme="solid" onClick={gotoApp}>
|
|
||||||
前往组织空间
|
|
||||||
</Button>
|
</Button>
|
||||||
|
<Button style={{ marginLeft: 12 }} onClick={toGithub}>
|
||||||
|
Github
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</SingleColumnLayout>
|
</SingleColumnLayout>
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { Button, Col, Form, Layout, Modal, Row, Space, Toast, Typography } from
|
||||||
import { Author } from 'components/author';
|
import { Author } from 'components/author';
|
||||||
import { LogoImage, LogoText } from 'components/logo';
|
import { LogoImage, LogoText } from 'components/logo';
|
||||||
import { Seo } from 'components/seo';
|
import { Seo } from 'components/seo';
|
||||||
import { useRegister, useVerifyCode } from 'data/user';
|
import { useRegister, useSystemPublicConfig, useVerifyCode } from 'data/user';
|
||||||
import { isEmail } from 'helpers/validator';
|
import { isEmail } from 'helpers/validator';
|
||||||
import { useInterval } from 'hooks/use-interval';
|
import { useInterval } from 'hooks/use-interval';
|
||||||
import { useRouterQuery } from 'hooks/use-router-query';
|
import { useRouterQuery } from 'hooks/use-router-query';
|
||||||
|
@ -24,6 +24,7 @@ const Page = () => {
|
||||||
const [hasSendVerifyCode, toggleHasSendVerifyCode] = useToggle(false);
|
const [hasSendVerifyCode, toggleHasSendVerifyCode] = useToggle(false);
|
||||||
const [countDown, setCountDown] = useState(0);
|
const [countDown, setCountDown] = useState(0);
|
||||||
const { register, loading } = useRegister();
|
const { register, loading } = useRegister();
|
||||||
|
const { data: systemConfig } = useSystemPublicConfig();
|
||||||
const { sendVerifyCode, loading: sendVerifyCodeLoading } = useVerifyCode();
|
const { sendVerifyCode, loading: sendVerifyCodeLoading } = useVerifyCode();
|
||||||
|
|
||||||
const onFormChange = useCallback((formState) => {
|
const onFormChange = useCallback((formState) => {
|
||||||
|
@ -133,6 +134,7 @@ const Page = () => {
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
{systemConfig && systemConfig.enableEmailVerify ? (
|
||||||
<Row gutter={8} style={{ paddingTop: 12 }}>
|
<Row gutter={8} style={{ paddingTop: 12 }}>
|
||||||
<Col span={16}>
|
<Col span={16}>
|
||||||
<Form.Input
|
<Form.Input
|
||||||
|
@ -144,11 +146,17 @@ const Page = () => {
|
||||||
/>
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={8}>
|
<Col span={8}>
|
||||||
<Button disabled={!email || countDown > 0} loading={sendVerifyCodeLoading} onClick={getVerifyCode} block>
|
<Button
|
||||||
|
disabled={!email || countDown > 0}
|
||||||
|
loading={sendVerifyCodeLoading}
|
||||||
|
onClick={getVerifyCode}
|
||||||
|
block
|
||||||
|
>
|
||||||
{hasSendVerifyCode ? countDown : '获取验证码'}
|
{hasSendVerifyCode ? countDown : '获取验证码'}
|
||||||
</Button>
|
</Button>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
|
) : null}
|
||||||
|
|
||||||
<Button htmlType="submit" type="primary" theme="solid" block loading={loading} style={{ margin: '16px 0' }}>
|
<Button htmlType="submit" type="primary" theme="solid" block loading={loading} style={{ margin: '16px 0' }}>
|
||||||
注册
|
注册
|
||||||
|
|
|
@ -10,7 +10,7 @@ interface AxiosInstance extends Axios {
|
||||||
|
|
||||||
export const HttpClient = axios.create({
|
export const HttpClient = axios.create({
|
||||||
baseURL: process.env.SERVER_API_URL,
|
baseURL: process.env.SERVER_API_URL,
|
||||||
timeout: 1 * 3 * 1000,
|
timeout: process.env.NODE_ENV === 'production' ? 10 * 60 * 1000 : 3000,
|
||||||
withCredentials: true,
|
withCredentials: true,
|
||||||
}) as AxiosInstance;
|
}) as AxiosInstance;
|
||||||
|
|
||||||
|
|
|
@ -7,3 +7,4 @@ export * from './template';
|
||||||
export * from './comment';
|
export * from './comment';
|
||||||
export * from './star';
|
export * from './star';
|
||||||
export * from './organization';
|
export * from './organization';
|
||||||
|
export * from './system';
|
||||||
|
|
|
@ -19,3 +19,4 @@ __exportStar(require("./template"), exports);
|
||||||
__exportStar(require("./comment"), exports);
|
__exportStar(require("./comment"), exports);
|
||||||
__exportStar(require("./star"), exports);
|
__exportStar(require("./star"), exports);
|
||||||
__exportStar(require("./organization"), exports);
|
__exportStar(require("./organization"), exports);
|
||||||
|
__exportStar(require("./system"), exports);
|
||||||
|
|
|
@ -40,6 +40,14 @@ export declare const OrganizationApiDefinition: {
|
||||||
server: "/update/:id";
|
server: "/update/:id";
|
||||||
client: (id: IOrganization['id']) => string;
|
client: (id: IOrganization['id']) => string;
|
||||||
};
|
};
|
||||||
|
/**
|
||||||
|
* 更新组织基本信息
|
||||||
|
*/
|
||||||
|
deleteOrganization: {
|
||||||
|
method: "delete";
|
||||||
|
server: "/delete/:id";
|
||||||
|
client: (id: IOrganization['id']) => string;
|
||||||
|
};
|
||||||
/**
|
/**
|
||||||
* 获取组织成员
|
* 获取组织成员
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -42,6 +42,14 @@ exports.OrganizationApiDefinition = {
|
||||||
server: '/update/:id',
|
server: '/update/:id',
|
||||||
client: function (id) { return "/organization/update/".concat(id); }
|
client: function (id) { return "/organization/update/".concat(id); }
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* 更新组织基本信息
|
||||||
|
*/
|
||||||
|
deleteOrganization: {
|
||||||
|
method: 'delete',
|
||||||
|
server: '/delete/:id',
|
||||||
|
client: function (id) { return "/organization/delete/".concat(id); }
|
||||||
|
},
|
||||||
/**
|
/**
|
||||||
* 获取组织成员
|
* 获取组织成员
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
export declare const SystemApiDefinition: {
|
||||||
|
getPublicConfig: {
|
||||||
|
method: "get";
|
||||||
|
server: "/";
|
||||||
|
client: () => string;
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,10 @@
|
||||||
|
"use strict";
|
||||||
|
exports.__esModule = true;
|
||||||
|
exports.SystemApiDefinition = void 0;
|
||||||
|
exports.SystemApiDefinition = {
|
||||||
|
getPublicConfig: {
|
||||||
|
method: 'get',
|
||||||
|
server: '/',
|
||||||
|
client: function () { return '/system'; }
|
||||||
|
}
|
||||||
|
};
|
|
@ -1,33 +1,2 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
exports.__esModule = true;
|
exports.__esModule = true;
|
||||||
// /**
|
|
||||||
// * 创建组织数据定义
|
|
||||||
// */
|
|
||||||
// export interface CreateOrganizationDto {
|
|
||||||
// name: string;
|
|
||||||
// description: string;
|
|
||||||
// logo: string;
|
|
||||||
// }
|
|
||||||
// export enum OrganizationAuthEnum {
|
|
||||||
// superAdmin = 'superAdmin',
|
|
||||||
// admin = 'admin',
|
|
||||||
// member = 'member',
|
|
||||||
// noAccess = 'noAccess',
|
|
||||||
// }
|
|
||||||
// export const AuthEnumTextMap = {
|
|
||||||
// [OrganizationAuthEnum.superAdmin]: '超级管理员',
|
|
||||||
// [OrganizationAuthEnum.admin]: '管理员',
|
|
||||||
// [OrganizationAuthEnum.member]: '成员',
|
|
||||||
// [OrganizationAuthEnum.noAccess]: '无权限',
|
|
||||||
// };
|
|
||||||
// export const OrganizationAuthEnumArray = Object.keys(AuthEnumTextMap).map((value) => ({
|
|
||||||
// label: AuthEnumTextMap[value],
|
|
||||||
// value,
|
|
||||||
// }));
|
|
||||||
// export interface IOrganizationAuth {
|
|
||||||
// id: string;
|
|
||||||
// auth: OrganizationAuthEnum;
|
|
||||||
// organizationId: IOrganization['id'];
|
|
||||||
// createdAt: string;
|
|
||||||
// updatedAt: string;
|
|
||||||
// }
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
export interface ISystemConfig {
|
export interface ISystemConfig {
|
||||||
isSystemLocked: boolean;
|
isSystemLocked: boolean;
|
||||||
|
enableEmailVerify: boolean;
|
||||||
emailServiceHost: string;
|
emailServiceHost: string;
|
||||||
emailServicePassword: string;
|
emailServicePassword: string;
|
||||||
emailServicePort: string;
|
emailServicePort: string;
|
||||||
|
|
|
@ -1,11 +1,3 @@
|
||||||
/**
|
|
||||||
* 用户角色枚举
|
|
||||||
*/
|
|
||||||
export declare enum UserRole {
|
|
||||||
normal = "normal",
|
|
||||||
admin = "admin",
|
|
||||||
superadmin = "superadmin"
|
|
||||||
}
|
|
||||||
/**
|
/**
|
||||||
* 用户状态枚举
|
* 用户状态枚举
|
||||||
*/
|
*/
|
||||||
|
@ -22,7 +14,6 @@ export interface IUser {
|
||||||
password?: string;
|
password?: string;
|
||||||
avatar?: string;
|
avatar?: string;
|
||||||
email?: string;
|
email?: string;
|
||||||
role: UserRole;
|
|
||||||
status: UserStatus;
|
status: UserStatus;
|
||||||
isSystemAdmin?: boolean;
|
isSystemAdmin?: boolean;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,6 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
exports.__esModule = true;
|
exports.__esModule = true;
|
||||||
exports.UserStatus = exports.UserRole = void 0;
|
exports.UserStatus = void 0;
|
||||||
/**
|
|
||||||
* 用户角色枚举
|
|
||||||
*/
|
|
||||||
var UserRole;
|
|
||||||
(function (UserRole) {
|
|
||||||
UserRole["normal"] = "normal";
|
|
||||||
UserRole["admin"] = "admin";
|
|
||||||
UserRole["superadmin"] = "superadmin";
|
|
||||||
})(UserRole = exports.UserRole || (exports.UserRole = {}));
|
|
||||||
/**
|
/**
|
||||||
* 用户状态枚举
|
* 用户状态枚举
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -8,21 +8,6 @@ export declare enum WikiStatus {
|
||||||
private = "private",
|
private = "private",
|
||||||
public = "public"
|
public = "public"
|
||||||
}
|
}
|
||||||
/**
|
|
||||||
* 知识库成员状态枚举
|
|
||||||
*/
|
|
||||||
export declare enum WikiUserStatus {
|
|
||||||
applying = "applying",
|
|
||||||
inviting = "inviting",
|
|
||||||
normal = "normal"
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* 知识库成员角色枚举
|
|
||||||
*/
|
|
||||||
export declare enum WikiUserRole {
|
|
||||||
normal = "normal",
|
|
||||||
admin = "admin"
|
|
||||||
}
|
|
||||||
/**
|
/**
|
||||||
* 知识库数据定义
|
* 知识库数据定义
|
||||||
*/
|
*/
|
||||||
|
@ -39,11 +24,3 @@ export interface IWiki {
|
||||||
createdAt: Date;
|
createdAt: Date;
|
||||||
updatedAt: Date;
|
updatedAt: Date;
|
||||||
}
|
}
|
||||||
/**
|
|
||||||
* 知识库成员数据定义
|
|
||||||
*/
|
|
||||||
export interface IWikiUser extends IUser {
|
|
||||||
userRole: WikiUserRole;
|
|
||||||
userStatus: WikiUserStatus;
|
|
||||||
isCreator: boolean;
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
exports.__esModule = true;
|
exports.__esModule = true;
|
||||||
exports.WikiUserRole = exports.WikiUserStatus = exports.WikiStatus = void 0;
|
exports.WikiStatus = void 0;
|
||||||
/**
|
/**
|
||||||
* 知识库状态枚举
|
* 知识库状态枚举
|
||||||
*/
|
*/
|
||||||
|
@ -9,20 +9,3 @@ var WikiStatus;
|
||||||
WikiStatus["private"] = "private";
|
WikiStatus["private"] = "private";
|
||||||
WikiStatus["public"] = "public";
|
WikiStatus["public"] = "public";
|
||||||
})(WikiStatus = exports.WikiStatus || (exports.WikiStatus = {}));
|
})(WikiStatus = exports.WikiStatus || (exports.WikiStatus = {}));
|
||||||
/**
|
|
||||||
* 知识库成员状态枚举
|
|
||||||
*/
|
|
||||||
var WikiUserStatus;
|
|
||||||
(function (WikiUserStatus) {
|
|
||||||
WikiUserStatus["applying"] = "applying";
|
|
||||||
WikiUserStatus["inviting"] = "inviting";
|
|
||||||
WikiUserStatus["normal"] = "normal";
|
|
||||||
})(WikiUserStatus = exports.WikiUserStatus || (exports.WikiUserStatus = {}));
|
|
||||||
/**
|
|
||||||
* 知识库成员角色枚举
|
|
||||||
*/
|
|
||||||
var WikiUserRole;
|
|
||||||
(function (WikiUserRole) {
|
|
||||||
WikiUserRole["normal"] = "normal";
|
|
||||||
WikiUserRole["admin"] = "admin";
|
|
||||||
})(WikiUserRole = exports.WikiUserRole || (exports.WikiUserRole = {}));
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { WikiStatus, WikiUserRole, DocumentStatus, IWiki, IDocument } from './models';
|
import { WikiStatus, DocumentStatus, IWiki, IDocument } from './models';
|
||||||
/**
|
/**
|
||||||
* 知识库状态列表数据
|
* 知识库状态列表数据
|
||||||
*/
|
*/
|
||||||
|
@ -32,12 +32,6 @@ export declare const getWikiStatusText: (wiki: IWiki) => string;
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export declare const isPublicWiki: (currentStatus: IWiki['status']) => boolean;
|
export declare const isPublicWiki: (currentStatus: IWiki['status']) => boolean;
|
||||||
/**
|
|
||||||
* 获取知识库成员角色对应文本
|
|
||||||
* @param role 实例数据的 role 字段
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
export declare const getWikiUserRoleText: (role: WikiUserRole) => string;
|
|
||||||
/**
|
/**
|
||||||
* 检查文档是否公开
|
* 检查文档是否公开
|
||||||
* @param currentStatus document 实例数据的 status 字段
|
* @param currentStatus document 实例数据的 status 字段
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
exports.__esModule = true;
|
exports.__esModule = true;
|
||||||
exports.isPublicDocument = exports.getWikiUserRoleText = exports.isPublicWiki = exports.getWikiStatusText = exports.DOCUMENT_STATUS = exports.WIKI_USER_ROLES = exports.WIKI_STATUS_LIST = void 0;
|
exports.isPublicDocument = exports.isPublicWiki = exports.getWikiStatusText = exports.DOCUMENT_STATUS = exports.WIKI_USER_ROLES = exports.WIKI_STATUS_LIST = void 0;
|
||||||
var models_1 = require("./models");
|
var models_1 = require("./models");
|
||||||
/**
|
/**
|
||||||
* 知识库状态列表数据
|
* 知识库状态列表数据
|
||||||
|
@ -57,15 +57,6 @@ exports.getWikiStatusText = getWikiStatusText;
|
||||||
*/
|
*/
|
||||||
var isPublicWiki = function (currentStatus) { return currentStatus === models_1.WikiStatus.public; };
|
var isPublicWiki = function (currentStatus) { return currentStatus === models_1.WikiStatus.public; };
|
||||||
exports.isPublicWiki = isPublicWiki;
|
exports.isPublicWiki = isPublicWiki;
|
||||||
/**
|
|
||||||
* 获取知识库成员角色对应文本
|
|
||||||
* @param role 实例数据的 role 字段
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
var getWikiUserRoleText = function (role) {
|
|
||||||
return exports.WIKI_USER_ROLES.find(function (d) { return d.value === role; }).label;
|
|
||||||
};
|
|
||||||
exports.getWikiUserRoleText = getWikiUserRoleText;
|
|
||||||
/**
|
/**
|
||||||
* 检查文档是否公开
|
* 检查文档是否公开
|
||||||
* @param currentStatus document 实例数据的 status 字段
|
* @param currentStatus document 实例数据的 status 字段
|
||||||
|
|
|
@ -7,3 +7,4 @@ export * from './template';
|
||||||
export * from './comment';
|
export * from './comment';
|
||||||
export * from './star';
|
export * from './star';
|
||||||
export * from './organization';
|
export * from './organization';
|
||||||
|
export * from './system';
|
||||||
|
|
|
@ -46,6 +46,15 @@ export const OrganizationApiDefinition = {
|
||||||
client: (id: IOrganization['id']) => `/organization/update/${id}`,
|
client: (id: IOrganization['id']) => `/organization/update/${id}`,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新组织基本信息
|
||||||
|
*/
|
||||||
|
deleteOrganization: {
|
||||||
|
method: 'delete' as const,
|
||||||
|
server: '/delete/:id' as const,
|
||||||
|
client: (id: IOrganization['id']) => `/organization/delete/${id}`,
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取组织成员
|
* 获取组织成员
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
export const SystemApiDefinition = {
|
||||||
|
getPublicConfig: {
|
||||||
|
method: 'get' as const,
|
||||||
|
server: '/' as const,
|
||||||
|
client: () => '/system',
|
||||||
|
},
|
||||||
|
};
|
|
@ -1,5 +1,3 @@
|
||||||
import { IUser } from '../models';
|
|
||||||
|
|
||||||
export const UserApiDefinition = {
|
export const UserApiDefinition = {
|
||||||
/**
|
/**
|
||||||
* 获取用户
|
* 获取用户
|
||||||
|
|
|
@ -11,39 +11,3 @@ export interface IOrganization {
|
||||||
createUserId: IUser['id'];
|
createUserId: IUser['id'];
|
||||||
isPersonal: boolean;
|
isPersonal: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
// /**
|
|
||||||
// * 创建组织数据定义
|
|
||||||
// */
|
|
||||||
// export interface CreateOrganizationDto {
|
|
||||||
// name: string;
|
|
||||||
// description: string;
|
|
||||||
// logo: string;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// export enum OrganizationAuthEnum {
|
|
||||||
// superAdmin = 'superAdmin',
|
|
||||||
// admin = 'admin',
|
|
||||||
// member = 'member',
|
|
||||||
// noAccess = 'noAccess',
|
|
||||||
// }
|
|
||||||
|
|
||||||
// export const AuthEnumTextMap = {
|
|
||||||
// [OrganizationAuthEnum.superAdmin]: '超级管理员',
|
|
||||||
// [OrganizationAuthEnum.admin]: '管理员',
|
|
||||||
// [OrganizationAuthEnum.member]: '成员',
|
|
||||||
// [OrganizationAuthEnum.noAccess]: '无权限',
|
|
||||||
// };
|
|
||||||
|
|
||||||
// export const OrganizationAuthEnumArray = Object.keys(AuthEnumTextMap).map((value) => ({
|
|
||||||
// label: AuthEnumTextMap[value],
|
|
||||||
// value,
|
|
||||||
// }));
|
|
||||||
|
|
||||||
// export interface IOrganizationAuth {
|
|
||||||
// id: string;
|
|
||||||
// auth: OrganizationAuthEnum;
|
|
||||||
// organizationId: IOrganization['id'];
|
|
||||||
// createdAt: string;
|
|
||||||
// updatedAt: string;
|
|
||||||
// }
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
export interface ISystemConfig {
|
export interface ISystemConfig {
|
||||||
isSystemLocked: boolean;
|
isSystemLocked: boolean;
|
||||||
|
enableEmailVerify: boolean;
|
||||||
emailServiceHost: string;
|
emailServiceHost: string;
|
||||||
emailServicePassword: string;
|
emailServicePassword: string;
|
||||||
emailServicePort: string;
|
emailServicePort: string;
|
||||||
|
|
|
@ -1,12 +1,3 @@
|
||||||
/**
|
|
||||||
* 用户角色枚举
|
|
||||||
*/
|
|
||||||
export enum UserRole {
|
|
||||||
normal = 'normal',
|
|
||||||
admin = 'admin',
|
|
||||||
superadmin = 'superadmin',
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 用户状态枚举
|
* 用户状态枚举
|
||||||
*/
|
*/
|
||||||
|
@ -24,7 +15,6 @@ export interface IUser {
|
||||||
password?: string;
|
password?: string;
|
||||||
avatar?: string;
|
avatar?: string;
|
||||||
email?: string;
|
email?: string;
|
||||||
role: UserRole;
|
|
||||||
status: UserStatus;
|
status: UserStatus;
|
||||||
isSystemAdmin?: boolean;
|
isSystemAdmin?: boolean;
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,23 +10,6 @@ export enum WikiStatus {
|
||||||
public = 'public',
|
public = 'public',
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 知识库成员状态枚举
|
|
||||||
*/
|
|
||||||
export enum WikiUserStatus {
|
|
||||||
applying = 'applying',
|
|
||||||
inviting = 'inviting',
|
|
||||||
normal = 'normal',
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 知识库成员角色枚举
|
|
||||||
*/
|
|
||||||
export enum WikiUserRole {
|
|
||||||
normal = 'normal',
|
|
||||||
admin = 'admin',
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 知识库数据定义
|
* 知识库数据定义
|
||||||
*/
|
*/
|
||||||
|
@ -43,12 +26,3 @@ export interface IWiki {
|
||||||
createdAt: Date;
|
createdAt: Date;
|
||||||
updatedAt: Date;
|
updatedAt: Date;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 知识库成员数据定义
|
|
||||||
*/
|
|
||||||
export interface IWikiUser extends IUser {
|
|
||||||
userRole: WikiUserRole;
|
|
||||||
userStatus: WikiUserStatus;
|
|
||||||
isCreator: boolean;
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { WikiStatus, WikiUserRole, DocumentStatus, IWiki, IDocument } from './models';
|
import { WikiStatus, DocumentStatus, IWiki, IDocument } from './models';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 知识库状态列表数据
|
* 知识库状态列表数据
|
||||||
|
@ -48,7 +48,7 @@ export const DOCUMENT_STATUS = [
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const getWikiStatusText = (wiki: IWiki): string => {
|
export const getWikiStatusText = (wiki: IWiki): string => {
|
||||||
return WIKI_STATUS_LIST.find((t) => t.value === wiki.status).label;
|
return WIKI_STATUS_LIST.find((t) => t.value === wiki.status)!.label;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -58,15 +58,6 @@ export const getWikiStatusText = (wiki: IWiki): string => {
|
||||||
*/
|
*/
|
||||||
export const isPublicWiki = (currentStatus: IWiki['status']) => currentStatus === WikiStatus.public;
|
export const isPublicWiki = (currentStatus: IWiki['status']) => currentStatus === WikiStatus.public;
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取知识库成员角色对应文本
|
|
||||||
* @param role 实例数据的 role 字段
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
export const getWikiUserRoleText = (role: WikiUserRole) => {
|
|
||||||
return WIKI_USER_ROLES.find((d) => d.value === role).label;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 检查文档是否公开
|
* 检查文档是否公开
|
||||||
* @param currentStatus document 实例数据的 status 字段
|
* @param currentStatus document 实例数据的 status 字段
|
||||||
|
|
|
@ -1,18 +1,13 @@
|
||||||
import { AuthEntity } from '@entities/auth.entity';
|
import { AuthEntity } from '@entities/auth.entity';
|
||||||
import { CommentEntity } from '@entities/comment.entity';
|
import { CommentEntity } from '@entities/comment.entity';
|
||||||
import { DocumentEntity } from '@entities/document.entity';
|
import { DocumentEntity } from '@entities/document.entity';
|
||||||
// import { DocumentUserEntity } from '@entities/document-user.entity';
|
|
||||||
import { MessageEntity } from '@entities/message.entity';
|
import { MessageEntity } from '@entities/message.entity';
|
||||||
import { OrganizationEntity } from '@entities/organization.entity';
|
import { OrganizationEntity } from '@entities/organization.entity';
|
||||||
// import { OrganizationUserEntity } from '@entities/organization-user.entity';
|
|
||||||
import { StarEntity } from '@entities/star.entity';
|
import { StarEntity } from '@entities/star.entity';
|
||||||
import { SystemEntity } from '@entities/system.entity';
|
import { SystemEntity } from '@entities/system.entity';
|
||||||
import { TemplateEntity } from '@entities/template.entity';
|
import { TemplateEntity } from '@entities/template.entity';
|
||||||
import { UserEntity } from '@entities/user.entity';
|
import { UserEntity } from '@entities/user.entity';
|
||||||
import { VerifyEntity } from '@entities/verify.entity';
|
|
||||||
import { ViewEntity } from '@entities/view.entity';
|
|
||||||
import { WikiEntity } from '@entities/wiki.entity';
|
import { WikiEntity } from '@entities/wiki.entity';
|
||||||
// import { WikiUserEntity } from '@entities/wiki-user.entity';
|
|
||||||
import { IS_PRODUCTION } from '@helpers/env.helper';
|
import { IS_PRODUCTION } from '@helpers/env.helper';
|
||||||
import { getLogFileName, ONE_DAY } from '@helpers/log.helper';
|
import { getLogFileName, ONE_DAY } from '@helpers/log.helper';
|
||||||
import { AuthModule } from '@modules/auth.module';
|
import { AuthModule } from '@modules/auth.module';
|
||||||
|
@ -42,17 +37,12 @@ const ENTITIES = [
|
||||||
UserEntity,
|
UserEntity,
|
||||||
AuthEntity,
|
AuthEntity,
|
||||||
OrganizationEntity,
|
OrganizationEntity,
|
||||||
// OrganizationUserEntity,
|
|
||||||
WikiEntity,
|
WikiEntity,
|
||||||
// WikiUserEntity,
|
|
||||||
DocumentEntity,
|
DocumentEntity,
|
||||||
// DocumentUserEntity,
|
|
||||||
StarEntity,
|
StarEntity,
|
||||||
CommentEntity,
|
CommentEntity,
|
||||||
MessageEntity,
|
MessageEntity,
|
||||||
TemplateEntity,
|
TemplateEntity,
|
||||||
ViewEntity,
|
|
||||||
VerifyEntity,
|
|
||||||
SystemEntity,
|
SystemEntity,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
export enum RedisDBEnum {
|
export enum RedisDBEnum {
|
||||||
documentVersion = 0,
|
documentVersion = 0,
|
||||||
view = 1,
|
view = 1,
|
||||||
|
verify = 2,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
import { OperateUserAuthDto } from '@dtos/auth.dto';
|
import { OperateUserAuthDto } from '@dtos/auth.dto';
|
||||||
import { CreateDocumentDto } from '@dtos/create-document.dto';
|
import { CreateDocumentDto } from '@dtos/create-document.dto';
|
||||||
import { DocAuthDto } from '@dtos/doc-auth.dto';
|
|
||||||
import { ShareDocumentDto } from '@dtos/share-document.dto';
|
import { ShareDocumentDto } from '@dtos/share-document.dto';
|
||||||
import { UpdateDocumentDto } from '@dtos/update-document.dto';
|
import { UpdateDocumentDto } from '@dtos/update-document.dto';
|
||||||
// import { CheckDocumentAuthority, DocumentAuthorityGuard } from '@guard/document-auth.guard';
|
|
||||||
import { CheckDocumentStatus, DocumentStatusGuard } from '@guard/document-status.guard';
|
import { CheckDocumentStatus, DocumentStatusGuard } from '@guard/document-status.guard';
|
||||||
import { JwtGuard } from '@guard/jwt.guard';
|
import { JwtGuard } from '@guard/jwt.guard';
|
||||||
import {
|
import {
|
||||||
|
@ -23,10 +21,9 @@ import {
|
||||||
UseInterceptors,
|
UseInterceptors,
|
||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
import { DocumentService } from '@services/document.service';
|
import { DocumentService } from '@services/document.service';
|
||||||
import { DocumentApiDefinition, DocumentStatus } from '@think/domains';
|
import { DocumentApiDefinition, DocumentStatus, IPagination } from '@think/domains';
|
||||||
|
|
||||||
@Controller('document')
|
@Controller('document')
|
||||||
// @UseGuards(DocumentAuthorityGuard)
|
|
||||||
@UseGuards(DocumentStatusGuard)
|
@UseGuards(DocumentStatusGuard)
|
||||||
export class DocumentController {
|
export class DocumentController {
|
||||||
constructor(private readonly documentService: DocumentService) {}
|
constructor(private readonly documentService: DocumentService) {}
|
||||||
|
@ -78,7 +75,6 @@ export class DocumentController {
|
||||||
@UseInterceptors(ClassSerializerInterceptor)
|
@UseInterceptors(ClassSerializerInterceptor)
|
||||||
@Get(DocumentApiDefinition.getDetailById.server)
|
@Get(DocumentApiDefinition.getDetailById.server)
|
||||||
@HttpCode(HttpStatus.OK)
|
@HttpCode(HttpStatus.OK)
|
||||||
// @CheckDocumentAuthority('readable')
|
|
||||||
@UseGuards(JwtGuard)
|
@UseGuards(JwtGuard)
|
||||||
async getDocumentDetail(@Request() req, @Param('id') documentId) {
|
async getDocumentDetail(@Request() req, @Param('id') documentId) {
|
||||||
return await this.documentService.getDocumentDetail(req.user, documentId);
|
return await this.documentService.getDocumentDetail(req.user, documentId);
|
||||||
|
@ -94,7 +90,6 @@ export class DocumentController {
|
||||||
@UseInterceptors(ClassSerializerInterceptor)
|
@UseInterceptors(ClassSerializerInterceptor)
|
||||||
@Patch(DocumentApiDefinition.updateById.server)
|
@Patch(DocumentApiDefinition.updateById.server)
|
||||||
@HttpCode(HttpStatus.OK)
|
@HttpCode(HttpStatus.OK)
|
||||||
// @CheckDocumentAuthority('editable')
|
|
||||||
@UseGuards(JwtGuard)
|
@UseGuards(JwtGuard)
|
||||||
async updateDocument(@Request() req, @Param('id') documentId, @Body() dto: UpdateDocumentDto) {
|
async updateDocument(@Request() req, @Param('id') documentId, @Body() dto: UpdateDocumentDto) {
|
||||||
return await this.documentService.updateDocument(req.user, documentId, dto);
|
return await this.documentService.updateDocument(req.user, documentId, dto);
|
||||||
|
@ -109,7 +104,6 @@ export class DocumentController {
|
||||||
@UseInterceptors(ClassSerializerInterceptor)
|
@UseInterceptors(ClassSerializerInterceptor)
|
||||||
@Get(DocumentApiDefinition.getVersionById.server)
|
@Get(DocumentApiDefinition.getVersionById.server)
|
||||||
@HttpCode(HttpStatus.OK)
|
@HttpCode(HttpStatus.OK)
|
||||||
// @CheckDocumentAuthority('readable')
|
|
||||||
@UseGuards(JwtGuard)
|
@UseGuards(JwtGuard)
|
||||||
async getDocumentVersion(@Request() req, @Param('id') documentId) {
|
async getDocumentVersion(@Request() req, @Param('id') documentId) {
|
||||||
return await this.documentService.getDocumentVersion(req.user, documentId);
|
return await this.documentService.getDocumentVersion(req.user, documentId);
|
||||||
|
@ -124,10 +118,9 @@ export class DocumentController {
|
||||||
@UseInterceptors(ClassSerializerInterceptor)
|
@UseInterceptors(ClassSerializerInterceptor)
|
||||||
@Get(DocumentApiDefinition.getMemberById.server)
|
@Get(DocumentApiDefinition.getMemberById.server)
|
||||||
@HttpCode(HttpStatus.OK)
|
@HttpCode(HttpStatus.OK)
|
||||||
// @CheckDocumentAuthority('readable')
|
|
||||||
@UseGuards(JwtGuard)
|
@UseGuards(JwtGuard)
|
||||||
async getDocUsers(@Request() req, @Param('id') documentId) {
|
async getDocUsers(@Request() req, @Param('id') documentId, @Query() pagination: IPagination) {
|
||||||
return await this.documentService.getDocUsers(req.user, documentId);
|
return await this.documentService.getDocUsers(req.user, documentId, pagination);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -140,7 +133,6 @@ export class DocumentController {
|
||||||
@UseInterceptors(ClassSerializerInterceptor)
|
@UseInterceptors(ClassSerializerInterceptor)
|
||||||
@Post(DocumentApiDefinition.addMemberById.server)
|
@Post(DocumentApiDefinition.addMemberById.server)
|
||||||
@HttpCode(HttpStatus.OK)
|
@HttpCode(HttpStatus.OK)
|
||||||
// @CheckDocumentAuthority('createUser')
|
|
||||||
@UseGuards(JwtGuard)
|
@UseGuards(JwtGuard)
|
||||||
async addDocUser(@Request() req, @Param('id') documentId, @Body() dto: OperateUserAuthDto) {
|
async addDocUser(@Request() req, @Param('id') documentId, @Body() dto: OperateUserAuthDto) {
|
||||||
return await this.documentService.addDocUser(req.user, documentId, dto);
|
return await this.documentService.addDocUser(req.user, documentId, dto);
|
||||||
|
@ -156,7 +148,6 @@ export class DocumentController {
|
||||||
@UseInterceptors(ClassSerializerInterceptor)
|
@UseInterceptors(ClassSerializerInterceptor)
|
||||||
@Patch(DocumentApiDefinition.updateMemberById.server)
|
@Patch(DocumentApiDefinition.updateMemberById.server)
|
||||||
@HttpCode(HttpStatus.OK)
|
@HttpCode(HttpStatus.OK)
|
||||||
// @CheckDocumentAuthority('createUser')
|
|
||||||
@UseGuards(JwtGuard)
|
@UseGuards(JwtGuard)
|
||||||
async updateDocUser(@Request() req, @Param('id') documentId, @Body() dto: OperateUserAuthDto) {
|
async updateDocUser(@Request() req, @Param('id') documentId, @Body() dto: OperateUserAuthDto) {
|
||||||
return await this.documentService.updateDocUser(req.user, documentId, dto);
|
return await this.documentService.updateDocUser(req.user, documentId, dto);
|
||||||
|
@ -172,7 +163,6 @@ export class DocumentController {
|
||||||
@UseInterceptors(ClassSerializerInterceptor)
|
@UseInterceptors(ClassSerializerInterceptor)
|
||||||
@Post(DocumentApiDefinition.deleteMemberById.server)
|
@Post(DocumentApiDefinition.deleteMemberById.server)
|
||||||
@HttpCode(HttpStatus.OK)
|
@HttpCode(HttpStatus.OK)
|
||||||
// @CheckDocumentAuthority('createUser')
|
|
||||||
@UseGuards(JwtGuard)
|
@UseGuards(JwtGuard)
|
||||||
async deleteDocUser(@Request() req, @Param('id') documentId, @Body() dto: OperateUserAuthDto) {
|
async deleteDocUser(@Request() req, @Param('id') documentId, @Body() dto: OperateUserAuthDto) {
|
||||||
return await this.documentService.deleteDocUser(req.user, documentId, dto);
|
return await this.documentService.deleteDocUser(req.user, documentId, dto);
|
||||||
|
@ -187,7 +177,6 @@ export class DocumentController {
|
||||||
@UseInterceptors(ClassSerializerInterceptor)
|
@UseInterceptors(ClassSerializerInterceptor)
|
||||||
@Post(DocumentApiDefinition.getChildren.server)
|
@Post(DocumentApiDefinition.getChildren.server)
|
||||||
@HttpCode(HttpStatus.OK)
|
@HttpCode(HttpStatus.OK)
|
||||||
// @CheckDocumentAuthority('readable')
|
|
||||||
@UseGuards(JwtGuard)
|
@UseGuards(JwtGuard)
|
||||||
async getChildrenDocuments(@Request() req, @Body() data) {
|
async getChildrenDocuments(@Request() req, @Body() data) {
|
||||||
return await this.documentService.getChildrenDocuments(req.user, data);
|
return await this.documentService.getChildrenDocuments(req.user, data);
|
||||||
|
@ -202,7 +191,6 @@ export class DocumentController {
|
||||||
@UseInterceptors(ClassSerializerInterceptor)
|
@UseInterceptors(ClassSerializerInterceptor)
|
||||||
@Delete(DocumentApiDefinition.deleteById.server)
|
@Delete(DocumentApiDefinition.deleteById.server)
|
||||||
@HttpCode(HttpStatus.OK)
|
@HttpCode(HttpStatus.OK)
|
||||||
// @CheckDocumentAuthority('createUser')
|
|
||||||
@UseGuards(JwtGuard)
|
@UseGuards(JwtGuard)
|
||||||
async deleteDocument(@Request() req, @Param('id') documentId) {
|
async deleteDocument(@Request() req, @Param('id') documentId) {
|
||||||
return await this.documentService.deleteDocument(req.user, documentId);
|
return await this.documentService.deleteDocument(req.user, documentId);
|
||||||
|
@ -218,7 +206,6 @@ export class DocumentController {
|
||||||
@UseInterceptors(ClassSerializerInterceptor)
|
@UseInterceptors(ClassSerializerInterceptor)
|
||||||
@Post(DocumentApiDefinition.shareById.server)
|
@Post(DocumentApiDefinition.shareById.server)
|
||||||
@HttpCode(HttpStatus.OK)
|
@HttpCode(HttpStatus.OK)
|
||||||
// @CheckDocumentAuthority('editable')
|
|
||||||
@UseGuards(JwtGuard)
|
@UseGuards(JwtGuard)
|
||||||
async shareDocument(@Request() req, @Param('id') documentId, @Body() dto: ShareDocumentDto) {
|
async shareDocument(@Request() req, @Param('id') documentId, @Body() dto: ShareDocumentDto) {
|
||||||
return await this.documentService.shareDocument(req.user, documentId, dto);
|
return await this.documentService.shareDocument(req.user, documentId, dto);
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import { OperateUserAuthDto } from '@dtos/auth.dto';
|
import { OperateUserAuthDto } from '@dtos/auth.dto';
|
||||||
import { CreateOrganizationDto } from '@dtos/organization.dto';
|
import { CreateOrganizationDto } from '@dtos/organization.dto';
|
||||||
// import { OrganizationUserDto } from '@dtos/organization-user.dto';
|
|
||||||
import { JwtGuard } from '@guard/jwt.guard';
|
import { JwtGuard } from '@guard/jwt.guard';
|
||||||
import {
|
import {
|
||||||
Body,
|
Body,
|
||||||
|
@ -13,12 +12,13 @@ import {
|
||||||
Param,
|
Param,
|
||||||
Patch,
|
Patch,
|
||||||
Post,
|
Post,
|
||||||
|
Query,
|
||||||
Request,
|
Request,
|
||||||
UseGuards,
|
UseGuards,
|
||||||
UseInterceptors,
|
UseInterceptors,
|
||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
import { OrganizationService } from '@services/organization.service';
|
import { OrganizationService } from '@services/organization.service';
|
||||||
import { OrganizationApiDefinition } from '@think/domains';
|
import { IPagination, OrganizationApiDefinition } from '@think/domains';
|
||||||
|
|
||||||
@Controller('organization')
|
@Controller('organization')
|
||||||
export class OrganizationController {
|
export class OrganizationController {
|
||||||
|
@ -91,6 +91,20 @@ export class OrganizationController {
|
||||||
return await this.organizationService.getOrganizationDetail(req.user, id);
|
return await this.organizationService.getOrganizationDetail(req.user, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除组织
|
||||||
|
* @param req
|
||||||
|
* @param id
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
@UseInterceptors(ClassSerializerInterceptor)
|
||||||
|
@Delete(OrganizationApiDefinition.deleteOrganization.server)
|
||||||
|
@HttpCode(HttpStatus.OK)
|
||||||
|
@UseGuards(JwtGuard)
|
||||||
|
async deleteWiki(@Request() req, @Param('id') id) {
|
||||||
|
return await this.organizationService.deleteOrganization(req.user, id);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取组织成员
|
* 获取组织成员
|
||||||
* @param req
|
* @param req
|
||||||
|
@ -100,8 +114,8 @@ export class OrganizationController {
|
||||||
@Get(OrganizationApiDefinition.getMembers.server)
|
@Get(OrganizationApiDefinition.getMembers.server)
|
||||||
@HttpCode(HttpStatus.OK)
|
@HttpCode(HttpStatus.OK)
|
||||||
@UseGuards(JwtGuard)
|
@UseGuards(JwtGuard)
|
||||||
async getMembers(@Request() req, @Param('id') id) {
|
async getMembers(@Request() req, @Param('id') id, @Query() pagination: IPagination) {
|
||||||
return await this.organizationService.getMembers(req.user, id);
|
return await this.organizationService.getMembers(req.user, id, pagination);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,7 +1,15 @@
|
||||||
import { Controller } from '@nestjs/common';
|
import { ClassSerializerInterceptor, Controller, Get, HttpCode, HttpStatus, UseInterceptors } from '@nestjs/common';
|
||||||
import { SystemService } from '@services/system.service';
|
import { SystemService } from '@services/system.service';
|
||||||
|
import { SystemApiDefinition } from '@think/domains';
|
||||||
|
|
||||||
@Controller('system')
|
@Controller('system')
|
||||||
export class SystemController {
|
export class SystemController {
|
||||||
constructor(private readonly systemService: SystemService) {}
|
constructor(private readonly systemService: SystemService) {}
|
||||||
|
|
||||||
|
@UseInterceptors(ClassSerializerInterceptor)
|
||||||
|
@Get(SystemApiDefinition.getPublicConfig.server)
|
||||||
|
@HttpCode(HttpStatus.CREATED)
|
||||||
|
async getPublicConfig() {
|
||||||
|
return await this.systemService.getPublicConfig();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,6 @@ import { ShareWikiDto } from '@dtos/share-wiki.dto';
|
||||||
import { UpdateWikiDto } from '@dtos/update-wiki.dto';
|
import { UpdateWikiDto } from '@dtos/update-wiki.dto';
|
||||||
import { JwtGuard } from '@guard/jwt.guard';
|
import { JwtGuard } from '@guard/jwt.guard';
|
||||||
import { CheckWikiStatus, WikiStatusGuard } from '@guard/wiki-status.guard';
|
import { CheckWikiStatus, WikiStatusGuard } from '@guard/wiki-status.guard';
|
||||||
// import { CheckWikiUserRole, WikiUserRoleGuard } from '@guard/wiki-user.guard';
|
|
||||||
import {
|
import {
|
||||||
Body,
|
Body,
|
||||||
ClassSerializerInterceptor,
|
ClassSerializerInterceptor,
|
||||||
|
@ -22,7 +21,7 @@ import {
|
||||||
UseInterceptors,
|
UseInterceptors,
|
||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
import { WikiService } from '@services/wiki.service';
|
import { WikiService } from '@services/wiki.service';
|
||||||
import { IPagination, WikiApiDefinition, WikiStatus, WikiUserRole } from '@think/domains';
|
import { IPagination, WikiApiDefinition, WikiStatus } from '@think/domains';
|
||||||
|
|
||||||
@Controller('wiki')
|
@Controller('wiki')
|
||||||
export class WikiController {
|
export class WikiController {
|
||||||
|
@ -93,8 +92,6 @@ export class WikiController {
|
||||||
@UseInterceptors(ClassSerializerInterceptor)
|
@UseInterceptors(ClassSerializerInterceptor)
|
||||||
@Get(WikiApiDefinition.getHomeDocumentById.server)
|
@Get(WikiApiDefinition.getHomeDocumentById.server)
|
||||||
@HttpCode(HttpStatus.OK)
|
@HttpCode(HttpStatus.OK)
|
||||||
// @CheckWikiUserRole()
|
|
||||||
// @UseGuards(WikiUserRoleGuard)
|
|
||||||
@UseGuards(JwtGuard)
|
@UseGuards(JwtGuard)
|
||||||
async getWikiHomeDocument(@Request() req, @Param('id') wikiId) {
|
async getWikiHomeDocument(@Request() req, @Param('id') wikiId) {
|
||||||
return await this.wikiService.getWikiHomeDocument(req.user, wikiId);
|
return await this.wikiService.getWikiHomeDocument(req.user, wikiId);
|
||||||
|
@ -109,8 +106,6 @@ export class WikiController {
|
||||||
@UseInterceptors(ClassSerializerInterceptor)
|
@UseInterceptors(ClassSerializerInterceptor)
|
||||||
@Get(WikiApiDefinition.getTocsById.server)
|
@Get(WikiApiDefinition.getTocsById.server)
|
||||||
@HttpCode(HttpStatus.OK)
|
@HttpCode(HttpStatus.OK)
|
||||||
// @CheckWikiUserRole()
|
|
||||||
// @UseGuards(WikiUserRoleGuard)
|
|
||||||
@UseGuards(JwtGuard)
|
@UseGuards(JwtGuard)
|
||||||
async getWikiTocs(@Request() req, @Param('id') wikiId) {
|
async getWikiTocs(@Request() req, @Param('id') wikiId) {
|
||||||
return await this.wikiService.getWikiTocs(req.user, wikiId);
|
return await this.wikiService.getWikiTocs(req.user, wikiId);
|
||||||
|
@ -126,29 +121,11 @@ export class WikiController {
|
||||||
@UseInterceptors(ClassSerializerInterceptor)
|
@UseInterceptors(ClassSerializerInterceptor)
|
||||||
@Patch(WikiApiDefinition.updateTocsById.server)
|
@Patch(WikiApiDefinition.updateTocsById.server)
|
||||||
@HttpCode(HttpStatus.OK)
|
@HttpCode(HttpStatus.OK)
|
||||||
// @CheckWikiUserRole()
|
|
||||||
// @UseGuards(WikiUserRoleGuard)
|
|
||||||
@UseGuards(JwtGuard)
|
@UseGuards(JwtGuard)
|
||||||
async orderWikiTocs(@Body() relations) {
|
async orderWikiTocs(@Body() relations) {
|
||||||
return await this.wikiService.orderWikiTocs(relations);
|
return await this.wikiService.orderWikiTocs(relations);
|
||||||
}
|
}
|
||||||
|
|
||||||
// /**
|
|
||||||
// * 获取知识库所有文档
|
|
||||||
// * @param req
|
|
||||||
// * @param wikiId
|
|
||||||
// * @returns
|
|
||||||
// */
|
|
||||||
// @UseInterceptors(ClassSerializerInterceptor)
|
|
||||||
// @Get(WikiApiDefinition.getDocumentsById.server)
|
|
||||||
// @HttpCode(HttpStatus.OK)
|
|
||||||
// @CheckWikiUserRole()
|
|
||||||
// @UseGuards(WikiUserRoleGuard)
|
|
||||||
// @UseGuards(JwtGuard)
|
|
||||||
// async getWikiDocs(@Request() req, @Param('id') wikiId) {
|
|
||||||
// return await this.wikiService.getWikiDocs(req.user, wikiId);
|
|
||||||
// }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取知识库详情
|
* 获取知识库详情
|
||||||
* @param req
|
* @param req
|
||||||
|
@ -158,8 +135,6 @@ export class WikiController {
|
||||||
@UseInterceptors(ClassSerializerInterceptor)
|
@UseInterceptors(ClassSerializerInterceptor)
|
||||||
@Get(WikiApiDefinition.getDetailById.server)
|
@Get(WikiApiDefinition.getDetailById.server)
|
||||||
@HttpCode(HttpStatus.OK)
|
@HttpCode(HttpStatus.OK)
|
||||||
// @CheckWikiUserRole()
|
|
||||||
// @UseGuards(WikiUserRoleGuard)
|
|
||||||
@UseGuards(JwtGuard)
|
@UseGuards(JwtGuard)
|
||||||
async getWikiDetail(@Request() req, @Param('id') wikiId) {
|
async getWikiDetail(@Request() req, @Param('id') wikiId) {
|
||||||
return await this.wikiService.getWikiDetail(req.user, wikiId);
|
return await this.wikiService.getWikiDetail(req.user, wikiId);
|
||||||
|
@ -167,7 +142,6 @@ export class WikiController {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 修改知识库
|
* 修改知识库
|
||||||
* 只有管理员可操作
|
|
||||||
* @param req
|
* @param req
|
||||||
* @param wikiId
|
* @param wikiId
|
||||||
* @param dto
|
* @param dto
|
||||||
|
@ -176,8 +150,6 @@ export class WikiController {
|
||||||
@UseInterceptors(ClassSerializerInterceptor)
|
@UseInterceptors(ClassSerializerInterceptor)
|
||||||
@Patch(WikiApiDefinition.updateById.server)
|
@Patch(WikiApiDefinition.updateById.server)
|
||||||
@HttpCode(HttpStatus.OK)
|
@HttpCode(HttpStatus.OK)
|
||||||
// @CheckWikiUserRole(WikiUserRole.admin)
|
|
||||||
// @UseGuards(WikiUserRoleGuard)
|
|
||||||
@UseGuards(JwtGuard)
|
@UseGuards(JwtGuard)
|
||||||
async updateWiki(@Request() req, @Param('id') wikiId, @Body() dto: UpdateWikiDto) {
|
async updateWiki(@Request() req, @Param('id') wikiId, @Body() dto: UpdateWikiDto) {
|
||||||
return await this.wikiService.updateWiki(req.user, wikiId, dto);
|
return await this.wikiService.updateWiki(req.user, wikiId, dto);
|
||||||
|
@ -185,7 +157,6 @@ export class WikiController {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 删除知识库
|
* 删除知识库
|
||||||
* 只有管理员可操作
|
|
||||||
* @param req
|
* @param req
|
||||||
* @param wikiId
|
* @param wikiId
|
||||||
* @returns
|
* @returns
|
||||||
|
@ -193,8 +164,6 @@ export class WikiController {
|
||||||
@UseInterceptors(ClassSerializerInterceptor)
|
@UseInterceptors(ClassSerializerInterceptor)
|
||||||
@Delete(WikiApiDefinition.deleteById.server)
|
@Delete(WikiApiDefinition.deleteById.server)
|
||||||
@HttpCode(HttpStatus.OK)
|
@HttpCode(HttpStatus.OK)
|
||||||
// @CheckWikiUserRole(WikiUserRole.admin)
|
|
||||||
// @UseGuards(WikiUserRoleGuard)
|
|
||||||
@UseGuards(JwtGuard)
|
@UseGuards(JwtGuard)
|
||||||
async deleteWiki(@Request() req, @Param('id') wikiId) {
|
async deleteWiki(@Request() req, @Param('id') wikiId) {
|
||||||
return await this.wikiService.deleteWiki(req.user, wikiId);
|
return await this.wikiService.deleteWiki(req.user, wikiId);
|
||||||
|
@ -202,7 +171,6 @@ export class WikiController {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查看知识库成员
|
* 查看知识库成员
|
||||||
* 只有管理员可操作
|
|
||||||
* @param req
|
* @param req
|
||||||
* @param wikiId
|
* @param wikiId
|
||||||
* @returns
|
* @returns
|
||||||
|
@ -210,16 +178,13 @@ export class WikiController {
|
||||||
@UseInterceptors(ClassSerializerInterceptor)
|
@UseInterceptors(ClassSerializerInterceptor)
|
||||||
@Get(WikiApiDefinition.getMemberById.server)
|
@Get(WikiApiDefinition.getMemberById.server)
|
||||||
@HttpCode(HttpStatus.OK)
|
@HttpCode(HttpStatus.OK)
|
||||||
// @CheckWikiUserRole(WikiUserRole.admin)
|
|
||||||
// @UseGuards(WikiUserRoleGuard)
|
|
||||||
@UseGuards(JwtGuard)
|
@UseGuards(JwtGuard)
|
||||||
async getWikiUsers(@Request() req, @Param('id') wikiId) {
|
async getWikiUsers(@Request() req, @Param('id') wikiId, @Query() pagination: IPagination) {
|
||||||
return await this.wikiService.getWikiUsers(req.user, wikiId);
|
return await this.wikiService.getWikiUsers(req.user, wikiId, pagination);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 添加知识库成员
|
* 添加知识库成员
|
||||||
* 只有管理员可操作
|
|
||||||
* @param req
|
* @param req
|
||||||
* @param wikiId
|
* @param wikiId
|
||||||
* @param dto
|
* @param dto
|
||||||
|
@ -228,8 +193,6 @@ export class WikiController {
|
||||||
@UseInterceptors(ClassSerializerInterceptor)
|
@UseInterceptors(ClassSerializerInterceptor)
|
||||||
@Post(WikiApiDefinition.addMemberById.server)
|
@Post(WikiApiDefinition.addMemberById.server)
|
||||||
@HttpCode(HttpStatus.OK)
|
@HttpCode(HttpStatus.OK)
|
||||||
// @CheckWikiUserRole(WikiUserRole.admin)
|
|
||||||
// @UseGuards(WikiUserRoleGuard)
|
|
||||||
@UseGuards(JwtGuard)
|
@UseGuards(JwtGuard)
|
||||||
async addWikiUser(@Request() req, @Param('id') wikiId, @Body() dto: OperateUserAuthDto) {
|
async addWikiUser(@Request() req, @Param('id') wikiId, @Body() dto: OperateUserAuthDto) {
|
||||||
return await this.wikiService.addWikiUser(req.user, wikiId, dto);
|
return await this.wikiService.addWikiUser(req.user, wikiId, dto);
|
||||||
|
@ -237,7 +200,6 @@ export class WikiController {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 更新知识库成员(一般为角色操作)
|
* 更新知识库成员(一般为角色操作)
|
||||||
* 只有管理员可操作
|
|
||||||
* @param req
|
* @param req
|
||||||
* @param wikiId
|
* @param wikiId
|
||||||
* @param dto
|
* @param dto
|
||||||
|
@ -253,7 +215,6 @@ export class WikiController {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 删除知识库成员
|
* 删除知识库成员
|
||||||
* 只有管理员可操作
|
|
||||||
* @param req
|
* @param req
|
||||||
* @param wikiId
|
* @param wikiId
|
||||||
* @param dto
|
* @param dto
|
||||||
|
@ -269,7 +230,6 @@ export class WikiController {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 分享(或关闭分享)知识库
|
* 分享(或关闭分享)知识库
|
||||||
* 只有管理员可操作
|
|
||||||
* @param req
|
* @param req
|
||||||
* @param wikiId
|
* @param wikiId
|
||||||
* @param dto
|
* @param dto
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { IsEmail, IsNotEmpty, IsString, MaxLength, MinLength } from 'class-validator';
|
import { IsEmail, IsNotEmpty, IsOptional, IsString, MaxLength, MinLength } from 'class-validator';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 用户注册
|
* 用户注册
|
||||||
|
@ -22,8 +22,8 @@ export class RegisterUserDto {
|
||||||
|
|
||||||
@MinLength(5, { message: '邮箱验证码至少5个字符' })
|
@MinLength(5, { message: '邮箱验证码至少5个字符' })
|
||||||
@IsString({ message: '邮箱验证码错误(正确类型为:String)' })
|
@IsString({ message: '邮箱验证码错误(正确类型为:String)' })
|
||||||
@IsNotEmpty({ message: '邮箱验证码不能为空' })
|
@IsOptional({ message: '邮箱验证码不能为空' })
|
||||||
verifyCode: string;
|
verifyCode?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,15 +0,0 @@
|
||||||
import { IsBoolean, IsString } from 'class-validator';
|
|
||||||
|
|
||||||
export class DocAuthDto {
|
|
||||||
@IsString()
|
|
||||||
readonly documentId: string;
|
|
||||||
|
|
||||||
@IsString()
|
|
||||||
readonly userName: string;
|
|
||||||
|
|
||||||
@IsBoolean()
|
|
||||||
readonly readable: boolean;
|
|
||||||
|
|
||||||
@IsBoolean()
|
|
||||||
readonly editable: boolean;
|
|
||||||
}
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { IsEmail, IsNotEmpty, IsString, MaxLength, MinLength } from 'class-validator';
|
import { IsNotEmpty, IsString, MaxLength, MinLength } from 'class-validator';
|
||||||
|
|
||||||
export class LoginUserDto {
|
export class LoginUserDto {
|
||||||
@IsString({ message: '用户名称类型错误(正确类型为:String)' })
|
@IsString({ message: '用户名称类型错误(正确类型为:String)' })
|
||||||
|
|
|
@ -1,12 +0,0 @@
|
||||||
// import { OrganizationAuthEnum } from '@think/domains';
|
|
||||||
// import { IsNotEmpty, IsString } from 'class-validator';
|
|
||||||
|
|
||||||
// export class OrganizationAuthDto {
|
|
||||||
// @IsString({ message: '权限类型类型错误(正确类型为:String)' })
|
|
||||||
// @IsNotEmpty({ message: '权限类型不能为空' })
|
|
||||||
// auth: OrganizationAuthEnum;
|
|
||||||
|
|
||||||
// @IsString({ message: '组织 Id 类型错误(正确类型为:String)' })
|
|
||||||
// @IsNotEmpty({ message: '组织 Id 不能为空' })
|
|
||||||
// organizationId: string;
|
|
||||||
// }
|
|
|
@ -1,10 +0,0 @@
|
||||||
// import { OrganizationAuthEnum } from '@think/domains';
|
|
||||||
// import { IsString } from 'class-validator';
|
|
||||||
|
|
||||||
// export class OrganizationUserDto {
|
|
||||||
// @IsString()
|
|
||||||
// readonly userName: string;
|
|
||||||
|
|
||||||
// @IsString()
|
|
||||||
// readonly userAuth: OrganizationAuthEnum;
|
|
||||||
// }
|
|
|
@ -1,10 +0,0 @@
|
||||||
import { WikiUserRole } from '@think/domains';
|
|
||||||
import { IsString } from 'class-validator';
|
|
||||||
|
|
||||||
export class WikiUserDto {
|
|
||||||
@IsString()
|
|
||||||
readonly userName: string;
|
|
||||||
|
|
||||||
@IsString()
|
|
||||||
readonly userRole: WikiUserRole;
|
|
||||||
}
|
|
|
@ -8,10 +8,15 @@ export class SystemEntity {
|
||||||
/**
|
/**
|
||||||
* 是否锁定系统,锁定后除系统管理员外均不可登录,同时禁止注册
|
* 是否锁定系统,锁定后除系统管理员外均不可登录,同时禁止注册
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@Column({ type: 'boolean', default: false, comment: '是否锁定系统' })
|
@Column({ type: 'boolean', default: false, comment: '是否锁定系统' })
|
||||||
isSystemLocked: boolean;
|
isSystemLocked: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 启用邮箱校验后,必须通过邮箱验证码验证后注册
|
||||||
|
*/
|
||||||
|
@Column({ type: 'boolean', default: false, comment: '是否启用邮箱校验' })
|
||||||
|
enableEmailVerify: boolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 邮箱服务地址
|
* 邮箱服务地址
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { UserRole, UserStatus } from '@think/domains';
|
import { UserStatus } from '@think/domains';
|
||||||
import * as bcrypt from 'bcryptjs';
|
import * as bcrypt from 'bcryptjs';
|
||||||
import { Exclude } from 'class-transformer';
|
import { Exclude } from 'class-transformer';
|
||||||
import { BeforeInsert, Column, CreateDateColumn, Entity, PrimaryGeneratedColumn, UpdateDateColumn } from 'typeorm';
|
import { BeforeInsert, Column, CreateDateColumn, Entity, PrimaryGeneratedColumn, UpdateDateColumn } from 'typeorm';
|
||||||
|
@ -37,14 +37,6 @@ export class UserEntity {
|
||||||
@Column({ type: 'boolean', default: false, comment: '是否为系统管理员' })
|
@Column({ type: 'boolean', default: false, comment: '是否为系统管理员' })
|
||||||
isSystemAdmin: boolean;
|
isSystemAdmin: boolean;
|
||||||
|
|
||||||
@Column({
|
|
||||||
type: 'enum',
|
|
||||||
enum: UserRole,
|
|
||||||
default: UserRole.normal,
|
|
||||||
comment: '用户角色',
|
|
||||||
})
|
|
||||||
public role: UserRole;
|
|
||||||
|
|
||||||
@Column({
|
@Column({
|
||||||
type: 'enum',
|
type: 'enum',
|
||||||
enum: UserStatus,
|
enum: UserStatus,
|
||||||
|
|
|
@ -1,27 +0,0 @@
|
||||||
import { Column, CreateDateColumn, Entity, PrimaryGeneratedColumn, UpdateDateColumn } from 'typeorm';
|
|
||||||
|
|
||||||
@Entity('verify')
|
|
||||||
export class VerifyEntity {
|
|
||||||
@PrimaryGeneratedColumn('uuid')
|
|
||||||
public id: string;
|
|
||||||
|
|
||||||
@Column({ type: 'varchar', comment: '邮箱地址' })
|
|
||||||
public email: string;
|
|
||||||
|
|
||||||
@Column({ type: 'varchar', comment: '验证码' })
|
|
||||||
public verifyCode: string;
|
|
||||||
|
|
||||||
@CreateDateColumn({
|
|
||||||
type: 'timestamp',
|
|
||||||
name: 'createdAt',
|
|
||||||
comment: '创建时间',
|
|
||||||
})
|
|
||||||
createdAt: Date;
|
|
||||||
|
|
||||||
@UpdateDateColumn({
|
|
||||||
type: 'timestamp',
|
|
||||||
name: 'updatedAt',
|
|
||||||
comment: '更新时间',
|
|
||||||
})
|
|
||||||
updatedAt: Date;
|
|
||||||
}
|
|
|
@ -1,34 +0,0 @@
|
||||||
import { Column, CreateDateColumn, Entity, PrimaryGeneratedColumn, UpdateDateColumn } from 'typeorm';
|
|
||||||
|
|
||||||
@Entity('view')
|
|
||||||
export class ViewEntity {
|
|
||||||
@PrimaryGeneratedColumn('uuid')
|
|
||||||
id: string;
|
|
||||||
|
|
||||||
@Column({ type: 'varchar', comment: '文档 Id' })
|
|
||||||
documentId: string;
|
|
||||||
|
|
||||||
// public 表示从公开渠道访问
|
|
||||||
@Column({ type: 'varchar', comment: '访问用户 Id', default: 'public' })
|
|
||||||
userId: string;
|
|
||||||
|
|
||||||
@Column({ type: 'mediumtext', default: null, charset: 'utf8mb4' })
|
|
||||||
originUserAgent: string;
|
|
||||||
|
|
||||||
@Column({ type: 'mediumtext', default: null, charset: 'utf8mb4' })
|
|
||||||
parsedUserAgent: string;
|
|
||||||
|
|
||||||
@CreateDateColumn({
|
|
||||||
type: 'timestamp',
|
|
||||||
name: 'createdAt',
|
|
||||||
comment: '创建时间',
|
|
||||||
})
|
|
||||||
createdAt: Date;
|
|
||||||
|
|
||||||
@UpdateDateColumn({
|
|
||||||
type: 'timestamp',
|
|
||||||
name: 'updatedAt',
|
|
||||||
comment: '更新时间',
|
|
||||||
})
|
|
||||||
updatedAt: Date;
|
|
||||||
}
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { CommentController } from '@controllers/comment.controller';
|
import { CommentController } from '@controllers/comment.controller';
|
||||||
import { CommentEntity } from '@entities/comment.entity';
|
import { CommentEntity } from '@entities/comment.entity';
|
||||||
|
import { AuthModule } from '@modules/auth.module';
|
||||||
import { DocumentModule } from '@modules/document.module';
|
import { DocumentModule } from '@modules/document.module';
|
||||||
import { MessageModule } from '@modules/message.module';
|
import { MessageModule } from '@modules/message.module';
|
||||||
import { UserModule } from '@modules/user.module';
|
import { UserModule } from '@modules/user.module';
|
||||||
|
@ -11,6 +12,7 @@ import { CommentService } from '@services/comment.service';
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
TypeOrmModule.forFeature([CommentEntity]),
|
TypeOrmModule.forFeature([CommentEntity]),
|
||||||
|
forwardRef(() => AuthModule),
|
||||||
forwardRef(() => UserModule),
|
forwardRef(() => UserModule),
|
||||||
forwardRef(() => WikiModule),
|
forwardRef(() => WikiModule),
|
||||||
forwardRef(() => DocumentModule),
|
forwardRef(() => DocumentModule),
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { OrganizationEntity } from '@entities/organization.entity';
|
||||||
import { AuthModule } from '@modules/auth.module';
|
import { AuthModule } from '@modules/auth.module';
|
||||||
import { MessageModule } from '@modules/message.module';
|
import { MessageModule } from '@modules/message.module';
|
||||||
import { UserModule } from '@modules/user.module';
|
import { UserModule } from '@modules/user.module';
|
||||||
|
import { WikiModule } from '@modules/wiki.module';
|
||||||
import { forwardRef, Module } from '@nestjs/common';
|
import { forwardRef, Module } from '@nestjs/common';
|
||||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||||
import { OrganizationService } from '@services/organization.service';
|
import { OrganizationService } from '@services/organization.service';
|
||||||
|
@ -13,6 +14,7 @@ import { OrganizationService } from '@services/organization.service';
|
||||||
forwardRef(() => UserModule),
|
forwardRef(() => UserModule),
|
||||||
forwardRef(() => MessageModule),
|
forwardRef(() => MessageModule),
|
||||||
forwardRef(() => AuthModule),
|
forwardRef(() => AuthModule),
|
||||||
|
forwardRef(() => WikiModule),
|
||||||
],
|
],
|
||||||
providers: [OrganizationService],
|
providers: [OrganizationService],
|
||||||
exports: [OrganizationService],
|
exports: [OrganizationService],
|
||||||
|
|
|
@ -1,12 +1,10 @@
|
||||||
import { VerifyController } from '@controllers/verify.controller';
|
import { VerifyController } from '@controllers/verify.controller';
|
||||||
import { VerifyEntity } from '@entities/verify.entity';
|
|
||||||
import { SystemModule } from '@modules/system.module';
|
import { SystemModule } from '@modules/system.module';
|
||||||
import { forwardRef, Module } from '@nestjs/common';
|
import { forwardRef, Module } from '@nestjs/common';
|
||||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
|
||||||
import { VerifyService } from '@services/verify.service';
|
import { VerifyService } from '@services/verify.service';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [TypeOrmModule.forFeature([VerifyEntity]), forwardRef(() => SystemModule)],
|
imports: [forwardRef(() => SystemModule)],
|
||||||
providers: [VerifyService],
|
providers: [VerifyService],
|
||||||
exports: [VerifyService],
|
exports: [VerifyService],
|
||||||
controllers: [VerifyController],
|
controllers: [VerifyController],
|
||||||
|
|
|
@ -1,11 +1,8 @@
|
||||||
import { ViewController } from '@controllers/view.controller';
|
import { ViewController } from '@controllers/view.controller';
|
||||||
import { ViewEntity } from '@entities/view.entity';
|
|
||||||
import { Module } from '@nestjs/common';
|
import { Module } from '@nestjs/common';
|
||||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
|
||||||
import { ViewService } from '@services/view.service';
|
import { ViewService } from '@services/view.service';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [TypeOrmModule.forFeature([ViewEntity])],
|
|
||||||
providers: [ViewService],
|
providers: [ViewService],
|
||||||
exports: [ViewService],
|
exports: [ViewService],
|
||||||
controllers: [ViewController],
|
controllers: [ViewController],
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { AuthEntity } from '@entities/auth.entity';
|
||||||
import { forwardRef, HttpException, HttpStatus, Inject, Injectable } from '@nestjs/common';
|
import { forwardRef, HttpException, HttpStatus, Inject, Injectable } from '@nestjs/common';
|
||||||
import { InjectRepository } from '@nestjs/typeorm';
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
import { UserService } from '@services/user.service';
|
import { UserService } from '@services/user.service';
|
||||||
import { AuthEnum, IDocument, IOrganization, IUser, IWiki } from '@think/domains';
|
import { AuthEnum, IDocument, IOrganization, IPagination, IUser, IWiki } from '@think/domains';
|
||||||
import * as lodash from 'lodash';
|
import * as lodash from 'lodash';
|
||||||
import { Repository } from 'typeorm';
|
import { Repository } from 'typeorm';
|
||||||
|
|
||||||
|
@ -41,8 +41,6 @@ export class AuthService {
|
||||||
const wrappedAuth = { userId, ...auth };
|
const wrappedAuth = { userId, ...auth };
|
||||||
const oldAuth = await this.authRepo.findOne(wrappedAuth);
|
const oldAuth = await this.authRepo.findOne(wrappedAuth);
|
||||||
|
|
||||||
// TODO: 这里可以判断权限继承
|
|
||||||
|
|
||||||
let newAuth: AuthEntity;
|
let newAuth: AuthEntity;
|
||||||
|
|
||||||
if (oldAuth) {
|
if (oldAuth) {
|
||||||
|
@ -80,6 +78,48 @@ export class AuthService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除组织
|
||||||
|
* 注意:该方法是直接删除,可调用 canDelete 判断是否可删除
|
||||||
|
* @param organizationId
|
||||||
|
*/
|
||||||
|
async deleteOrganization(organizationId: IOrganization['id']) {
|
||||||
|
const res = await this.authRepo.find({
|
||||||
|
organizationId,
|
||||||
|
});
|
||||||
|
await this.authRepo.remove(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除知识库
|
||||||
|
* 注意:该方法是直接删除,可调用 canDelete 判断是否可删除
|
||||||
|
* @param organizationId
|
||||||
|
* @param wikiId
|
||||||
|
*/
|
||||||
|
async deleteWiki(organizationId: IOrganization['id'], wikiId: IWiki['id']) {
|
||||||
|
const res = await this.authRepo.find({
|
||||||
|
organizationId,
|
||||||
|
wikiId,
|
||||||
|
});
|
||||||
|
await this.authRepo.remove(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除文档
|
||||||
|
* 注意:该方法是直接删除,可调用 canDelete 判断是否可删除
|
||||||
|
* @param organizationId
|
||||||
|
* @param wikiId
|
||||||
|
* @param documentId
|
||||||
|
*/
|
||||||
|
async deleteDocument(organizationId: IOrganization['id'], wikiId: IWiki['id'], documentId: IDocument['id']) {
|
||||||
|
const res = await this.authRepo.find({
|
||||||
|
organizationId,
|
||||||
|
wikiId,
|
||||||
|
documentId,
|
||||||
|
});
|
||||||
|
await this.authRepo.remove(res);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 用户是否可查看目标
|
* 用户是否可查看目标
|
||||||
* @param userId
|
* @param userId
|
||||||
|
@ -97,7 +137,7 @@ export class AuthService {
|
||||||
const userAuth = await this.authRepo.findOne(conditions);
|
const userAuth = await this.authRepo.findOne(conditions);
|
||||||
|
|
||||||
if (!userAuth || userAuth.auth === AuthEnum.noAccess) {
|
if (!userAuth || userAuth.auth === AuthEnum.noAccess) {
|
||||||
throw new HttpException('您没有权限查看', HttpStatus.FORBIDDEN);
|
throw new HttpException('您没有权限', HttpStatus.FORBIDDEN);
|
||||||
}
|
}
|
||||||
|
|
||||||
return userAuth;
|
return userAuth;
|
||||||
|
@ -120,7 +160,7 @@ export class AuthService {
|
||||||
const userAuth = await this.authRepo.findOne(conditions);
|
const userAuth = await this.authRepo.findOne(conditions);
|
||||||
|
|
||||||
if (!userAuth || ![AuthEnum.creator, AuthEnum.admin].includes(userAuth.auth)) {
|
if (!userAuth || ![AuthEnum.creator, AuthEnum.admin].includes(userAuth.auth)) {
|
||||||
throw new HttpException('您没有权限编辑', HttpStatus.FORBIDDEN);
|
throw new HttpException('您没有权限', HttpStatus.FORBIDDEN);
|
||||||
}
|
}
|
||||||
|
|
||||||
return userAuth;
|
return userAuth;
|
||||||
|
@ -143,7 +183,7 @@ export class AuthService {
|
||||||
const userAuth = await this.authRepo.findOne(conditions);
|
const userAuth = await this.authRepo.findOne(conditions);
|
||||||
|
|
||||||
if (!userAuth || ![AuthEnum.creator].includes(userAuth.auth)) {
|
if (!userAuth || ![AuthEnum.creator].includes(userAuth.auth)) {
|
||||||
throw new HttpException('您没有权限删除', HttpStatus.FORBIDDEN);
|
throw new HttpException('您没有权限', HttpStatus.FORBIDDEN);
|
||||||
}
|
}
|
||||||
|
|
||||||
return userAuth;
|
return userAuth;
|
||||||
|
@ -156,6 +196,12 @@ export class AuthService {
|
||||||
* @param dto
|
* @param dto
|
||||||
*/
|
*/
|
||||||
private async operateOtherUserAuth(currentUserId: IUser['id'], targetUserId: IUser['id'], dto: AuthDto) {
|
private async operateOtherUserAuth(currentUserId: IUser['id'], targetUserId: IUser['id'], dto: AuthDto) {
|
||||||
|
const targetUser = await this.userService.findOne({ id: targetUserId });
|
||||||
|
|
||||||
|
if (!targetUser) {
|
||||||
|
throw new HttpException('用户不存在', HttpStatus.NOT_FOUND);
|
||||||
|
}
|
||||||
|
|
||||||
const conditions: Partial<AuthEntity> = {
|
const conditions: Partial<AuthEntity> = {
|
||||||
organizationId: dto.organizationId,
|
organizationId: dto.organizationId,
|
||||||
wikiId: dto.wikiId || null,
|
wikiId: dto.wikiId || null,
|
||||||
|
@ -168,17 +214,17 @@ export class AuthService {
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!currentUserAuth) {
|
if (!currentUserAuth) {
|
||||||
throw new HttpException('您没有权限操作1', HttpStatus.FORBIDDEN);
|
throw new HttpException('您没有权限操作', HttpStatus.FORBIDDEN);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 仅创建者、管理员可操作他人权限
|
// 仅创建者、管理员可操作他人权限
|
||||||
if (![AuthEnum.creator, AuthEnum.admin].includes(currentUserAuth.auth)) {
|
if (![AuthEnum.creator, AuthEnum.admin].includes(currentUserAuth.auth)) {
|
||||||
throw new HttpException('您没有权限操作2', HttpStatus.FORBIDDEN);
|
throw new HttpException('您没有权限操作', HttpStatus.FORBIDDEN);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 仅创建者可赋予他人创建者、管理员权限
|
// 仅创建者可赋予他人创建者、管理员权限
|
||||||
if ([AuthEnum.creator, AuthEnum.admin].includes(dto.auth) && currentUserAuth.auth !== AuthEnum.creator) {
|
if ([AuthEnum.creator, AuthEnum.admin].includes(dto.auth) && currentUserAuth.auth !== AuthEnum.creator) {
|
||||||
throw new HttpException('您没有权限操作3', HttpStatus.FORBIDDEN);
|
throw new HttpException('您没有权限操作', HttpStatus.FORBIDDEN);
|
||||||
}
|
}
|
||||||
|
|
||||||
const maybeTargetUserAuth = await this.authRepo.findOne({
|
const maybeTargetUserAuth = await this.authRepo.findOne({
|
||||||
|
@ -189,12 +235,12 @@ export class AuthService {
|
||||||
if (maybeTargetUserAuth) {
|
if (maybeTargetUserAuth) {
|
||||||
// 对方是创建者,无权操作
|
// 对方是创建者,无权操作
|
||||||
if (maybeTargetUserAuth.auth === AuthEnum.creator) {
|
if (maybeTargetUserAuth.auth === AuthEnum.creator) {
|
||||||
throw new HttpException('您没有权限操作4', HttpStatus.FORBIDDEN);
|
throw new HttpException('您没有权限操作', HttpStatus.FORBIDDEN);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 对方是管理员,仅创建者可操作
|
// 对方是管理员,仅创建者可操作
|
||||||
if (maybeTargetUserAuth.auth === AuthEnum.admin && currentUserAuth.auth !== AuthEnum.creator) {
|
if (maybeTargetUserAuth.auth === AuthEnum.admin && currentUserAuth.auth !== AuthEnum.creator) {
|
||||||
throw new HttpException('您没有权限操作5', HttpStatus.FORBIDDEN);
|
throw new HttpException('您没有权限操作', HttpStatus.FORBIDDEN);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -300,10 +346,12 @@ export class AuthService {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取指定组织的所有用户权限
|
* 获取指定组织的所有用户权限
|
||||||
* @param userId
|
* @param organizationId
|
||||||
|
* @param pagination 分页参数,不传获取所有
|
||||||
|
* @returns
|
||||||
*/
|
*/
|
||||||
async getUsersAuthInOrganization(organizationId: IOrganization['id']) {
|
async getUsersAuthInOrganization(organizationId: IOrganization['id'], pagination: IPagination | null) {
|
||||||
const [data, total] = await this.authRepo
|
const query = await this.authRepo
|
||||||
.createQueryBuilder('auth')
|
.createQueryBuilder('auth')
|
||||||
.where('auth.auth IN (:...types)', {
|
.where('auth.auth IN (:...types)', {
|
||||||
types: [AuthEnum.creator, AuthEnum.admin, AuthEnum.member, AuthEnum.noAccess],
|
types: [AuthEnum.creator, AuthEnum.admin, AuthEnum.member, AuthEnum.noAccess],
|
||||||
|
@ -311,18 +359,28 @@ export class AuthService {
|
||||||
.andWhere('auth.organizationId=:organizationId')
|
.andWhere('auth.organizationId=:organizationId')
|
||||||
.andWhere('auth.wikiId is NULL')
|
.andWhere('auth.wikiId is NULL')
|
||||||
.andWhere('auth.documentId is NULL')
|
.andWhere('auth.documentId is NULL')
|
||||||
.setParameter('organizationId', organizationId)
|
.setParameter('organizationId', organizationId);
|
||||||
.getManyAndCount();
|
|
||||||
|
if (pagination) {
|
||||||
|
const { page = 1, pageSize = 12 } = pagination;
|
||||||
|
query.skip((+page - 1) * +pageSize);
|
||||||
|
query.take(+pageSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
const [data, total] = await query.getManyAndCount();
|
||||||
|
|
||||||
return { data: data || [], total };
|
return { data: data || [], total };
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取指定知识库的所有用户权限
|
* 获取指定知识库的所有用户权限
|
||||||
* @param userId
|
* @param organizationId
|
||||||
|
* @param wikiId
|
||||||
|
* @param pagination 分页参数,不传获取所有
|
||||||
|
* @returns
|
||||||
*/
|
*/
|
||||||
async getUsersAuthInWiki(organizationId: IOrganization['id'], wikiId: IWiki['id']) {
|
async getUsersAuthInWiki(organizationId: IOrganization['id'], wikiId: IWiki['id'], pagination: IPagination | null) {
|
||||||
const [data, total] = await this.authRepo
|
const query = await this.authRepo
|
||||||
.createQueryBuilder('auth')
|
.createQueryBuilder('auth')
|
||||||
.where('auth.auth IN (:...types)', {
|
.where('auth.auth IN (:...types)', {
|
||||||
types: [AuthEnum.creator, AuthEnum.admin, AuthEnum.member, AuthEnum.noAccess],
|
types: [AuthEnum.creator, AuthEnum.admin, AuthEnum.member, AuthEnum.noAccess],
|
||||||
|
@ -331,8 +389,14 @@ export class AuthService {
|
||||||
.andWhere('auth.wikiId=:wikiId')
|
.andWhere('auth.wikiId=:wikiId')
|
||||||
.andWhere('auth.documentId is NULL')
|
.andWhere('auth.documentId is NULL')
|
||||||
.setParameter('organizationId', organizationId)
|
.setParameter('organizationId', organizationId)
|
||||||
.setParameter('wikiId', wikiId)
|
.setParameter('wikiId', wikiId);
|
||||||
.getManyAndCount();
|
|
||||||
|
if (pagination) {
|
||||||
|
const { page = 1, pageSize = 12 } = pagination;
|
||||||
|
query.skip((+page - 1) * +pageSize);
|
||||||
|
query.take(+pageSize);
|
||||||
|
}
|
||||||
|
const [data, total] = await query.getManyAndCount();
|
||||||
|
|
||||||
return { data: data || [], total };
|
return { data: data || [], total };
|
||||||
}
|
}
|
||||||
|
@ -413,11 +477,20 @@ export class AuthService {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取指定知识库的所有用户权限
|
* 获取指定文档的所有用户权限
|
||||||
* @param userId
|
* @param organizationId
|
||||||
|
* @param wikiId
|
||||||
|
* @param documentId
|
||||||
|
* @param pagination 分页参数,不传获取所有
|
||||||
|
* @returns
|
||||||
*/
|
*/
|
||||||
async getUsersAuthInDocument(organizationId: IOrganization['id'], wikiId: IWiki['id'], documentId: IDocument['id']) {
|
async getUsersAuthInDocument(
|
||||||
const [data, total] = await this.authRepo
|
organizationId: IOrganization['id'],
|
||||||
|
wikiId: IWiki['id'],
|
||||||
|
documentId: IDocument['id'],
|
||||||
|
pagination: IPagination | null
|
||||||
|
) {
|
||||||
|
const query = await this.authRepo
|
||||||
.createQueryBuilder('auth')
|
.createQueryBuilder('auth')
|
||||||
.where('auth.auth IN (:...types)', {
|
.where('auth.auth IN (:...types)', {
|
||||||
types: [AuthEnum.creator, AuthEnum.admin, AuthEnum.member, AuthEnum.noAccess],
|
types: [AuthEnum.creator, AuthEnum.admin, AuthEnum.member, AuthEnum.noAccess],
|
||||||
|
@ -427,8 +500,14 @@ export class AuthService {
|
||||||
.andWhere('auth.documentId=:documentId')
|
.andWhere('auth.documentId=:documentId')
|
||||||
.setParameter('organizationId', organizationId)
|
.setParameter('organizationId', organizationId)
|
||||||
.setParameter('wikiId', wikiId)
|
.setParameter('wikiId', wikiId)
|
||||||
.setParameter('documentId', documentId)
|
.setParameter('documentId', documentId);
|
||||||
.getManyAndCount();
|
|
||||||
|
if (pagination) {
|
||||||
|
const { page = 1, pageSize = 12 } = pagination;
|
||||||
|
query.skip((+page - 1) * +pageSize);
|
||||||
|
query.take(+pageSize);
|
||||||
|
}
|
||||||
|
const [data, total] = await query.getManyAndCount();
|
||||||
|
|
||||||
return { data: data || [], total };
|
return { data: data || [], total };
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,8 +5,8 @@ import { ConfigService } from '@nestjs/config';
|
||||||
import { DocumentService } from '@services/document.service';
|
import { DocumentService } from '@services/document.service';
|
||||||
import { DocumentVersionService } from '@services/document-version.service';
|
import { DocumentVersionService } from '@services/document-version.service';
|
||||||
import { TemplateService } from '@services/template.service';
|
import { TemplateService } from '@services/template.service';
|
||||||
import { OutUser, UserService } from '@services/user.service';
|
import { UserService } from '@services/user.service';
|
||||||
import { DocumentStatus } from '@think/domains';
|
import { DocumentStatus, IUser } from '@think/domains';
|
||||||
import * as lodash from 'lodash';
|
import * as lodash from 'lodash';
|
||||||
import * as Y from 'yjs';
|
import * as Y from 'yjs';
|
||||||
|
|
||||||
|
@ -101,14 +101,16 @@ export class CollaborationService {
|
||||||
}
|
}
|
||||||
return { user: { name: '匿名用户' } };
|
return { user: { name: '匿名用户' } };
|
||||||
} else {
|
} else {
|
||||||
// TODO:权限校验
|
const authority = await this.documentService.getDocumentUserAuth(user.id, targetId);
|
||||||
// const authority = await this.documentService.getDocumentAuthority(targetId, user.id);
|
|
||||||
// if (!authority.readable) {
|
if (!authority.readable) {
|
||||||
// throw new HttpException('您无权查看此文档', HttpStatus.FORBIDDEN);
|
throw new HttpException('您无权查看此文档', HttpStatus.FORBIDDEN);
|
||||||
// }
|
}
|
||||||
// if (!authority.editable) {
|
|
||||||
// connection.readOnly = true;
|
if (!authority.editable) {
|
||||||
// }
|
connection.readOnly = true;
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
user,
|
user,
|
||||||
};
|
};
|
||||||
|
@ -121,9 +123,11 @@ export class CollaborationService {
|
||||||
}
|
}
|
||||||
|
|
||||||
const template = await this.templateService.findById(targetId);
|
const template = await this.templateService.findById(targetId);
|
||||||
|
|
||||||
if (template.createUserId !== user.id) {
|
if (template.createUserId !== user.id) {
|
||||||
throw new HttpException('您无权查看此模板', HttpStatus.FORBIDDEN);
|
throw new HttpException('您无权查看此模板', HttpStatus.FORBIDDEN);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
user,
|
user,
|
||||||
};
|
};
|
||||||
|
@ -149,6 +153,9 @@ export class CollaborationService {
|
||||||
switch (docType) {
|
switch (docType) {
|
||||||
case 'document': {
|
case 'document': {
|
||||||
const res = await this.documentService.findById(targetId);
|
const res = await this.documentService.findById(targetId);
|
||||||
|
if (!res) {
|
||||||
|
throw new Error('文档不存在');
|
||||||
|
}
|
||||||
state = res.state;
|
state = res.state;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -179,7 +186,7 @@ export class CollaborationService {
|
||||||
const docType = requestParameters.get('docType');
|
const docType = requestParameters.get('docType');
|
||||||
const userId = requestParameters.get('userId');
|
const userId = requestParameters.get('userId');
|
||||||
|
|
||||||
const updateDocument = async (user: OutUser, documentId: string, data) => {
|
const updateDocument = async (user: IUser, documentId: string, data) => {
|
||||||
await this.documentService.updateDocument(user, documentId, data);
|
await this.documentService.updateDocument(user, documentId, data);
|
||||||
this.debounce(
|
this.debounce(
|
||||||
`onStoreDocumentVersion-${documentId}`,
|
`onStoreDocumentVersion-${documentId}`,
|
||||||
|
@ -218,7 +225,7 @@ export class CollaborationService {
|
||||||
const node = TiptapTransformer.fromYdoc(data.document);
|
const node = TiptapTransformer.fromYdoc(data.document);
|
||||||
const title = lodash.get(node, `default.content[0].content[0].text`, '').replace(/\s/g, '').slice(0, 255);
|
const title = lodash.get(node, `default.content[0].content[0].text`, '').replace(/\s/g, '').slice(0, 255);
|
||||||
const state = Buffer.from(Y.encodeStateAsUpdate(data.document));
|
const state = Buffer.from(Y.encodeStateAsUpdate(data.document));
|
||||||
await updateHandler({ id: userId } as OutUser, targetId, {
|
await updateHandler({ id: userId } as IUser, targetId, {
|
||||||
title,
|
title,
|
||||||
content: JSON.stringify(node),
|
content: JSON.stringify(node),
|
||||||
state,
|
state,
|
||||||
|
@ -234,7 +241,7 @@ export class CollaborationService {
|
||||||
if (docType === 'document') {
|
if (docType === 'document') {
|
||||||
const data = await this.documentService.findById(targetId);
|
const data = await this.documentService.findById(targetId);
|
||||||
if (data && !data.title) {
|
if (data && !data.title) {
|
||||||
await this.documentService.updateDocument({ id: userId } as OutUser, targetId, {
|
await this.documentService.updateDocument({ id: userId } as IUser, targetId, {
|
||||||
title: '未命名文档',
|
title: '未命名文档',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -244,7 +251,7 @@ export class CollaborationService {
|
||||||
if (docType === 'template') {
|
if (docType === 'template') {
|
||||||
const data = await this.templateService.findById(targetId);
|
const data = await this.templateService.findById(targetId);
|
||||||
if (data && !data.title) {
|
if (data && !data.title) {
|
||||||
await this.templateService.updateTemplate({ id: userId } as OutUser, targetId, {
|
await this.templateService.updateTemplate({ id: userId } as IUser, targetId, {
|
||||||
title: '未命名模板',
|
title: '未命名模板',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,10 +3,11 @@ import { CommentEntity } from '@entities/comment.entity';
|
||||||
import { parseUserAgent } from '@helpers/ua.helper';
|
import { parseUserAgent } from '@helpers/ua.helper';
|
||||||
import { forwardRef, HttpException, HttpStatus, Inject, Injectable } from '@nestjs/common';
|
import { forwardRef, HttpException, HttpStatus, Inject, Injectable } from '@nestjs/common';
|
||||||
import { InjectRepository } from '@nestjs/typeorm';
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
|
import { AuthService } from '@services/auth.service';
|
||||||
import { DocumentService } from '@services/document.service';
|
import { DocumentService } from '@services/document.service';
|
||||||
import { MessageService } from '@services/message.service';
|
import { MessageService } from '@services/message.service';
|
||||||
import { OutUser, UserService } from '@services/user.service';
|
import { UserService } from '@services/user.service';
|
||||||
import { buildMessageURL, DocumentStatus } from '@think/domains';
|
import { AuthEnum, buildMessageURL, DocumentStatus, IUser } from '@think/domains';
|
||||||
import { Repository } from 'typeorm';
|
import { Repository } from 'typeorm';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
|
@ -14,9 +15,17 @@ export class CommentService {
|
||||||
constructor(
|
constructor(
|
||||||
@InjectRepository(CommentEntity)
|
@InjectRepository(CommentEntity)
|
||||||
private readonly commentRepo: Repository<CommentEntity>,
|
private readonly commentRepo: Repository<CommentEntity>,
|
||||||
|
|
||||||
|
@Inject(forwardRef(() => AuthService))
|
||||||
|
private readonly authService: AuthService,
|
||||||
|
|
||||||
|
@Inject(forwardRef(() => UserService))
|
||||||
|
private readonly userService: UserService,
|
||||||
|
|
||||||
@Inject(forwardRef(() => MessageService))
|
@Inject(forwardRef(() => MessageService))
|
||||||
private readonly messageService: MessageService,
|
private readonly messageService: MessageService,
|
||||||
private readonly userService: UserService,
|
|
||||||
|
@Inject(forwardRef(() => DocumentService))
|
||||||
private readonly documentService: DocumentService
|
private readonly documentService: DocumentService
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
|
@ -44,20 +53,20 @@ export class CommentService {
|
||||||
* @param dto
|
* @param dto
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
async create(user: OutUser, userAgent: string, dto: CommentDto) {
|
async create(user: IUser, userAgent: string, dto: CommentDto) {
|
||||||
const { documentId, html, replyUserId } = dto;
|
const { documentId, html, replyUserId } = dto;
|
||||||
|
|
||||||
const doc = await this.documentService.findById(documentId);
|
const doc = await this.documentService.findById(documentId);
|
||||||
|
|
||||||
if (doc.status !== DocumentStatus.public) {
|
if (doc.status !== DocumentStatus.public) {
|
||||||
// TODO:权限校验
|
const authority = await this.documentService.getDocumentUserAuth(user.id, documentId);
|
||||||
// const docAuth = await this.documentService.getDocumentAuthority(documentId, user.id);
|
|
||||||
// if (!docAuth) {
|
if (!authority) {
|
||||||
// throw new HttpException('文档不存在', HttpStatus.NOT_FOUND);
|
throw new HttpException('文档不存在', HttpStatus.NOT_FOUND);
|
||||||
// }
|
}
|
||||||
// if (!docAuth.readable) {
|
if (!authority.readable) {
|
||||||
// throw new HttpException('权限不足,无法评论', HttpStatus.FORBIDDEN);
|
throw new HttpException('权限不足,无法评论', HttpStatus.FORBIDDEN);
|
||||||
// }
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const { text: uaText } = parseUserAgent(userAgent);
|
const { text: uaText } = parseUserAgent(userAgent);
|
||||||
|
@ -74,21 +83,23 @@ export class CommentService {
|
||||||
const res = await this.commentRepo.create(comment);
|
const res = await this.commentRepo.create(comment);
|
||||||
const ret = await this.commentRepo.save(res);
|
const ret = await this.commentRepo.save(res);
|
||||||
|
|
||||||
// const wikiUsersAuth = await this.documentService.getDocUsersWithoutAuthCheck(user, documentId);
|
const { data: users } = await this.authService.getUsersAuthInDocument(doc.organizationId, doc.wikiId, doc.id, null);
|
||||||
|
|
||||||
// await Promise.all(
|
await Promise.all(
|
||||||
// wikiUsersAuth.map(async (userAuth) => {
|
users
|
||||||
// await this.messageService.notify(userAuth.user, {
|
.filter((user) => user.auth !== AuthEnum.noAccess)
|
||||||
// title: `文档「${doc.title}」收到新评论`,
|
.map((user) => {
|
||||||
// message: `文档「${doc.title}」收到新评论,快去看看!`,
|
this.messageService.notify(user.userId, {
|
||||||
// url: buildMessageURL('toDocument')({
|
title: `文档「${doc.title}」收到新评论`,
|
||||||
// organizationId: doc.organizationId,
|
message: `文档「${doc.title}」收到新评论,快去看看!`,
|
||||||
// wikiId: doc.wikiId,
|
url: buildMessageURL('toDocument')({
|
||||||
// documentId: doc.id,
|
organizationId: doc.organizationId,
|
||||||
// }),
|
wikiId: doc.wikiId,
|
||||||
// });
|
documentId: doc.id,
|
||||||
// })
|
}),
|
||||||
// );
|
});
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -180,21 +191,24 @@ export class CommentService {
|
||||||
const newData = await this.commentRepo.merge(old, { html: dto.html });
|
const newData = await this.commentRepo.merge(old, { html: dto.html });
|
||||||
|
|
||||||
const doc = await this.documentService.findById(old.documentId);
|
const doc = await this.documentService.findById(old.documentId);
|
||||||
// const wikiUsersAuth = await this.documentService.getDocUsersWithoutAuthCheck(user, old.documentId);
|
|
||||||
|
|
||||||
// await Promise.all(
|
const { data: users } = await this.authService.getUsersAuthInDocument(doc.organizationId, doc.wikiId, doc.id, null);
|
||||||
// wikiUsersAuth.map(async (userAuth) => {
|
|
||||||
// await this.messageService.notify(userAuth.user, {
|
await Promise.all(
|
||||||
// title: `文档「${doc.title}」评论更新`,
|
users
|
||||||
// message: `文档「${doc.title}」的评论已更新,快去看看!`,
|
.filter((user) => user.auth !== AuthEnum.noAccess)
|
||||||
// url: buildMessageURL('toDocument')({
|
.map((user) => {
|
||||||
// organizationId: doc.organizationId,
|
this.messageService.notify(user.userId, {
|
||||||
// wikiId: doc.wikiId,
|
title: `文档「${doc.title}」收到新评论`,
|
||||||
// documentId: doc.id,
|
message: `文档「${doc.title}」收到新评论,快去看看!`,
|
||||||
// }),
|
url: buildMessageURL('toDocument')({
|
||||||
// });
|
organizationId: doc.organizationId,
|
||||||
// })
|
wikiId: doc.wikiId,
|
||||||
// );
|
documentId: doc.id,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
return this.commentRepo.save(newData);
|
return this.commentRepo.save(newData);
|
||||||
}
|
}
|
||||||
|
@ -205,20 +219,25 @@ export class CommentService {
|
||||||
throw new HttpException('您不是评论创建者,无法删除', HttpStatus.FORBIDDEN);
|
throw new HttpException('您不是评论创建者,无法删除', HttpStatus.FORBIDDEN);
|
||||||
}
|
}
|
||||||
const doc = await this.documentService.findById(data.documentId);
|
const doc = await this.documentService.findById(data.documentId);
|
||||||
// const wikiUsersAuth = await this.documentService.getDocUsersWithoutAuthCheck(user, data.documentId);
|
|
||||||
// await Promise.all(
|
const { data: users } = await this.authService.getUsersAuthInDocument(doc.organizationId, doc.wikiId, doc.id, null);
|
||||||
// wikiUsersAuth.map(async (userAuth) => {
|
|
||||||
// await this.messageService.notify(userAuth.user, {
|
await Promise.all(
|
||||||
// title: `文档「${doc.title}」的评论已被删除`,
|
users
|
||||||
// message: `文档「${doc.title}」的评论已被删除,快去看看`,
|
.filter((user) => user.auth !== AuthEnum.noAccess)
|
||||||
// url: buildMessageURL('toDocument')({
|
.map((user) => {
|
||||||
// organizationId: doc.organizationId,
|
this.messageService.notify(user.userId, {
|
||||||
// wikiId: doc.wikiId,
|
title: `文档「${doc.title}」收到新评论`,
|
||||||
// documentId: doc.id,
|
message: `文档「${doc.title}」收到新评论,快去看看!`,
|
||||||
// }),
|
url: buildMessageURL('toDocument')({
|
||||||
// });
|
organizationId: doc.organizationId,
|
||||||
// })
|
wikiId: doc.wikiId,
|
||||||
// );
|
documentId: doc.id,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
return this.commentRepo.remove(data);
|
return this.commentRepo.remove(data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import { OperateUserAuthDto } from '@dtos/auth.dto';
|
import { OperateUserAuthDto } from '@dtos/auth.dto';
|
||||||
import { CreateDocumentDto } from '@dtos/create-document.dto';
|
import { CreateDocumentDto } from '@dtos/create-document.dto';
|
||||||
// import { DocAuthDto } from '@dtos/doc-auth.dto';
|
|
||||||
import { ShareDocumentDto } from '@dtos/share-document.dto';
|
import { ShareDocumentDto } from '@dtos/share-document.dto';
|
||||||
import { UpdateDocumentDto } from '@dtos/update-document.dto';
|
import { UpdateDocumentDto } from '@dtos/update-document.dto';
|
||||||
import { DocumentEntity } from '@entities/document.entity';
|
import { DocumentEntity } from '@entities/document.entity';
|
||||||
|
@ -12,11 +11,11 @@ import { CollaborationService } from '@services/collaboration.service';
|
||||||
import { DocumentVersionService } from '@services/document-version.service';
|
import { DocumentVersionService } from '@services/document-version.service';
|
||||||
import { MessageService } from '@services/message.service';
|
import { MessageService } from '@services/message.service';
|
||||||
import { TemplateService } from '@services/template.service';
|
import { TemplateService } from '@services/template.service';
|
||||||
import { OutUser, UserService } from '@services/user.service';
|
import { UserService } from '@services/user.service';
|
||||||
import { ViewService } from '@services/view.service';
|
import { ViewService } from '@services/view.service';
|
||||||
import { WikiService } from '@services/wiki.service';
|
import { WikiService } from '@services/wiki.service';
|
||||||
import { EMPTY_DOCUMNENT } from '@think/constants';
|
import { EMPTY_DOCUMNENT } from '@think/constants';
|
||||||
import { AuthEnum, buildMessageURL, DocumentStatus, WikiUserRole } from '@think/domains';
|
import { AuthEnum, buildMessageURL, DocumentStatus, IUser } from '@think/domains';
|
||||||
import { instanceToPlain } from 'class-transformer';
|
import { instanceToPlain } from 'class-transformer';
|
||||||
import * as lodash from 'lodash';
|
import * as lodash from 'lodash';
|
||||||
import { Repository } from 'typeorm';
|
import { Repository } from 'typeorm';
|
||||||
|
@ -27,9 +26,6 @@ export class DocumentService {
|
||||||
private documentVersionService: DocumentVersionService;
|
private documentVersionService: DocumentVersionService;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
// @InjectRepository(DocumentUserEntity)
|
|
||||||
// public readonly documentUserRepo: Repository<DocumentUserEntity>,
|
|
||||||
|
|
||||||
@InjectRepository(DocumentEntity)
|
@InjectRepository(DocumentEntity)
|
||||||
public readonly documentRepo: Repository<DocumentEntity>,
|
public readonly documentRepo: Repository<DocumentEntity>,
|
||||||
|
|
||||||
|
@ -70,7 +66,7 @@ export class DocumentService {
|
||||||
* @param dto
|
* @param dto
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
public async findById(id: string) {
|
public async findById(id: string): Promise<Partial<DocumentEntity>> {
|
||||||
const document = await this.documentRepo.findOne(id);
|
const document = await this.documentRepo.findOne(id);
|
||||||
return instanceToPlain(document);
|
return instanceToPlain(document);
|
||||||
}
|
}
|
||||||
|
@ -81,7 +77,7 @@ export class DocumentService {
|
||||||
* @param dto
|
* @param dto
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
public async findByIds(ids: string[]) {
|
public async findByIds(ids: string[]): Promise<Array<Partial<DocumentEntity>>> {
|
||||||
const documents = await this.documentRepo.findByIds(ids);
|
const documents = await this.documentRepo.findByIds(ids);
|
||||||
return documents.map((doc) => instanceToPlain(doc));
|
return documents.map((doc) => instanceToPlain(doc));
|
||||||
}
|
}
|
||||||
|
@ -95,90 +91,6 @@ export class DocumentService {
|
||||||
return await this.documentRepo.findOne({ wikiId, isWikiHome: true });
|
return await this.documentRepo.findOne({ wikiId, isWikiHome: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
// /**
|
|
||||||
// * 获取用户在指定文档的权限
|
|
||||||
// * @param documentId
|
|
||||||
// * @param userId
|
|
||||||
// * @returns
|
|
||||||
// */
|
|
||||||
// public async getDocumentAuthority(documentId: string, userId: string) {
|
|
||||||
// const authority = await this.documentUserRepo.findOne({
|
|
||||||
// documentId,
|
|
||||||
// userId,
|
|
||||||
// });
|
|
||||||
// return authority;
|
|
||||||
// }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 操作文档成员权限(可读、可编辑)
|
|
||||||
* @param param0
|
|
||||||
* @returns
|
|
||||||
// */
|
|
||||||
// async operateDocumentAuth({ currentUserId, documentId, targetUserId, readable = false, editable = false }) {
|
|
||||||
// const doc = await this.documentRepo.findOne({ id: documentId });
|
|
||||||
// if (!doc) {
|
|
||||||
// throw new HttpException('文档不存在', HttpStatus.BAD_REQUEST);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// const isCurrentUserCreator = currentUserId === doc.createUserId;
|
|
||||||
// const isTargetUserCreator = targetUserId === doc.createUserId;
|
|
||||||
|
|
||||||
// if (!isCurrentUserCreator) {
|
|
||||||
// throw new HttpException('您不是文档创建者,无权操作', HttpStatus.FORBIDDEN);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// const targetUser = await this.userService.findOne(targetUserId);
|
|
||||||
// const targetDocAuth = await this.documentUserRepo.findOne({
|
|
||||||
// documentId,
|
|
||||||
// userId: targetUserId,
|
|
||||||
// });
|
|
||||||
|
|
||||||
// if (!targetDocAuth) {
|
|
||||||
// const documentUser = {
|
|
||||||
// documentId,
|
|
||||||
// createUserId: doc.createUserId,
|
|
||||||
// wikiId: doc.wikiId,
|
|
||||||
// userId: targetUserId,
|
|
||||||
// readable: isTargetUserCreator ? true : editable ? true : readable,
|
|
||||||
// editable: isTargetUserCreator ? true : editable,
|
|
||||||
// };
|
|
||||||
// const res = await this.documentUserRepo.create(documentUser);
|
|
||||||
// const ret = await this.documentUserRepo.save(res);
|
|
||||||
|
|
||||||
// await this.messageService.notify(targetUser, {
|
|
||||||
// title: `您已被添加到文档「${doc.title}」`,
|
|
||||||
// message: `您已被添加到文档「${doc.title}」,快去看看!`,
|
|
||||||
// url: buildMessageURL('toDocument')({
|
|
||||||
// organizationId: doc.organizationId,
|
|
||||||
// wikiId: doc.wikiId,
|
|
||||||
// documentId: doc.id,
|
|
||||||
// }),
|
|
||||||
// });
|
|
||||||
|
|
||||||
// return ret;
|
|
||||||
// } else {
|
|
||||||
// const newData = {
|
|
||||||
// ...targetDocAuth,
|
|
||||||
// readable: isTargetUserCreator ? true : editable ? true : readable,
|
|
||||||
// editable: isTargetUserCreator ? true : editable,
|
|
||||||
// };
|
|
||||||
// const res = await this.documentUserRepo.merge(targetDocAuth, newData);
|
|
||||||
// const ret = await this.documentUserRepo.save(res);
|
|
||||||
|
|
||||||
// await this.messageService.notify(targetUser, {
|
|
||||||
// title: `您在文档「${doc.title}」的权限已变更`,
|
|
||||||
// message: `您在文档「${doc.title}」的权限已变更,快去看看!`,
|
|
||||||
// url: buildMessageURL('toDocument')({
|
|
||||||
// organizationId: doc.organizationId,
|
|
||||||
// wikiId: doc.wikiId,
|
|
||||||
// documentId: doc.id,
|
|
||||||
// }),
|
|
||||||
// });
|
|
||||||
|
|
||||||
// return ret;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 添加文档成员
|
* 添加文档成员
|
||||||
* @param user
|
* @param user
|
||||||
|
@ -186,7 +98,7 @@ export class DocumentService {
|
||||||
* @param dto
|
* @param dto
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
async addDocUser(user: OutUser, documentId, dto: OperateUserAuthDto) {
|
async addDocUser(user: IUser, documentId, dto: OperateUserAuthDto) {
|
||||||
const targetUser = await this.userService.findOne({ name: dto.userName });
|
const targetUser = await this.userService.findOne({ name: dto.userName });
|
||||||
|
|
||||||
if (!targetUser) {
|
if (!targetUser) {
|
||||||
|
@ -199,12 +111,32 @@ export class DocumentService {
|
||||||
throw new HttpException('目标文档不存在', HttpStatus.NOT_FOUND);
|
throw new HttpException('目标文档不存在', HttpStatus.NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
!(await this.authService.getAuth(targetUser.id, {
|
||||||
|
organizationId: doc.organizationId,
|
||||||
|
wikiId: null,
|
||||||
|
documentId: null,
|
||||||
|
}))
|
||||||
|
) {
|
||||||
|
throw new HttpException('该用户非组织成员', HttpStatus.FORBIDDEN);
|
||||||
|
}
|
||||||
|
|
||||||
await this.authService.createOrUpdateOtherUserAuth(user.id, targetUser.id, {
|
await this.authService.createOrUpdateOtherUserAuth(user.id, targetUser.id, {
|
||||||
auth: dto.userAuth,
|
auth: dto.userAuth,
|
||||||
organizationId: doc.organizationId,
|
organizationId: doc.organizationId,
|
||||||
wikiId: doc.wikiId,
|
wikiId: doc.wikiId,
|
||||||
documentId: doc.id,
|
documentId: doc.id,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await this.messageService.notify(targetUser.id, {
|
||||||
|
title: `您被添加到文档「${doc.title}」`,
|
||||||
|
message: `您被添加到文档「${doc.title}」,快去看看吧!`,
|
||||||
|
url: buildMessageURL('toWiki')({
|
||||||
|
organizationId: doc.organizationId,
|
||||||
|
wikiId: doc.wikiId,
|
||||||
|
documentId: doc.id,
|
||||||
|
}),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -214,7 +146,7 @@ export class DocumentService {
|
||||||
* @param dto
|
* @param dto
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
async updateDocUser(user: OutUser, documentId, dto: OperateUserAuthDto) {
|
async updateDocUser(user: IUser, documentId, dto: OperateUserAuthDto) {
|
||||||
const targetUser = await this.userService.findOne({ name: dto.userName });
|
const targetUser = await this.userService.findOne({ name: dto.userName });
|
||||||
|
|
||||||
if (!targetUser) {
|
if (!targetUser) {
|
||||||
|
@ -233,6 +165,16 @@ export class DocumentService {
|
||||||
wikiId: doc.wikiId,
|
wikiId: doc.wikiId,
|
||||||
documentId: doc.id,
|
documentId: doc.id,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await this.messageService.notify(targetUser.id, {
|
||||||
|
title: `文档「${doc.title}」权限更新`,
|
||||||
|
message: `您在文档「${doc.title}」的权限已变更,快去看看吧!`,
|
||||||
|
url: buildMessageURL('toWiki')({
|
||||||
|
organizationId: doc.organizationId,
|
||||||
|
wikiId: doc.wikiId,
|
||||||
|
documentId: doc.id,
|
||||||
|
}),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -242,7 +184,7 @@ export class DocumentService {
|
||||||
* @param dto
|
* @param dto
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
async deleteDocUser(user: OutUser, documentId, dto: OperateUserAuthDto) {
|
async deleteDocUser(user: IUser, documentId, dto: OperateUserAuthDto) {
|
||||||
const targetUser = await this.userService.findOne({ name: dto.userName });
|
const targetUser = await this.userService.findOne({ name: dto.userName });
|
||||||
|
|
||||||
if (!targetUser) {
|
if (!targetUser) {
|
||||||
|
@ -261,6 +203,16 @@ export class DocumentService {
|
||||||
wikiId: doc.wikiId,
|
wikiId: doc.wikiId,
|
||||||
documentId: doc.id,
|
documentId: doc.id,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await this.messageService.notify(targetUser.id, {
|
||||||
|
title: `文档「${doc.title}」权限已收回`,
|
||||||
|
message: `您在文档「${doc.title}」的权限已收回!`,
|
||||||
|
url: buildMessageURL('toWiki')({
|
||||||
|
organizationId: doc.organizationId,
|
||||||
|
wikiId: doc.wikiId,
|
||||||
|
documentId: doc.id,
|
||||||
|
}),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -268,7 +220,7 @@ export class DocumentService {
|
||||||
* @param userId
|
* @param userId
|
||||||
* @param wikiId
|
* @param wikiId
|
||||||
*/
|
*/
|
||||||
async getDocUsers(user: OutUser, documentId) {
|
async getDocUsers(user: IUser, documentId, pagination) {
|
||||||
const doc = await this.documentRepo.findOne({ id: documentId });
|
const doc = await this.documentRepo.findOne({ id: documentId });
|
||||||
|
|
||||||
if (!doc) {
|
if (!doc) {
|
||||||
|
@ -284,7 +236,8 @@ export class DocumentService {
|
||||||
const { data: auths, total } = await this.authService.getUsersAuthInDocument(
|
const { data: auths, total } = await this.authService.getUsersAuthInDocument(
|
||||||
doc.organizationId,
|
doc.organizationId,
|
||||||
doc.wikiId,
|
doc.wikiId,
|
||||||
doc.id
|
doc.id,
|
||||||
|
pagination
|
||||||
);
|
);
|
||||||
|
|
||||||
const res = await Promise.all(
|
const res = await Promise.all(
|
||||||
|
@ -297,29 +250,6 @@ export class DocumentService {
|
||||||
return { data: res, total };
|
return { data: res, total };
|
||||||
}
|
}
|
||||||
|
|
||||||
// /**
|
|
||||||
// * 获取文档成员
|
|
||||||
// * 忽略权限检查
|
|
||||||
// * @param userId
|
|
||||||
// * @param wikiId
|
|
||||||
// */
|
|
||||||
// async getDocUsersWithoutAuthCheck(user: OutUser, documentId) {
|
|
||||||
// const doc = await this.documentRepo.findOne({ id: documentId });
|
|
||||||
|
|
||||||
// if (!doc) {
|
|
||||||
// throw new HttpException('文档不存在', HttpStatus.BAD_REQUEST);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// const data = await this.documentUserRepo.find({ documentId });
|
|
||||||
|
|
||||||
// return await Promise.all(
|
|
||||||
// data.map(async (auth) => {
|
|
||||||
// const user = await this.userService.findById(auth.userId);
|
|
||||||
// return { auth, user };
|
|
||||||
// })
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建文档
|
* 创建文档
|
||||||
* @param user
|
* @param user
|
||||||
|
@ -327,7 +257,7 @@ export class DocumentService {
|
||||||
* @param isWikiHome 知识库首页文档
|
* @param isWikiHome 知识库首页文档
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
public async createDocument(user: OutUser, dto: CreateDocumentDto, isWikiHome = false) {
|
public async createDocument(user: IUser, dto: CreateDocumentDto, isWikiHome = false) {
|
||||||
await this.authService.canView(user.id, {
|
await this.authService.canView(user.id, {
|
||||||
organizationId: dto.organizationId,
|
organizationId: dto.organizationId,
|
||||||
wikiId: dto.wikiId,
|
wikiId: dto.wikiId,
|
||||||
|
@ -375,7 +305,11 @@ export class DocumentService {
|
||||||
}
|
}
|
||||||
|
|
||||||
const document = await this.documentRepo.save(await this.documentRepo.create(data));
|
const document = await this.documentRepo.save(await this.documentRepo.create(data));
|
||||||
const { data: userAuths } = await this.authService.getUsersAuthInWiki(document.organizationId, document.wikiId);
|
const { data: userAuths } = await this.authService.getUsersAuthInWiki(
|
||||||
|
document.organizationId,
|
||||||
|
document.wikiId,
|
||||||
|
null
|
||||||
|
);
|
||||||
|
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
...userAuths
|
...userAuths
|
||||||
|
@ -388,7 +322,7 @@ export class DocumentService {
|
||||||
documentId: document.id,
|
documentId: document.id,
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
await this.authService.createOrUpdateAuth(user.id, {
|
this.authService.createOrUpdateAuth(user.id, {
|
||||||
auth: AuthEnum.creator,
|
auth: AuthEnum.creator,
|
||||||
organizationId: document.organizationId,
|
organizationId: document.organizationId,
|
||||||
wikiId: document.wikiId,
|
wikiId: document.wikiId,
|
||||||
|
@ -399,26 +333,27 @@ export class DocumentService {
|
||||||
return instanceToPlain(document);
|
return instanceToPlain(document);
|
||||||
}
|
}
|
||||||
|
|
||||||
// /**
|
/**
|
||||||
// * 删除知识库下所有文档
|
* 删除知识库下所有文档
|
||||||
// * @param user
|
* @param user
|
||||||
// * @param wikiId
|
* @param wikiId
|
||||||
// */
|
*/
|
||||||
// async deleteWikiDocuments(user, wikiId) {
|
async deleteWikiDocuments(user, wikiId) {
|
||||||
// const docs = await this.documentRepo.find({ wikiId });
|
const docs = await this.documentRepo.find({ wikiId });
|
||||||
// await Promise.all(
|
await Promise.all(
|
||||||
// docs.map((doc) => {
|
docs.map((doc) => {
|
||||||
// return this.deleteDocument(user, doc.id);
|
return this.deleteDocument(user, doc.id);
|
||||||
// })
|
})
|
||||||
// );
|
);
|
||||||
// }
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 删除文档
|
* 删除文档
|
||||||
* @param idd
|
* @param idd
|
||||||
*/
|
*/
|
||||||
async deleteDocument(user: OutUser, documentId) {
|
async deleteDocument(user: IUser, documentId) {
|
||||||
const document = await this.documentRepo.findOne(documentId);
|
const document = await this.documentRepo.findOne(documentId);
|
||||||
|
|
||||||
if (document.isWikiHome) {
|
if (document.isWikiHome) {
|
||||||
const isWikiExist = await this.wikiService.findById(document.wikiId);
|
const isWikiExist = await this.wikiService.findById(document.wikiId);
|
||||||
if (isWikiExist) {
|
if (isWikiExist) {
|
||||||
|
@ -435,6 +370,7 @@ export class DocumentService {
|
||||||
const children = await this.documentRepo.find({
|
const children = await this.documentRepo.find({
|
||||||
parentDocumentId: document.id,
|
parentDocumentId: document.id,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (children && children.length) {
|
if (children && children.length) {
|
||||||
const parentDocumentId = document.parentDocumentId;
|
const parentDocumentId = document.parentDocumentId;
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
|
@ -448,11 +384,10 @@ export class DocumentService {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO:权限删除
|
await Promise.all([
|
||||||
// const auths = await this.documentUserRepo.find({ documentId });
|
this.authService.deleteDocument(document.organizationId, document.wikiId, document.id),
|
||||||
// await this.documentUserRepo.remove(auths);
|
this.documentRepo.remove(document),
|
||||||
|
]);
|
||||||
return this.documentRepo.remove(document);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -462,7 +397,7 @@ export class DocumentService {
|
||||||
* @param dto
|
* @param dto
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
public async updateDocument(user: OutUser, documentId: string, dto: UpdateDocumentDto) {
|
public async updateDocument(user: IUser, documentId: string, dto: UpdateDocumentDto) {
|
||||||
const document = await this.documentRepo.findOne(documentId);
|
const document = await this.documentRepo.findOne(documentId);
|
||||||
|
|
||||||
await this.authService.canEdit(user.id, {
|
await this.authService.canEdit(user.id, {
|
||||||
|
@ -511,13 +446,40 @@ export class DocumentService {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取指定用户在指定文档的权限
|
||||||
|
* @param userId
|
||||||
|
* @param documentId
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
public async getDocumentUserAuth(userId, documentId) {
|
||||||
|
const document = await this.documentRepo.findOne(documentId);
|
||||||
|
const authority = await this.authService.getAuth(userId, {
|
||||||
|
organizationId: document.organizationId,
|
||||||
|
wikiId: document.wikiId,
|
||||||
|
documentId: document.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
...authority,
|
||||||
|
readable: [AuthEnum.creator, AuthEnum.admin, AuthEnum.member].includes(authority.auth),
|
||||||
|
editable: [AuthEnum.creator, AuthEnum.admin].includes(authority.auth),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取文档历史版本
|
* 获取文档历史版本
|
||||||
* @param user
|
* @param user
|
||||||
* @param documentId
|
* @param documentId
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
public async getDocumentVersion(user: OutUser, documentId: string) {
|
public async getDocumentVersion(user: IUser, documentId: string) {
|
||||||
|
const document = await this.documentRepo.findOne(documentId);
|
||||||
|
await this.authService.canView(user.id, {
|
||||||
|
organizationId: document.organizationId,
|
||||||
|
wikiId: document.wikiId,
|
||||||
|
documentId: document.id,
|
||||||
|
});
|
||||||
const data = await this.documentVersionService.getDocumentVersions(documentId);
|
const data = await this.documentVersionService.getDocumentVersions(documentId);
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
@ -526,7 +488,7 @@ export class DocumentService {
|
||||||
* 分享(或关闭分享)文档
|
* 分享(或关闭分享)文档
|
||||||
* @param id
|
* @param id
|
||||||
*/
|
*/
|
||||||
async shareDocument(user: OutUser, documentId, dto: ShareDocumentDto, nextStatus = null) {
|
async shareDocument(user: IUser, documentId, dto: ShareDocumentDto, nextStatus = null) {
|
||||||
const document = await this.documentRepo.findOne(documentId);
|
const document = await this.documentRepo.findOne(documentId);
|
||||||
await this.authService.canEdit(user.id, {
|
await this.authService.canEdit(user.id, {
|
||||||
organizationId: document.organizationId,
|
organizationId: document.organizationId,
|
||||||
|
@ -570,7 +532,6 @@ export class DocumentService {
|
||||||
]);
|
]);
|
||||||
// 异步创建
|
// 异步创建
|
||||||
this.viewService.create(null, document);
|
this.viewService.create(null, document);
|
||||||
|
|
||||||
return { ...doc, views, wiki, createUser };
|
return { ...doc, views, wiki, createUser };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -581,7 +542,7 @@ export class DocumentService {
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
public async getChildrenDocuments(
|
public async getChildrenDocuments(
|
||||||
user: OutUser,
|
user: IUser,
|
||||||
data: {
|
data: {
|
||||||
wikiId: string;
|
wikiId: string;
|
||||||
documentId?: string;
|
documentId?: string;
|
||||||
|
@ -694,7 +655,7 @@ export class DocumentService {
|
||||||
* @param dto
|
* @param dto
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
public async getRecentDocuments(user: OutUser, organizationId) {
|
public async getRecentDocuments(user: IUser, organizationId) {
|
||||||
await this.authService.canView(user.id, {
|
await this.authService.canView(user.id, {
|
||||||
organizationId: organizationId,
|
organizationId: organizationId,
|
||||||
wikiId: null,
|
wikiId: null,
|
||||||
|
@ -744,24 +705,25 @@ export class DocumentService {
|
||||||
.createQueryBuilder('document')
|
.createQueryBuilder('document')
|
||||||
.andWhere('document.organizationId = :organizationId')
|
.andWhere('document.organizationId = :organizationId')
|
||||||
.andWhere('document.title LIKE :keyword')
|
.andWhere('document.title LIKE :keyword')
|
||||||
// FIXME: 编辑器内容的 json 字段可能也被匹配
|
|
||||||
.orWhere('document.content LIKE :keyword')
|
.orWhere('document.content LIKE :keyword')
|
||||||
.setParameter('organizationId', organizationId)
|
.setParameter('organizationId', organizationId)
|
||||||
.setParameter('keyword', `%${keyword}%`)
|
.setParameter('keyword', `%${keyword}%`)
|
||||||
.getMany();
|
.getMany();
|
||||||
|
|
||||||
// const ret = await Promise.all(
|
const ret = await Promise.all(
|
||||||
// res.map(async (doc) => {
|
res.map(async (doc) => {
|
||||||
// const auth = await this.documentUserRepo.findOne({
|
const auth = await this.authService.getAuth(user.Id, {
|
||||||
// documentId: doc.id,
|
organizationId: doc.organizationId,
|
||||||
// userId: user.id,
|
wikiId: doc.wikiId,
|
||||||
// });
|
documentId: doc.id,
|
||||||
// return auth && auth.readable ? doc : null;
|
});
|
||||||
// })
|
|
||||||
// );
|
|
||||||
|
|
||||||
// const data = ret.filter(Boolean);
|
return auth && [AuthEnum.creator, AuthEnum.admin, AuthEnum.member].includes(auth.auth) ? doc : null;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
return res;
|
const data = ret.filter(Boolean);
|
||||||
|
|
||||||
|
return data;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { MessageEntity } from '@entities/message.entity';
|
import { MessageEntity } from '@entities/message.entity';
|
||||||
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
|
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
|
||||||
import { InjectRepository } from '@nestjs/typeorm';
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
import { OutUser } from '@services/user.service';
|
import { IUser } from '@think/domains';
|
||||||
import { Repository } from 'typeorm';
|
import { Repository } from 'typeorm';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
|
@ -17,8 +17,8 @@ export class MessageService {
|
||||||
* @param msg
|
* @param msg
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
async notify(user: OutUser, msg) {
|
async notify(userId: IUser['id'], msg) {
|
||||||
const data = { userId: user.id, ...msg };
|
const data = { userId, ...msg };
|
||||||
const res = await this.messageRepo.create(data);
|
const res = await this.messageRepo.create(data);
|
||||||
const ret = await this.messageRepo.save(res);
|
const ret = await this.messageRepo.save(res);
|
||||||
return ret;
|
return ret;
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import { OperateUserAuthDto } from '@dtos/auth.dto';
|
import { OperateUserAuthDto } from '@dtos/auth.dto';
|
||||||
import { CreateOrganizationDto } from '@dtos/organization.dto';
|
import { CreateOrganizationDto } from '@dtos/organization.dto';
|
||||||
// import { OperateUserAuthDto } from '@dtos/organization-user.dto';
|
|
||||||
import { OrganizationEntity } from '@entities/organization.entity';
|
import { OrganizationEntity } from '@entities/organization.entity';
|
||||||
import { UserEntity } from '@entities/user.entity';
|
import { UserEntity } from '@entities/user.entity';
|
||||||
import { forwardRef, HttpException, HttpStatus, Inject, Injectable } from '@nestjs/common';
|
import { forwardRef, HttpException, HttpStatus, Inject, Injectable } from '@nestjs/common';
|
||||||
|
@ -8,6 +7,7 @@ import { InjectRepository } from '@nestjs/typeorm';
|
||||||
import { AuthService } from '@services/auth.service';
|
import { AuthService } from '@services/auth.service';
|
||||||
import { MessageService } from '@services/message.service';
|
import { MessageService } from '@services/message.service';
|
||||||
import { UserService } from '@services/user.service';
|
import { UserService } from '@services/user.service';
|
||||||
|
import { WikiService } from '@services/wiki.service';
|
||||||
import { AuthEnum, buildMessageURL, IOrganization, IUser } from '@think/domains';
|
import { AuthEnum, buildMessageURL, IOrganization, IUser } from '@think/domains';
|
||||||
import { Repository } from 'typeorm';
|
import { Repository } from 'typeorm';
|
||||||
|
|
||||||
|
@ -24,7 +24,10 @@ export class OrganizationService {
|
||||||
private readonly userService: UserService,
|
private readonly userService: UserService,
|
||||||
|
|
||||||
@Inject(forwardRef(() => MessageService))
|
@Inject(forwardRef(() => MessageService))
|
||||||
private readonly messageService: MessageService
|
private readonly messageService: MessageService,
|
||||||
|
|
||||||
|
@Inject(forwardRef(() => WikiService))
|
||||||
|
private readonly wikiService: WikiService
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public async findById(id: string) {
|
public async findById(id: string) {
|
||||||
|
@ -90,6 +93,27 @@ export class OrganizationService {
|
||||||
return await this.organizationRepo.save(await this.organizationRepo.merge(oldData, dto));
|
return await this.organizationRepo.save(await this.organizationRepo.merge(oldData, dto));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除组织
|
||||||
|
* @param user
|
||||||
|
* @param organizationId
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
async deleteOrganization(user: IUser, organizationId) {
|
||||||
|
const organization = await this.organizationRepo.findOne(organizationId);
|
||||||
|
await this.authService.canDelete(user.id, {
|
||||||
|
organizationId: organization.id,
|
||||||
|
wikiId: null,
|
||||||
|
documentId: null,
|
||||||
|
});
|
||||||
|
await Promise.all([
|
||||||
|
this.authService.deleteOrganization(organization.id),
|
||||||
|
this.organizationRepo.remove(organization),
|
||||||
|
this.wikiService.deleteOrganizationWiki(user, organizationId),
|
||||||
|
]);
|
||||||
|
return organization;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取用户个人组织
|
* 获取用户个人组织
|
||||||
* @param user
|
* @param user
|
||||||
|
@ -101,7 +125,7 @@ export class OrganizationService {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取用户除个人组织外可访问的组织
|
* 获取用户可访问的组织
|
||||||
* @param user
|
* @param user
|
||||||
*/
|
*/
|
||||||
public async getUserOrganizations(user: IUser) {
|
public async getUserOrganizations(user: IUser) {
|
||||||
|
@ -130,7 +154,7 @@ export class OrganizationService {
|
||||||
* @param shortId
|
* @param shortId
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
public async getMembers(user: IUser, id: IOrganization['id']) {
|
public async getMembers(user: IUser, id: IOrganization['id'], pagination) {
|
||||||
const organization = await this.organizationRepo.findOne({ id });
|
const organization = await this.organizationRepo.findOne({ id });
|
||||||
|
|
||||||
if (!organization) {
|
if (!organization) {
|
||||||
|
@ -143,7 +167,7 @@ export class OrganizationService {
|
||||||
documentId: null,
|
documentId: null,
|
||||||
});
|
});
|
||||||
|
|
||||||
const { data: usersAuth, total } = await this.authService.getUsersAuthInOrganization(organization.id);
|
const { data: usersAuth, total } = await this.authService.getUsersAuthInOrganization(organization.id, pagination);
|
||||||
|
|
||||||
const userIds = usersAuth.map((auth) => auth.userId);
|
const userIds = usersAuth.map((auth) => auth.userId);
|
||||||
const users = await this.userService.findByIds(userIds);
|
const users = await this.userService.findByIds(userIds);
|
||||||
|
@ -185,7 +209,7 @@ export class OrganizationService {
|
||||||
documentId: null,
|
documentId: null,
|
||||||
});
|
});
|
||||||
|
|
||||||
await this.messageService.notify(targetUser, {
|
await this.messageService.notify(targetUser.id, {
|
||||||
title: `您被添加到组织「${organization.name}」`,
|
title: `您被添加到组织「${organization.name}」`,
|
||||||
message: `您被添加到知识库「${organization.name}」,快去看看吧!`,
|
message: `您被添加到知识库「${organization.name}」,快去看看吧!`,
|
||||||
url: buildMessageURL('toOrganization')({
|
url: buildMessageURL('toOrganization')({
|
||||||
|
@ -223,7 +247,7 @@ export class OrganizationService {
|
||||||
documentId: null,
|
documentId: null,
|
||||||
});
|
});
|
||||||
|
|
||||||
await this.messageService.notify(targetUser, {
|
await this.messageService.notify(targetUser.id, {
|
||||||
title: `组织「${organization.name}」权限变更`,
|
title: `组织「${organization.name}」权限变更`,
|
||||||
message: `您在组织「${organization.name}」权限已变更,快去看看吧!`,
|
message: `您在组织「${organization.name}」权限已变更,快去看看吧!`,
|
||||||
url: buildMessageURL('toOrganization')({
|
url: buildMessageURL('toOrganization')({
|
||||||
|
@ -261,9 +285,9 @@ export class OrganizationService {
|
||||||
documentId: null,
|
documentId: null,
|
||||||
});
|
});
|
||||||
|
|
||||||
await this.messageService.notify(targetUser, {
|
await this.messageService.notify(targetUser.id, {
|
||||||
title: `组织「${organization.name}」权限收回`,
|
title: `组织「${organization.name}」权限收回`,
|
||||||
message: `您在组织「${organization.name}」权限已收回,快去看看吧!`,
|
message: `您在组织「${organization.name}」权限已收回!`,
|
||||||
url: buildMessageURL('toOrganization')({
|
url: buildMessageURL('toOrganization')({
|
||||||
organizationId: organization.id,
|
organizationId: organization.id,
|
||||||
}),
|
}),
|
||||||
|
|
|
@ -5,9 +5,9 @@ import { InjectRepository } from '@nestjs/typeorm';
|
||||||
import { AuthService } from '@services/auth.service';
|
import { AuthService } from '@services/auth.service';
|
||||||
import { DocumentService } from '@services/document.service';
|
import { DocumentService } from '@services/document.service';
|
||||||
import { OrganizationService } from '@services/organization.service';
|
import { OrganizationService } from '@services/organization.service';
|
||||||
import { OutUser, UserService } from '@services/user.service';
|
import { UserService } from '@services/user.service';
|
||||||
import { WikiService } from '@services/wiki.service';
|
import { WikiService } from '@services/wiki.service';
|
||||||
import { IDocument } from '@think/domains';
|
import { IDocument, IUser } from '@think/domains';
|
||||||
import * as lodash from 'lodash';
|
import * as lodash from 'lodash';
|
||||||
import { Repository } from 'typeorm';
|
import { Repository } from 'typeorm';
|
||||||
|
|
||||||
|
@ -39,7 +39,7 @@ export class StarService {
|
||||||
* @param dto
|
* @param dto
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
async toggleStar(user: OutUser, dto: StarDto) {
|
async toggleStar(user: IUser, dto: StarDto) {
|
||||||
const data = {
|
const data = {
|
||||||
...dto,
|
...dto,
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
|
@ -61,7 +61,7 @@ export class StarService {
|
||||||
* @param dto
|
* @param dto
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
async isStared(user: OutUser, dto: StarDto) {
|
async isStared(user: IUser, dto: StarDto) {
|
||||||
const res = await this.starRepo.findOne({ userId: user.id, ...dto });
|
const res = await this.starRepo.findOne({ userId: user.id, ...dto });
|
||||||
return Boolean(res);
|
return Boolean(res);
|
||||||
}
|
}
|
||||||
|
@ -71,7 +71,7 @@ export class StarService {
|
||||||
* @param user
|
* @param user
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
async getStarWikisInOrganization(user: OutUser, organizationId) {
|
async getStarWikisInOrganization(user: IUser, organizationId) {
|
||||||
await this.authService.canView(user.id, {
|
await this.authService.canView(user.id, {
|
||||||
organizationId: organizationId,
|
organizationId: organizationId,
|
||||||
wikiId: null,
|
wikiId: null,
|
||||||
|
@ -100,7 +100,7 @@ export class StarService {
|
||||||
* @param user
|
* @param user
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
async getStarDocumentsInWiki(user: OutUser, dto: StarDto) {
|
async getStarDocumentsInWiki(user: IUser, dto: StarDto) {
|
||||||
const records = await this.starRepo.find({
|
const records = await this.starRepo.find({
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
wikiId: dto.wikiId,
|
wikiId: dto.wikiId,
|
||||||
|
@ -114,7 +114,7 @@ export class StarService {
|
||||||
const createUser = await this.userService.findById(doc.createUserId);
|
const createUser = await this.userService.findById(doc.createUserId);
|
||||||
return { createUser, ...doc };
|
return { createUser, ...doc };
|
||||||
})
|
})
|
||||||
)) as Array<IDocument & { createUser: OutUser }>;
|
)) as Array<IDocument & { createUser: IUser }>;
|
||||||
|
|
||||||
return withCreateUserRes
|
return withCreateUserRes
|
||||||
.map((document) => {
|
.map((document) => {
|
||||||
|
@ -134,7 +134,7 @@ export class StarService {
|
||||||
* @param user
|
* @param user
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
async getStarDocumentsInOrganization(user: OutUser, organizationId) {
|
async getStarDocumentsInOrganization(user: IUser, organizationId) {
|
||||||
await this.authService.canView(user.id, {
|
await this.authService.canView(user.id, {
|
||||||
organizationId: organizationId,
|
organizationId: organizationId,
|
||||||
wikiId: null,
|
wikiId: null,
|
||||||
|
|
|
@ -40,21 +40,18 @@ export class SystemService {
|
||||||
*/
|
*/
|
||||||
private async loadFromConfigFile() {
|
private async loadFromConfigFile() {
|
||||||
const currentConfig = await this.getConfigFromDatabase();
|
const currentConfig = await this.getConfigFromDatabase();
|
||||||
const emailConfigKeys = ['emailServiceHost', 'emailServicePort', 'emailServiceUser', 'emailServicePassword'];
|
|
||||||
|
|
||||||
if (currentConfig && emailConfigKeys.every((configKey) => Boolean(currentConfig[configKey]))) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 同步邮件服务配置
|
// 同步邮件服务配置
|
||||||
const emailConfigFromConfigFile = await this.confifgService.get('server.email');
|
const emailConfigFromConfigFile = await this.confifgService.get('server.email');
|
||||||
|
|
||||||
let emailConfig = {};
|
let emailConfig = {};
|
||||||
|
|
||||||
if (emailConfigFromConfigFile && typeof emailConfigFromConfigFile === 'object') {
|
if (emailConfigFromConfigFile && typeof emailConfigFromConfigFile === 'object') {
|
||||||
emailConfig = {
|
emailConfig = {
|
||||||
emailServiceHost: emailConfigFromConfigFile.host,
|
emailServiceHost: currentConfig ? currentConfig.emailServiceHost : emailConfigFromConfigFile.host,
|
||||||
emailServicePort: emailConfigFromConfigFile.port,
|
emailServicePort: currentConfig ? currentConfig.emailServicePort : emailConfigFromConfigFile.port,
|
||||||
emailServiceUser: emailConfigFromConfigFile.user,
|
emailServiceUser: currentConfig ? currentConfig.emailServiceUser : emailConfigFromConfigFile.user,
|
||||||
emailServicePassword: emailConfigFromConfigFile.password,
|
emailServicePassword: currentConfig ? currentConfig.emailServicePassword : emailConfigFromConfigFile.password,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -118,4 +115,9 @@ export class SystemService {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getPublicConfig() {
|
||||||
|
const config = await this.getConfigFromDatabase();
|
||||||
|
return { isSystemLocked: config.isSystemLocked, enableEmailVerify: config.enableEmailVerify };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,8 @@ import { TemplateDto } from '@dtos/template.dto';
|
||||||
import { TemplateEntity } from '@entities/template.entity';
|
import { TemplateEntity } from '@entities/template.entity';
|
||||||
import { forwardRef, HttpException, HttpStatus, Inject, Injectable } from '@nestjs/common';
|
import { forwardRef, HttpException, HttpStatus, Inject, Injectable } from '@nestjs/common';
|
||||||
import { InjectRepository } from '@nestjs/typeorm';
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
import { OutUser, UserService } from '@services/user.service';
|
import { UserService } from '@services/user.service';
|
||||||
|
import { IUser } from '@think/domains';
|
||||||
import { instanceToPlain } from 'class-transformer';
|
import { instanceToPlain } from 'class-transformer';
|
||||||
import * as lodash from 'lodash';
|
import * as lodash from 'lodash';
|
||||||
import { Repository } from 'typeorm';
|
import { Repository } from 'typeorm';
|
||||||
|
@ -45,7 +46,7 @@ export class TemplateService {
|
||||||
* @param dto
|
* @param dto
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
async create(user: OutUser, dto: TemplateDto) {
|
async create(user: IUser, dto: TemplateDto) {
|
||||||
const data = {
|
const data = {
|
||||||
createUserId: user.id,
|
createUserId: user.id,
|
||||||
...dto,
|
...dto,
|
||||||
|
@ -103,7 +104,7 @@ export class TemplateService {
|
||||||
* @param templateId
|
* @param templateId
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
async useTemplate(user: OutUser, templateId) {
|
async useTemplate(user: IUser, templateId) {
|
||||||
const data = await this.templateRepo.findOne(templateId);
|
const data = await this.templateRepo.findOne(templateId);
|
||||||
if (user.id !== data.createUserId && !data.isPublic) {
|
if (user.id !== data.createUserId && !data.isPublic) {
|
||||||
throw new HttpException('您不是模板创建者,无法编辑', HttpStatus.FORBIDDEN);
|
throw new HttpException('您不是模板创建者,无法编辑', HttpStatus.FORBIDDEN);
|
||||||
|
@ -146,7 +147,7 @@ export class TemplateService {
|
||||||
* @param queryParams
|
* @param queryParams
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
async getOwnTemplates(user: OutUser, queryParams) {
|
async getOwnTemplates(user: IUser, queryParams) {
|
||||||
const query = this.templateRepo
|
const query = this.templateRepo
|
||||||
.createQueryBuilder('template')
|
.createQueryBuilder('template')
|
||||||
.where('template.createUserId=:createUserId')
|
.where('template.createUserId=:createUserId')
|
||||||
|
|
|
@ -18,8 +18,6 @@ import { IUser, UserStatus } from '@think/domains';
|
||||||
import { instanceToPlain } from 'class-transformer';
|
import { instanceToPlain } from 'class-transformer';
|
||||||
import { Repository } from 'typeorm';
|
import { Repository } from 'typeorm';
|
||||||
|
|
||||||
export type OutUser = Omit<UserEntity, 'comparePassword' | 'encryptPassword' | 'encrypt' | 'password'>;
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class UserService {
|
export class UserService {
|
||||||
constructor(
|
constructor(
|
||||||
|
@ -95,9 +93,9 @@ export class UserService {
|
||||||
* @param id
|
* @param id
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
async findById(id): Promise<OutUser> {
|
async findById(id): Promise<IUser> {
|
||||||
const user = await this.userRepo.findOne(id);
|
const user = await this.userRepo.findOne(id);
|
||||||
return instanceToPlain(user) as OutUser;
|
return instanceToPlain(user) as IUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -105,7 +103,7 @@ export class UserService {
|
||||||
* @param opts
|
* @param opts
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
async findOne(opts: Partial<OutUser>): Promise<UserEntity> {
|
async findOne(opts: Partial<UserEntity>): Promise<UserEntity> {
|
||||||
const user = await this.userRepo.findOne(opts);
|
const user = await this.userRepo.findOne(opts);
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
@ -115,9 +113,9 @@ export class UserService {
|
||||||
* @param id
|
* @param id
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
async findByIds(ids): Promise<OutUser[]> {
|
async findByIds(ids): Promise<IUser[]> {
|
||||||
const users = await this.userRepo.findByIds(ids);
|
const users = await this.userRepo.findByIds(ids);
|
||||||
return users.map((user) => instanceToPlain(user)) as OutUser[];
|
return users.map((user) => instanceToPlain(user)) as IUser[];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -125,7 +123,7 @@ export class UserService {
|
||||||
* @param user CreateUserDto
|
* @param user CreateUserDto
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
async createUser(user: RegisterUserDto): Promise<OutUser> {
|
async createUser(user: RegisterUserDto): Promise<IUser> {
|
||||||
const currentSystemConfig = await this.systemService.getConfigFromDatabase();
|
const currentSystemConfig = await this.systemService.getConfigFromDatabase();
|
||||||
|
|
||||||
if (currentSystemConfig.isSystemLocked) {
|
if (currentSystemConfig.isSystemLocked) {
|
||||||
|
@ -144,7 +142,10 @@ export class UserService {
|
||||||
throw new HttpException('该邮箱已被注册', HttpStatus.BAD_REQUEST);
|
throw new HttpException('该邮箱已被注册', HttpStatus.BAD_REQUEST);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(await this.verifyService.checkVerifyCode(user.email, user.verifyCode))) {
|
if (
|
||||||
|
currentSystemConfig.enableEmailVerify &&
|
||||||
|
!(await this.verifyService.checkVerifyCode(user.email, user.verifyCode))
|
||||||
|
) {
|
||||||
throw new HttpException('验证码不正确,请检查', HttpStatus.BAD_REQUEST);
|
throw new HttpException('验证码不正确,请检查', HttpStatus.BAD_REQUEST);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -158,20 +159,7 @@ export class UserService {
|
||||||
isPersonal: true,
|
isPersonal: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
// const wiki = await this.wikiService.createWiki(createdUser, {
|
return instanceToPlain(createdUser) as IUser;
|
||||||
// name: createdUser.name,
|
|
||||||
// description: `${createdUser.name}的个人空间`,
|
|
||||||
// });
|
|
||||||
// await this.starService.toggleStar(createdUser, {
|
|
||||||
// wikiId: wiki.id,
|
|
||||||
// });
|
|
||||||
// await this.messageService.notify(createdUser, {
|
|
||||||
// title: `欢迎「${createdUser.name}」`,
|
|
||||||
// message: `系统已自动为您创建知识库,快去看看吧!`,
|
|
||||||
// url: `/wiki/${wiki.id}`,
|
|
||||||
// });
|
|
||||||
|
|
||||||
return instanceToPlain(createdUser) as OutUser;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -213,7 +201,7 @@ export class UserService {
|
||||||
* @param user
|
* @param user
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
async login(user: LoginUserDto): Promise<{ user: OutUser; token: string; domain: string; expiresIn: number }> {
|
async login(user: LoginUserDto): Promise<{ user: IUser; token: string; domain: string; expiresIn: number }> {
|
||||||
const currentSystemConfig = await this.systemService.getConfigFromDatabase();
|
const currentSystemConfig = await this.systemService.getConfigFromDatabase();
|
||||||
|
|
||||||
const { name, password } = user;
|
const { name, password } = user;
|
||||||
|
@ -237,7 +225,7 @@ export class UserService {
|
||||||
throw new HttpException('用户已锁定,无法登录', HttpStatus.BAD_REQUEST);
|
throw new HttpException('用户已锁定,无法登录', HttpStatus.BAD_REQUEST);
|
||||||
}
|
}
|
||||||
|
|
||||||
const res = instanceToPlain(existUser) as OutUser;
|
const res = instanceToPlain(existUser) as IUser;
|
||||||
const token = this.jwtService.sign(res);
|
const token = this.jwtService.sign(res);
|
||||||
const domain = this.confifgService.get('client.siteDomain');
|
const domain = this.confifgService.get('client.siteDomain');
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
@ -261,11 +249,11 @@ export class UserService {
|
||||||
* @param dto
|
* @param dto
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
async updateUser(user: UserEntity, dto: UpdateUserDto): Promise<OutUser> {
|
async updateUser(user: UserEntity, dto: UpdateUserDto): Promise<IUser> {
|
||||||
const oldData = await this.userRepo.findOne(user.id);
|
const oldData = await this.userRepo.findOne(user.id);
|
||||||
const res = await this.userRepo.merge(oldData, dto);
|
const res = await this.userRepo.merge(oldData, dto);
|
||||||
const ret = await this.userRepo.save(res);
|
const ret = await this.userRepo.save(res);
|
||||||
return instanceToPlain(ret) as OutUser;
|
return instanceToPlain(ret) as IUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
async decodeToken(token) {
|
async decodeToken(token) {
|
||||||
|
|
|
@ -1,54 +1,49 @@
|
||||||
import { VerifyEntity } from '@entities/verify.entity';
|
import { RedisDBEnum } from '@constants/*';
|
||||||
|
import { buildRedis } from '@helpers/redis.helper';
|
||||||
import { forwardRef, HttpException, HttpStatus, Inject, Injectable } from '@nestjs/common';
|
import { forwardRef, HttpException, HttpStatus, Inject, Injectable } from '@nestjs/common';
|
||||||
import { InjectRepository } from '@nestjs/typeorm';
|
|
||||||
import { SystemService } from '@services/system.service';
|
import { SystemService } from '@services/system.service';
|
||||||
|
import Redis from 'ioredis';
|
||||||
import { randomInt } from 'node:crypto';
|
import { randomInt } from 'node:crypto';
|
||||||
import { Repository } from 'typeorm';
|
|
||||||
import { isEmail } from 'validator';
|
import { isEmail } from 'validator';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class VerifyService {
|
export class VerifyService {
|
||||||
constructor(
|
private redis: Redis;
|
||||||
@InjectRepository(VerifyEntity)
|
|
||||||
private readonly verifyRepo: Repository<VerifyEntity>,
|
|
||||||
|
|
||||||
|
constructor(
|
||||||
@Inject(forwardRef(() => SystemService))
|
@Inject(forwardRef(() => SystemService))
|
||||||
private readonly systemService: SystemService
|
private readonly systemService: SystemService
|
||||||
) {}
|
) {
|
||||||
|
this.buildRedis();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
private async buildRedis() {
|
||||||
* 删除验证记录
|
try {
|
||||||
* @param record
|
this.redis = await buildRedis(RedisDBEnum.verify);
|
||||||
*/
|
console.log('[think] 验证码服务启动成功');
|
||||||
private async deleteVerifyCode(id) {
|
} catch (e) {
|
||||||
const record = await this.verifyRepo.findOne(id);
|
console.error(`[think] 验证码服务启动错误: "${e.message}"`);
|
||||||
await this.verifyRepo.remove(record);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 向指定邮箱发送验证码
|
* 向指定邮箱发送验证码
|
||||||
* @param email
|
* @param email
|
||||||
*/
|
*/
|
||||||
public async sendVerifyCode(email: string) {
|
public sendVerifyCode = async (email: string) => {
|
||||||
if (!email || !isEmail(email)) {
|
if (!email || !isEmail(email)) {
|
||||||
throw new HttpException('请检查邮箱地址', HttpStatus.BAD_REQUEST);
|
throw new HttpException('请检查邮箱地址', HttpStatus.BAD_REQUEST);
|
||||||
}
|
}
|
||||||
|
|
||||||
const verifyCode = randomInt(1000000).toString().padStart(6, '0');
|
const verifyCode = randomInt(1000000).toString().padStart(6, '0');
|
||||||
const record = await this.verifyRepo.save(await this.verifyRepo.create({ email, verifyCode }));
|
|
||||||
console.log(verifyCode);
|
|
||||||
|
|
||||||
|
await this.redis.set(`verify-${email}`, verifyCode, 'EX', 10);
|
||||||
await this.systemService.sendEmail({
|
await this.systemService.sendEmail({
|
||||||
to: email,
|
to: email,
|
||||||
subject: '验证码',
|
subject: '验证码',
|
||||||
html: `<p>您的验证码为 ${verifyCode}</p>`,
|
html: `<p>您的验证码为 ${verifyCode}</p>`,
|
||||||
});
|
});
|
||||||
|
};
|
||||||
const timer = setTimeout(() => {
|
|
||||||
this.deleteVerifyCode(record.id);
|
|
||||||
clearTimeout(timer);
|
|
||||||
}, 5 * 60 * 1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 检验验证码
|
* 检验验证码
|
||||||
|
@ -56,17 +51,21 @@ export class VerifyService {
|
||||||
* @param verifyCode
|
* @param verifyCode
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
public async checkVerifyCode(email: string, verifyCode: string) {
|
public checkVerifyCode = async (email: string, verifyCode: string) => {
|
||||||
if (!email || !isEmail(email)) {
|
if (!email || !isEmail(email)) {
|
||||||
throw new HttpException('请检查邮箱地址', HttpStatus.BAD_REQUEST);
|
throw new HttpException('请检查邮箱地址', HttpStatus.BAD_REQUEST);
|
||||||
}
|
}
|
||||||
|
|
||||||
const ret = await this.verifyRepo.findOne({ email, verifyCode });
|
const ret = await this.redis.get(`verify-${email}`);
|
||||||
|
|
||||||
if (!ret) {
|
if (!ret) {
|
||||||
|
throw new HttpException('验证码已过期,请重新获取', HttpStatus.BAD_REQUEST);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret !== verifyCode) {
|
||||||
throw new HttpException('验证码错误', HttpStatus.BAD_REQUEST);
|
throw new HttpException('验证码错误', HttpStatus.BAD_REQUEST);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Boolean(ret);
|
return Boolean(ret);
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,23 +1,17 @@
|
||||||
import { RedisDBEnum } from '@constants/*';
|
import { RedisDBEnum } from '@constants/*';
|
||||||
import { DocumentEntity } from '@entities/document.entity';
|
import { DocumentEntity } from '@entities/document.entity';
|
||||||
import { UserEntity } from '@entities/user.entity';
|
import { UserEntity } from '@entities/user.entity';
|
||||||
import { ViewEntity } from '@entities/view.entity';
|
|
||||||
import { buildRedis } from '@helpers/redis.helper';
|
import { buildRedis } from '@helpers/redis.helper';
|
||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
import { InjectRepository } from '@nestjs/typeorm';
|
|
||||||
import { IDocument, IOrganization, IUser } from '@think/domains';
|
import { IDocument, IOrganization, IUser } from '@think/domains';
|
||||||
import Redis from 'ioredis';
|
import Redis from 'ioredis';
|
||||||
import * as lodash from 'lodash';
|
import * as lodash from 'lodash';
|
||||||
import { Repository } from 'typeorm';
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ViewService {
|
export class ViewService {
|
||||||
private redis: Redis;
|
private redis: Redis;
|
||||||
|
|
||||||
constructor(
|
constructor() {
|
||||||
@InjectRepository(ViewEntity)
|
|
||||||
private readonly viewRepo: Repository<ViewEntity>
|
|
||||||
) {
|
|
||||||
this.buildRedis();
|
this.buildRedis();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,21 +2,17 @@ import { OperateUserAuthDto } from '@dtos/auth.dto';
|
||||||
import { CreateWikiDto } from '@dtos/create-wiki.dto';
|
import { CreateWikiDto } from '@dtos/create-wiki.dto';
|
||||||
import { ShareWikiDto } from '@dtos/share-wiki.dto';
|
import { ShareWikiDto } from '@dtos/share-wiki.dto';
|
||||||
import { UpdateWikiDto } from '@dtos/update-wiki.dto';
|
import { UpdateWikiDto } from '@dtos/update-wiki.dto';
|
||||||
// import { WikiUserDto } from '@dtos/wiki-user.dto';
|
|
||||||
import { WikiEntity } from '@entities/wiki.entity';
|
import { WikiEntity } from '@entities/wiki.entity';
|
||||||
// import { WikiUserEntity } from '@entities/wiki-user.entity';
|
|
||||||
import { array2tree } from '@helpers/tree.helper';
|
import { array2tree } from '@helpers/tree.helper';
|
||||||
import { forwardRef, HttpException, HttpStatus, Inject, Injectable } from '@nestjs/common';
|
import { forwardRef, HttpException, HttpStatus, Inject, Injectable } from '@nestjs/common';
|
||||||
import { InjectRepository } from '@nestjs/typeorm';
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
import { AuthService } from '@services/auth.service';
|
import { AuthService } from '@services/auth.service';
|
||||||
import { DocumentService } from '@services/document.service';
|
import { DocumentService } from '@services/document.service';
|
||||||
import { MessageService } from '@services/message.service';
|
import { MessageService } from '@services/message.service';
|
||||||
import { OrganizationService } from '@services/organization.service';
|
|
||||||
import { StarService } from '@services/star.service';
|
import { StarService } from '@services/star.service';
|
||||||
import { UserService } from '@services/user.service';
|
import { UserService } from '@services/user.service';
|
||||||
import { OutUser } from '@services/user.service';
|
|
||||||
import { ViewService } from '@services/view.service';
|
import { ViewService } from '@services/view.service';
|
||||||
import { AuthEnum, buildMessageURL, DocumentStatus, IPagination, WikiStatus, WikiUserRole } from '@think/domains';
|
import { AuthEnum, buildMessageURL, DocumentStatus, IPagination, IUser, WikiStatus } from '@think/domains';
|
||||||
import { instanceToPlain } from 'class-transformer';
|
import { instanceToPlain } from 'class-transformer';
|
||||||
import * as lodash from 'lodash';
|
import * as lodash from 'lodash';
|
||||||
import { Repository } from 'typeorm';
|
import { Repository } from 'typeorm';
|
||||||
|
@ -30,9 +26,6 @@ export class WikiService {
|
||||||
@Inject(forwardRef(() => AuthService))
|
@Inject(forwardRef(() => AuthService))
|
||||||
private readonly authService: AuthService,
|
private readonly authService: AuthService,
|
||||||
|
|
||||||
// @InjectRepository(WikiUserEntity)
|
|
||||||
// private readonly wikiUserRepo: Repository<WikiUserEntity>,
|
|
||||||
|
|
||||||
@Inject(forwardRef(() => MessageService))
|
@Inject(forwardRef(() => MessageService))
|
||||||
private readonly messageService: MessageService,
|
private readonly messageService: MessageService,
|
||||||
|
|
||||||
|
@ -46,10 +39,7 @@ export class WikiService {
|
||||||
private readonly userService: UserService,
|
private readonly userService: UserService,
|
||||||
|
|
||||||
@Inject(forwardRef(() => ViewService))
|
@Inject(forwardRef(() => ViewService))
|
||||||
private readonly viewService: ViewService,
|
private readonly viewService: ViewService
|
||||||
|
|
||||||
@Inject(forwardRef(() => OrganizationService))
|
|
||||||
private readonly organizationService: OrganizationService
|
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -73,115 +63,6 @@ export class WikiService {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
// /**
|
|
||||||
// * 目标用户是否为知识库成员
|
|
||||||
// * @param wikiId
|
|
||||||
// * @param userId
|
|
||||||
// * @returns
|
|
||||||
// */
|
|
||||||
// public async isMember(wikiId: string, userId: string) {
|
|
||||||
// const auth = await this.wikiUserRepo.findOne({ wikiId, userId });
|
|
||||||
// return !!auth && [WikiUserRole.admin, WikiUserRole.normal].includes(auth.userRole);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// /**
|
|
||||||
// * 获取知识库成员信息
|
|
||||||
// * @param wikiId
|
|
||||||
// * @param userId
|
|
||||||
// * @returns
|
|
||||||
// */
|
|
||||||
// public async findWikiUser(wikiId: string, userId: string) {
|
|
||||||
// return await this.wikiUserRepo.findOne({
|
|
||||||
// userId,
|
|
||||||
// wikiId,
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
|
|
||||||
// /**
|
|
||||||
// * 操作知识库成员(添加、修改角色)
|
|
||||||
// * @param param0
|
|
||||||
// * @returns
|
|
||||||
// */
|
|
||||||
// async operateWikiUser({ wikiId, currentUserId, targetUserId, targetUserRole }) {
|
|
||||||
// const wiki = await this.wikiRepo.findOne(wikiId);
|
|
||||||
|
|
||||||
// // await this.organizationService.canUserVisitOrganization(currentUserId, wiki.organizationId);
|
|
||||||
|
|
||||||
// // 1. 检查知识库
|
|
||||||
// if (!wiki) {
|
|
||||||
// throw new HttpException('目标知识库不存在', HttpStatus.BAD_REQUEST);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// const isCurrentUserCreator = currentUserId === wiki.createUserId;
|
|
||||||
// const isTargetUserCreator = targetUserId === wiki.createUserId;
|
|
||||||
|
|
||||||
// const currentWikiUserRole = isCurrentUserCreator
|
|
||||||
// ? WikiUserRole.admin
|
|
||||||
// : (
|
|
||||||
// await this.wikiUserRepo.findOne({
|
|
||||||
// wikiId: wiki.id,
|
|
||||||
// userId: currentUserId,
|
|
||||||
// })
|
|
||||||
// ).userRole;
|
|
||||||
|
|
||||||
// // 2. 检查成员是否存在
|
|
||||||
// const targetUser = await this.userService.findOne(targetUserId);
|
|
||||||
// const targetWikiUser = await this.wikiUserRepo.findOne({
|
|
||||||
// wikiId: wiki.id,
|
|
||||||
// userId: targetUserId,
|
|
||||||
// });
|
|
||||||
|
|
||||||
// if (targetWikiUser) {
|
|
||||||
// if (targetWikiUser.userRole === targetUserRole) return;
|
|
||||||
|
|
||||||
// // 2.1 修改知识库用户角色
|
|
||||||
// if (targetUserRole === WikiUserRole.admin) {
|
|
||||||
// if (currentWikiUserRole !== WikiUserRole.admin) {
|
|
||||||
// throw new HttpException('您无权限进行该操作', HttpStatus.FORBIDDEN);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// const userRole = isTargetUserCreator ? WikiUserRole.admin : targetUserRole;
|
|
||||||
// const newData = {
|
|
||||||
// ...targetWikiUser,
|
|
||||||
// userRole,
|
|
||||||
// };
|
|
||||||
// const res = await this.wikiUserRepo.merge(targetWikiUser, newData);
|
|
||||||
// const ret = await this.wikiUserRepo.save(res);
|
|
||||||
// await this.messageService.notify(targetUser, {
|
|
||||||
// title: `您在「${wiki.name}」的权限已变更`,
|
|
||||||
// message: `您在「${wiki.name}」的权限已变更,快去看看吧!`,
|
|
||||||
// url: buildMessageURL('toWiki')({
|
|
||||||
// organizationId: wiki.organizationId,
|
|
||||||
// wikiId: wiki.id,
|
|
||||||
// }),
|
|
||||||
// });
|
|
||||||
// return ret;
|
|
||||||
// } else {
|
|
||||||
// // 2.2. 添加知识库新用户
|
|
||||||
// if (currentWikiUserRole !== WikiUserRole.admin) {
|
|
||||||
// throw new HttpException('您无权限进行该操作', HttpStatus.FORBIDDEN);
|
|
||||||
// }
|
|
||||||
// const data: Partial<WikiUserEntity> = {
|
|
||||||
// wikiId,
|
|
||||||
// organizationId: wiki.organizationId,
|
|
||||||
// createUserId: wiki.createUserId,
|
|
||||||
// userId: targetUserId,
|
|
||||||
// userRole: isTargetUserCreator ? WikiUserRole.admin : targetUserRole,
|
|
||||||
// };
|
|
||||||
// const res = await this.wikiUserRepo.create(data);
|
|
||||||
// const ret = await this.wikiUserRepo.save(res);
|
|
||||||
// await this.messageService.notify(targetUser, {
|
|
||||||
// title: `您被添加到知识库「${wiki.name}」`,
|
|
||||||
// message: `您被添加到知识库「${wiki.name}」,快去看看吧!`,
|
|
||||||
// url: buildMessageURL('toWiki')({
|
|
||||||
// organizationId: wiki.organizationId,
|
|
||||||
// wikiId: wiki.id,
|
|
||||||
// }),
|
|
||||||
// });
|
|
||||||
// return ret;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 添加知识库成员
|
* 添加知识库成员
|
||||||
* @param user
|
* @param user
|
||||||
|
@ -189,7 +70,7 @@ export class WikiService {
|
||||||
* @param dto
|
* @param dto
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
async addWikiUser(user: OutUser, wikiId, dto: OperateUserAuthDto) {
|
async addWikiUser(user: IUser, wikiId, dto: OperateUserAuthDto) {
|
||||||
const targetUser = await this.userService.findOne({ name: dto.userName });
|
const targetUser = await this.userService.findOne({ name: dto.userName });
|
||||||
|
|
||||||
if (!targetUser) {
|
if (!targetUser) {
|
||||||
|
@ -197,27 +78,40 @@ export class WikiService {
|
||||||
}
|
}
|
||||||
|
|
||||||
const wiki = await this.wikiRepo.findOne(wikiId);
|
const wiki = await this.wikiRepo.findOne(wikiId);
|
||||||
const homeDoc = await this.getWikiHomeDocument(user, wikiId);
|
|
||||||
|
|
||||||
if (!wiki) {
|
if (!wiki) {
|
||||||
throw new HttpException('目标知识库不存在', HttpStatus.NOT_FOUND);
|
throw new HttpException('目标知识库不存在', HttpStatus.NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.authService.createOrUpdateOtherUserAuth(user.id, targetUser.id, {
|
if (
|
||||||
|
!(await this.authService.getAuth(targetUser.id, {
|
||||||
|
organizationId: wiki.organizationId,
|
||||||
|
wikiId: null,
|
||||||
|
documentId: null,
|
||||||
|
}))
|
||||||
|
) {
|
||||||
|
throw new HttpException('该用户非组织成员', HttpStatus.FORBIDDEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
const homeDoc = await this.getWikiHomeDocument(user, wikiId);
|
||||||
|
|
||||||
|
await Promise.all([
|
||||||
|
this.authService.createOrUpdateOtherUserAuth(user.id, targetUser.id, {
|
||||||
auth: dto.userAuth,
|
auth: dto.userAuth,
|
||||||
organizationId: wiki.organizationId,
|
organizationId: wiki.organizationId,
|
||||||
wikiId: wiki.id,
|
wikiId: wiki.id,
|
||||||
documentId: null,
|
documentId: null,
|
||||||
});
|
}),
|
||||||
|
|
||||||
await this.authService.createOrUpdateOtherUserAuth(user.id, targetUser.id, {
|
this.authService.createOrUpdateOtherUserAuth(user.id, targetUser.id, {
|
||||||
auth: dto.userAuth,
|
auth: dto.userAuth,
|
||||||
organizationId: wiki.organizationId,
|
organizationId: wiki.organizationId,
|
||||||
wikiId: wiki.id,
|
wikiId: wiki.id,
|
||||||
documentId: homeDoc.id,
|
documentId: homeDoc.id,
|
||||||
});
|
}),
|
||||||
|
]);
|
||||||
|
|
||||||
await this.messageService.notify(targetUser, {
|
await this.messageService.notify(targetUser.id, {
|
||||||
title: `您被添加到知识库「${wiki.name}」`,
|
title: `您被添加到知识库「${wiki.name}」`,
|
||||||
message: `您被添加到知识库「${wiki.name}」,快去看看吧!`,
|
message: `您被添加到知识库「${wiki.name}」,快去看看吧!`,
|
||||||
url: buildMessageURL('toWiki')({
|
url: buildMessageURL('toWiki')({
|
||||||
|
@ -234,7 +128,7 @@ export class WikiService {
|
||||||
* @param dto
|
* @param dto
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
async updateWikiUser(user: OutUser, wikiId, dto: OperateUserAuthDto) {
|
async updateWikiUser(user: IUser, wikiId, dto: OperateUserAuthDto) {
|
||||||
const targetUser = await this.userService.findOne({ name: dto.userName });
|
const targetUser = await this.userService.findOne({ name: dto.userName });
|
||||||
|
|
||||||
if (!targetUser) {
|
if (!targetUser) {
|
||||||
|
@ -248,21 +142,23 @@ export class WikiService {
|
||||||
throw new HttpException('目标知识库不存在', HttpStatus.NOT_FOUND);
|
throw new HttpException('目标知识库不存在', HttpStatus.NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.authService.createOrUpdateOtherUserAuth(user.id, targetUser.id, {
|
await Promise.all([
|
||||||
|
this.authService.createOrUpdateOtherUserAuth(user.id, targetUser.id, {
|
||||||
auth: dto.userAuth,
|
auth: dto.userAuth,
|
||||||
organizationId: wiki.organizationId,
|
organizationId: wiki.organizationId,
|
||||||
wikiId: wiki.id,
|
wikiId: wiki.id,
|
||||||
documentId: null,
|
documentId: null,
|
||||||
});
|
}),
|
||||||
|
|
||||||
await this.authService.createOrUpdateOtherUserAuth(user.id, targetUser.id, {
|
this.authService.createOrUpdateOtherUserAuth(user.id, targetUser.id, {
|
||||||
auth: dto.userAuth,
|
auth: dto.userAuth,
|
||||||
organizationId: wiki.organizationId,
|
organizationId: wiki.organizationId,
|
||||||
wikiId: wiki.id,
|
wikiId: wiki.id,
|
||||||
documentId: homeDoc.id,
|
documentId: homeDoc.id,
|
||||||
});
|
}),
|
||||||
|
]);
|
||||||
|
|
||||||
await this.messageService.notify(targetUser, {
|
await this.messageService.notify(targetUser.id, {
|
||||||
title: `您在知识库「${wiki.name}」的权限有更新`,
|
title: `您在知识库「${wiki.name}」的权限有更新`,
|
||||||
message: `您在知识库「${wiki.name}」的权限有更新,快去看看吧!`,
|
message: `您在知识库「${wiki.name}」的权限有更新,快去看看吧!`,
|
||||||
url: buildMessageURL('toWiki')({
|
url: buildMessageURL('toWiki')({
|
||||||
|
@ -279,7 +175,7 @@ export class WikiService {
|
||||||
* @param dto
|
* @param dto
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
async deleteWikiUser(user: OutUser, wikiId, dto: OperateUserAuthDto) {
|
async deleteWikiUser(user: IUser, wikiId, dto: OperateUserAuthDto) {
|
||||||
const targetUser = await this.userService.findOne({ name: dto.userName });
|
const targetUser = await this.userService.findOne({ name: dto.userName });
|
||||||
|
|
||||||
if (!targetUser) {
|
if (!targetUser) {
|
||||||
|
@ -293,23 +189,25 @@ export class WikiService {
|
||||||
throw new HttpException('目标知识库不存在', HttpStatus.NOT_FOUND);
|
throw new HttpException('目标知识库不存在', HttpStatus.NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.authService.deleteOtherUserAuth(user.id, targetUser.id, {
|
await Promise.all([
|
||||||
|
this.authService.deleteOtherUserAuth(user.id, targetUser.id, {
|
||||||
auth: dto.userAuth,
|
auth: dto.userAuth,
|
||||||
organizationId: wiki.organizationId,
|
organizationId: wiki.organizationId,
|
||||||
wikiId: wiki.id,
|
wikiId: wiki.id,
|
||||||
documentId: null,
|
documentId: null,
|
||||||
});
|
}),
|
||||||
|
|
||||||
await this.authService.deleteOtherUserAuth(user.id, targetUser.id, {
|
this.authService.deleteOtherUserAuth(user.id, targetUser.id, {
|
||||||
auth: dto.userAuth,
|
auth: dto.userAuth,
|
||||||
organizationId: wiki.organizationId,
|
organizationId: wiki.organizationId,
|
||||||
wikiId: wiki.id,
|
wikiId: wiki.id,
|
||||||
documentId: homeDoc.id,
|
documentId: homeDoc.id,
|
||||||
});
|
}),
|
||||||
|
]);
|
||||||
|
|
||||||
await this.messageService.notify(targetUser, {
|
await this.messageService.notify(targetUser.id, {
|
||||||
title: `您在「${wiki.name}」的权限已变更`,
|
title: `知识库「${wiki.name}」的权限收回`,
|
||||||
message: `您在「${wiki.name}」的权限已变更,快去看看吧!`,
|
message: `您在知识库「「${wiki.name}」的权限已收回!`,
|
||||||
url: buildMessageURL('toWiki')({
|
url: buildMessageURL('toWiki')({
|
||||||
organizationId: wiki.organizationId,
|
organizationId: wiki.organizationId,
|
||||||
wikiId: wiki.id,
|
wikiId: wiki.id,
|
||||||
|
@ -322,7 +220,7 @@ export class WikiService {
|
||||||
* @param userId
|
* @param userId
|
||||||
* @param wikiId
|
* @param wikiId
|
||||||
*/
|
*/
|
||||||
async getWikiUsers(user, wikiId) {
|
async getWikiUsers(user, wikiId, pagination) {
|
||||||
const wiki = await this.wikiRepo.findOne(wikiId);
|
const wiki = await this.wikiRepo.findOne(wikiId);
|
||||||
|
|
||||||
if (!wiki) {
|
if (!wiki) {
|
||||||
|
@ -335,7 +233,11 @@ export class WikiService {
|
||||||
documentId: null,
|
documentId: null,
|
||||||
});
|
});
|
||||||
|
|
||||||
const { data: usersAuth, total } = await this.authService.getUsersAuthInWiki(wiki.organizationId, wiki.id);
|
const { data: usersAuth, total } = await this.authService.getUsersAuthInWiki(
|
||||||
|
wiki.organizationId,
|
||||||
|
wiki.id,
|
||||||
|
pagination
|
||||||
|
);
|
||||||
|
|
||||||
const userIds = usersAuth.map((auth) => auth.userId);
|
const userIds = usersAuth.map((auth) => auth.userId);
|
||||||
const users = await this.userService.findByIds(userIds);
|
const users = await this.userService.findByIds(userIds);
|
||||||
|
@ -350,28 +252,13 @@ export class WikiService {
|
||||||
return { data: withUserData, total };
|
return { data: withUserData, total };
|
||||||
}
|
}
|
||||||
|
|
||||||
// /**
|
|
||||||
// * 查询知识库指定用户详情
|
|
||||||
// * @param workspaceId
|
|
||||||
// * @param userId
|
|
||||||
// * @returns
|
|
||||||
// */
|
|
||||||
// async getWikiUserDetail({ wikiId, userId }): Promise<WikiUserEntity> {
|
|
||||||
// const data = { wikiId, userId };
|
|
||||||
// const wikiUser = await this.wikiUserRepo.findOne(data);
|
|
||||||
// if (!wikiUser) {
|
|
||||||
// throw new HttpException('您不在该知识库中', HttpStatus.FORBIDDEN);
|
|
||||||
// }
|
|
||||||
// return wikiUser;
|
|
||||||
// }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建知识库
|
* 创建知识库
|
||||||
* @param user
|
* @param user
|
||||||
* @param dto CreateWikiDto
|
* @param dto CreateWikiDto
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
async createWiki(user: OutUser, dto: CreateWikiDto) {
|
async createWiki(user: IUser, dto: CreateWikiDto) {
|
||||||
await this.authService.canView(user.id, {
|
await this.authService.canView(user.id, {
|
||||||
organizationId: dto.organizationId,
|
organizationId: dto.organizationId,
|
||||||
wikiId: null,
|
wikiId: null,
|
||||||
|
@ -386,7 +273,7 @@ export class WikiService {
|
||||||
const toSaveWiki = await this.wikiRepo.create(data);
|
const toSaveWiki = await this.wikiRepo.create(data);
|
||||||
const wiki = await this.wikiRepo.save(toSaveWiki);
|
const wiki = await this.wikiRepo.save(toSaveWiki);
|
||||||
|
|
||||||
const { data: userAuths } = await this.authService.getUsersAuthInOrganization(wiki.organizationId);
|
const { data: userAuths } = await this.authService.getUsersAuthInOrganization(wiki.organizationId, null);
|
||||||
|
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
...userAuths
|
...userAuths
|
||||||
|
@ -399,6 +286,7 @@ export class WikiService {
|
||||||
documentId: null,
|
documentId: null,
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
|
|
||||||
await this.authService.createOrUpdateAuth(user.id, {
|
await this.authService.createOrUpdateAuth(user.id, {
|
||||||
auth: AuthEnum.creator,
|
auth: AuthEnum.creator,
|
||||||
organizationId: wiki.organizationId,
|
organizationId: wiki.organizationId,
|
||||||
|
@ -426,7 +314,6 @@ export class WikiService {
|
||||||
const homeDocumentId = homeDoc.id;
|
const homeDocumentId = homeDoc.id;
|
||||||
const withHomeDocumentIdWiki = await this.wikiRepo.merge(wiki, { homeDocumentId });
|
const withHomeDocumentIdWiki = await this.wikiRepo.merge(wiki, { homeDocumentId });
|
||||||
await this.wikiRepo.save(withHomeDocumentIdWiki);
|
await this.wikiRepo.save(withHomeDocumentIdWiki);
|
||||||
|
|
||||||
return withHomeDocumentIdWiki;
|
return withHomeDocumentIdWiki;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -436,7 +323,7 @@ export class WikiService {
|
||||||
* @param pagination
|
* @param pagination
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
async getAllWikis(user: OutUser, organizationId, pagination: IPagination) {
|
async getAllWikis(user: IUser, organizationId, pagination: IPagination) {
|
||||||
await this.authService.canView(user.id, {
|
await this.authService.canView(user.id, {
|
||||||
organizationId,
|
organizationId,
|
||||||
wikiId: null,
|
wikiId: null,
|
||||||
|
@ -467,7 +354,7 @@ export class WikiService {
|
||||||
* @param pagination
|
* @param pagination
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
async getOwnWikis(user: OutUser, organizationId, pagination: IPagination) {
|
async getOwnWikis(user: IUser, organizationId, pagination: IPagination) {
|
||||||
await this.authService.canView(user.id, {
|
await this.authService.canView(user.id, {
|
||||||
organizationId,
|
organizationId,
|
||||||
wikiId: null,
|
wikiId: null,
|
||||||
|
@ -497,7 +384,7 @@ export class WikiService {
|
||||||
* @param pagination
|
* @param pagination
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
async getJoinWikis(user: OutUser, organizationId, pagination: IPagination) {
|
async getJoinWikis(user: IUser, organizationId, pagination: IPagination) {
|
||||||
await this.authService.canView(user.id, {
|
await this.authService.canView(user.id, {
|
||||||
organizationId,
|
organizationId,
|
||||||
wikiId: null,
|
wikiId: null,
|
||||||
|
@ -528,7 +415,7 @@ export class WikiService {
|
||||||
* @param wikiId
|
* @param wikiId
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
async getWikiDetail(user: OutUser, wikiId: string) {
|
async getWikiDetail(user: IUser, wikiId: string) {
|
||||||
const wiki = await this.wikiRepo.findOne(wikiId);
|
const wiki = await this.wikiRepo.findOne(wikiId);
|
||||||
await this.authService.canView(user.id, {
|
await this.authService.canView(user.id, {
|
||||||
organizationId: wiki.organizationId,
|
organizationId: wiki.organizationId,
|
||||||
|
@ -545,7 +432,7 @@ export class WikiService {
|
||||||
* @param wikiId
|
* @param wikiId
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
async getWikiHomeDocument(user: OutUser, wikiId) {
|
async getWikiHomeDocument(user: IUser, wikiId) {
|
||||||
const res = await this.documentService.documentRepo.findOne({ wikiId, isWikiHome: true });
|
const res = await this.documentService.documentRepo.findOne({ wikiId, isWikiHome: true });
|
||||||
await this.authService.canView(user.id, {
|
await this.authService.canView(user.id, {
|
||||||
organizationId: res.organizationId,
|
organizationId: res.organizationId,
|
||||||
|
@ -562,7 +449,7 @@ export class WikiService {
|
||||||
* @param dto
|
* @param dto
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
async updateWiki(user: OutUser, wikiId, dto: UpdateWikiDto) {
|
async updateWiki(user: IUser, wikiId, dto: UpdateWikiDto) {
|
||||||
const oldData = await this.wikiRepo.findOne(wikiId);
|
const oldData = await this.wikiRepo.findOne(wikiId);
|
||||||
if (!oldData) {
|
if (!oldData) {
|
||||||
throw new HttpException('目标知识库不存在', HttpStatus.NOT_FOUND);
|
throw new HttpException('目标知识库不存在', HttpStatus.NOT_FOUND);
|
||||||
|
@ -586,26 +473,35 @@ export class WikiService {
|
||||||
* @param wikiId
|
* @param wikiId
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
async deleteWiki(user: OutUser, wikiId) {
|
async deleteWiki(user: IUser, wikiId) {
|
||||||
const wiki = await this.wikiRepo.findOne(wikiId);
|
const wiki = await this.wikiRepo.findOne(wikiId);
|
||||||
await this.authService.canDelete(user.id, {
|
await this.authService.canDelete(user.id, {
|
||||||
organizationId: wiki.organizationId,
|
organizationId: wiki.organizationId,
|
||||||
wikiId: wiki.id,
|
wikiId: wiki.id,
|
||||||
documentId: null,
|
documentId: null,
|
||||||
});
|
});
|
||||||
await this.wikiRepo.remove(wiki);
|
await Promise.all([
|
||||||
// TODO: 删除相应文档以及对应权限
|
this.authService.deleteWiki(wiki.organizationId, wiki.id),
|
||||||
// if (user.id !== wiki.createUserId) {
|
this.wikiRepo.remove(wiki),
|
||||||
// throw new HttpException('您不是创建者,无法删除该知识库', HttpStatus.FORBIDDEN);
|
this.documentService.deleteWikiDocuments(user, wikiId),
|
||||||
// }
|
]);
|
||||||
|
|
||||||
// await this.documentService.deleteWikiDocuments(user, wikiId);
|
|
||||||
|
|
||||||
// const users = await this.wikiUserRepo.find({ wikiId });
|
|
||||||
// await Promise.all([this.wikiUserRepo.remove(users)]);
|
|
||||||
return wiki;
|
return wiki;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除组织下所有知识库
|
||||||
|
* @param user
|
||||||
|
* @param wikiId
|
||||||
|
*/
|
||||||
|
async deleteOrganizationWiki(user, organizationId) {
|
||||||
|
const wikis = await this.wikiRepo.find({ organizationId });
|
||||||
|
await Promise.all(
|
||||||
|
wikis.map((wiki) => {
|
||||||
|
return this.deleteWiki(user, wiki.id);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 分享(或关闭分享)知识库
|
* 分享(或关闭分享)知识库
|
||||||
* @param user
|
* @param user
|
||||||
|
@ -613,7 +509,7 @@ export class WikiService {
|
||||||
* @param dto
|
* @param dto
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
async shareWiki(user: OutUser, wikiId, dto: ShareWikiDto) {
|
async shareWiki(user: IUser, wikiId, dto: ShareWikiDto) {
|
||||||
const wiki = await this.wikiRepo.findOne(wikiId);
|
const wiki = await this.wikiRepo.findOne(wikiId);
|
||||||
|
|
||||||
if (!wiki) {
|
if (!wiki) {
|
||||||
|
@ -668,7 +564,7 @@ export class WikiService {
|
||||||
* @param wikiId
|
* @param wikiId
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
async getWikiTocs(user: OutUser, wikiId) {
|
async getWikiTocs(user: IUser, wikiId) {
|
||||||
const wiki = await this.wikiRepo.findOne(wikiId);
|
const wiki = await this.wikiRepo.findOne(wikiId);
|
||||||
|
|
||||||
const records = await this.authService.getUserCanViewDocumentsInWiki(wiki.organizationId, wiki.id);
|
const records = await this.authService.getUserCanViewDocumentsInWiki(wiki.organizationId, wiki.id);
|
||||||
|
@ -719,46 +615,6 @@ export class WikiService {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// /**
|
|
||||||
// * 获取知识库所有文档(无结构嵌套)
|
|
||||||
// * @param user
|
|
||||||
// * @param wikiId
|
|
||||||
// * @returns
|
|
||||||
// */
|
|
||||||
// async getWikiDocs(user: OutUser, wikiId) {
|
|
||||||
// // 通过文档成员表获取当前用户可查阅的所有文档
|
|
||||||
// const records = await this.documentService.documentUserRepo.find({
|
|
||||||
// userId: user.id,
|
|
||||||
// wikiId,
|
|
||||||
// });
|
|
||||||
|
|
||||||
// const ids = records.map((record) => record.documentId);
|
|
||||||
|
|
||||||
// const documents = await this.documentService.documentRepo.findByIds(ids);
|
|
||||||
|
|
||||||
// const docs = documents
|
|
||||||
// .filter((doc) => !doc.isWikiHome)
|
|
||||||
// .map((doc) => {
|
|
||||||
// const res = instanceToPlain(doc);
|
|
||||||
// res.key = res.id;
|
|
||||||
// return res;
|
|
||||||
// })
|
|
||||||
// .map((item) => {
|
|
||||||
// return lodash.omit(item, ['content', 'state']);
|
|
||||||
// });
|
|
||||||
|
|
||||||
// docs.sort((a, b) => a.index - b.index);
|
|
||||||
|
|
||||||
// const docsWithCreateUser = await Promise.all(
|
|
||||||
// docs.map(async (doc) => {
|
|
||||||
// const createUser = await this.userService.findById(doc.createUserId);
|
|
||||||
// return { ...doc, createUser };
|
|
||||||
// })
|
|
||||||
// );
|
|
||||||
|
|
||||||
// return docsWithCreateUser;
|
|
||||||
// }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取公开知识库目录
|
* 获取公开知识库目录
|
||||||
* @param wikiId
|
* @param wikiId
|
||||||
|
|
Loading…
Reference in New Issue