Merge pull request #59 from fantasticit/feat/cookie-ssr

pull/60/head
fantasticit 2022-05-24 17:38:22 +08:00 committed by GitHub
commit f57fcbaf6b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
97 changed files with 3378 additions and 909 deletions

View File

@ -10,6 +10,9 @@ client:
seoKeywords: '云策文档,协作,文档,前端面试题,fantasticit,https://github.com/fantasticit/think'
# 预先连接的来源,空格分割(比如图片存储服务器)
dnsPrefetch: '//wipi.oss-cn-shanghai.aliyuncs.com'
# 站点地址http://think.codingit.cn/),一定要设置,否则会出现 cookie、跨域等问题
siteUrl: 'http://localhost:5001'
siteDomain: ''
server:
prefix: '/api'

View File

@ -86,9 +86,9 @@
"react-helmet": "^6.1.0",
"react-lazy-load-image-component": "^1.5.4",
"react-pdf": "^5.7.2",
"react-query": "^3.39.0",
"react-split-pane": "^0.1.92",
"scroll-into-view-if-needed": "^2.2.29",
"swr": "^1.2.0",
"timeago.js": "^4.0.2",
"tippy.js": "^6.3.7",
"toggle-selection": "^1.0.6",

View File

@ -8,7 +8,7 @@ type RenderProps = React.ReactNode | (() => React.ReactNode);
interface IProps {
loading: boolean;
error: Error | null;
error: Error | null | unknown;
empty?: boolean;
loadingContent?: RenderProps;
errorContent?: RenderProps;

View File

@ -17,7 +17,7 @@ import {
} from '@douyinfe/semi-ui';
import { DataRender } from 'components/data-render';
import { DocumentLinkCopyer } from 'components/document/link';
import { useCollaborationDocument } from 'data/document';
import { useDoumentMembers } from 'data/document';
import { useUser } from 'data/user';
import { event, JOIN_USER } from 'event';
import { useToggle } from 'hooks/use-toggle';
@ -48,7 +48,7 @@ const renderChecked = (onChange, authKey: 'readable' | 'editable') => (checked,
export const DocumentCollaboration: React.FC<IProps> = ({ wikiId, documentId }) => {
const { user: currentUser } = useUser();
const [visible, toggleVisible] = useToggle(false);
const { users, loading, error, addUser, updateUser, deleteUser } = useCollaborationDocument(documentId);
const { users, loading, error, addUser, updateUser, deleteUser } = useDoumentMembers(documentId);
const [inviteUser, setInviteUser] = useState('');
const [collaborationUsers, setCollaborationUsers] = useState([]);

View File

@ -1,6 +1,6 @@
import { IAuthority, ILoginUser } from '@think/domains';
import cls from 'classnames';
import { useCollaborationDocument } from 'data/document';
import { useDoumentMembers } from 'data/document';
import { event, triggerChangeDocumentTitle, triggerJoinUser, USE_DOCUMENT_VERSION } from 'event';
import { useToggle } from 'hooks/use-toggle';
import Router from 'next/router';
@ -22,7 +22,7 @@ interface IProps {
export const Editor: React.FC<IProps> = ({ user: currentUser, documentId, authority, className, style }) => {
const $hasShowUserSettingModal = useRef(false);
const $editor = useRef<ICollaborationRefProps>();
const { users, addUser, updateUser } = useCollaborationDocument(documentId);
const { users, addUser, updateUser } = useDoumentMembers(documentId);
const [mentionUsersSettingVisible, toggleMentionUsersSettingVisible] = useToggle(false);
const [mentionUsers, setMentionUsers] = useState([]);

View File

@ -18,8 +18,9 @@ import { LogoImage, LogoText } from 'components/logo';
import { Seo } from 'components/seo';
import { Theme } from 'components/theme';
import { User } from 'components/user';
import { usePublicDocument } from 'data/document';
import { usePublicDocumentDetail } from 'data/document';
import { useDocumentStyle } from 'hooks/use-document-style';
import { useMount } from 'hooks/use-mount';
import { IsOnMobile } from 'hooks/use-on-mobile';
import Link from 'next/link';
import React, { useCallback, useMemo, useRef } from 'react';
@ -40,7 +41,8 @@ interface IProps {
export const DocumentPublicReader: React.FC<IProps> = ({ documentId, hideLogo = true }) => {
const $form = useRef<FormApi>();
const { data, loading, error, query } = usePublicDocument(documentId);
const mounted = useMount()
const { data, loading, error, query } = usePublicDocumentDetail(documentId);
const { width, fontSize } = useDocumentStyle();
const { isMobile } = IsOnMobile.useHook();
const editorWrapClassNames = useMemo(() => {
@ -148,14 +150,14 @@ export const DocumentPublicReader: React.FC<IProps> = ({ documentId, hideLogo =
style={{ fontSize }}
>
<Seo title={data.title} />
<CollaborationEditor
{mounted && <CollaborationEditor
menubar={false}
editable={false}
user={null}
id={documentId}
type="document"
renderInEditorPortal={renderAuthor}
/>
/>}
<ImageViewer containerSelector="#js-share-document-editor-container" />
<BackTop
style={{ bottom: 65, right: isMobile ? 16 : 100 }}

View File

@ -1,6 +1,6 @@
import { IconStar } from '@douyinfe/semi-icons';
import { Button, Tooltip } from '@douyinfe/semi-ui';
import { useDocumentStar } from 'data/document';
import { useDocumentCollectToggle } from 'data/collector';
import React from 'react';
interface IProps {
@ -9,7 +9,7 @@ interface IProps {
}
export const DocumentStar: React.FC<IProps> = ({ documentId, render }) => {
const { data, toggleStar } = useDocumentStar(documentId);
const { data, toggle: toggleStar } = useDocumentCollectToggle(documentId);
const text = data ? '取消收藏' : '收藏文档';
return (

View File

@ -250,5 +250,6 @@ const MessageBox = () => {
export const Message = () => {
const { loading, error } = useUser();
return <DataRender loading={loading} error={error} normalContent={() => <MessageBox />} />;
return null;
// return <DataRender loading={loading} error={error} normalContent={() => <MessageBox />} />;
};

View File

@ -1,5 +1,5 @@
import { IconArticle, IconChevronLeft } from '@douyinfe/semi-icons';
import { Button, Nav, Popconfirm, Popover, Space, Spin, Switch, Tooltip, Typography } from '@douyinfe/semi-ui';
import { IconChevronLeft } from '@douyinfe/semi-icons';
import { Button, Nav, Popconfirm, Space, Spin, Switch, Tooltip, Typography } from '@douyinfe/semi-ui';
import cls from 'classnames';
import { DataRender } from 'components/data-render';
import { DocumentStyle } from 'components/document/style';
@ -9,6 +9,7 @@ import { User } from 'components/user';
import { useTemplate } from 'data/template';
import { useUser } from 'data/user';
import { useDocumentStyle } from 'hooks/use-document-style';
import { useMount } from 'hooks/use-mount';
import { useWindowSize } from 'hooks/use-window-size';
import Router from 'next/router';
import React, { useCallback, useEffect, useMemo, useState } from 'react';
@ -24,6 +25,7 @@ const { Text } = Typography;
export const TemplateEditor: React.FC<IProps> = ({ templateId }) => {
const { user } = useUser();
const mounted = useMount();
const { data, loading, error, updateTemplate, deleteTemplate } = useTemplate(templateId);
const { width: windowWidth } = useWindowSize();
const [title, setTitle] = useState(data && data.title);
@ -81,7 +83,7 @@ export const TemplateEditor: React.FC<IProps> = ({ templateId }) => {
<Space>
<DocumentStyle />
<Tooltip position="bottom" content={isPublic ? '公开模板' : '个人模板'}>
<Switch onChange={(v) => updateTemplate({ isPublic: v })}></Switch>
<Switch checked={isPublic} onChange={(v) => updateTemplate({ isPublic: v })}></Switch>
</Tooltip>
<Popconfirm title="删除模板" content="模板删除后不可恢复,谨慎操作!" onConfirm={handleDelte}>
<Button type="danger"></Button>
@ -95,14 +97,16 @@ export const TemplateEditor: React.FC<IProps> = ({ templateId }) => {
<main className={styles.contentWrap}>
<div className={styles.editorWrap}>
<div className={cls(styles.contentWrap, editorWrapClassNames)} style={{ fontSize }}>
<CollaborationEditor
menubar
editable
user={user}
id={data.id}
type="template"
onTitleUpdate={setTitle}
/>
{mounted && (
<CollaborationEditor
menubar
editable
user={user}
id={data.id}
type="template"
onTitleUpdate={setTitle}
/>
)}
</div>
</div>
</main>

View File

@ -1,5 +1,5 @@
import { IconArticle, IconChevronLeft } from '@douyinfe/semi-icons';
import { Button, Nav, Popconfirm, Popover, Space, Switch, Tooltip, Typography } from '@douyinfe/semi-ui';
import { IconChevronLeft } from '@douyinfe/semi-icons';
import { Button, Nav, Popconfirm, Space, Switch, Tooltip, Typography } from '@douyinfe/semi-ui';
import { ILoginUser, ITemplate } from '@think/domains';
import cls from 'classnames';
import { DocumentStyle } from 'components/document/style';

View File

@ -45,7 +45,7 @@ export const User: React.FC = () => {
<Avatar size="extra-extra-small" src={user.avatar}></Avatar>
) : (
<Avatar size="extra-extra-small" color="orange">
{user && user.name[0]}
{user && user.name && user.name[0]}
</Avatar>
)
}

View File

@ -3,7 +3,7 @@ import { Avatar, Skeleton, Space, Typography } from '@douyinfe/semi-ui';
import { IconDocument } from 'components/icons/IconDocument';
import { LocaleTime } from 'components/locale-time';
import { WikiStar } from 'components/wiki/star';
import { IWikiWithIsMember } from 'data/wiki';
import { IWikiWithIsMember } from 'data/collector';
import Link from 'next/link';
import styles from './index.module.scss';

View File

@ -3,7 +3,7 @@ import { Button, Popconfirm, Table } from '@douyinfe/semi-ui';
import { getWikiUserRoleText } from '@think/domains';
import { DataRender } from 'components/data-render';
import { LocaleTime } from 'components/locale-time';
import { useWikiUsers } from 'data/wiki';
import { useWikiMembers } from 'data/wiki';
import { useToggle } from 'hooks/use-toggle';
import React, { useState } from 'react';
@ -21,7 +21,7 @@ export const Users: React.FC<IProps> = ({ wikiId }) => {
const [visible, toggleVisible] = useToggle(false);
const [editVisible, toggleEditVisible] = useToggle(false);
const [currentUser, setCurrentUser] = useState(null);
const { data: users, loading, error, addUser, updateUser, deleteUser } = useWikiUsers(wikiId);
const { users, loading, error, addUser, updateUser, deleteUser } = useWikiMembers(wikiId);
const editUser = (user) => {
setCurrentUser(user);

View File

@ -1,6 +1,6 @@
import { IconStar } from '@douyinfe/semi-icons';
import { Button, Tooltip } from '@douyinfe/semi-ui';
import { useWikiStar } from 'data/wiki';
import { useWikiCollectToggle } from 'data/collector';
import React from 'react';
interface IProps {
@ -10,13 +10,13 @@ interface IProps {
}
export const WikiStar: React.FC<IProps> = ({ wikiId, render, onChange }) => {
const { data, toggleStar } = useWikiStar(wikiId);
const { data, toggle } = useWikiCollectToggle(wikiId);
const text = data ? '取消收藏' : '收藏知识库';
return (
<>
{render ? (
render({ star: data, toggleStar, text })
render({ star: data, toggleStar: toggle, text })
) : (
<Tooltip content={text} position="bottom">
<Button
@ -28,7 +28,7 @@ export const WikiStar: React.FC<IProps> = ({ wikiId, render, onChange }) => {
onClick={(e) => {
e.stopPropagation();
e.preventDefault();
toggleStar().then(onChange);
toggle().then(onChange);
}}
/>
</Tooltip>

View File

@ -0,0 +1,155 @@
import { CollectorApiDefinition, CollectType, IDocument, IWiki } from '@think/domains';
import { useCallback } from 'react';
import { useQuery } from 'react-query';
import { HttpClient } from 'services/http-client';
export type IWikiWithIsMember = IWiki & { isMember?: boolean };
/**
*
* @returns
*/
export const getCollectedWikis = (cookie = null): Promise<IWikiWithIsMember[]> => {
return HttpClient.request({
method: CollectorApiDefinition.wikis.method,
url: CollectorApiDefinition.wikis.client(),
cookie,
});
};
/**
*
* @returns
*/
export const useCollectedWikis = () => {
const { data, error, isLoading, refetch } = useQuery(CollectorApiDefinition.wikis.client(), getCollectedWikis);
return { data, error, loading: isLoading, refresh: refetch };
};
/**
*
* @param wikiId
* @returns
*/
export const getWikiIsCollected = (wikiId, cookie = null): Promise<boolean> => {
return HttpClient.request({
method: CollectorApiDefinition.check.method,
url: CollectorApiDefinition.check.client(),
cookie,
data: {
type: CollectType.wiki,
targetId: wikiId,
},
});
};
/**
*
* @param wikiId
* @returns
*/
export const toggleCollectWiki = (wikiId, cookie = null): Promise<boolean> => {
return HttpClient.request({
method: CollectorApiDefinition.toggle.method,
url: CollectorApiDefinition.toggle.client(),
cookie,
data: {
type: CollectType.wiki,
targetId: wikiId,
},
});
};
/**
*
* @param wikiId
* @returns
*/
export const useWikiCollectToggle = (wikiId) => {
const { data, error, refetch } = useQuery(`${CollectorApiDefinition.check.client()}?wikiId=${wikiId}`, () =>
getWikiIsCollected(wikiId)
);
const toggle = useCallback(async () => {
await toggleCollectWiki(wikiId);
refetch();
}, [refetch, wikiId]);
return { data, error, toggle };
};
/**
*
* @returns
*/
export const getCollectedDocuments = (cookie = null): Promise<IDocument[]> => {
return HttpClient.request({
method: CollectorApiDefinition.documents.method,
url: CollectorApiDefinition.documents.client(),
cookie,
});
};
/**
*
* @returns
*/
export const useCollectedDocuments = () => {
const { data, error, isLoading, refetch } = useQuery(
CollectorApiDefinition.documents.client(),
getCollectedDocuments
);
return { data, error, loading: isLoading, refresh: refetch };
};
/**
*
* @param documentId
* @returns
*/
export const getDocumentIsCollected = (documentId, cookie = null): Promise<boolean> => {
return HttpClient.request({
method: CollectorApiDefinition.check.method,
url: CollectorApiDefinition.check.client(),
cookie,
data: {
type: CollectType.document,
targetId: documentId,
},
});
};
/**
*
* @param wikiId
* @returns
*/
export const toggleCollectDocument = (documentId, cookie = null): Promise<boolean> => {
return HttpClient.request({
method: CollectorApiDefinition.toggle.method,
url: CollectorApiDefinition.toggle.client(),
cookie,
data: {
type: CollectType.document,
targetId: documentId,
},
});
};
/**
*
* @param documentId
* @returns
*/
export const useDocumentCollectToggle = (documentId) => {
const { data, error, refetch } = useQuery(`${CollectorApiDefinition.check.client()}?documentId=${documentId}`, () =>
getDocumentIsCollected(documentId)
);
const toggle = useCallback(async () => {
await toggleCollectDocument(documentId);
refetch();
}, [refetch, documentId]);
return { data, error, toggle };
};

View File

@ -1,12 +1,29 @@
import type { IComment } from '@think/domains';
import React, { useCallback, useState } from 'react';
import { CommentApiDefinition, IComment } from '@think/domains';
import { useCallback, useState } from 'react';
import { useQuery } from 'react-query';
import { HttpClient } from 'services/http-client';
import useSWR from 'swr';
export type CreateCommentDto = Pick<IComment, 'parentCommentId' | 'html' | 'replyUserId'>;
export type UpdateCommentDto = Pick<IComment, 'id' | 'html'>;
export const getComments = (
documentId,
page = 1,
cookie = null
): Promise<{
data: Array<IComment>;
total: number;
}> => {
return HttpClient.request({
method: CommentApiDefinition.documents.method,
url: CommentApiDefinition.documents.client(documentId),
cookie,
params: {
page,
},
});
};
/**
*
* @param documentId
@ -14,49 +31,61 @@ export type UpdateCommentDto = Pick<IComment, 'id' | 'html'>;
*/
export const useComments = (documentId) => {
const [page, setPage] = useState(1);
const { data, error, mutate } = useSWR<{
data: Array<IComment>;
total: number;
}>(`/comment/document/${documentId}?page=${page}`, (url) => HttpClient.get(url));
const loading = !data && !error;
const { data, error, isLoading, refetch } = useQuery(
[CommentApiDefinition.documents.client(documentId), page],
() => getComments(documentId, page),
{ keepPreviousData: true }
);
const addComment = useCallback(
async (data: CreateCommentDto) => {
const ret = await HttpClient.post(`/comment/add`, {
documentId,
...data,
const ret = await HttpClient.request({
method: CommentApiDefinition.add.method,
url: CommentApiDefinition.add.client(),
data: {
documentId,
...data,
},
});
mutate();
refetch();
return ret;
},
[mutate, documentId]
[refetch, documentId]
);
const updateComment = useCallback(
async (data: UpdateCommentDto) => {
const ret = await HttpClient.post(`/comment/update`, {
documentId,
...data,
const ret = await HttpClient.request({
method: CommentApiDefinition.update.method,
url: CommentApiDefinition.update.client(),
data: {
documentId,
...data,
},
});
mutate();
refetch();
return ret;
},
[mutate, documentId]
[refetch, documentId]
);
const deleteComment = useCallback(
async (comment: IComment) => {
const ret = await HttpClient.post(`/comment/delete/${comment.id}`);
mutate();
const ret = await HttpClient.request({
method: CommentApiDefinition.delete.method,
url: CommentApiDefinition.delete.client(comment.id),
});
refetch();
return ret;
},
[mutate]
[refetch]
);
return {
data,
loading,
loading: isLoading,
error,
page,
setPage,
addComment,
updateComment,

View File

@ -1,252 +0,0 @@
import type { IAuthority, IDocument, IUser, IWiki } from '@think/domains';
import { useAsyncLoading } from 'hooks/use-async-loading';
import { string } from 'lib0';
import { useCallback, useEffect, useState } from 'react';
import { getPublicDocumentDetail } from 'services/document';
import { HttpClient } from 'services/http-client';
import useSWR from 'swr';
type ICreateDocument = Partial<Pick<IDocument, 'wikiId' | 'parentDocumentId'>>;
type IDocumentWithAuth = { document: IDocument; authority: IAuthority };
type IUpdateDocument = Partial<Pick<IDocument, 'title' | 'content'>>;
/**
*
* @returns
*/
export const useCreateDocument = () => {
const [create, loading] = useAsyncLoading((data: ICreateDocument): Promise<IDocument> => {
return HttpClient.post('/document/create', data);
});
return { create, loading };
};
/**
*
* @param id
* @returns
*/
export const updateDocumentViews = (id: string) => {
return HttpClient.get('/document/views/' + id);
};
/**
*
* @param id
* @returns
*/
export const useDeleteDocument = (id) => {
const [deleteDocument, loading] = useAsyncLoading((): Promise<IDocument> => {
return HttpClient.delete('/document/delete/' + id);
});
return { deleteDocument, loading };
};
/**
*
* @param documentId
* @returns
*/
export const useDocumentDetail = (documentId, options = null) => {
const { data, error, mutate } = useSWR<IDocumentWithAuth>(
`/document/detail/${documentId}`,
(url) => HttpClient.get(url),
options
);
const loading = !data && !error;
const update = useCallback(
async (data: IUpdateDocument) => {
const res = await HttpClient.post('/document/update/' + documentId, data);
mutate();
return res;
},
[mutate, documentId]
);
const toggleStatus = useCallback(
async (data: Partial<Pick<IDocument, 'sharePassword'>>) => {
const ret = await HttpClient.post('/document/share/' + documentId, data);
mutate();
return ret;
},
[mutate, documentId]
);
return { data, loading, error, update, toggleStatus };
};
/**
*
* @param documentId
* @returns
*/
export const useDocumentVersion = (documentId) => {
const { data, error, mutate } = useSWR<Array<{ version: string; data: string }>>(
`/document/version/${documentId}`,
(url) => HttpClient.get(url),
{ errorRetryCount: 0 }
);
const loading = !data && !error;
return { data: data || [], loading, error, refresh: mutate };
};
/**
* 10
* @returns
*/
export const useRecentDocuments = () => {
const { data, error, mutate } = useSWR<Array<IDocument & { visitedAt: string }>>('/document/recent', (url) =>
HttpClient.get(url)
);
const loading = !data && !error;
return { data, error, loading, refresh: mutate };
};
/**
*
* @param documentId
* @returns
*/
export const useDocumentStar = (documentId) => {
const { data, error, mutate } = useSWR<boolean>(`/collector/check/${documentId}`, () =>
HttpClient.post(`/collector/check`, {
type: 'document',
targetId: documentId,
})
);
const toggleStar = useCallback(async () => {
await HttpClient.post('/collector/toggle/', {
type: 'document',
targetId: documentId,
});
mutate();
}, [mutate, documentId]);
return { data, error, toggleStar };
};
/**
*
* @returns
*/
export const useStaredDocuments = () => {
const { data, error, mutate } = useSWR<IDocument[]>('/collector/documents', (url) => HttpClient.post(url));
const loading = !data && !error;
return { data, error, loading, refresh: mutate };
};
/**
*
* @param documentId
* @returns
*/
export const usePublicDocument = (documentId: string) => {
const [fetch] = useAsyncLoading(getPublicDocumentDetail);
const [document, setDocument] = useState<(IDocument & { createUse: IUser; wiki: IWiki }) | null>(null);
const [error, setError] = useState<(Error & { statusCode?: number }) | null>(null);
const loading = !document && !error;
const queryData = useCallback(
(sharePassword = '') => {
fetch(documentId, { sharePassword })
.then((doc) => {
setDocument(doc);
setError(null);
})
.catch(setError);
},
[fetch, documentId]
);
useEffect(() => {
queryData();
}, [documentId, queryData]);
return {
data: document,
loading,
error,
query: queryData,
};
};
export type DocAuth = {
userName: string;
readable?: boolean;
editable?: boolean;
};
/**
*
* @param documentId
* @returns
*/
export const useCollaborationDocument = (documentId) => {
const { data, error, mutate } = useSWR<Array<{ user: IUser; auth: IAuthority }>>(
`/document/user/${documentId}`,
(url) => HttpClient.get(url),
{ shouldRetryOnError: false }
);
const loading = !data && !error;
const addUser = useCallback(
async (userName) => {
const ret = await HttpClient.post(`/document/user/${documentId}/add`, {
documentId,
userName,
readable: true,
editable: false,
});
mutate();
return ret;
},
[mutate, documentId]
);
const updateUser = useCallback(
async (docAuth: DocAuth) => {
const ret = await HttpClient.post(`/document/user/${documentId}/update`, {
documentId,
...docAuth,
});
mutate();
return ret;
},
[mutate, documentId]
);
const deleteUser = useCallback(
async (docAuth: DocAuth) => {
const ret = await HttpClient.post(`/document/user/${documentId}/delete`, {
documentId,
...docAuth,
});
mutate();
return ret;
},
[mutate, documentId]
);
return { users: data, loading, error, addUser, updateUser, deleteUser };
};
/**
*
* @param documentId
* @param isShare 访
* @returns
*/
export const useChildrenDocument = ({ wikiId, documentId, isShare = false }) => {
const { data, error, mutate } = useSWR<Array<IDocument>>(
wikiId + '/' + documentId,
wikiId || documentId
? () =>
HttpClient.post(isShare ? '/document/public/children' : `/document/children`, { wikiId, documentId, isShare })
: null,
{ shouldRetryOnError: false }
);
const loading = !data && !error;
return { data, loading, error, refresh: mutate };
};

View File

@ -0,0 +1,305 @@
import { DocumentApiDefinition, IAuthority, IDocument, IUser, IWiki } from '@think/domains';
import { useAsyncLoading } from 'hooks/use-async-loading';
import { useCallback, useState } from 'react';
import { useQuery } from 'react-query';
import { HttpClient } from 'services/http-client';
type IDocumentWithVisitedAt = IDocument & { visitedAt: string };
type ICreateDocument = Partial<Pick<IDocument, 'wikiId' | 'parentDocumentId'>>;
type IDocumentWithAuth = { document: IDocument; authority: IAuthority };
type IUpdateDocument = Partial<Pick<IDocument, 'title' | 'content'>>;
/**
* 访
* @returns
*/
export const getRecentVisitedDocuments = (cookie = null): Promise<IDocumentWithVisitedAt[]> => {
return HttpClient.request({
method: DocumentApiDefinition.recent.method,
url: DocumentApiDefinition.recent.client(),
cookie,
});
};
/**
* 访
* @returns
*/
export const useRecentDocuments = () => {
const { data, error, isLoading, refetch } = useQuery(
DocumentApiDefinition.recent.client(),
getRecentVisitedDocuments
);
return { data, error, loading: isLoading, refresh: refetch };
};
export type DocAuth = {
userName: string;
readable?: boolean;
editable?: boolean;
};
/**
*
* @param cookie
* @returns
*/
export const getDocumentMembers = (documentId, cookie = null): Promise<Array<{ user: IUser; auth: IAuthority }>> => {
return HttpClient.request({
method: DocumentApiDefinition.getMemberById.method,
url: DocumentApiDefinition.getMemberById.client(documentId),
cookie,
});
};
/**
*
* @param documentId
* @returns
*/
export const useDoumentMembers = (documentId) => {
const { data, error, isLoading, refetch } = useQuery(DocumentApiDefinition.getMemberById.client(documentId), () =>
getDocumentMembers(documentId)
);
const addUser = useCallback(
async (userName) => {
const ret = await HttpClient.request({
method: DocumentApiDefinition.addMemberById.method,
url: DocumentApiDefinition.addMemberById.client(documentId),
data: {
documentId,
userName,
readable: true,
editable: false,
},
});
refetch();
return ret;
},
[refetch, documentId]
);
const updateUser = useCallback(
async (docAuth: DocAuth) => {
const ret = await HttpClient.request({
method: DocumentApiDefinition.updateMemberById.method,
url: DocumentApiDefinition.updateMemberById.client(documentId),
data: {
documentId,
...docAuth,
},
});
refetch();
return ret;
},
[refetch, documentId]
);
const deleteUser = useCallback(
async (docAuth: DocAuth) => {
const ret = await HttpClient.request({
method: DocumentApiDefinition.deleteMemberById.method,
url: DocumentApiDefinition.deleteMemberById.client(documentId),
data: {
documentId,
...docAuth,
},
});
refetch();
return ret;
},
[refetch, documentId]
);
return { users: data, loading: isLoading, error, addUser, updateUser, deleteUser };
};
/**
*
* @param documentId
* @returns
*/
export const getDocumentDetail = (documentId, cookie = null): Promise<IDocumentWithAuth> => {
return HttpClient.request({
method: DocumentApiDefinition.getDetailById.method,
url: DocumentApiDefinition.getDetailById.client(documentId),
cookie,
});
};
/**
*
* @param documentId
* @returns
*/
export const useDocumentDetail = (documentId) => {
const { data, error, isLoading, refetch } = useQuery(DocumentApiDefinition.getDetailById.client(documentId), (url) =>
getDocumentDetail(documentId)
);
/**
*
* @param data
* @returns
*/
const update = useCallback(
async (data: IUpdateDocument) => {
const res = await HttpClient.request({
method: DocumentApiDefinition.updateById.method,
url: DocumentApiDefinition.updateById.client(documentId),
data,
});
refetch();
return res;
},
[refetch, documentId]
);
/**
*
* @param data
* @returns
*/
const toggleStatus = useCallback(
async (data) => {
const res = await HttpClient.request({
method: DocumentApiDefinition.shareById.method,
url: DocumentApiDefinition.shareById.client(documentId),
data,
});
refetch();
return res;
},
[refetch, documentId]
);
return { data, loading: isLoading, error, update, toggleStatus };
};
/**
*
* @param documentId
* @returns
*/
export const getDocumentVersion = (documentId, cookie = null): Promise<Array<{ version: string; data: string }>> => {
return HttpClient.request({
method: DocumentApiDefinition.getVersionById.method,
url: DocumentApiDefinition.getVersionById.client(documentId),
cookie,
});
};
/**
*
* @param documentId
* @returns
*/
export const useDocumentVersion = (documentId) => {
const { data, error, isLoading, refetch } = useQuery(DocumentApiDefinition.getVersionById.client(documentId), () =>
getDocumentVersion(documentId)
);
return { data: data || [], loading: isLoading, error, refresh: refetch };
};
/**
*
* @param id
* @returns
*/
export const useDeleteDocument = (documentId) => {
const [deleteDocument, loading] = useAsyncLoading((): Promise<IDocument> => {
return HttpClient.request({
method: DocumentApiDefinition.deleteById.method,
url: DocumentApiDefinition.deleteById.client(documentId),
});
});
return { deleteDocument, loading };
};
/**
*
* @returns
*/
export const useCreateDocument = () => {
const [create, loading] = useAsyncLoading((data: ICreateDocument): Promise<IDocument> => {
return HttpClient.request({
method: DocumentApiDefinition.create.method,
url: DocumentApiDefinition.create.client(),
data,
});
});
return { create, loading };
};
/**
*
* @param documentId
* @returns
*/
export const getPublicDocumentDetail = (
documentId,
data,
cookie = null
): Promise<IDocument & { createUse: IUser; wiki: IWiki }> => {
return HttpClient.request({
method: DocumentApiDefinition.getPublicDetailById.method,
url: DocumentApiDefinition.getPublicDetailById.client(documentId),
cookie,
data,
});
};
/**
*
* @param documentId
* @returns
*/
export const usePublicDocumentDetail = (documentId) => {
const [sharePassword, setSharePassword] = useState();
const { data, error, isLoading, refetch } = useQuery(
DocumentApiDefinition.getPublicDetailById.client(documentId),
() => getPublicDocumentDetail(documentId, { sharePassword }),
{ retry: 0, refetchOnWindowFocus: true, refetchOnReconnect: false }
);
const query = useCallback(
(password) => {
setSharePassword(password);
refetch();
},
[refetch]
);
return {
data,
loading: isLoading,
error,
query,
};
};
export const getDocumentChildren = (data, cookie = null): Promise<Array<IDocument>> => {
return HttpClient.request({
method: data.isShare ? DocumentApiDefinition.getPublicChildren.method : DocumentApiDefinition.getChildren.method,
url: data.isShare ? DocumentApiDefinition.getPublicChildren.client() : DocumentApiDefinition.getChildren.client(),
cookie,
data,
});
};
/**
*
* @param documentId
* @param isShare 访
* @returns
*/
export const useChildrenDocument = ({ wikiId, documentId, isShare = false }) => {
const { data, error, refetch } = useQuery(
isShare ? DocumentApiDefinition.getPublicChildren.client() : DocumentApiDefinition.getChildren.client(),
() => getDocumentChildren({ wikiId, documentId, isShare })
);
const loading = !data && !error;
return { data, loading, error, refresh: refetch };
};

View File

@ -1,83 +1,87 @@
import type { IMessage } from '@think/domains';
import React, { useCallback, useState } from 'react';
import { IMessage, MessageApiDefinition } from '@think/domains';
import { useCallback, useState } from 'react';
import { useQuery } from 'react-query';
import { HttpClient } from 'services/http-client';
import useSWR from 'swr';
/**
*
* @returns
*/
const getMessagesApi =
(apiKey) =>
(
page = 1,
cookie = null
): Promise<{
data: Array<IMessage>;
total: number;
}> => {
return HttpClient.request({
method: MessageApiDefinition[apiKey].method,
url: MessageApiDefinition[apiKey].client(),
cookie,
params: {
page,
},
});
};
export const useAllMessages = () => {
const [page, setPage] = useState(1);
const { data, error, mutate } = useSWR<{
data: Array<IMessage>;
total: number;
}>(`/message/all?page=${page}`, (url) => HttpClient.get(url), {
refreshInterval: 200,
});
const loading = !data && !error;
return {
data,
loading,
error,
page,
setPage,
};
};
/**
*
* @returns
*/
export const useReadMessages = () => {
const [page, setPage] = useState(1);
const { data, error, mutate } = useSWR<{
data: Array<IMessage>;
total: number;
}>(`/message/read?page=${page}`, (url) => HttpClient.get(url), {
refreshInterval: 200,
});
const loading = !data && !error;
return {
data,
loading,
error,
page,
setPage,
};
};
/**
*
* @returns
*/
export const useUnreadMessages = () => {
const [page, setPage] = useState(1);
const { data, error, mutate } = useSWR<{
data: Array<IMessage>;
total: number;
}>(`/message/unread?page=${page}`, (url) => HttpClient.get(url), {
refreshInterval: 200,
});
const loading = !data && !error;
const readMessage = useCallback(
async (messageId) => {
const ret = await HttpClient.post(`/message/read/${messageId}`);
mutate();
return ret;
},
[mutate]
const { data, error, isLoading } = useQuery(
[MessageApiDefinition.getAll.client(), page],
() => getMessagesApi('getAll')(page),
{ keepPreviousData: true }
);
return {
data,
loading,
loading: isLoading,
error,
page,
setPage,
readMessage,
};
};
export const useReadMessages = () => {
const [page, setPage] = useState(1);
const { data, error, isLoading } = useQuery(
[MessageApiDefinition.getRead.client(), page],
() => getMessagesApi('getRead')(page),
{ keepPreviousData: true }
);
return {
data,
loading: isLoading,
error,
page,
setPage,
};
};
export const useUnreadMessages = () => {
const [page, setPage] = useState(1);
const { data, error, isLoading, refetch } = useQuery(
[MessageApiDefinition.getUnread.client(), page],
() => getMessagesApi('getUnread')(page),
{ keepPreviousData: true }
);
const readMessage = useCallback(
async (messageId) => {
const ret = await HttpClient.request({
method: MessageApiDefinition.readMessage.method,
url: MessageApiDefinition.readMessage.client(messageId),
});
refetch();
return ret;
},
[refetch]
);
return {
data,
loading: isLoading,
error,
readMessage,
page,
setPage,
};
};

View File

@ -1,39 +1,72 @@
import type { ITemplate } from '@think/domains';
import { ITemplate, TemplateApiDefinition } from '@think/domains';
import { useCallback, useState } from 'react';
import { useQuery } from 'react-query';
import { HttpClient } from 'services/http-client';
import useSWR from 'swr';
export const getPublicTemplates = (
page = 1,
cookie = null
): Promise<{
data: Array<ITemplate>;
total: number;
}> => {
return HttpClient.request({
method: TemplateApiDefinition.public.method,
url: TemplateApiDefinition.public.client(),
cookie,
params: {
page,
},
});
};
export const usePublicTemplates = () => {
const [page, setPage] = useState(1);
const { data, error, mutate } = useSWR<{
data: Array<ITemplate>;
total: number;
}>(`/template/public?page=${page}`, (url) => HttpClient.get(url));
const loading = !data && !error;
const { data, error, isLoading } = useQuery(`${TemplateApiDefinition.public.client()}?page=${page}`, () =>
getPublicTemplates(page)
);
return {
data,
loading,
loading: isLoading,
error,
setPage,
};
};
export const getOwnTemplates = (
page = 1,
cookie = null
): Promise<{
data: Array<ITemplate>;
total: number;
}> => {
return HttpClient.request({
method: TemplateApiDefinition.own.method,
url: TemplateApiDefinition.own.client(),
cookie,
params: {
page,
},
});
};
/**
*
* @returns
*/
export const useOwnTemplates = () => {
const [page, setPage] = useState(1);
const { data, error, mutate } = useSWR<{
data: Array<ITemplate>;
total: number;
}>(`/template/own?page=${page}`, (url) => HttpClient.get(url));
const loading = !data && !error;
const {
data,
error,
isLoading,
refetch: mutate,
} = useQuery(`${TemplateApiDefinition.own.client()}?page=${page}`, () => getOwnTemplates(page));
const addTemplate = useCallback(
async (data): Promise<ITemplate> => {
const ret = await HttpClient.post(`/template/add`, data);
const ret = await HttpClient.post(TemplateApiDefinition.add.client(), data);
mutate();
return ret as unknown as ITemplate;
},
@ -42,31 +75,59 @@ export const useOwnTemplates = () => {
return {
data,
loading,
loading: isLoading,
error,
setPage,
addTemplate,
};
};
/**
*
* @param templateId
* @param cookie
* @returns
*/
export const getTemplateDetail = (templateId, cookie = null): Promise<ITemplate> => {
return HttpClient.request({
method: TemplateApiDefinition.getDetailById.method,
url: TemplateApiDefinition.getDetailById.client(templateId),
cookie,
});
};
/**
*
* @param templateId
* @returns
*/
export const useTemplate = (templateId) => {
const { data, error, mutate } = useSWR<ITemplate>(`/template/detail/${templateId}`, (url) => HttpClient.get(url));
const { data, error, refetch } = useQuery(TemplateApiDefinition.getDetailById.client(templateId), () =>
getTemplateDetail(templateId)
);
const loading = !data && !error;
const updateTemplate = useCallback(
async (data): Promise<ITemplate> => {
const ret = await HttpClient.post(`/template/update`, {
id: templateId,
...data,
const ret = await HttpClient.request({
method: TemplateApiDefinition.updateById.method,
url: TemplateApiDefinition.updateById.client(templateId),
data: {
id: templateId,
...data,
},
});
mutate();
refetch();
return ret as unknown as ITemplate;
},
[mutate, templateId]
[refetch, templateId]
);
const deleteTemplate = useCallback(async () => {
await HttpClient.post(`/template/delete/${templateId}`);
await HttpClient.request({
method: TemplateApiDefinition.deleteById.method,
url: TemplateApiDefinition.deleteById.client(templateId),
});
}, [templateId]);
return {

View File

@ -1,45 +1,56 @@
import type { ILoginUser, IUser } from '@think/domains';
import { ILoginUser, IUser, UserApiDefinition } from '@think/domains';
import { getStorage, setStorage } from 'helpers/storage';
import Router, { useRouter } from 'next/router';
import { useCallback, useEffect } from 'react';
import { useQuery } from 'react-query';
import { HttpClient } from 'services/http-client';
import useSWR from 'swr';
export const useUser = () => {
const router = useRouter();
const { data, error, mutate } = useSWR<ILoginUser>('user', getStorage);
const { data, error, refetch } = useQuery<ILoginUser>('user', () => {
return getStorage('user');
});
const logout = useCallback(() => {
window.localStorage.removeItem('user');
window.localStorage.removeItem('token');
mutate(null);
Router.replace('/login');
}, [mutate]);
refetch();
HttpClient.request({
method: UserApiDefinition.logout.method,
url: UserApiDefinition.logout.client(),
}).then(() => {
Router.replace('/login');
});
}, [refetch]);
const login = useCallback(
(data) => {
return HttpClient.post<IUser>('/user/login', data).then((res) => {
return HttpClient.request({
method: UserApiDefinition.login.method,
url: UserApiDefinition.login.client(),
data,
}).then((res) => {
const user = res as unknown as ILoginUser;
mutate(user);
refetch();
setStorage('user', JSON.stringify(user));
user.token && setStorage('token', user.token);
user.token && setStorage('token,', user.token);
const next = router.query?.redirect || '/';
Router.replace(next as string);
});
},
[mutate, router.query?.redirect]
[refetch, router.query?.redirect]
);
const updateUser = async (patch: Pick<IUser, 'email' | 'avatar'>) => {
const res = await HttpClient.patch('/user/update', patch);
const ret = { ...data, ...res } as unknown as ILoginUser;
setStorage('user', JSON.stringify(ret));
mutate(ret);
refetch();
};
useEffect(() => {
mutate();
}, [mutate]);
refetch();
}, [refetch]);
return {
user: data,

View File

@ -1,7 +1,7 @@
import { CollectType, IDocument, IUser, IWiki, IWikiUser } from '@think/domains';
import { IDocument, IUser, IWiki, IWikiUser, WikiApiDefinition } from '@think/domains';
import { useCallback, useState } from 'react';
import { useQuery } from 'react-query';
import { HttpClient } from 'services/http-client';
import useSWR from 'swr';
export type ICreateWiki = Pick<IWiki, 'name' | 'description'>;
export type IUpdateWiki = Partial<IWiki>;
@ -15,14 +15,35 @@ export type IWikiWithIsMember = IWiki & { isMember: boolean };
*
* @returns
*/
export const useAllWikis = () => {
const { data, error } = useSWR<{ data: IWiki[]; total: number }>('/wiki/list/all', (url) => HttpClient.get(url));
export const getAllWikis = (cookie = null): Promise<{ data: IWiki[]; total: number }> => {
return HttpClient.request({
method: WikiApiDefinition.getAllWikis.method,
url: WikiApiDefinition.getAllWikis.client(),
cookie,
});
};
const loading = !data && !error;
/**
*
* @returns
*/
export const useAllWikis = () => {
const { data, error, isLoading } = useQuery(WikiApiDefinition.getAllWikis.client(), getAllWikis);
const list = (data && data.data) || [];
const total = (data && data.total) || 0;
return { data: list, total, error, loading: isLoading };
};
return { data: list, total, error, loading };
/**
*
* @returns
*/
export const getJoinWikis = (cookie = null): Promise<{ data: IWiki[]; total: number }> => {
return HttpClient.request({
method: WikiApiDefinition.getJoinWikis.method,
url: WikiApiDefinition.getJoinWikis.client(),
cookie,
});
};
/**
@ -30,13 +51,23 @@ export const useAllWikis = () => {
* @returns
*/
export const useJoinWikis = () => {
const { data, error } = useSWR<{ data: IWiki[]; total: number }>('/wiki/list/join', (url) => HttpClient.get(url));
const loading = !data && !error;
const { data, error, isLoading } = useQuery(WikiApiDefinition.getJoinWikis.client(), getJoinWikis);
const list = (data && data.data) || [];
const total = (data && data.total) || 0;
return { data: list, total, error, loading };
return { data: list, total, error, loading: isLoading };
};
/**
*
* @returns
*/
export const getOwnWikis = (cookie = null): Promise<{ data: IWiki[]; total: number }> => {
return HttpClient.request({
method: WikiApiDefinition.getOwnWikis.method,
url: WikiApiDefinition.getOwnWikis.client(),
cookie,
});
};
/**
@ -44,17 +75,19 @@ export const useJoinWikis = () => {
* @returns
*/
export const useOwnWikis = () => {
const { data, error, mutate } = useSWR<{ data: IWiki[]; total: number }>('/wiki/list/own', (url) =>
HttpClient.get(url)
);
const { data, error, refetch } = useQuery(WikiApiDefinition.getOwnWikis.client(), getOwnWikis);
const createWiki = useCallback(
async (data: ICreateWiki) => {
const res = await HttpClient.post<IWiki>('/wiki/create', data);
mutate();
const res = await HttpClient.request({
method: WikiApiDefinition.add.method,
url: WikiApiDefinition.add.client(),
data,
});
refetch();
return res;
},
[mutate]
[refetch]
);
/**
@ -64,11 +97,14 @@ export const useOwnWikis = () => {
*/
const deletWiki = useCallback(
async (id) => {
const res = await HttpClient.delete<IWiki>('/wiki/delete/' + id);
mutate();
const res = await HttpClient.request({
method: WikiApiDefinition.deleteById.method,
url: WikiApiDefinition.deleteById.client(id),
});
refetch();
return res;
},
[mutate]
[refetch]
);
const loading = !data && !error;
@ -78,50 +114,81 @@ export const useOwnWikis = () => {
return { data: list, total, error, loading, createWiki, deletWiki };
};
/**
*
* @param documentId
* @returns
*/
export const getAllPublicWikis = (
page = 1,
cookie = null
): Promise<{
data: Array<IWiki>;
total: number;
}> => {
return HttpClient.request({
method: WikiApiDefinition.getPublicWikis.method,
url: WikiApiDefinition.getPublicWikis.client(),
cookie,
params: {
page,
},
});
};
/**
*
* @param documentId
* @returns
*/
export const useAllPublicWikis = () => {
const [page, setPage] = useState(1);
const { data, error, isLoading } = useQuery(`${WikiApiDefinition.getPublicWikis.client()}?page=${page}`, () =>
getAllPublicWikis(page)
);
return {
data,
loading: isLoading,
error,
setPage,
};
};
/**
*
* @returns
*/
export const useWikiHomeDoc = (wikiId) => {
const { data, error } = useSWR<IDocument>('/wiki/homedoc/' + wikiId, (url) => HttpClient.get(url));
const loading = !data && !error;
return { data, error, loading };
export const getWikiHomeDocument = (wikiId, cookie = null): Promise<IDocument> => {
return HttpClient.request({
method: WikiApiDefinition.getHomeDocumentById.method,
url: WikiApiDefinition.getHomeDocumentById.client(wikiId),
cookie,
});
};
/**
*
* @param workspaceId
*
* @returns
*/
export const useWikiTocs = (wikiId) => {
const { data, error, mutate } = useSWR<Array<IDocument & { createUser: IUser }>>(`/wiki/tocs/${wikiId}`, (url) =>
wikiId ? HttpClient.get(url) : null
export const useWikiHomeDocument = (wikiId) => {
const { data, error, isLoading } = useQuery(WikiApiDefinition.getHomeDocumentById.client(wikiId), () =>
getWikiHomeDocument(wikiId)
);
const loading = !data && !error;
const update = useCallback(
async (relations: Array<{ id: string; parentDocumentId: string }>) => {
const res = await HttpClient.post(`/wiki/tocs/${wikiId}/update`, relations);
mutate();
return res;
},
[mutate, wikiId]
);
return { data, loading, error, refresh: mutate, update };
return { data, error, loading: isLoading };
};
/**
*
* @param workspaceId
*
* @param wikiId
* @returns
*/
export const useWikiDocs = (wikiId) => {
const { data, error, mutate } = useSWR<Array<IDocument & { createUser: IUser }>>(`/wiki/docs/${wikiId}`, (url) =>
HttpClient.get(url)
);
const loading = !data && !error;
return { data, loading, error, refresh: mutate };
export const getWikiDetail = (wikiId, cookie = null): Promise<IWiki> => {
return HttpClient.request({
method: WikiApiDefinition.getDetailById.method,
url: WikiApiDefinition.getDetailById.client(wikiId),
cookie,
});
};
/**
@ -130,8 +197,9 @@ export const useWikiDocs = (wikiId) => {
* @returns
*/
export const useWikiDetail = (wikiId) => {
const { data, error, mutate } = useSWR<IWiki>(wikiId ? `/wiki/detail/${wikiId}` : null, (url) => HttpClient.get(url));
const loading = !data && !error;
const { data, error, isLoading, refetch } = useQuery(WikiApiDefinition.getDetailById.client(wikiId), () =>
wikiId ? getWikiDetail(wikiId) : null
);
/**
*
@ -140,11 +208,15 @@ export const useWikiDetail = (wikiId) => {
*/
const update = useCallback(
async (data: IUpdateWiki) => {
const res = await HttpClient.patch('/wiki/update/' + wikiId, data);
mutate();
const res = await HttpClient.request({
method: WikiApiDefinition.updateById.method,
url: WikiApiDefinition.updateById.client(wikiId),
data,
});
refetch();
return res;
},
[mutate, wikiId]
[refetch, wikiId]
);
/**
@ -154,98 +226,161 @@ export const useWikiDetail = (wikiId) => {
*/
const toggleStatus = useCallback(
async (data) => {
const res = await HttpClient.post('/wiki/share/' + wikiId, data);
mutate();
const res = await HttpClient.request({
method: WikiApiDefinition.shareById.method,
url: WikiApiDefinition.shareById.client(wikiId),
data,
});
refetch();
return res;
},
[mutate, wikiId]
[refetch, wikiId]
);
return { data, loading, error, update, toggleStatus };
return { data, loading: isLoading, error, update, toggleStatus };
};
/**
*
*
* @param workspaceId
* @returns
*/
export const getWikiTocs = (wikiId, cookie = null): Promise<Array<IDocument & { createUser: IUser }>> => {
return HttpClient.request({
method: WikiApiDefinition.getTocsById.method,
url: WikiApiDefinition.getTocsById.client(wikiId),
cookie,
});
};
/**
*
* @param workspaceId
* @returns
*/
export const useWikiTocs = (wikiId) => {
const { data, error, refetch } = useQuery(WikiApiDefinition.getTocsById.client(wikiId), () =>
wikiId ? getWikiTocs(wikiId) : null
);
const loading = !data && !error;
const update = useCallback(
async (relations: Array<{ id: string; parentDocumentId: string }>) => {
const res = await HttpClient.request({
method: WikiApiDefinition.updateTocsById.method,
url: WikiApiDefinition.updateTocsById.client(wikiId),
data: relations,
});
refetch();
return res;
},
[refetch, wikiId]
);
return { data, loading, error, refresh: refetch, update };
};
/**
*
* @param workspaceId
* @returns
*/
export const getWikiDocuments = (wikiId, cookie = null): Promise<Array<IDocument & { createUser: IUser }>> => {
return HttpClient.request({
method: WikiApiDefinition.getDocumentsById.method,
url: WikiApiDefinition.getDocumentsById.client(wikiId),
cookie,
});
};
/**
*
* @param workspaceId
* @returns
*/
export const useWikiDocuments = (wikiId) => {
const { data, error, isLoading, refetch } = useQuery(WikiApiDefinition.getDocumentsById.client(wikiId), () =>
getWikiDocuments(wikiId)
);
return { data, loading: isLoading, error, refresh: refetch };
};
/**
*
* @param wikiId
* @param cookie
* @returns
*/
export const getWikiMembers = (wikiId, cookie = null): Promise<IWikiUser[]> => {
return HttpClient.request({
method: WikiApiDefinition.getMemberById.method,
url: WikiApiDefinition.getMemberById.client(wikiId),
cookie,
});
};
/**
*
* @param wikiId
* @returns
*/
export const useWikiUsers = (wikiId) => {
const { data, error, mutate } = useSWR<IWikiUser[]>('/wiki/user/' + wikiId, (url) => HttpClient.get(url));
const loading = !data && !error;
export const useWikiMembers = (wikiId) => {
const { data, error, isLoading, refetch } = useQuery(WikiApiDefinition.getMemberById.client(wikiId), () =>
getWikiMembers(wikiId)
);
const addUser = useCallback(
async (data: IWikiUserOpeateData) => {
const ret = await HttpClient.post(`/wiki/user/${wikiId}/add`, data);
mutate();
const ret = await HttpClient.request({
method: WikiApiDefinition.addMemberById.method,
url: WikiApiDefinition.addMemberById.client(wikiId),
data,
});
refetch();
return ret;
},
[mutate, wikiId]
[refetch, wikiId]
);
const updateUser = useCallback(
async (data: IWikiUserOpeateData) => {
const ret = await HttpClient.post(`/wiki/user/${wikiId}/update`, data);
mutate();
const ret = await HttpClient.request({
method: WikiApiDefinition.updateMemberById.method,
url: WikiApiDefinition.updateMemberById.client(wikiId),
data,
});
refetch();
return ret;
},
[mutate, wikiId]
[refetch, wikiId]
);
const deleteUser = useCallback(
async (data: IWikiUserOpeateData) => {
const ret = await HttpClient.post(`/wiki/user/${wikiId}/delete`, data);
mutate();
const ret = await HttpClient.request({
method: WikiApiDefinition.deleteMemberById.method,
url: WikiApiDefinition.deleteMemberById.client(wikiId),
data,
});
refetch();
return ret;
},
[mutate, wikiId]
[refetch, wikiId]
);
return {
data,
error,
loading,
refresh: mutate,
addUser,
updateUser,
deleteUser,
};
return { users: data, loading: isLoading, error, addUser, updateUser, deleteUser };
};
/**
*
* @param wikiId
*
* @returns
*/
export const useWikiStar = (wikiId) => {
const { data, error, mutate } = useSWR<boolean>(`/collector/check/${wikiId}`, () =>
HttpClient.post(`/collector/check`, {
type: CollectType.wiki,
targetId: wikiId,
})
);
const toggleStar = useCallback(async () => {
await HttpClient.post('/collector/toggle/', {
type: CollectType.wiki,
targetId: wikiId,
});
mutate();
}, [mutate, wikiId]);
return { data, error, toggleStar };
};
/**
*
* @returns
*/
export const useStaredWikis = () => {
const { data, error, mutate } = useSWR<IWikiWithIsMember[]>('/collector/wikis', (url) => HttpClient.post(url), {
revalidateOnFocus: true,
export const getPublicWikiHomeDocument = (wikiId, cookie = null): Promise<IDocument> => {
return HttpClient.request({
method: WikiApiDefinition.getPublicHomeDocumentById.method,
url: WikiApiDefinition.getPublicHomeDocumentById.client(wikiId),
cookie,
});
const loading = !data && !error;
return { data, error, loading, refresh: mutate };
};
/**
@ -253,19 +388,38 @@ export const useStaredWikis = () => {
* @returns
*/
export const usePublicWikiHomeDoc = (wikiId) => {
const { data, error } = useSWR<IDocument>('/wiki/public/homedoc/' + wikiId, (url) => HttpClient.get(url));
const { data, error } = useQuery(
WikiApiDefinition.getPublicHomeDocumentById.client(wikiId),
() => getPublicWikiHomeDocument(wikiId),
{ retry: 0 }
);
const loading = !data && !error;
return { data, error, loading };
};
/**
*
* @param wikiId
* @returns
*/
export const getPublicWikiDetail = (wikiId, cookie = null): Promise<IWiki> => {
return HttpClient.request({
method: WikiApiDefinition.getPublicDetailById.method,
url: WikiApiDefinition.getPublicDetailById.client(wikiId),
cookie,
});
};
/**
*
* @param wikiId
* @returns
*/
export const usePublicWikiDetail = (wikiId) => {
const { data, error, mutate } = useSWR<IWiki>(wikiId ? `/wiki/public/detail/${wikiId}` : null, (url) =>
HttpClient.post(url)
const { data, error } = useQuery(
WikiApiDefinition.getPublicDetailById.client(wikiId),
() => getPublicWikiDetail(wikiId),
{ retry: 0 }
);
const loading = !data && !error;
return { data, loading, error };
@ -276,32 +430,26 @@ export const usePublicWikiDetail = (wikiId) => {
* @param workspaceId
* @returns
*/
export const usePublicWikiTocs = (wikiId) => {
const { data, error, mutate } = useSWR<Array<IDocument>>(`/wiki/public/tocs/${wikiId}`, (url) =>
HttpClient.post(url)
);
const loading = !data && !error;
return { data, loading, error, refresh: mutate };
export const getPublicWikiTocs = (wikiId, cookie = null): Promise<Array<IDocument & { createUser: IUser }>> => {
return HttpClient.request({
method: WikiApiDefinition.getPublicTocsById.method,
url: WikiApiDefinition.getPublicTocsById.client(wikiId),
cookie,
});
};
/**
*
* @param documentId
*
* @param workspaceId
* @returns
*/
export const useAllPublicWikis = () => {
const [page, setPage] = useState(1);
const { data, error, mutate } = useSWR<{
data: Array<IWiki>;
total: number;
}>(`/wiki/public/wikis?page=${page}`, (url) => HttpClient.get(url));
export const usePublicWikiTocs = (wikiId) => {
const { data, error, refetch } = useQuery(
WikiApiDefinition.getPublicTocsById.client(wikiId),
() => getPublicWikiTocs(wikiId),
{ retry: 0 }
);
const loading = !data && !error;
return {
data,
loading,
error,
setPage,
};
return { data, loading, error, refresh: refetch };
};

View File

@ -4,9 +4,13 @@ export const buildUrl = (url) => {
};
export const getWikiShareURL = (wikiId) => {
return window.location.origin + '/share/wiki/' + wikiId;
const url = '/share/wiki/' + wikiId;
if (typeof window === 'undefined') return url;
return window.location.origin + url;
};
export const getDocumentShareURL = (documentId) => {
return window.location.origin + '/share/document/' + documentId;
const url = '/share/document/' + documentId;
if (typeof window === 'undefined') return url;
return window.location.origin + url;
};

View File

@ -1,6 +1,6 @@
import { getStorage, setStorage } from 'helpers/storage';
import { useEffect } from 'react';
import useSWR from 'swr';
import { useQuery } from 'react-query';
export enum Width {
'standardWidth' = 'standardWidth',
@ -13,7 +13,7 @@ const DEFAULT_WIDTH = Width.standardWidth;
const DEFAULT_FONT_SIZE = 16;
export const useDocumentStyle = () => {
const { data, mutate } = useSWR(`/fe/mock/${WIDTH_KEY}/${FONT_SIZE_KEY}`, () => {
const { data, refetch } = useQuery(`/fe/mock/${WIDTH_KEY}/${FONT_SIZE_KEY}`, () => {
if (typeof window !== 'undefined') {
return {
width: getStorage(WIDTH_KEY) || DEFAULT_WIDTH,
@ -29,17 +29,17 @@ export const useDocumentStyle = () => {
const setWidth = (width: Width) => {
setStorage(WIDTH_KEY, width);
mutate();
refetch();
};
const setFontSize = (fontSize: number) => {
setStorage(FONT_SIZE_KEY, fontSize);
mutate();
refetch();
};
useEffect(() => {
mutate();
}, [mutate]);
refetch();
}, [refetch]);
return {
width: (data && data.width) || DEFAULT_WIDTH,

View File

@ -2,7 +2,7 @@ import { clamp } from 'helpers/clamp';
import { getStorage, setStorage } from 'helpers/storage';
import { useWindowSize } from 'hooks/use-window-size';
import { useCallback, useEffect, useRef, useState } from 'react';
import useSWR from 'swr';
import { useQuery } from 'react-query';
const key = 'dragable-menu-width';
@ -21,7 +21,7 @@ export const useDragableWidth = () => {
const { width: windowWidth } = useWindowSize();
const [minWidth, setMinWidth] = useState(DEFAULT_MOBILE_MIN_WIDTH);
const [maxWidth, setMaxWidth] = useState(DEFAULT_MOBILE_MAX_WIDTH);
const { data: currentWidth, mutate } = useSWR<number>(key, () => {
const { data: currentWidth, refetch } = useQuery<number>(key, () => {
const nextWidth = getStorage(key, minWidth);
if (nextWidth <= COLLAPSED_WIDTH) {
@ -41,9 +41,9 @@ export const useDragableWidth = () => {
}
setStorage(key, size);
prevWidthRef.current = size;
mutate();
refetch();
},
[mutate, windowWidth, minWidth, maxWidth]
[refetch, windowWidth, minWidth, maxWidth]
);
const toggleCollapsed = useCallback(() => {
@ -65,15 +65,15 @@ export const useDragableWidth = () => {
setStorage(key, nextWidth);
}
mutate();
}, [mutate, currentWidth, minWidth, maxWidth]);
refetch();
}, [refetch, currentWidth, minWidth, maxWidth]);
useEffect(() => {
const min = windowWidth <= PC_MOBILE_CRITICAL_WIDTH ? DEFAULT_MOBILE_MIN_WIDTH : DEFAULT_PC_MIN_WIDTH;
const max = windowWidth <= PC_MOBILE_CRITICAL_WIDTH ? DEFAULT_MOBILE_MAX_WIDTH : DEFAULT_PC_MAX_WIDTH;
setMinWidth(min);
setMaxWidth(max);
}, [windowWidth, mutate, currentWidth]);
}, [windowWidth, refetch, currentWidth]);
return {
minWidth,

View File

@ -117,7 +117,7 @@ export const RouterHeader: React.FC = () => {
<Space>
<WikiOrDocumentCreator />
<Search />
<Message />
{/* <Message /> */}
<Theme />
<User />
</Space>

View File

@ -6,7 +6,6 @@ import { Empty } from 'components/empty';
import { IconDocumentFill } from 'components/icons/IconDocumentFill';
import { LocaleTime } from 'components/locale-time';
import { useRecentDocuments } from 'data/document';
import { useToggle } from 'hooks/use-toggle';
import Link from 'next/link';
import React from 'react';

View File

@ -3,7 +3,8 @@ import { Avatar, Dropdown, Modal, Space, Typography } from '@douyinfe/semi-ui';
import { DataRender } from 'components/data-render';
import { Empty } from 'components/empty';
import { WikiStar } from 'components/wiki/star';
import { useStaredWikis, useWikiDetail } from 'data/wiki';
import { useCollectedWikis } from 'data/collector';
import { useWikiDetail } from 'data/wiki';
import Link from 'next/link';
import { useRouter } from 'next/router';
import React from 'react';
@ -15,7 +16,7 @@ const { Text } = Typography;
const WikiContent = () => {
const { query } = useRouter();
const { data: starWikis, loading, error, refresh: refreshStarWikis } = useStaredWikis();
const { data: starWikis, loading, error, refresh: refreshStarWikis } = useCollectedWikis();
const { data: currentWiki } = useWikiDetail(query.wikiId);
return (

View File

@ -9,9 +9,12 @@ import { Theme } from 'hooks/use-theme';
import App from 'next/app';
import Head from 'next/head';
import React from 'react';
import { Hydrate, QueryClient, QueryClientProvider } from 'react-query';
class MyApp extends App<{ isMobile: boolean }> {
state = {};
state = {
queryClient: new QueryClient(),
};
static getInitialProps = async ({ Component, ctx }) => {
const request = ctx?.req;
@ -26,6 +29,7 @@ class MyApp extends App<{ isMobile: boolean }> {
render() {
const { Component, pageProps, isMobile } = this.props;
const { queryClient } = this.state;
return (
<>
@ -57,11 +61,15 @@ class MyApp extends App<{ isMobile: boolean }> {
<link key={url} rel="dns-prefetch" href={url} />
))}
</Head>
<Theme.Provider>
<IsOnMobile.Provider initialState={isMobile}>
<Component {...pageProps} />
</IsOnMobile.Provider>
</Theme.Provider>
<QueryClientProvider client={queryClient}>
<Hydrate state={pageProps.dehydratedState}>
<Theme.Provider>
<IsOnMobile.Provider initialState={isMobile}>
<Component {...pageProps} />
</IsOnMobile.Provider>
</Theme.Provider>
</Hydrate>
</QueryClientProvider>
</>
);
}

View File

@ -1,12 +1,14 @@
import { List, Pagination, Typography } from '@douyinfe/semi-ui';
import { WikiApiDefinition } from '@think/domains';
import { DataRender } from 'components/data-render';
import { Empty } from 'components/empty';
import { Seo } from 'components/seo';
import { WikiCard, WikiCardPlaceholder } from 'components/wiki/card';
import { useAllPublicWikis } from 'data/wiki';
import { getAllPublicWikis, useAllPublicWikis } from 'data/wiki';
import { SingleColumnLayout } from 'layouts/single-column';
import type { NextPage } from 'next';
import React from 'react';
import { serverPrefetcher } from 'services/server-prefetcher';
const grid = {
gutter: 16,
@ -71,4 +73,11 @@ const Page: NextPage = () => {
);
};
Page.getInitialProps = async (ctx) => {
const props = await serverPrefetcher(ctx, [
{ url: `${WikiApiDefinition.getAllWikis.client()}?page=1`, action: (cookie) => getAllPublicWikis(1, cookie) },
]);
return props;
};
export default Page;

View File

@ -1,5 +1,5 @@
import { Avatar, Button, List, Table, Typography } from '@douyinfe/semi-ui';
import type { IDocument } from '@think/domains';
import { CollectorApiDefinition, DocumentApiDefinition, IDocument } from '@think/domains';
import { DataRender } from 'components/data-render';
import { DocumentActions } from 'components/document/actions';
import { Empty } from 'components/empty';
@ -7,13 +7,14 @@ import { LocaleTime } from 'components/locale-time';
import { Seo } from 'components/seo';
import { WikiCreator } from 'components/wiki/create';
import { WikiPinCard, WikiPinCardPlaceholder } from 'components/wiki/pin-card';
import { useRecentDocuments } from 'data/document';
import { useStaredWikis } from 'data/wiki';
import { getCollectedWikis, useCollectedWikis } from 'data/collector';
import { getRecentVisitedDocuments, useRecentDocuments } from 'data/document';
import { useToggle } from 'hooks/use-toggle';
import { SingleColumnLayout } from 'layouts/single-column';
import type { NextPage } from 'next';
import Link from 'next/link';
import React, { useMemo } from 'react';
import { serverPrefetcher } from 'services/server-prefetcher';
import styles from './index.module.scss';
@ -112,7 +113,7 @@ const RecentDocs = () => {
const Page: NextPage = () => {
const [visible, toggleVisible] = useToggle(false);
const { data: staredWikis, loading, error } = useStaredWikis();
const { data: staredWikis, loading, error } = useCollectedWikis();
return (
<SingleColumnLayout>
@ -160,4 +161,12 @@ const Page: NextPage = () => {
);
};
Page.getInitialProps = async (ctx) => {
const props = await serverPrefetcher(ctx, [
{ url: CollectorApiDefinition.wikis.client(), action: (cookie) => getCollectedWikis(cookie) },
{ url: DocumentApiDefinition.recent.client(), action: (cookie) => getRecentVisitedDocuments(cookie) },
]);
return props;
};
export default Page;

View File

@ -1,6 +1,9 @@
import { DocumentApiDefinition, IDocument } from '@think/domains';
import { DocumentPublicReader } from 'components/document/reader/public';
import { NextPage } from 'next';
import React from 'react';
import { getPublicDocumentDetail } from 'services/document';
import { serverPrefetcher } from 'services/server-prefetcher';
interface IProps {
documentId: string;
@ -12,7 +15,15 @@ const Page: NextPage<IProps> = ({ documentId }) => {
Page.getInitialProps = async (ctx) => {
const { documentId } = ctx.query;
return { documentId } as IProps;
const res = await serverPrefetcher(ctx, [
{
url: DocumentApiDefinition.getPublicDetailById.client(documentId as IDocument['id']),
// 默认无密码公开文档,如果有密码,客户端重新获取
action: () => getPublicDocumentDetail(documentId as IDocument['id'], { sharePassword: '' }),
ignoreCookie: true,
},
]);
return { ...res, documentId } as IProps;
};
export default Page;

View File

@ -1,8 +1,12 @@
import { DocumentApiDefinition, IDocument, IWiki, WikiApiDefinition } from '@think/domains';
import { DocumentPublicReader } from 'components/document/reader/public';
import { WikiPublicTocs } from 'components/wiki/tocs/public';
import { getPublicWikiTocs } from 'data/wiki';
import { PublicDoubleColumnLayout } from 'layouts/public-double-column';
import { NextPage } from 'next';
import React from 'react';
import { getPublicDocumentDetail } from 'services/document';
import { serverPrefetcher } from 'services/server-prefetcher';
interface IProps {
wikiId: string;
@ -20,7 +24,20 @@ const Page: NextPage<IProps> = ({ wikiId, documentId }) => {
Page.getInitialProps = async (ctx) => {
const { wikiId, documentId } = ctx.query;
return { wikiId, documentId } as IProps;
const res = await serverPrefetcher(ctx, [
{
url: WikiApiDefinition.getPublicTocsById.client(wikiId as IWiki['id']),
action: () => getPublicWikiTocs(wikiId),
ignoreCookie: true,
},
{
url: DocumentApiDefinition.getPublicDetailById.client(documentId as IDocument['id']),
// 默认无密码公开文档,如果有密码,客户端重新获取
action: () => getPublicDocumentDetail(documentId as IDocument['id'], { sharePassword: '' }),
ignoreCookie: true,
},
]);
return { ...res, wikiId, documentId } as IProps;
};
export default Page;

View File

@ -1,10 +1,12 @@
import { IWiki, WikiApiDefinition } from '@think/domains';
import { DataRender } from 'components/data-render';
import { DocumentPublicReader } from 'components/document/reader/public';
import { WikiPublicTocs } from 'components/wiki/tocs/public';
import { usePublicWikiHomeDoc } from 'data/wiki';
import { getPublicWikiDetail, getPublicWikiHomeDocument, getPublicWikiTocs, usePublicWikiHomeDoc } from 'data/wiki';
import { PublicDoubleColumnLayout } from 'layouts/public-double-column';
import { NextPage } from 'next';
import React from 'react';
import { serverPrefetcher } from 'services/server-prefetcher';
interface IProps {
wikiId: string;
@ -31,7 +33,24 @@ const Page: NextPage<IProps> = ({ wikiId }) => {
Page.getInitialProps = async (ctx) => {
const { wikiId } = ctx.query;
return { wikiId } as IProps;
const res = await serverPrefetcher(ctx, [
{
url: WikiApiDefinition.getPublicDetailById.client(wikiId as IWiki['id']),
action: () => getPublicWikiDetail(wikiId),
ignoreCookie: true,
},
{
url: WikiApiDefinition.getPublicHomeDocumentById.client(wikiId as IWiki['id']),
action: () => getPublicWikiHomeDocument(wikiId),
ignoreCookie: true,
},
{
url: WikiApiDefinition.getPublicTocsById.client(wikiId as IWiki['id']),
action: () => getPublicWikiTocs(wikiId),
ignoreCookie: true,
},
]);
return { wikiId, ...res } as IProps;
};
export default Page;

View File

@ -1,14 +1,15 @@
import { List, Typography } from '@douyinfe/semi-ui';
import { CollectorApiDefinition } from '@think/domains';
import { DataRender } from 'components/data-render';
import { DocumentCard, DocumentCardPlaceholder } from 'components/document/card';
import { Empty } from 'components/empty';
import { Seo } from 'components/seo';
import { WikiCard, WikiCardPlaceholder } from 'components/wiki/card';
import { useStaredDocuments } from 'data/document';
import { useStaredWikis } from 'data/wiki';
import { getCollectedDocuments, getCollectedWikis, useCollectedDocuments, useCollectedWikis } from 'data/collector';
import { SingleColumnLayout } from 'layouts/single-column';
import type { NextPage } from 'next';
import React from 'react';
import { serverPrefetcher } from 'services/server-prefetcher';
import styles from './index.module.scss';
@ -24,7 +25,7 @@ const grid = {
};
const StarDocs = () => {
const { data: docs, loading, error } = useStaredDocuments();
const { data: docs, loading, error } = useCollectedDocuments();
return (
<DataRender
@ -58,7 +59,7 @@ const StarDocs = () => {
};
const StarWikis = () => {
const { data, loading, error } = useStaredWikis();
const { data, loading, error } = useCollectedWikis();
return (
<DataRender
@ -114,4 +115,12 @@ const Page: NextPage = () => {
);
};
Page.getInitialProps = async (ctx) => {
const props = await serverPrefetcher(ctx, [
{ url: CollectorApiDefinition.wikis.client(), action: (cookie) => getCollectedWikis(cookie) },
{ url: CollectorApiDefinition.documents.client(), action: (cookie) => getCollectedDocuments(cookie) },
]);
return props;
};
export default Page;

View File

@ -1,5 +1,8 @@
import { ITemplate, TemplateApiDefinition } from '@think/domains';
import { TemplateEditor } from 'components/template/editor';
import { getTemplateDetail } from 'data/template';
import { NextPage } from 'next';
import { serverPrefetcher } from 'services/server-prefetcher';
interface IProps {
templateId: string;
@ -11,7 +14,13 @@ const Page: NextPage<IProps> = ({ templateId }) => {
Page.getInitialProps = async (ctx) => {
const { templateId } = ctx.query;
return { templateId } as IProps;
const res = await serverPrefetcher(ctx, [
{
url: TemplateApiDefinition.getDetailById.client(templateId as ITemplate['id']),
action: (cookie) => getTemplateDetail(templateId, cookie),
},
]);
return { ...res, templateId } as IProps;
};
export default Page;

View File

@ -1,6 +1,9 @@
import { DocumentApiDefinition, IDocument } from '@think/domains';
import { DocumentEditor } from 'components/document/editor';
import { getDocumentDetail } from 'data/document';
import { NextPage } from 'next';
import React from 'react';
import { serverPrefetcher } from 'services/server-prefetcher';
interface IProps {
wikiId: string;
@ -13,7 +16,14 @@ const Page: NextPage<IProps> = ({ wikiId, documentId }) => {
Page.getInitialProps = async (ctx) => {
const { wikiId, documentId } = ctx.query;
return { wikiId, documentId } as IProps;
const res = await serverPrefetcher(ctx, [
{
url: DocumentApiDefinition.getDetailById.client(documentId as IDocument['id']),
action: (cookie) => getDocumentDetail(documentId, cookie),
},
]);
return { ...res, wikiId, documentId } as IProps;
};
export default Page;

View File

@ -1,8 +1,12 @@
import { DocumentApiDefinition, IDocument, IWiki, WikiApiDefinition } from '@think/domains';
import { DocumentReader } from 'components/document/reader';
import { WikiTocs } from 'components/wiki/tocs';
import { getDocumentDetail } from 'data/document';
import { getWikiTocs } from 'data/wiki';
import { DoubleColumnLayout } from 'layouts/double-column';
import { NextPage } from 'next';
import React from 'react';
import { serverPrefetcher } from 'services/server-prefetcher';
interface IProps {
wikiId: string;
@ -20,7 +24,18 @@ const Page: NextPage<IProps> = ({ wikiId, documentId }) => {
Page.getInitialProps = async (ctx) => {
const { wikiId, documentId } = ctx.query;
return { wikiId, documentId } as IProps;
const res = await serverPrefetcher(ctx, [
{
url: WikiApiDefinition.getTocsById.client(wikiId as IWiki['id']),
action: (cookie) => getWikiTocs(wikiId, cookie),
},
{
url: DocumentApiDefinition.getDetailById.client(documentId as IDocument['id']),
action: (cookie) => getDocumentDetail(documentId, cookie),
},
]);
return { ...res, wikiId, documentId } as IProps;
};
export default Page;

View File

@ -1,4 +1,5 @@
import { List, TabPane, Tabs, Typography } from '@douyinfe/semi-ui';
import { IWiki, WikiApiDefinition } from '@think/domains';
import { DataRender } from 'components/data-render';
import { DocumentCard, DocumentCardPlaceholder } from 'components/document/card';
import { DocumentCreator } from 'components/document-creator';
@ -7,12 +8,13 @@ import { Seo } from 'components/seo';
import { WikiDocumentsShare } from 'components/wiki/documents-share';
import { WikiTocs } from 'components/wiki/tocs';
import { WikiTocsManager } from 'components/wiki/tocs/manager';
import { useWikiDocs } from 'data/wiki';
import { getWikiTocs, useWikiDocuments } from 'data/wiki';
import { CreateDocumentIllustration } from 'illustrations/create-document';
import { DoubleColumnLayout } from 'layouts/double-column';
import { NextPage } from 'next';
import Router, { useRouter } from 'next/router';
import React, { useCallback } from 'react';
import { serverPrefetcher } from 'services/server-prefetcher';
interface IProps {
wikiId: string;
@ -31,7 +33,7 @@ const grid = {
};
const AllDocs = ({ wikiId }) => {
const { data: docs, loading, error } = useWikiDocs(wikiId);
const { data: docs, loading, error } = useWikiDocuments(wikiId);
return (
<DataRender
loading={loading}
@ -110,7 +112,13 @@ const Page: NextPage<IProps> = ({ wikiId }) => {
Page.getInitialProps = async (ctx) => {
const { wikiId } = ctx.query;
return { wikiId } as IProps;
const res = await serverPrefetcher(ctx, [
{
url: WikiApiDefinition.getTocsById.client(wikiId as IWiki['id']),
action: (cookie) => getWikiTocs(wikiId, cookie),
},
]);
return { ...res, wikiId } as IProps;
};
export default Page;

View File

@ -1,17 +1,19 @@
import { IWiki, WikiApiDefinition } from '@think/domains';
import { DataRender } from 'components/data-render';
import { DocumentReader } from 'components/document/reader';
import { WikiTocs } from 'components/wiki/tocs';
import { useWikiHomeDoc } from 'data/wiki';
import { getWikiDetail, getWikiHomeDocument, getWikiTocs, useWikiHomeDocument } from 'data/wiki';
import { DoubleColumnLayout } from 'layouts/double-column';
import { NextPage } from 'next';
import React from 'react';
import { serverPrefetcher } from 'services/server-prefetcher';
interface IProps {
wikiId: string;
}
const Page: NextPage<IProps> = ({ wikiId }) => {
const { data: doc, loading, error } = useWikiHomeDoc(wikiId);
const { data: doc, loading, error } = useWikiHomeDocument(wikiId);
return (
<DoubleColumnLayout
@ -29,7 +31,21 @@ const Page: NextPage<IProps> = ({ wikiId }) => {
Page.getInitialProps = async (ctx) => {
const { wikiId } = ctx.query;
return { wikiId } as IProps;
const res = await serverPrefetcher(ctx, [
{
url: WikiApiDefinition.getHomeDocumentById.client(wikiId as IWiki['id']),
action: (cookie) => getWikiHomeDocument(cookie),
},
{
url: WikiApiDefinition.getDetailById.client(wikiId as IWiki['id']),
action: (cookie) => getWikiDetail(wikiId, cookie),
},
{
url: WikiApiDefinition.getTocsById.client(wikiId as IWiki['id']),
action: (cookie) => getWikiTocs(wikiId, cookie),
},
]);
return { ...res, wikiId } as IProps;
};
export default Page;

View File

@ -1,9 +1,12 @@
import { IWiki, WikiApiDefinition } from '@think/domains';
import { WikiSetting } from 'components/wiki/setting';
import { WikiTocs } from 'components/wiki/tocs';
import { getWikiMembers, getWikiTocs } from 'data/wiki';
import { DoubleColumnLayout } from 'layouts/double-column';
import { NextPage } from 'next';
import Router, { useRouter } from 'next/router';
import React, { useCallback } from 'react';
import { serverPrefetcher } from 'services/server-prefetcher';
interface IProps {
wikiId: string;
@ -41,7 +44,16 @@ const Page: NextPage<IProps> = ({ wikiId }) => {
Page.getInitialProps = async (ctx) => {
const { wikiId } = ctx.query;
return { wikiId } as IProps;
const res = await serverPrefetcher(ctx, [
{
url: WikiApiDefinition.getTocsById.client(wikiId as IWiki['id']),
action: (cookie) => getWikiTocs(wikiId, cookie),
},
{
url: WikiApiDefinition.getMemberById.client(wikiId as IWiki['id']),
action: (cookie) => getWikiMembers(wikiId, cookie),
},
]);
return { ...res, wikiId } as IProps;
};
export default Page;

View File

@ -1,14 +1,16 @@
import { List, TabPane, Tabs, Typography } from '@douyinfe/semi-ui';
import { WikiApiDefinition } from '@think/domains';
import { DataRender } from 'components/data-render';
import { Empty } from 'components/empty';
import { Seo } from 'components/seo';
import { WikiCard, WikiCardPlaceholder } from 'components/wiki/card';
import { WikiCreator } from 'components/wiki-creator';
import { useAllWikis, useJoinWikis, useOwnWikis } from 'data/wiki';
import { getAllWikis, getJoinWikis, getOwnWikis, useAllWikis, useJoinWikis, useOwnWikis } from 'data/wiki';
import { CreateWikiIllustration } from 'illustrations/create-wiki';
import { SingleColumnLayout } from 'layouts/single-column';
import type { NextPage } from 'next';
import React from 'react';
import { serverPrefetcher } from 'services/server-prefetcher';
const grid = {
gutter: 16,
@ -79,4 +81,13 @@ const Page: NextPage = () => {
);
};
Page.getInitialProps = async (ctx) => {
const props = await serverPrefetcher(ctx, [
{ url: WikiApiDefinition.getAllWikis.client(), action: (cookie) => getAllWikis(cookie) },
{ url: WikiApiDefinition.getJoinWikis.client(), action: (cookie) => getJoinWikis(cookie) },
{ url: WikiApiDefinition.getOwnWikis.client(), action: (cookie) => getOwnWikis(cookie) },
]);
return props;
};
export default Page;

View File

@ -1,21 +1,29 @@
import { Toast } from '@douyinfe/semi-ui';
import axios from 'axios';
import axios, { Axios, AxiosRequestConfig, AxiosResponse } from 'axios';
import Router from 'next/router';
type WithCookieAxiosRequestConfig = AxiosRequestConfig & { cookie?: string };
interface AxiosInstance extends Axios {
request<T = any, R = AxiosResponse<T>>(config: WithCookieAxiosRequestConfig): Promise<R>;
}
export const HttpClient = axios.create({
baseURL: process.env.SERVER_API_URL,
timeout: 60000,
});
withCredentials: true,
}) as AxiosInstance;
const isBrowser = typeof window !== 'undefined';
HttpClient.interceptors.request.use(
(config) => {
if (isBrowser) {
const token = window.localStorage.getItem('token');
if (config && config.headers && token) {
config.headers.Authorization = `Bearer ${token}`;
(config: WithCookieAxiosRequestConfig) => {
const cookie = config.cookie;
if (cookie) {
if (typeof window === 'undefined' && !config.headers.cookie) {
config.headers.cookie = cookie;
}
delete config.cookie;
}
return config;
},

View File

@ -0,0 +1,28 @@
import { NextPageContext } from 'next';
import { dehydrate, QueryClient } from 'react-query';
type PrefetchActions = Array<{
url: string;
action: (cookie: string) => void;
ignoreCookie?: boolean;
}>;
export async function serverPrefetcher(ctx: NextPageContext, actions: PrefetchActions) {
const cookie = ctx.req?.headers?.cookie;
if (!cookie && !actions.filter((action) => action.ignoreCookie === true).length) {
return {};
}
const queryClient = new QueryClient();
await Promise.all(
actions.map((action) => {
return queryClient.prefetchQuery(action.url, () => action.action(cookie));
})
);
return {
dehydratedState: dehydrate(queryClient),
};
}

View File

@ -0,0 +1,34 @@
export declare const CollectorApiDefinition: {
/**
*
*/
toggle: {
method: "post";
server: "toggle";
client: () => string;
};
/**
*
*/
check: {
method: "post";
server: "check";
client: () => string;
};
/**
*
*/
wikis: {
method: "get";
server: "wikis";
client: () => string;
};
/**
*
*/
documents: {
method: "get";
server: "documents";
client: () => string;
};
};

View File

@ -0,0 +1,37 @@
"use strict";
exports.__esModule = true;
exports.CollectorApiDefinition = void 0;
exports.CollectorApiDefinition = {
/**
* 收藏或取消收藏
*/
toggle: {
method: 'post',
server: 'toggle',
client: function () { return '/collector/toggle'; }
},
/**
* 检测是否收藏
*/
check: {
method: 'post',
server: 'check',
client: function () { return '/collector/check'; }
},
/**
* 获取收藏的知识库
*/
wikis: {
method: 'get',
server: 'wikis',
client: function () { return '/collector/wikis'; }
},
/**
* 获取收藏的文档
*/
documents: {
method: 'get',
server: 'documents',
client: function () { return '/collector/documents'; }
}
};

View File

@ -0,0 +1,35 @@
import { IComment, IDocument } from '../models';
export declare const CommentApiDefinition: {
/**
*
*/
add: {
method: "post";
server: "add";
client: () => string;
};
/**
*
*/
update: {
method: "patch";
server: "update";
client: () => string;
};
/**
*
*/
delete: {
method: "delete";
server: "delete/:id";
client: (id: IComment['id']) => string;
};
/**
*
*/
documents: {
method: "get";
server: "document/:documentId";
client: (documentId: IDocument['id']) => string;
};
};

View File

@ -0,0 +1,37 @@
"use strict";
exports.__esModule = true;
exports.CommentApiDefinition = void 0;
exports.CommentApiDefinition = {
/**
* 新建评论
*/
add: {
method: 'post',
server: 'add',
client: function () { return '/comment/add'; }
},
/**
* 更新评论
*/
update: {
method: 'patch',
server: 'update',
client: function () { return '/comment/update'; }
},
/**
* 删除评论
*/
"delete": {
method: 'delete',
server: 'delete/:id',
client: function (id) { return "/comment/delete/".concat(id); }
},
/**
* 获取指定文档评论
*/
documents: {
method: 'get',
server: 'document/:documentId',
client: function (documentId) { return "/comment/document/".concat(documentId); }
}
};

View File

@ -0,0 +1,123 @@
import { IDocument } from '../models';
export declare const DocumentApiDefinition: {
/**
*
*/
search: {
method: "get";
server: "search";
client: () => string;
};
/**
* 访
*/
recent: {
method: "get";
server: "recent";
client: () => string;
};
/**
*
*/
create: {
method: "post";
server: "create";
client: () => string;
};
/**
*
*/
getDetailById: {
method: "get";
server: "detail/:id";
client: (id: IDocument['id']) => string;
};
/**
*
*/
updateById: {
method: "patch";
server: "update/:id";
client: (id: IDocument['id']) => string;
};
/**
*
*/
getVersionById: {
method: "get";
server: "version/:id";
client: (id: IDocument['id']) => string;
};
/**
*
*/
getMemberById: {
method: "get";
server: "member/:id";
client: (id: IDocument['id']) => string;
};
/**
*
*/
addMemberById: {
method: "post";
server: "member/:id/add";
client: (id: IDocument['id']) => string;
};
/**
*
*/
updateMemberById: {
method: "patch";
server: "member/:id/update";
client: (id: IDocument['id']) => string;
};
/**
*
*/
deleteMemberById: {
method: "post";
server: "member/:id/delete";
client: (id: IDocument['id']) => string;
};
/**
*
*/
getChildren: {
method: "post";
server: "children";
client: () => string;
};
/**
*
*/
deleteById: {
method: "delete";
server: "delete/:id";
client: (id: IDocument['id']) => string;
};
/**
*
*/
shareById: {
method: "post";
server: "share/:id";
client: (id: IDocument['id']) => string;
};
/**
*
*/
getPublicDetailById: {
method: "post";
server: "public/detail/:id";
client: (id: IDocument['id']) => string;
};
/**
*
*/
getPublicChildren: {
method: "post";
server: "public/children";
client: () => string;
};
};

View File

@ -0,0 +1,125 @@
"use strict";
exports.__esModule = true;
exports.DocumentApiDefinition = void 0;
exports.DocumentApiDefinition = {
/**
* 搜索文档
*/
search: {
method: 'get',
server: 'search',
client: function () { return '/document/search'; }
},
/**
* 获取用户最近访问的文档
*/
recent: {
method: 'get',
server: 'recent',
client: function () { return '/document/recent'; }
},
/**
* 新建文档
*/
create: {
method: 'post',
server: 'create',
client: function () { return '/document/create'; }
},
/**
* 获取文档详情
*/
getDetailById: {
method: 'get',
server: 'detail/:id',
client: function (id) { return "/document/detail/".concat(id); }
},
/**
* 更新文档
*/
updateById: {
method: 'patch',
server: 'update/:id',
client: function (id) { return "/document/update/".concat(id); }
},
/**
* 获取文档版本记录
*/
getVersionById: {
method: 'get',
server: 'version/:id',
client: function (id) { return "/document/version/".concat(id); }
},
/**
* 获取文档成员
*/
getMemberById: {
method: 'get',
server: 'member/:id',
client: function (id) { return "/document/member/".concat(id); }
},
/**
* 添加文档成员
*/
addMemberById: {
method: 'post',
server: 'member/:id/add',
client: function (id) { return "/document/member/".concat(id, "/add"); }
},
/**
* 更新文档成员
*/
updateMemberById: {
method: 'patch',
server: 'member/:id/update',
client: function (id) { return "/document/member/".concat(id, "/update"); }
},
/**
* 删除文档成员
*/
deleteMemberById: {
method: 'post',
server: 'member/:id/delete',
client: function (id) { return "/document/member/".concat(id, "/delete"); }
},
/**
* 获取子文档
*/
getChildren: {
method: 'post',
server: 'children',
client: function () { return "/document/children"; }
},
/**
* 删除文档
*/
deleteById: {
method: 'delete',
server: 'delete/:id',
client: function (id) { return "/document/delete/".concat(id); }
},
/**
* 分享文档
*/
shareById: {
method: 'post',
server: 'share/:id',
client: function (id) { return "/document/share/".concat(id); }
},
/**
* 获取公开文档详情
*/
getPublicDetailById: {
method: 'post',
server: 'public/detail/:id',
client: function (id) { return "/document/public/detail/".concat(id); }
},
/**
* 获取公开文档的子文档
*/
getPublicChildren: {
method: 'post',
server: 'public/children',
client: function () { return "/document/public/children"; }
}
};

View File

@ -0,0 +1,10 @@
export declare const FileApiDefinition: {
/**
*
*/
upload: {
method: "post";
server: "upload";
client: () => string;
};
};

View File

@ -0,0 +1,13 @@
"use strict";
exports.__esModule = true;
exports.FileApiDefinition = void 0;
exports.FileApiDefinition = {
/**
* 上传文件
*/
upload: {
method: 'post',
server: 'upload',
client: function () { return '/file/upload'; }
}
};

View File

@ -0,0 +1,8 @@
export * from './user';
export * from './wiki';
export * from './document';
export * from './file';
export * from './message';
export * from './template';
export * from './comment';
export * from './collector';

View File

@ -0,0 +1,20 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
exports.__esModule = true;
__exportStar(require("./user"), exports);
__exportStar(require("./wiki"), exports);
__exportStar(require("./document"), exports);
__exportStar(require("./file"), exports);
__exportStar(require("./message"), exports);
__exportStar(require("./template"), exports);
__exportStar(require("./comment"), exports);
__exportStar(require("./collector"), exports);

View File

@ -0,0 +1,35 @@
import { IMessage } from '../models';
export declare const MessageApiDefinition: {
/**
*
*/
getUnread: {
method: "get";
server: "unread";
client: () => string;
};
/**
*
*/
getRead: {
method: "get";
server: "read";
client: () => string;
};
/**
*
*/
getAll: {
method: "get";
server: "all";
client: () => string;
};
/**
*
*/
readMessage: {
method: "post";
server: "read/:id";
client: (id: IMessage['id']) => string;
};
};

View File

@ -0,0 +1,37 @@
"use strict";
exports.__esModule = true;
exports.MessageApiDefinition = void 0;
exports.MessageApiDefinition = {
/**
* 获取未读消息
*/
getUnread: {
method: 'get',
server: 'unread',
client: function () { return '/message/unread'; }
},
/**
* 获取已读消息
*/
getRead: {
method: 'get',
server: 'read',
client: function () { return '/message/read'; }
},
/**
* 获取所有消息
*/
getAll: {
method: 'get',
server: 'all',
client: function () { return '/message/all'; }
},
/**
* 将消息标记为已读
*/
readMessage: {
method: 'post',
server: 'read/:id',
client: function (id) { return "/message/read/".concat(id); }
}
};

View File

@ -0,0 +1,51 @@
import { ITemplate } from '../models';
export declare const TemplateApiDefinition: {
/**
*
*/
public: {
method: "get";
server: "public";
client: () => string;
};
/**
*
*/
own: {
method: "get";
server: "own";
client: () => string;
};
/**
*
*/
add: {
method: "post";
server: "add";
client: () => string;
};
/**
*
*/
updateById: {
method: "patch";
server: "update/:id";
client: (id: ITemplate['id']) => string;
};
/**
*
*/
getDetailById: {
method: "get";
server: "detail/:id";
client: (id: ITemplate['id']) => string;
};
/**
*
*/
deleteById: {
method: "delete";
server: "delete/:id";
client: (id: ITemplate['id']) => string;
};
};

View File

@ -0,0 +1,53 @@
"use strict";
exports.__esModule = true;
exports.TemplateApiDefinition = void 0;
exports.TemplateApiDefinition = {
/**
* 获取公开模板
*/
public: {
method: 'get',
server: 'public',
client: function () { return '/template/public'; }
},
/**
* 获取个人创建模板
*/
own: {
method: 'get',
server: 'own',
client: function () { return '/template/own'; }
},
/**
* 新建模板
*/
add: {
method: 'post',
server: 'add',
client: function () { return '/template/add'; }
},
/**
* 更新模板
*/
updateById: {
method: 'patch',
server: 'update/:id',
client: function (id) { return "/template/update/".concat(id); }
},
/**
* 获取模板详情
*/
getDetailById: {
method: 'get',
server: 'detail/:id',
client: function (id) { return "/template/detail/".concat(id); }
},
/**
* 删除模板
*/
deleteById: {
method: 'delete',
server: 'delete/:id',
client: function (id) { return "/template/delete/".concat(id); }
}
};

View File

@ -0,0 +1,42 @@
export declare const UserApiDefinition: {
/**
*
*/
getAllUsers: {
method: "get";
server: "/";
client: () => string;
};
/**
*
*/
register: {
method: "post";
server: "register";
client: () => string;
};
/**
*
*/
login: {
method: "post";
server: "login";
client: () => string;
};
/**
*
*/
logout: {
method: "post";
server: "logout";
client: () => string;
};
/**
*
*/
update: {
method: "patch";
server: "update";
client: () => string;
};
};

View File

@ -0,0 +1,45 @@
"use strict";
exports.__esModule = true;
exports.UserApiDefinition = void 0;
exports.UserApiDefinition = {
/**
* 获取用户
*/
getAllUsers: {
method: 'get',
server: '/',
client: function () { return '/user'; }
},
/**
* 注册
*/
register: {
method: 'post',
server: 'register',
client: function () { return '/user/register'; }
},
/**
* 登录
*/
login: {
method: 'post',
server: 'login',
client: function () { return '/user/login'; }
},
/**
* 登出
*/
logout: {
method: 'post',
server: 'logout',
client: function () { return '/user/logout'; }
},
/**
* 更新
*/
update: {
method: 'patch',
server: 'update',
client: function () { return "/user/update"; }
}
};

View File

@ -0,0 +1,163 @@
import { IWiki } from '../models';
export declare const WikiApiDefinition: {
/**
*
*/
getAllWikis: {
method: "get";
server: "list/all";
client: () => string;
};
/**
*
*/
getOwnWikis: {
method: "get";
server: "list/own";
client: () => string;
};
/**
*
*/
getJoinWikis: {
method: "get";
server: "list/join";
client: () => string;
};
/**
*
*/
add: {
method: "post";
server: "add";
client: () => string;
};
/**
*
*/
getHomeDocumentById: {
method: "get";
server: "homedoc/:id";
client: (id: IWiki['id']) => string;
};
/**
*
*/
getTocsById: {
method: "get";
server: "tocs/:id";
client: (id: IWiki['id']) => string;
};
/**
*
*/
updateTocsById: {
method: "patch";
server: "tocs/:id/update";
client: (id: IWiki['id']) => string;
};
/**
*
*/
getDocumentsById: {
method: "get";
server: "documents/:id";
client: (id: IWiki['id']) => string;
};
/**
*
*/
getDetailById: {
method: "get";
server: "detail/:id";
client: (id: IWiki['id']) => string;
};
/**
*
*/
updateById: {
method: "patch";
server: "update/:id";
client: (id: IWiki['id']) => string;
};
/**
*
*/
deleteById: {
method: "delete";
server: "delet/:id";
client: (id: IWiki['id']) => string;
};
/**
*
*/
getMemberById: {
method: "get";
server: "member/:id";
client: (id: IWiki['id']) => string;
};
/**
*
*/
addMemberById: {
method: "post";
server: "member/:id/add";
client: (id: IWiki['id']) => string;
};
/**
*
*/
updateMemberById: {
method: "patch";
server: "member/:id/update";
client: (id: IWiki['id']) => string;
};
/**
*
*/
deleteMemberById: {
method: "delete";
server: "member/:id/delete";
client: (id: IWiki['id']) => string;
};
/**
*
*/
shareById: {
method: "post";
server: "share/:id";
client: (id: IWiki['id']) => string;
};
/**
*
*/
getPublicHomeDocumentById: {
method: "get";
server: "/public/homedoc/:id";
client: (id: IWiki['id']) => string;
};
/**
*
*/
getPublicTocsById: {
method: "get";
server: "/public/tocs/:id";
client: (id: IWiki['id']) => string;
};
/**
*
*/
getPublicDetailById: {
method: "get";
server: "/public/detail/:id";
client: (id: IWiki['id']) => string;
};
/**
*
*/
getPublicWikis: {
method: "get";
server: "/public/wikis";
client: () => string;
};
};

View File

@ -0,0 +1,165 @@
"use strict";
exports.__esModule = true;
exports.WikiApiDefinition = void 0;
exports.WikiApiDefinition = {
/**
* 获取用户所有知识库创建的参与的
*/
getAllWikis: {
method: 'get',
server: 'list/all',
client: function () { return '/wiki/list/all'; }
},
/**
* 获取用户创建的知识库
*/
getOwnWikis: {
method: 'get',
server: 'list/own',
client: function () { return '/wiki/list/own'; }
},
/**
* 获取用户参与的知识库
*/
getJoinWikis: {
method: 'get',
server: 'list/join',
client: function () { return '/wiki/list/join'; }
},
/**
* 新建知识库
*/
add: {
method: 'post',
server: 'add',
client: function () { return '/wiki/add'; }
},
/**
* 获取知识库首页文档
*/
getHomeDocumentById: {
method: 'get',
server: 'homedoc/:id',
client: function (id) { return "/wiki/homedoc/".concat(id); }
},
/**
* 获取知识库目录
*/
getTocsById: {
method: 'get',
server: 'tocs/:id',
client: function (id) { return "/wiki/tocs/".concat(id); }
},
/**
* 更新知识库目录
*/
updateTocsById: {
method: 'patch',
server: 'tocs/:id/update',
client: function (id) { return "/wiki/tocs/".concat(id, "/update"); }
},
/**
* 获取知识库所有文档
*/
getDocumentsById: {
method: 'get',
server: 'documents/:id',
client: function (id) { return "/wiki/documents/".concat(id); }
},
/**
* 获取知识库详情
*/
getDetailById: {
method: 'get',
server: 'detail/:id',
client: function (id) { return "/wiki/detail/".concat(id); }
},
/**
* 更新知识库
*/
updateById: {
method: 'patch',
server: 'update/:id',
client: function (id) { return "/wiki/update/".concat(id); }
},
/**
* 删除知识库
*/
deleteById: {
method: 'delete',
server: 'delet/:id',
client: function (id) { return "/wiki/delet/".concat(id); }
},
/**
* 获取知识库成员
*/
getMemberById: {
method: 'get',
server: 'member/:id',
client: function (id) { return "/wiki/member/".concat(id); }
},
/**
* 添加知识库成员
*/
addMemberById: {
method: 'post',
server: 'member/:id/add',
client: function (id) { return "/wiki/member/".concat(id, "/add"); }
},
/**
* 更新知识库成员
*/
updateMemberById: {
method: 'patch',
server: 'member/:id/update',
client: function (id) { return "/wiki/member/".concat(id, "/update"); }
},
/**
* 删除知识库成员
*/
deleteMemberById: {
method: 'delete',
server: 'member/:id/delete',
client: function (id) { return "/wiki/member/".concat(id, "/delete"); }
},
/**
* 分享知识库
*/
shareById: {
method: 'post',
server: 'share/:id',
client: function (id) { return "/wiki/share/".concat(id); }
},
/**
* 获取公开知识库首页文档
*/
getPublicHomeDocumentById: {
method: 'get',
server: '/public/homedoc/:id',
client: function (id) { return "/wiki/public/homedoc/".concat(id); }
},
/**
* 获取公开知识库目录
*/
getPublicTocsById: {
method: 'get',
server: '/public/tocs/:id',
client: function (id) { return "/wiki/public/tocs/".concat(id); }
},
/**
* 获取知识库详情
*/
getPublicDetailById: {
method: 'get',
server: '/public/detail/:id',
client: function (id) { return "/wiki/public/detail/".concat(id); }
},
/**
* 获取所有公开知识库
*/
getPublicWikis: {
method: 'get',
server: '/public/wikis',
client: function () { return "/wiki/public/wikis"; }
}
};

View File

@ -1,2 +1,3 @@
export * from './models';
export * from './util';
export * from './api';

View File

@ -12,3 +12,4 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
exports.__esModule = true;
__exportStar(require("./models"), exports);
__exportStar(require("./util"), exports);
__exportStar(require("./api"), exports);

View File

@ -0,0 +1,39 @@
import { IDocument, IWiki, CollectType } from '../models';
export const CollectorApiDefinition = {
/**
*
*/
toggle: {
method: 'post' as const,
server: 'toggle' as const,
client: () => '/collector/toggle',
},
/**
*
*/
check: {
method: 'post' as const,
server: 'check' as const,
client: () => '/collector/check',
},
/**
*
*/
wikis: {
method: 'get' as const,
server: 'wikis' as const,
client: () => '/collector/wikis',
},
/**
*
*/
documents: {
method: 'get' as const,
server: 'documents' as const,
client: () => '/collector/documents',
},
};

View File

@ -0,0 +1,39 @@
import { IComment, IDocument } from '../models';
export const CommentApiDefinition = {
/**
*
*/
add: {
method: 'post' as const,
server: 'add' as const,
client: () => '/comment/add',
},
/**
*
*/
update: {
method: 'patch' as const,
server: 'update' as const,
client: () => '/comment/update',
},
/**
*
*/
delete: {
method: 'delete' as const,
server: 'delete/:id' as const,
client: (id: IComment['id']) => `/comment/delete/${id}`,
},
/**
*
*/
documents: {
method: 'get' as const,
server: 'document/:documentId' as const,
client: (documentId: IDocument['id']) => `/comment/document/${documentId}`,
},
};

View File

@ -0,0 +1,138 @@
import { IDocument } from '../models';
export const DocumentApiDefinition = {
/**
*
*/
search: {
method: 'get' as const,
server: 'search' as const,
client: () => '/document/search',
},
/**
* 访
*/
recent: {
method: 'get' as const,
server: 'recent' as const,
client: () => '/document/recent',
},
/**
*
*/
create: {
method: 'post' as const,
server: 'create' as const,
client: () => '/document/create',
},
/**
*
*/
getDetailById: {
method: 'get' as const,
server: 'detail/:id' as const,
client: (id: IDocument['id']) => `/document/detail/${id}`,
},
/**
*
*/
updateById: {
method: 'patch' as const,
server: 'update/:id' as const,
client: (id: IDocument['id']) => `/document/update/${id}`,
},
/**
*
*/
getVersionById: {
method: 'get' as const,
server: 'version/:id' as const,
client: (id: IDocument['id']) => `/document/version/${id}`,
},
/**
*
*/
getMemberById: {
method: 'get' as const,
server: 'member/:id' as const,
client: (id: IDocument['id']) => `/document/member/${id}`,
},
/**
*
*/
addMemberById: {
method: 'post' as const,
server: 'member/:id/add' as const,
client: (id: IDocument['id']) => `/document/member/${id}/add`,
},
/**
*
*/
updateMemberById: {
method: 'patch' as const,
server: 'member/:id/update' as const,
client: (id: IDocument['id']) => `/document/member/${id}/update`,
},
/**
*
*/
deleteMemberById: {
method: 'post' as const,
server: 'member/:id/delete' as const,
client: (id: IDocument['id']) => `/document/member/${id}/delete`,
},
/**
*
*/
getChildren: {
method: 'post' as const,
server: 'children' as const,
client: () => `/document/children`,
},
/**
*
*/
deleteById: {
method: 'delete' as const,
server: 'delete/:id' as const,
client: (id: IDocument['id']) => `/document/delete/${id}`,
},
/**
*
*/
shareById: {
method: 'post' as const,
server: 'share/:id' as const,
client: (id: IDocument['id']) => `/document/share/${id}`,
},
/**
*
*/
getPublicDetailById: {
method: 'post' as const,
server: 'public/detail/:id' as const,
client: (id: IDocument['id']) => `/document/public/detail/${id}`,
},
/**
*
*/
getPublicChildren: {
method: 'post' as const,
server: 'public/children' as const,
client: () => `/document/public/children`,
},
};

View File

@ -0,0 +1,10 @@
export const FileApiDefinition = {
/**
*
*/
upload: {
method: 'post' as const,
server: 'upload' as const,
client: () => '/file/upload',
},
};

View File

@ -0,0 +1,8 @@
export * from './user';
export * from './wiki';
export * from './document';
export * from './file';
export * from './message';
export * from './template';
export * from './comment';
export * from './collector';

View File

@ -0,0 +1,39 @@
import { IMessage } from '../models';
export const MessageApiDefinition = {
/**
*
*/
getUnread: {
method: 'get' as const,
server: 'unread' as const,
client: () => '/message/unread',
},
/**
*
*/
getRead: {
method: 'get' as const,
server: 'read' as const,
client: () => '/message/read',
},
/**
*
*/
getAll: {
method: 'get' as const,
server: 'all' as const,
client: () => '/message/all',
},
/**
*
*/
readMessage: {
method: 'post' as const,
server: 'read/:id' as const,
client: (id: IMessage['id']) => `/message/read/${id}`,
},
};

View File

@ -0,0 +1,57 @@
import { ITemplate } from '../models';
export const TemplateApiDefinition = {
/**
*
*/
public: {
method: 'get' as const,
server: 'public' as const,
client: () => '/template/public',
},
/**
*
*/
own: {
method: 'get' as const,
server: 'own' as const,
client: () => '/template/own',
},
/**
*
*/
add: {
method: 'post' as const,
server: 'add' as const,
client: () => '/template/add',
},
/**
*
*/
updateById: {
method: 'patch' as const,
server: 'update/:id' as const,
client: (id: ITemplate['id']) => `/template/update/${id}`,
},
/**
*
*/
getDetailById: {
method: 'get' as const,
server: 'detail/:id' as const,
client: (id: ITemplate['id']) => `/template/detail/${id}`,
},
/**
*
*/
deleteById: {
method: 'delete' as const,
server: 'delete/:id' as const,
client: (id: ITemplate['id']) => `/template/delete/${id}`,
},
};

View File

@ -0,0 +1,48 @@
import { IUser } from '../models';
export const UserApiDefinition = {
/**
*
*/
getAllUsers: {
method: 'get' as const,
server: '/' as const,
client: () => '/user',
},
/**
*
*/
register: {
method: 'post' as const,
server: 'register' as const,
client: () => '/user/register',
},
/**
*
*/
login: {
method: 'post' as const,
server: 'login' as const,
client: () => '/user/login',
},
/**
*
*/
logout: {
method: 'post' as const,
server: 'logout' as const,
client: () => '/user/logout',
},
/**
*
*/
update: {
method: 'patch' as const,
server: 'update' as const,
client: () => `/user/update`,
},
};

View File

@ -0,0 +1,183 @@
import { IWiki } from '../models';
export const WikiApiDefinition = {
/**
*
*/
getAllWikis: {
method: 'get' as const,
server: 'list/all' as const,
client: () => '/wiki/list/all',
},
/**
*
*/
getOwnWikis: {
method: 'get' as const,
server: 'list/own' as const,
client: () => '/wiki/list/own',
},
/**
*
*/
getJoinWikis: {
method: 'get' as const,
server: 'list/join' as const,
client: () => '/wiki/list/join',
},
/**
*
*/
add: {
method: 'post' as const,
server: 'add' as const,
client: () => '/wiki/add',
},
/**
*
*/
getHomeDocumentById: {
method: 'get' as const,
server: 'homedoc/:id' as const,
client: (id: IWiki['id']) => `/wiki/homedoc/${id}`,
},
/**
*
*/
getTocsById: {
method: 'get' as const,
server: 'tocs/:id' as const,
client: (id: IWiki['id']) => `/wiki/tocs/${id}`,
},
/**
*
*/
updateTocsById: {
method: 'patch' as const,
server: 'tocs/:id/update' as const,
client: (id: IWiki['id']) => `/wiki/tocs/${id}/update`,
},
/**
*
*/
getDocumentsById: {
method: 'get' as const,
server: 'documents/:id' as const,
client: (id: IWiki['id']) => `/wiki/documents/${id}`,
},
/**
*
*/
getDetailById: {
method: 'get' as const,
server: 'detail/:id' as const,
client: (id: IWiki['id']) => `/wiki/detail/${id}`,
},
/**
*
*/
updateById: {
method: 'patch' as const,
server: 'update/:id' as const,
client: (id: IWiki['id']) => `/wiki/update/${id}`,
},
/**
*
*/
deleteById: {
method: 'delete' as const,
server: 'delet/:id' as const,
client: (id: IWiki['id']) => `/wiki/delet/${id}`,
},
/**
*
*/
getMemberById: {
method: 'get' as const,
server: 'member/:id' as const,
client: (id: IWiki['id']) => `/wiki/member/${id}`,
},
/**
*
*/
addMemberById: {
method: 'post' as const,
server: 'member/:id/add' as const,
client: (id: IWiki['id']) => `/wiki/member/${id}/add`,
},
/**
*
*/
updateMemberById: {
method: 'patch' as const,
server: 'member/:id/update' as const,
client: (id: IWiki['id']) => `/wiki/member/${id}/update`,
},
/**
*
*/
deleteMemberById: {
method: 'delete' as const,
server: 'member/:id/delete' as const,
client: (id: IWiki['id']) => `/wiki/member/${id}/delete`,
},
/**
*
*/
shareById: {
method: 'post' as const,
server: 'share/:id' as const,
client: (id: IWiki['id']) => `/wiki/share/${id}`,
},
/**
*
*/
getPublicHomeDocumentById: {
method: 'get' as const,
server: '/public/homedoc/:id' as const,
client: (id: IWiki['id']) => `/wiki/public/homedoc/${id}`,
},
/**
*
*/
getPublicTocsById: {
method: 'get' as const,
server: '/public/tocs/:id' as const,
client: (id: IWiki['id']) => `/wiki/public/tocs/${id}`,
},
/**
*
*/
getPublicDetailById: {
method: 'get' as const,
server: '/public/detail/:id' as const,
client: (id: IWiki['id']) => `/wiki/public/detail/${id}`,
},
/**
*
*/
getPublicWikis: {
method: 'get' as const,
server: '/public/wikis' as const,
client: () => `/wiki/public/wikis`,
},
};

View File

@ -1,2 +1,3 @@
export * from './models';
export * from './util';
export * from './api';

View File

@ -36,6 +36,7 @@
"class-transformer": "^0.5.1",
"class-validator": "^0.13.2",
"compression": "^1.7.4",
"cookie-parser": "^1.4.6",
"date-fns": "^2.28.0",
"express": "^4.17.2",
"express-rate-limit": "^6.2.0",
@ -64,9 +65,11 @@
"@nestjs/cli": "^8.0.0",
"@nestjs/schematics": "^8.0.0",
"@nestjs/testing": "^8.0.0",
"@types/cookie-parser": "^1.4.3",
"@types/cron": "^2.0.0",
"@types/express": "^4.17.13",
"@types/jest": "27.0.2",
"@types/lodash": "^4.14.182",
"@types/node": "^16.0.0",
"@types/supertest": "^2.0.11",
"@typescript-eslint/eslint-plugin": "^5.21.0",

View File

@ -4,6 +4,7 @@ import {
Body,
ClassSerializerInterceptor,
Controller,
Get,
HttpCode,
HttpStatus,
Post,
@ -12,40 +13,53 @@ import {
UseInterceptors,
} from '@nestjs/common';
import { CollectorService } from '@services/collector.service';
import { CollectorApiDefinition } from '@think/domains';
@Controller('collector')
export class CollectorController {
constructor(private readonly collectorService: CollectorService) {}
/**
*
*/
@UseInterceptors(ClassSerializerInterceptor)
@Post('toggle')
@Post(CollectorApiDefinition.toggle.server)
@HttpCode(HttpStatus.OK)
@UseGuards(JwtGuard)
async toggleStar(@Request() req, @Body() dto: CollectDto) {
return await this.collectorService.toggleStar(req.user, dto);
}
/**
*
*/
@UseInterceptors(ClassSerializerInterceptor)
@Post('check')
@Post(CollectorApiDefinition.check.server)
@HttpCode(HttpStatus.OK)
@UseGuards(JwtGuard)
async checkStar(@Request() req, @Body() dto: CollectDto) {
return await this.collectorService.isStared(req.user, dto);
}
/**
*
*/
@UseInterceptors(ClassSerializerInterceptor)
@Post('documents')
@HttpCode(HttpStatus.OK)
@UseGuards(JwtGuard)
async getDocuments(@Request() req) {
return await this.collectorService.getDocuments(req.user);
}
@UseInterceptors(ClassSerializerInterceptor)
@Post('wikis')
@Get(CollectorApiDefinition.wikis.server)
@HttpCode(HttpStatus.OK)
@UseGuards(JwtGuard)
async getWikis(@Request() req) {
return await this.collectorService.getWikis(req.user);
}
/**
*
*/
@UseInterceptors(ClassSerializerInterceptor)
@Get(CollectorApiDefinition.documents.server)
@HttpCode(HttpStatus.OK)
@UseGuards(JwtGuard)
async getDocuments(@Request() req) {
return await this.collectorService.getDocuments(req.user);
}
}

View File

@ -4,10 +4,12 @@ import {
Body,
ClassSerializerInterceptor,
Controller,
Delete,
Get,
HttpCode,
HttpStatus,
Param,
Patch,
Post,
Query,
Request,
@ -15,13 +17,17 @@ import {
UseInterceptors,
} from '@nestjs/common';
import { CommentService } from '@services/comment.service';
import { CommentApiDefinition } from '@think/domains';
@Controller('comment')
export class CommentController {
constructor(private readonly commentService: CommentService) {}
/**
*
*/
@UseInterceptors(ClassSerializerInterceptor)
@Post('add')
@Post(CommentApiDefinition.add.server)
@HttpCode(HttpStatus.OK)
@UseGuards(JwtGuard)
async create(@Request() req, @Body() dto: CommentDto) {
@ -29,27 +35,36 @@ export class CommentController {
return await this.commentService.create(req.user, userAgent, dto);
}
/**
*
*/
@UseInterceptors(ClassSerializerInterceptor)
@Post('update')
@Patch(CommentApiDefinition.update.server)
@HttpCode(HttpStatus.OK)
@UseGuards(JwtGuard)
async updateComment(@Request() req, @Body() dto: UpdateCommentDto) {
return await this.commentService.updateComment(req.user, dto);
}
/**
*
*/
@UseInterceptors(ClassSerializerInterceptor)
@Post('delete/:id')
@Delete(CommentApiDefinition.delete.server)
@HttpCode(HttpStatus.OK)
@UseGuards(JwtGuard)
async deleteComment(@Request() req, @Param('id') documentId) {
return await this.commentService.deleteComment(req.user, documentId);
}
/**
*
*/
@UseInterceptors(ClassSerializerInterceptor)
@Get('document/:id')
@Get(CommentApiDefinition.documents.server)
@HttpCode(HttpStatus.OK)
@UseGuards(JwtGuard)
async getArticleComments(@Param('id') documentId, @Query() qurey) {
async getArticleComments(@Param('documentId') documentId, @Query() qurey) {
return this.commentService.getDocumentComments(documentId, qurey);
}
}

View File

@ -14,6 +14,7 @@ import {
HttpCode,
HttpStatus,
Param,
Patch,
Post,
Query,
Request,
@ -21,7 +22,7 @@ import {
UseInterceptors,
} from '@nestjs/common';
import { DocumentService } from '@services/document.service';
import { DocumentStatus } from '@think/domains';
import { DocumentApiDefinition, DocumentStatus } from '@think/domains';
@Controller('document')
@UseGuards(DocumentAuthorityGuard)
@ -29,8 +30,38 @@ import { DocumentStatus } from '@think/domains';
export class DocumentController {
constructor(private readonly documentService: DocumentService) {}
/**
*
* @param req
* @param keyword
* @returns
*/
@UseInterceptors(ClassSerializerInterceptor)
@Post('create')
@Get(DocumentApiDefinition.search.server)
@HttpCode(HttpStatus.OK)
@UseGuards(JwtGuard)
async search(@Request() req, @Query('keyword') keyword) {
return await this.documentService.search(req.user, keyword);
}
/**
* 访
* @param req
* @returns
*/
@UseInterceptors(ClassSerializerInterceptor)
@Get(DocumentApiDefinition.recent.server)
@HttpCode(HttpStatus.OK)
@UseGuards(JwtGuard)
async getWorkspaceDocuments(@Request() req) {
return await this.documentService.getRecentDocuments(req.user);
}
/**
*
*/
@UseInterceptors(ClassSerializerInterceptor)
@Post(DocumentApiDefinition.create.server)
@HttpCode(HttpStatus.CREATED)
@UseGuards(JwtGuard)
async createDocument(@Request() req, @Body() dto: CreateDocumentDto) {
@ -44,7 +75,7 @@ export class DocumentController {
* @returns
*/
@UseInterceptors(ClassSerializerInterceptor)
@Get('detail/:id')
@Get(DocumentApiDefinition.getDetailById.server)
@HttpCode(HttpStatus.OK)
@CheckDocumentAuthority('readable')
@UseGuards(JwtGuard)
@ -60,7 +91,7 @@ export class DocumentController {
* @returns
*/
@UseInterceptors(ClassSerializerInterceptor)
@Post('update/:id')
@Patch(DocumentApiDefinition.updateById.server)
@HttpCode(HttpStatus.OK)
@CheckDocumentAuthority('editable')
@UseGuards(JwtGuard)
@ -75,7 +106,7 @@ export class DocumentController {
* @returns
*/
@UseInterceptors(ClassSerializerInterceptor)
@Get('version/:id')
@Get(DocumentApiDefinition.getVersionById.server)
@HttpCode(HttpStatus.OK)
@CheckDocumentAuthority('readable')
@UseGuards(JwtGuard)
@ -83,66 +114,6 @@ export class DocumentController {
return await this.documentService.getDocumentVersion(req.user, documentId);
}
/**
*
* @param req
* @param data
* @returns
*/
@UseInterceptors(ClassSerializerInterceptor)
@Post('children')
@HttpCode(HttpStatus.OK)
@CheckDocumentAuthority('readable')
@UseGuards(JwtGuard)
async getChildrenDocuments(@Request() req, @Body() data) {
return await this.documentService.getChildrenDocuments(req.user, data);
}
/**
*
* @param req
* @param documentId
* @returns
*/
@UseInterceptors(ClassSerializerInterceptor)
@Delete('delete/:id')
@HttpCode(HttpStatus.OK)
@CheckDocumentAuthority('createUser')
@UseGuards(JwtGuard)
async deleteDocument(@Request() req, @Param('id') documentId) {
return await this.documentService.deleteDocument(req.user, documentId);
}
/**
*
* @param req
* @param documentId
* @param dto
* @returns
*/
@UseInterceptors(ClassSerializerInterceptor)
@Post('share/:id')
@HttpCode(HttpStatus.OK)
@CheckDocumentAuthority('editable')
@UseGuards(JwtGuard)
async shareDocument(@Request() req, @Param('id') documentId, @Body() dto: ShareDocumentDto) {
return await this.documentService.shareDocument(req.user, documentId, dto);
}
/**
*
* @param req
* @param keyword
* @returns
*/
@UseInterceptors(ClassSerializerInterceptor)
@Get('search')
@HttpCode(HttpStatus.OK)
@UseGuards(JwtGuard)
async search(@Request() req, @Query('keyword') keyword) {
return await this.documentService.search(req.user, keyword);
}
/**
*
* @param req
@ -150,7 +121,7 @@ export class DocumentController {
* @returns
*/
@UseInterceptors(ClassSerializerInterceptor)
@Get('user/:id')
@Get(DocumentApiDefinition.getMemberById.server)
@HttpCode(HttpStatus.OK)
@CheckDocumentAuthority('readable')
@UseGuards(JwtGuard)
@ -166,7 +137,7 @@ export class DocumentController {
* @returns
*/
@UseInterceptors(ClassSerializerInterceptor)
@Post('user/:id/add')
@Post(DocumentApiDefinition.addMemberById.server)
@HttpCode(HttpStatus.OK)
@CheckDocumentAuthority('createUser')
@UseGuards(JwtGuard)
@ -182,7 +153,7 @@ export class DocumentController {
* @returns
*/
@UseInterceptors(ClassSerializerInterceptor)
@Post('user/:id/update')
@Patch(DocumentApiDefinition.updateMemberById.server)
@HttpCode(HttpStatus.OK)
@CheckDocumentAuthority('createUser')
@UseGuards(JwtGuard)
@ -198,7 +169,7 @@ export class DocumentController {
* @returns
*/
@UseInterceptors(ClassSerializerInterceptor)
@Post('user/:id/delete')
@Post(DocumentApiDefinition.deleteMemberById.server)
@HttpCode(HttpStatus.OK)
@CheckDocumentAuthority('createUser')
@UseGuards(JwtGuard)
@ -207,16 +178,49 @@ export class DocumentController {
}
/**
* 访
*
* @param req
* @param data
* @returns
*/
@UseInterceptors(ClassSerializerInterceptor)
@Get('recent')
@Post(DocumentApiDefinition.getChildren.server)
@HttpCode(HttpStatus.OK)
@CheckDocumentAuthority('readable')
@UseGuards(JwtGuard)
async getWorkspaceDocuments(@Request() req) {
return await this.documentService.getRecentDocuments(req.user);
async getChildrenDocuments(@Request() req, @Body() data) {
return await this.documentService.getChildrenDocuments(req.user, data);
}
/**
*
* @param req
* @param documentId
* @returns
*/
@UseInterceptors(ClassSerializerInterceptor)
@Delete(DocumentApiDefinition.deleteById.server)
@HttpCode(HttpStatus.OK)
@CheckDocumentAuthority('createUser')
@UseGuards(JwtGuard)
async deleteDocument(@Request() req, @Param('id') documentId) {
return await this.documentService.deleteDocument(req.user, documentId);
}
/**
*
* @param req
* @param documentId
* @param dto
* @returns
*/
@UseInterceptors(ClassSerializerInterceptor)
@Post(DocumentApiDefinition.shareById.server)
@HttpCode(HttpStatus.OK)
@CheckDocumentAuthority('editable')
@UseGuards(JwtGuard)
async shareDocument(@Request() req, @Param('id') documentId, @Body() dto: ShareDocumentDto) {
return await this.documentService.shareDocument(req.user, documentId, dto);
}
/**
@ -227,7 +231,7 @@ export class DocumentController {
* @returns
*/
@UseInterceptors(ClassSerializerInterceptor)
@Post('public/detail/:id')
@Post(DocumentApiDefinition.getPublicDetailById.server)
@CheckDocumentStatus(DocumentStatus.public)
@HttpCode(HttpStatus.OK)
async getShareDocumentDetail(@Request() req, @Param('id') documentId, @Body() dto: ShareDocumentDto) {
@ -240,7 +244,7 @@ export class DocumentController {
* @returns
*/
@UseInterceptors(ClassSerializerInterceptor)
@Post('public/children')
@Post(DocumentApiDefinition.getPublicChildren.server)
@CheckDocumentStatus(DocumentStatus.public)
@HttpCode(HttpStatus.OK)
async getShareChildrenDocuments(@Body() data) {

View File

@ -2,6 +2,7 @@ import { JwtGuard } from '@guard/jwt.guard';
import { Controller, Post, UploadedFile, UseGuards, UseInterceptors } from '@nestjs/common';
import { FileInterceptor } from '@nestjs/platform-express';
import { FileService } from '@services/file.service';
import { FileApiDefinition } from '@think/domains';
@Controller('file')
export class FileController {
@ -11,7 +12,7 @@ export class FileController {
*
* @param file
*/
@Post('upload')
@Post(FileApiDefinition.upload.server)
@UseInterceptors(
FileInterceptor('file', {
limits: {

View File

@ -13,37 +13,50 @@ import {
UseInterceptors,
} from '@nestjs/common';
import { MessageService } from '@services/message.service';
import { MessageApiDefinition } from '@think/domains';
@Controller('message')
export class MessageController {
constructor(private readonly messageService: MessageService) {}
/**
*
*/
@UseInterceptors(ClassSerializerInterceptor)
@Get('/unread')
@Get(MessageApiDefinition.getUnread.server)
@HttpCode(HttpStatus.OK)
@UseGuards(JwtGuard)
async getUnreadMessages(@Request() req, @Query() query) {
return this.messageService.getMessages(req.user, false, query);
}
/**
*
*/
@UseInterceptors(ClassSerializerInterceptor)
@Get('/read')
@Get(MessageApiDefinition.getRead.server)
@HttpCode(HttpStatus.OK)
@UseGuards(JwtGuard)
async getReadMessages(@Request() req, @Query() query) {
return this.messageService.getMessages(req.user, true, query);
}
/**
*
*/
@UseInterceptors(ClassSerializerInterceptor)
@Get('/all')
@Get(MessageApiDefinition.getAll.server)
@HttpCode(HttpStatus.OK)
@UseGuards(JwtGuard)
async getAllReadMessages(@Request() req, @Query() query) {
return this.messageService.getAllMessages(req.user, query);
}
/**
*
*/
@UseInterceptors(ClassSerializerInterceptor)
@Post('/read/:id')
@Post(MessageApiDefinition.readMessage.server)
@HttpCode(HttpStatus.OK)
@UseGuards(JwtGuard)
async updateComment(@Request() req, @Param('id') messageId) {

View File

@ -4,10 +4,12 @@ import {
Body,
ClassSerializerInterceptor,
Controller,
Delete,
Get,
HttpCode,
HttpStatus,
Param,
Patch,
Post,
Query,
Request,
@ -15,55 +17,74 @@ import {
UseInterceptors,
} from '@nestjs/common';
import { TemplateService } from '@services/template.service';
import { TemplateApiDefinition } from '@think/domains';
@Controller('template')
export class TemplateController {
constructor(private readonly templateService: TemplateService) {}
/**
*
*/
@UseInterceptors(ClassSerializerInterceptor)
@Post('add')
@Get(TemplateApiDefinition.public.server)
@HttpCode(HttpStatus.OK)
async getPublicTemplates(@Query() qurey) {
return this.templateService.getPublicTemplates(qurey);
}
/**
*
*/
@UseInterceptors(ClassSerializerInterceptor)
@Get(TemplateApiDefinition.own.server)
@HttpCode(HttpStatus.OK)
@UseGuards(JwtGuard)
async getOwnTemplates(@Request() req, @Query() qurey) {
return this.templateService.getOwnTemplates(req.user, qurey);
}
/**
*
*/
@UseInterceptors(ClassSerializerInterceptor)
@Post(TemplateApiDefinition.add.server)
@HttpCode(HttpStatus.OK)
@UseGuards(JwtGuard)
async create(@Request() req, @Body() dto: TemplateDto) {
return await this.templateService.create(req.user, dto);
}
/**
*
*/
@UseInterceptors(ClassSerializerInterceptor)
@Post('update')
@Patch(TemplateApiDefinition.updateById.server)
@HttpCode(HttpStatus.OK)
@UseGuards(JwtGuard)
async updateTemplat(@Request() req, @Body() dto: TemplateDto & { id: string }) {
return await this.templateService.updateTemplate(req.user, dto.id, dto);
}
/**
*
*/
@UseInterceptors(ClassSerializerInterceptor)
@Post('delete/:id')
@HttpCode(HttpStatus.OK)
@UseGuards(JwtGuard)
async deleteTemplat(@Request() req, @Param('id') documentId) {
return await this.templateService.deleteTemplate(req.user, documentId);
}
@UseInterceptors(ClassSerializerInterceptor)
@Get('detail/:id')
@Get(TemplateApiDefinition.getDetailById.server)
@HttpCode(HttpStatus.OK)
@UseGuards(JwtGuard)
async getTemplate(@Request() req, @Param('id') id) {
return this.templateService.getTemplate(req.user, id);
}
/**
*
*/
@UseInterceptors(ClassSerializerInterceptor)
@Get('public')
@HttpCode(HttpStatus.OK)
async getPublicTemplates(@Query() qurey) {
return this.templateService.getPublicTemplates(qurey);
}
@UseInterceptors(ClassSerializerInterceptor)
@Get('own')
@Delete(TemplateApiDefinition.deleteById.server)
@HttpCode(HttpStatus.OK)
@UseGuards(JwtGuard)
async getOwnTemplates(@Request() req, @Query() qurey) {
return this.templateService.getOwnTemplates(req.user, qurey);
async deleteTemplat(@Request() req, @Param('id') documentId) {
return await this.templateService.deleteTemplate(req.user, documentId);
}
}

View File

@ -12,43 +12,74 @@ import {
Patch,
Post,
Request,
Res,
UseGuards,
UseInterceptors,
} from '@nestjs/common';
import { UserService } from '@services/user.service';
import { UserApiDefinition } from '@think/domains';
import { Response as ExpressResponse } from 'express';
@Controller('user')
export class UserController {
constructor(private readonly userService: UserService) {}
/**
*
*/
@UseInterceptors(ClassSerializerInterceptor)
@Post('register')
@HttpCode(HttpStatus.CREATED)
async register(@Body() user: CreateUserDto) {
return await this.userService.createUser(user);
}
@UseInterceptors(ClassSerializerInterceptor)
@Post('login')
@HttpCode(HttpStatus.OK)
async login(@Body() user: LoginUserDto) {
const res = await this.userService.login(user);
return res;
}
@UseInterceptors(ClassSerializerInterceptor)
@Patch('update')
@HttpCode(HttpStatus.OK)
@UseGuards(JwtGuard)
async updateUser(@Request() req, @Body() dto: UpdateUserDto) {
return await this.userService.updateUser(req.user, dto);
}
@UseInterceptors(ClassSerializerInterceptor)
@Get('/')
@Get(UserApiDefinition.getAllUsers.server)
@HttpCode(HttpStatus.OK)
@UseGuards(JwtGuard)
async getUsers() {
return this.userService.getUsers();
}
/**
*
*/
@UseInterceptors(ClassSerializerInterceptor)
@Post(UserApiDefinition.register.server)
@HttpCode(HttpStatus.CREATED)
async register(@Body() user: CreateUserDto) {
return await this.userService.createUser(user);
}
/**
*
*/
@UseInterceptors(ClassSerializerInterceptor)
@Post(UserApiDefinition.login.server)
@HttpCode(HttpStatus.OK)
async login(@Body() user: LoginUserDto, @Res({ passthrough: true }) response: ExpressResponse) {
const { user: data, token, domain, expiresIn } = await this.userService.login(user);
response.cookie('token', token, {
domain,
expires: new Date(new Date().getTime() + expiresIn),
httpOnly: true,
sameSite: 'lax',
});
return { ...data, token };
}
/**
*
*/
@Post(UserApiDefinition.logout.server)
@HttpCode(HttpStatus.OK)
async logout(@Res({ passthrough: true }) response: ExpressResponse) {
response.cookie('token', '', { expires: new Date() });
return;
}
/**
*
*/
@UseInterceptors(ClassSerializerInterceptor)
@Patch(UserApiDefinition.update.server)
@HttpCode(HttpStatus.OK)
@UseGuards(JwtGuard)
async updateUser(@Request() req, @Body() dto: UpdateUserDto) {
return await this.userService.updateUser(req.user, dto);
}
}

View File

@ -22,26 +22,12 @@ import {
UseInterceptors,
} from '@nestjs/common';
import { WikiService } from '@services/wiki.service';
import { IPagination, WikiStatus, WikiUserRole } from '@think/domains';
import { IPagination, WikiApiDefinition, WikiStatus, WikiUserRole } from '@think/domains';
@Controller('wiki')
export class WikiController {
constructor(private readonly wikiService: WikiService) {}
/**
*
* @param req
* @param dto
* @returns
*/
@UseInterceptors(ClassSerializerInterceptor)
@Post('create')
@HttpCode(HttpStatus.CREATED)
@UseGuards(JwtGuard)
async register(@Request() req, @Body() dto: CreateWikiDto) {
return await this.wikiService.createWiki(req.user, dto);
}
/**
*
* @param req
@ -49,7 +35,7 @@ export class WikiController {
* @returns
*/
@UseInterceptors(ClassSerializerInterceptor)
@Get('list/all')
@Get(WikiApiDefinition.getAllWikis.server)
@HttpCode(HttpStatus.OK)
@UseGuards(JwtGuard)
async getAllWikis(@Request() req, @Query() pagination: IPagination) {
@ -63,7 +49,7 @@ export class WikiController {
* @returns
*/
@UseInterceptors(ClassSerializerInterceptor)
@Get('list/own')
@Get(WikiApiDefinition.getOwnWikis.server)
@HttpCode(HttpStatus.OK)
@UseGuards(JwtGuard)
async getOwnWikis(@Request() req, @Query() pagination: IPagination) {
@ -77,7 +63,7 @@ export class WikiController {
* @returns
*/
@UseInterceptors(ClassSerializerInterceptor)
@Get('list/join')
@Get(WikiApiDefinition.getJoinWikis.server)
@HttpCode(HttpStatus.OK)
@UseGuards(JwtGuard)
async getJoinWikis(@Request() req, @Query() pagination: IPagination) {
@ -85,19 +71,17 @@ export class WikiController {
}
/**
*
*
* @param req
* @param wikiId
* @param dto
* @returns
*/
@UseInterceptors(ClassSerializerInterceptor)
@Get('detail/:id')
@HttpCode(HttpStatus.OK)
@CheckWikiUserRole()
@UseGuards(WikiUserRoleGuard)
@Post(WikiApiDefinition.add.server)
@HttpCode(HttpStatus.CREATED)
@UseGuards(JwtGuard)
async getWikiDetail(@Request() req, @Param('id') wikiId) {
return await this.wikiService.getWikiDetail(req.user, wikiId);
async register(@Request() req, @Body() dto: CreateWikiDto) {
return await this.wikiService.createWiki(req.user, dto);
}
/**
@ -107,7 +91,7 @@ export class WikiController {
* @returns
*/
@UseInterceptors(ClassSerializerInterceptor)
@Get('homedoc/:id')
@Get(WikiApiDefinition.getHomeDocumentById.server)
@HttpCode(HttpStatus.OK)
@CheckWikiUserRole()
@UseGuards(WikiUserRoleGuard)
@ -116,6 +100,71 @@ export class WikiController {
return await this.wikiService.getWikiHomeDocument(req.user, wikiId);
}
/**
*
* @param req
* @param wikiId
* @returns
*/
@UseInterceptors(ClassSerializerInterceptor)
@Get(WikiApiDefinition.getTocsById.server)
@HttpCode(HttpStatus.OK)
@CheckWikiUserRole()
@UseGuards(WikiUserRoleGuard)
@UseGuards(JwtGuard)
async getWikiTocs(@Request() req, @Param('id') wikiId) {
return await this.wikiService.getWikiTocs(req.user, wikiId);
}
/**
*
* @param req
* @param wikiId
* @param relations
* @returns
*/
@UseInterceptors(ClassSerializerInterceptor)
@Patch(WikiApiDefinition.updateTocsById.server)
@HttpCode(HttpStatus.OK)
@CheckWikiUserRole()
@UseGuards(WikiUserRoleGuard)
@UseGuards(JwtGuard)
async orderWikiTocs(@Body() 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 wikiId
* @returns
*/
@UseInterceptors(ClassSerializerInterceptor)
@Get(WikiApiDefinition.getDetailById.server)
@HttpCode(HttpStatus.OK)
@CheckWikiUserRole()
@UseGuards(WikiUserRoleGuard)
@UseGuards(JwtGuard)
async getWikiDetail(@Request() req, @Param('id') wikiId) {
return await this.wikiService.getWikiDetail(req.user, wikiId);
}
/**
*
*
@ -125,7 +174,7 @@ export class WikiController {
* @returns
*/
@UseInterceptors(ClassSerializerInterceptor)
@Patch('update/:id')
@Patch(WikiApiDefinition.updateById.server)
@HttpCode(HttpStatus.OK)
@CheckWikiUserRole(WikiUserRole.admin)
@UseGuards(WikiUserRoleGuard)
@ -142,7 +191,7 @@ export class WikiController {
* @returns
*/
@UseInterceptors(ClassSerializerInterceptor)
@Delete('delete/:id')
@Delete(WikiApiDefinition.deleteById.server)
@HttpCode(HttpStatus.OK)
@CheckWikiUserRole(WikiUserRole.admin)
@UseGuards(WikiUserRoleGuard)
@ -159,7 +208,7 @@ export class WikiController {
* @returns
*/
@UseInterceptors(ClassSerializerInterceptor)
@Get('user/:id')
@Get(WikiApiDefinition.getMemberById.server)
@HttpCode(HttpStatus.OK)
@CheckWikiUserRole(WikiUserRole.admin)
@UseGuards(WikiUserRoleGuard)
@ -177,7 +226,7 @@ export class WikiController {
* @returns
*/
@UseInterceptors(ClassSerializerInterceptor)
@Post('user/:id/add')
@Post(WikiApiDefinition.addMemberById.server)
@HttpCode(HttpStatus.OK)
@CheckWikiUserRole(WikiUserRole.admin)
@UseGuards(WikiUserRoleGuard)
@ -195,7 +244,7 @@ export class WikiController {
* @returns
*/
@UseInterceptors(ClassSerializerInterceptor)
@Post('user/:id/update')
@Patch(WikiApiDefinition.updateMemberById.server)
@HttpCode(HttpStatus.OK)
@CheckWikiUserRole(WikiUserRole.admin)
@UseGuards(WikiUserRoleGuard)
@ -213,7 +262,7 @@ export class WikiController {
* @returns
*/
@UseInterceptors(ClassSerializerInterceptor)
@Post('user/:id/delete')
@Delete(WikiApiDefinition.deleteMemberById.server)
@HttpCode(HttpStatus.OK)
@CheckWikiUserRole(WikiUserRole.admin)
@UseGuards(WikiUserRoleGuard)
@ -231,7 +280,7 @@ export class WikiController {
* @returns
*/
@UseInterceptors(ClassSerializerInterceptor)
@Post('share/:id')
@Post(WikiApiDefinition.shareById.server)
@HttpCode(HttpStatus.OK)
@CheckWikiUserRole(WikiUserRole.admin)
@UseGuards(WikiUserRoleGuard)
@ -240,55 +289,6 @@ export class WikiController {
return await this.wikiService.shareWiki(req.user, wikiId, dto);
}
/**
*
* @param req
* @param wikiId
* @returns
*/
@UseInterceptors(ClassSerializerInterceptor)
@Get('tocs/:id')
@HttpCode(HttpStatus.OK)
@CheckWikiUserRole()
@UseGuards(WikiUserRoleGuard)
@UseGuards(JwtGuard)
async getWikiTocs(@Request() req, @Param('id') wikiId) {
return await this.wikiService.getWikiTocs(req.user, wikiId);
}
/**
*
* @param req
* @param wikiId
* @param relations
* @returns
*/
@UseInterceptors(ClassSerializerInterceptor)
@Post('tocs/:id/update')
@HttpCode(HttpStatus.OK)
@CheckWikiUserRole()
@UseGuards(WikiUserRoleGuard)
@UseGuards(JwtGuard)
async orderWikiTocs(@Body() relations) {
return await this.wikiService.orderWikiTocs(relations);
}
/**
*
* @param req
* @param wikiId
* @returns
*/
@UseInterceptors(ClassSerializerInterceptor)
@Get('docs/:id')
@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
@ -296,7 +296,7 @@ export class WikiController {
* @returns
*/
@UseInterceptors(ClassSerializerInterceptor)
@Get('public/homedoc/:id')
@Get(WikiApiDefinition.getPublicHomeDocumentById.server)
@CheckWikiStatus(WikiStatus.public)
@UseGuards(WikiStatusGuard)
@HttpCode(HttpStatus.OK)
@ -304,20 +304,6 @@ export class WikiController {
return await this.wikiService.getPublicWikiHomeDocument(wikiId, req.headers['user-agent']);
}
/**
*
* @param wikiId
* @returns
*/
@UseInterceptors(ClassSerializerInterceptor)
@Post('public/detail/:id')
@CheckWikiStatus(WikiStatus.public)
@UseGuards(WikiStatusGuard)
@HttpCode(HttpStatus.OK)
async getPublicWorkspaceDetail(@Param('id') wikiId) {
return await this.wikiService.getPublicWikiDetail(wikiId);
}
/**
*
* @param wikiId
@ -325,13 +311,27 @@ export class WikiController {
*/
@UseInterceptors(ClassSerializerInterceptor)
@HttpCode(HttpStatus.OK)
@Post('public/tocs/:id')
@Get(WikiApiDefinition.getPublicTocsById.server)
@CheckWikiStatus(WikiStatus.public)
@UseGuards(WikiStatusGuard)
async getPublicWikiTocs(@Param('id') wikiId) {
return await this.wikiService.getPublicWikiTocs(wikiId);
}
/**
*
* @param wikiId
* @returns
*/
@UseInterceptors(ClassSerializerInterceptor)
@Get(WikiApiDefinition.getPublicDetailById.server)
@CheckWikiStatus(WikiStatus.public)
@UseGuards(WikiStatusGuard)
@HttpCode(HttpStatus.OK)
async getPublicWorkspaceDetail(@Param('id') wikiId) {
return await this.wikiService.getPublicWikiDetail(wikiId);
}
/**
*
* @param pagination
@ -339,7 +339,7 @@ export class WikiController {
*/
@UseInterceptors(ClassSerializerInterceptor)
@HttpCode(HttpStatus.OK)
@Get('public/wikis')
@Get(WikiApiDefinition.getPublicWikis.server)
async getAllPublicWikis(@Query() pagination: IPagination) {
return await this.wikiService.getAllPublicWikis(pagination);
}

View File

@ -23,13 +23,7 @@ export class DocumentAuthorityGuard implements CanActivate {
}
const request = context.switchToHttp().getRequest();
let token = request.headers.authorization;
if (/Bearer/.test(token)) {
token = token.split(' ').pop();
}
const token = request?.cookies['token'];
const user = this.jwtService.decode(token) as IUser;
const { params, query, body } = request;
const documentId = params?.id || params?.documentId || query?.id || query?.documentId || body?.documentId;

View File

@ -36,12 +36,7 @@ export class DocumentStatusGuard implements CanActivate {
}
if (document.status !== targetStatus) {
throw new HttpException(
targetStatus === DocumentStatus.private
? '私有文档,无法查看内容'
: '公共文档,无法查看内容,请提 issue 到 GitHub 仓库反馈',
HttpStatus.FORBIDDEN
);
throw new HttpException('私有文档,无法查看内容', HttpStatus.FORBIDDEN);
}
return true;

View File

@ -27,12 +27,7 @@ export class WikiStatusGuard implements CanActivate {
throw new HttpException('目标知识库不存在', HttpStatus.NOT_FOUND);
}
if (wiki.status !== targetStatus) {
throw new HttpException(
targetStatus === WikiStatus.private
? '私有知识库,无法查看内容'
: '公共知识库,无法查看内容,请提 issue 到 GitHub 仓库反馈',
HttpStatus.FORBIDDEN
);
throw new HttpException('私有知识库,无法查看内容', HttpStatus.FORBIDDEN);
}
return true;

View File

@ -23,15 +23,8 @@ export class WikiUserRoleGuard implements CanActivate {
async canActivate(context: ExecutionContext): Promise<boolean> {
const targetUserRole = this.reflector.get<WikiUserRole | null>(KEY, context.getHandler());
const request = context.switchToHttp().getRequest();
let token = request.headers.authorization;
if (/Bearer/.test(token)) {
token = token.split(' ').pop();
}
const token = request?.cookies['token'];
const user = this.jwtService.decode(token) as IUser;
if (!user) {

View File

@ -5,6 +5,7 @@ import { NestFactory } from '@nestjs/core';
import { ValidationPipe } from '@pipes/validation.pipe';
import { HttpResponseTransformInterceptor } from '@transforms/http-response.transform';
import * as compression from 'compression';
import * as cookieParser from 'cookie-parser';
import * as express from 'express';
import helmet from 'helmet';
@ -12,13 +13,16 @@ import { AppModule } from './app.module';
import { AppClusterService } from './app-cluster.service';
async function bootstrap() {
const app = await NestFactory.create(AppModule, {
logger: false,
});
const app = await NestFactory.create(AppModule);
const config = app.get(ConfigService);
const port = config.get('server.port') || 5002;
app.enableCors();
app.enableCors({
origin: config.get('client.siteUrl'),
methods: 'GET,HEAD,PUT,PATCH,POST,DELETE,OPTIONS',
credentials: true,
});
app.use(cookieParser());
app.use(compression());
app.use(helmet());
app.use(express.json());

View File

@ -10,6 +10,7 @@ import { PassportModule, PassportStrategy } from '@nestjs/passport';
import { TypeOrmModule } from '@nestjs/typeorm';
import { UserService } from '@services/user.service';
import { getConfig } from '@think/config';
import { Request as RequestType } from 'express';
import { ExtractJwt, Strategy } from 'passport-jwt';
const config = getConfig();
@ -25,8 +26,14 @@ export class JwtStrategy extends PassportStrategy(Strategy) {
private readonly userService: UserService
) {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
ignoreExpiration: false,
secretOrKey: jwtConfig.secretkey,
jwtFromRequest: ExtractJwt.fromExtractors([
(request: RequestType) => {
const token = request?.cookies?.token;
return token;
},
]),
});
}

View File

@ -6,6 +6,7 @@ import { DocumentService } from '@services/document.service';
import { OutUser, UserService } from '@services/user.service';
import { WikiService } from '@services/wiki.service';
import { CollectType } from '@think/domains';
import * as lodash from 'lodash';
import { Repository } from 'typeorm';
@Injectable()
@ -28,7 +29,8 @@ export class CollectorService {
};
const record = await this.collectorRepo.findOne(data);
if (record) {
return await this.collectorRepo.remove(record);
await this.collectorRepo.remove(record);
return;
} else {
const res = await this.collectorRepo.create(data);
const ret = await this.collectorRepo.save(res);
@ -71,6 +73,8 @@ export class CollectorService {
})
);
return withCreateUserRes;
return withCreateUserRes.map((document) => {
return lodash.omit(document, ['state', 'content', 'index', 'createUserId']);
});
}
}

View File

@ -31,7 +31,7 @@ export class DocumentVersionService {
return;
}
this.max = lodash.get(config, 'server.maxDocumentVersion', 0);
this.max = lodash.get(config, 'server.maxDocumentVersion', 0) as number;
try {
const redis = new Redis({

View File

@ -15,8 +15,9 @@ import { OutUser, UserService } from '@services/user.service';
import { ViewService } from '@services/view.service';
import { WikiService } from '@services/wiki.service';
import { EMPTY_DOCUMNENT } from '@think/constants';
import { DocumentStatus, IDocument, WikiUserRole } from '@think/domains';
import { DocumentStatus, WikiUserRole } from '@think/domains';
import { instanceToPlain } from 'class-transformer';
import * as lodash from 'lodash';
import { Repository } from 'typeorm';
@Injectable()
@ -424,7 +425,7 @@ export class DocumentService {
// 5. 生成响应
const doc = instanceToPlain(document);
const createUser = await this.userService.findById(doc.createUserId);
return { document: { ...doc, views, createUser }, authority };
return { document: lodash.omit({ ...doc, views, createUser }, ['state']), authority };
}
/**
@ -452,6 +453,7 @@ export class DocumentService {
const newData = await this.documentRepo.merge(document, {
status: nextStatus,
...dto,
sharePassword: dto.sharePassword || '',
});
const ret = await this.documentRepo.save(newData);
return ret;
@ -468,7 +470,7 @@ export class DocumentService {
throw new HttpException('输入密码后查看内容', HttpStatus.BAD_REQUEST);
}
if (document.sharePassword !== dto.sharePassword) {
if (document.sharePassword && document.sharePassword !== dto.sharePassword) {
throw new HttpException('密码错误,请重新输入', HttpStatus.BAD_REQUEST);
}
@ -636,7 +638,9 @@ export class DocumentService {
})
);
return ret.filter(Boolean);
return ret.filter(Boolean).map((item) => {
return lodash.omit(item, ['state', 'content', 'index', 'createUserId']);
});
}
/**

View File

@ -4,6 +4,7 @@ import { forwardRef, HttpException, HttpStatus, Inject, Injectable } from '@nest
import { InjectRepository } from '@nestjs/typeorm';
import { OutUser, UserService } from '@services/user.service';
import { instanceToPlain } from 'class-transformer';
import * as lodash from 'lodash';
import { Repository } from 'typeorm';
@Injectable()
@ -57,7 +58,7 @@ export class TemplateService {
}
/**
*
*
* @param id
* @param tag
*/
@ -69,7 +70,7 @@ export class TemplateService {
}
const createUser = await this.userService.findById(template.createUserId);
return { ...template, createUser };
return lodash.omit({ ...template, createUser }, ['state']);
}
/**

View File

@ -111,7 +111,7 @@ export class UserService {
* @param user
* @returns
*/
async login(user: LoginUserDto): Promise<OutUser & { token: string }> {
async login(user: LoginUserDto): Promise<{ user: OutUser; token: string; domain: string; expiresIn: number }> {
const { name, password } = user;
const existUser = await this.userRepo.findOne({ where: { name } });
@ -125,7 +125,11 @@ export class UserService {
const res = instanceToPlain(existUser) as OutUser;
const token = this.jwtService.sign(res);
return Object.assign(res, { token });
const domain = this.confifgService.get('client.siteDomain');
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
const expiresIn = this.jwtService.decode(token, { complete: true }).payload.exp;
return { user: res, token, domain, expiresIn };
}
async validateUser(user: UserEntity) {

View File

@ -540,6 +540,7 @@ export class WikiService {
documents.sort((a, b) => a.index - b.index);
documents.forEach((doc) => {
delete doc.content;
delete doc.state;
});
@ -654,6 +655,7 @@ export class WikiService {
docs.forEach((doc) => {
delete doc.state;
delete doc.content;
});
return array2tree(docs);

View File

@ -6,6 +6,15 @@ interface Response<T> {
data: T;
}
export function wrapResponse({ statusCode, data }) {
return {
statusCode,
message: null,
success: true,
data,
};
}
@Injectable()
export class HttpResponseTransformInterceptor<T> implements NestInterceptor<T, Response<T>> {
intercept(context: ExecutionContext, next: CallHandler<T>): Observable<Response<T>> {
@ -13,17 +22,8 @@ export class HttpResponseTransformInterceptor<T> implements NestInterceptor<T, R
map((data) => {
const ctx = context.switchToHttp();
const response = ctx.getResponse();
// const request = ctx.getRequest();
// const url = request.originalUrl;
const statusCode = response.statusCode;
const res = {
statusCode,
message: null,
success: true,
data,
};
// console.info(url, res);
return res;
return wrapResponse({ data, statusCode });
})
);
}

View File

@ -131,9 +131,9 @@ importers:
react-helmet: ^6.1.0
react-lazy-load-image-component: ^1.5.4
react-pdf: ^5.7.2
react-query: ^3.39.0
react-split-pane: ^0.1.92
scroll-into-view-if-needed: ^2.2.29
swr: ^1.2.0
timeago.js: ^4.0.2
tippy.js: ^6.3.7
toggle-selection: ^1.0.6
@ -219,9 +219,9 @@ importers:
react-helmet: 6.1.0_react@17.0.2
react-lazy-load-image-component: 1.5.4_react-dom@17.0.2+react@17.0.2
react-pdf: 5.7.2_react-dom@17.0.2+react@17.0.2
react-query: 3.39.0_react-dom@17.0.2+react@17.0.2
react-split-pane: 0.1.92_react-dom@17.0.2+react@17.0.2
scroll-into-view-if-needed: 2.2.29
swr: 1.2.0_react@17.0.2
timeago.js: 4.0.2
tippy.js: 6.3.7
toggle-selection: 1.0.6
@ -280,9 +280,11 @@ importers:
'@think/config': workspace:^1.0.0
'@think/constants': workspace:^1.0.0
'@think/domains': workspace:^1.0.0
'@types/cookie-parser': ^1.4.3
'@types/cron': ^2.0.0
'@types/express': ^4.17.13
'@types/jest': 27.0.2
'@types/lodash': ^4.14.182
'@types/node': ^16.0.0
'@types/supertest': ^2.0.11
'@typescript-eslint/eslint-plugin': ^5.21.0
@ -292,6 +294,7 @@ importers:
class-transformer: ^0.5.1
class-validator: ^0.13.2
compression: ^1.7.4
cookie-parser: ^1.4.6
date-fns: ^2.28.0
eslint: ^8.14.0
eslint-config-prettier: ^8.5.0
@ -348,6 +351,7 @@ importers:
class-transformer: 0.5.1
class-validator: 0.13.2
compression: 1.7.4
cookie-parser: 1.4.6
date-fns: 2.28.0
express: 4.17.2
express-rate-limit: 6.2.0_express@4.17.2
@ -375,9 +379,11 @@ importers:
'@nestjs/cli': 8.2.0_eslint@8.14.0
'@nestjs/schematics': 8.0.5_typescript@4.5.5
'@nestjs/testing': 8.2.6_b893ca8083ee374883b6d648098a9aeb
'@types/cookie-parser': 1.4.3
'@types/cron': 2.0.0
'@types/express': 4.17.13
'@types/jest': 27.0.2
'@types/lodash': 4.14.182
'@types/node': 16.11.21
'@types/supertest': 2.0.11
'@typescript-eslint/eslint-plugin': 5.21.0_19515efd875c7ffcd67055d8be736b9f
@ -3101,6 +3107,12 @@ packages:
'@types/node': 16.11.21
dev: true
/@types/cookie-parser/1.4.3:
resolution: {integrity: sha512-CqSKwFwefj4PzZ5n/iwad/bow2hTCh0FlNAeWLtQM3JA/NX/iYagIpWG2cf1bQKQ2c9gU2log5VUCrn7LDOs0w==}
dependencies:
'@types/express': 4.17.13
dev: true
/@types/cookiejar/2.1.2:
resolution: {integrity: sha512-t73xJJrvdTjXrn4jLS9VSGRbz0nUY3cl2DMGDU48lKl+HR9dbbjW2A9r3g40VA++mQpy6uuHg33gy7du2BKpog==}
dev: true
@ -3206,6 +3218,10 @@ packages:
'@types/node': 16.11.21
dev: false
/@types/lodash/4.14.182:
resolution: {integrity: sha512-/THyiqyQAP9AfARo4pF+aCGcyiQ94tX/Is2I7HofNRqoYLgN1PBoOWu2/zTA5zMxzP5EFutMtWtGAFRKUe961Q==}
dev: true
/@types/lowlight/0.0.3:
resolution: {integrity: sha512-R83q/yPX2nIlo9D3WtSjyUDd57t8s+GVLaL8YIv3k7zMMWpYpOXqjJgrWp80qXUJB/a1t76nTyBpxrv0JNYaEg==}
dev: false
@ -4172,6 +4188,11 @@ packages:
resolution: {integrity: sha1-wE3+i5JtbsrKGBPWn/F5t8ICXYY=}
dev: false
/big-integer/1.6.51:
resolution: {integrity: sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==}
engines: {node: '>=0.6'}
dev: false
/big.js/5.2.2:
resolution: {integrity: sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==}
dev: false
@ -4226,6 +4247,19 @@ packages:
dependencies:
fill-range: 7.0.1
/broadcast-channel/3.7.0:
resolution: {integrity: sha512-cIAKJXAxGJceNZGTZSBzMxzyOn72cVgPnKx4dc6LRjQgbaJUQqhy5rzL3zbMxkMWsGKkv2hSFkPRMEXfoMZ2Mg==}
dependencies:
'@babel/runtime': 7.16.7
detect-node: 2.1.0
js-sha3: 0.8.0
microseconds: 0.2.0
nano-time: 1.0.0
oblivious-set: 1.0.0
rimraf: 3.0.2
unload: 2.2.0
dev: false
/browser-process-hrtime/1.0.0:
resolution: {integrity: sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==}
dev: true
@ -4684,6 +4718,14 @@ packages:
dependencies:
safe-buffer: 5.1.2
/cookie-parser/1.4.6:
resolution: {integrity: sha512-z3IzaNjdwUC2olLIB5/ITd0/setiaFMLYiZJle7xg5Fe9KWAceil7xszYfHHBtDFYLSgJduS2Ty0P1uJdPDJeA==}
engines: {node: '>= 0.8.0'}
dependencies:
cookie: 0.4.1
cookie-signature: 1.0.6
dev: false
/cookie-signature/1.0.6:
resolution: {integrity: sha1-4wOogrNCzD7oylE6eZmXNNqzriw=}
dev: false
@ -5040,6 +5082,10 @@ packages:
engines: {node: '>=8'}
dev: true
/detect-node/2.1.0:
resolution: {integrity: sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==}
dev: false
/dezalgo/1.0.3:
resolution: {integrity: sha1-f3Qt4Gb8dIvI24IFad3c5Jvw1FY=}
dependencies:
@ -7276,6 +7322,10 @@ packages:
resolution: {integrity: sha512-pZe//GGmwJndub7ZghVHz7vjb2LgC1m8B07Au3eYqeqv9emhESByMXxaEgkUkEqJe87oBbSniGYoQNIBklc7IQ==}
dev: false
/js-sha3/0.8.0:
resolution: {integrity: sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==}
dev: false
/js-tokens/4.0.0:
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
@ -7828,6 +7878,13 @@ packages:
uc.micro: 1.0.6
dev: false
/match-sorter/6.3.1:
resolution: {integrity: sha512-mxybbo3pPNuA+ZuCUhm5bwNkXrJTbsk5VWbR5wiwz/GC6LIiegBGn2w3O08UG/jdbYLinw51fSQ5xNU1U3MgBw==}
dependencies:
'@babel/runtime': 7.16.7
remove-accents: 0.4.2
dev: false
/mathml-tag-names/2.1.3:
resolution: {integrity: sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg==}
dev: true
@ -7900,6 +7957,10 @@ packages:
braces: 3.0.2
picomatch: 2.3.1
/microseconds/0.2.0:
resolution: {integrity: sha512-n7DHHMjR1avBbSpsTBj6fmMGh2AGrifVV4e+WYc3Q9lO+xnSZ3NyhcBND3vzzatt05LFhoKFRxrIyklmLlUtyA==}
dev: false
/mime-db/1.51.0:
resolution: {integrity: sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==}
engines: {node: '>= 0.6'}
@ -8048,6 +8109,12 @@ packages:
lru-cache: 4.1.5
dev: false
/nano-time/1.0.0:
resolution: {integrity: sha1-sFVPaa2J4i0JB/ehKwmTpdlhN+8=}
dependencies:
big-integer: 1.6.51
dev: false
/nanoid/3.2.0:
resolution: {integrity: sha512-fmsZYa9lpn69Ad5eDn7FMcnnSR+8R34W9qJEijxYhTbfOWzr22n1QxCMzXLK+ODyW2973V3Fux959iQoUxzUIA==}
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
@ -8293,6 +8360,10 @@ packages:
es-abstract: 1.19.1
dev: true
/oblivious-set/1.0.0:
resolution: {integrity: sha512-z+pI07qxo4c2CulUHCDf9lcqDlMSo72N/4rLUpRXf6fu+q8vjt8y0xS+Tlf8NTJDdTXHbdeO1n3MlbctwEoXZw==}
dev: false
/on-exit-leak-free/0.2.0:
resolution: {integrity: sha512-dqaz3u44QbRXQooZLTUKU41ZrzYrcvLISVgbrzbyCMxpmSLJvZ3ZamIJIZ29P6OhZIkNIQKosdeM6t1LYbA9hg==}
dev: false
@ -9155,6 +9226,25 @@ packages:
- worker-loader
dev: false
/react-query/3.39.0_react-dom@17.0.2+react@17.0.2:
resolution: {integrity: sha512-Od0IkSuS79WJOhzWBx/ys0x13+7wFqgnn64vBqqAAnZ9whocVhl/y1padD5uuZ6EIkXbFbInax0qvY7zGM0thA==}
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0
react-dom: '*'
react-native: '*'
peerDependenciesMeta:
react-dom:
optional: true
react-native:
optional: true
dependencies:
'@babel/runtime': 7.16.7
broadcast-channel: 3.7.0
match-sorter: 6.3.1
react: 17.0.2
react-dom: 17.0.2_react@17.0.2
dev: false
/react-resizable/1.11.1_react-dom@17.0.2+react@17.0.2:
resolution: {integrity: sha512-S70gbLaAYqjuAd49utRHibtHLrHXInh7GuOR+6OO6RO6uleQfuBnWmZjRABfqNEx3C3Z6VPLg0/0uOYFrkfu9Q==}
peerDependencies:
@ -9374,6 +9464,10 @@ packages:
jsesc: 0.5.0
dev: false
/remove-accents/0.4.2:
resolution: {integrity: sha1-CkPTqq4egNuRngeuJUsoXZ4ce7U=}
dev: false
/require-directory/2.1.1:
resolution: {integrity: sha1-jGStX9MNqxyXbiNE/+f3kqam30I=}
engines: {node: '>=0.10.0'}
@ -10310,14 +10404,6 @@ packages:
resolution: {integrity: sha1-WPcc7jvVGbWdSyqEO2x95krAR2Q=}
dev: true
/swr/1.2.0_react@17.0.2:
resolution: {integrity: sha512-C3IXeKOREn0jQ1ewXRENE7ED7jjGbFTakwB64eLACkCqkF/A0N2ckvpCTftcaSYi5yV36PzoehgVCOVRmtECcA==}
peerDependencies:
react: ^16.11.0 || ^17.0.0 || ^18.0.0
dependencies:
react: 17.0.2
dev: false
/symbol-observable/4.0.0:
resolution: {integrity: sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==}
engines: {node: '>=0.10'}
@ -10910,6 +10996,13 @@ packages:
resolution: {integrity: sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==}
engines: {node: '>= 10.0.0'}
/unload/2.2.0:
resolution: {integrity: sha512-B60uB5TNBLtN6/LsgAf3udH9saB5p7gqJwcFfbOEZ8BcBHnGwCf6G/TGiEqkRAxX7zAFIUtzdrXQSdL3Q/wqNA==}
dependencies:
'@babel/runtime': 7.16.7
detect-node: 2.1.0
dev: false
/unpipe/1.0.0:
resolution: {integrity: sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=}
engines: {node: '>= 0.8'}