diff --git a/packages/constants/lib/index.d.ts b/packages/constants/lib/index.d.ts
index a6f5f6b2..bb08b90c 100644
--- a/packages/constants/lib/index.d.ts
+++ b/packages/constants/lib/index.d.ts
@@ -1,2 +1,7 @@
+///
export declare const DEFAULT_WIKI_AVATAR = "https://wipi.oss-cn-shanghai.aliyuncs.com/2022-02-01/default0-96.png";
export declare const WIKI_AVATARS: string[];
+export declare const EMPTY_DOCUMNENT: {
+ content: string;
+ state: Buffer;
+};
diff --git a/packages/constants/lib/index.js b/packages/constants/lib/index.js
index cc09876b..d4583e50 100644
--- a/packages/constants/lib/index.js
+++ b/packages/constants/lib/index.js
@@ -1,19 +1,37 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
-exports.WIKI_AVATARS = exports.DEFAULT_WIKI_AVATAR = void 0;
-exports.DEFAULT_WIKI_AVATAR = "https://wipi.oss-cn-shanghai.aliyuncs.com/2022-02-01/default0-96.png";
+exports.EMPTY_DOCUMNENT = exports.WIKI_AVATARS = exports.DEFAULT_WIKI_AVATAR = void 0;
+exports.DEFAULT_WIKI_AVATAR = 'https://wipi.oss-cn-shanghai.aliyuncs.com/2022-02-01/default0-96.png';
exports.WIKI_AVATARS = [
exports.DEFAULT_WIKI_AVATAR,
- "https://wipi.oss-cn-shanghai.aliyuncs.com/2022-02-01/default2-96.png",
- "https://wipi.oss-cn-shanghai.aliyuncs.com/2022-02-01/default7-96.png",
- "https://wipi.oss-cn-shanghai.aliyuncs.com/2022-02-01/default8-96.png",
- "https://wipi.oss-cn-shanghai.aliyuncs.com/2022-02-01/default14-96.png",
- "https://wipi.oss-cn-shanghai.aliyuncs.com/2022-02-01/default21-96.png",
- "https://wipi.oss-cn-shanghai.aliyuncs.com/2022-02-01/default23-96.png",
- "https://wipi.oss-cn-shanghai.aliyuncs.com/2022-02-01/default1-96%20(1).png",
- "https://wipi.oss-cn-shanghai.aliyuncs.com/2022-02-01/default4-96.png",
- "https://wipi.oss-cn-shanghai.aliyuncs.com/2022-02-01/default12-96.png",
- "https://wipi.oss-cn-shanghai.aliyuncs.com/2022-02-01/default17-96.png",
- "https://wipi.oss-cn-shanghai.aliyuncs.com/2022-02-01/default18-96.png",
+ 'https://wipi.oss-cn-shanghai.aliyuncs.com/2022-02-01/default2-96.png',
+ 'https://wipi.oss-cn-shanghai.aliyuncs.com/2022-02-01/default7-96.png',
+ 'https://wipi.oss-cn-shanghai.aliyuncs.com/2022-02-01/default8-96.png',
+ 'https://wipi.oss-cn-shanghai.aliyuncs.com/2022-02-01/default14-96.png',
+ 'https://wipi.oss-cn-shanghai.aliyuncs.com/2022-02-01/default21-96.png',
+ 'https://wipi.oss-cn-shanghai.aliyuncs.com/2022-02-01/default23-96.png',
+ 'https://wipi.oss-cn-shanghai.aliyuncs.com/2022-02-01/default1-96%20(1).png',
+ 'https://wipi.oss-cn-shanghai.aliyuncs.com/2022-02-01/default4-96.png',
+ 'https://wipi.oss-cn-shanghai.aliyuncs.com/2022-02-01/default12-96.png',
+ 'https://wipi.oss-cn-shanghai.aliyuncs.com/2022-02-01/default17-96.png',
+ 'https://wipi.oss-cn-shanghai.aliyuncs.com/2022-02-01/default18-96.png',
];
+exports.EMPTY_DOCUMNENT = {
+ content: JSON.stringify({
+ default: {
+ type: 'doc',
+ content: [{ type: 'title', content: [{ type: 'text', text: '未命名文档' }] }],
+ },
+ }),
+ state: Buffer.from(new Uint8Array([
+ 1, 14, 204, 224, 154, 225, 13, 0, 7, 1, 7, 100, 101, 102, 97, 117, 108, 116, 3, 5, 116, 105, 116, 108, 101, 1, 0,
+ 204, 224, 154, 225, 13, 0, 1, 0, 1, 135, 204, 224, 154, 225, 13, 0, 3, 9, 112, 97, 114, 97, 103, 114, 97, 112,
+ 104, 40, 0, 204, 224, 154, 225, 13, 3, 6, 105, 110, 100, 101, 110, 116, 1, 125, 0, 40, 0, 204, 224, 154, 225, 13,
+ 3, 9, 116, 101, 120, 116, 65, 108, 105, 103, 110, 1, 119, 4, 108, 101, 102, 116, 0, 4, 71, 204, 224, 154, 225, 13,
+ 1, 6, 1, 0, 204, 224, 154, 225, 13, 10, 3, 132, 204, 224, 154, 225, 13, 13, 3, 230, 156, 170, 129, 204, 224, 154,
+ 225, 13, 14, 6, 132, 204, 224, 154, 225, 13, 20, 6, 229, 145, 189, 229, 144, 141, 129, 204, 224, 154, 225, 13, 22,
+ 5, 132, 204, 224, 154, 225, 13, 27, 6, 230, 150, 135, 230, 161, 163, 1, 204, 224, 154, 225, 13, 5, 1, 2, 6, 4, 11,
+ 3, 15, 6, 23, 5,
+ ])),
+};
//# sourceMappingURL=index.js.map
\ No newline at end of file
diff --git a/packages/constants/lib/index.js.map b/packages/constants/lib/index.js.map
index 0605370f..6140044a 100644
--- a/packages/constants/lib/index.js.map
+++ b/packages/constants/lib/index.js.map
@@ -1 +1 @@
-{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAa,QAAA,mBAAmB,GAC9B,sEAAsE,CAAC;AAE5D,QAAA,YAAY,GAAG;IAC1B,2BAAmB;IACnB,sEAAsE;IACtE,sEAAsE;IACtE,sEAAsE;IACtE,uEAAuE;IACvE,uEAAuE;IACvE,uEAAuE;IACvE,4EAA4E;IAC5E,sEAAsE;IACtE,uEAAuE;IACvE,uEAAuE;IACvE,uEAAuE;CACxE,CAAC"}
\ No newline at end of file
+{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAa,QAAA,mBAAmB,GAAG,sEAAsE,CAAC;AAE7F,QAAA,YAAY,GAAG;IAC1B,2BAAmB;IACnB,sEAAsE;IACtE,sEAAsE;IACtE,sEAAsE;IACtE,uEAAuE;IACvE,uEAAuE;IACvE,uEAAuE;IACvE,4EAA4E;IAC5E,sEAAsE;IACtE,uEAAuE;IACvE,uEAAuE;IACvE,uEAAuE;CACxE,CAAC;AAEW,QAAA,eAAe,GAAG;IAC7B,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC;QACtB,OAAO,EAAE;YACP,IAAI,EAAE,KAAK;YACX,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;SACzE;KACF,CAAC;IACF,KAAK,EAAE,MAAM,CAAC,IAAI,CAChB,IAAI,UAAU,CAAC;QACb,CAAC,EAAE,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC;QAChH,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG;QAC7G,GAAG,EAAE,EAAE,EAAE,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE;QAChH,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE;QACjH,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG;QAChH,GAAG,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE;QACjH,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE;QACjH,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC;KAChB,CAAC,CACH;CACF,CAAC"}
\ No newline at end of file
diff --git a/packages/constants/src/index.ts b/packages/constants/src/index.ts
index a58ee235..96c73493 100644
--- a/packages/constants/src/index.ts
+++ b/packages/constants/src/index.ts
@@ -14,3 +14,24 @@ export const WIKI_AVATARS = [
'https://wipi.oss-cn-shanghai.aliyuncs.com/2022-02-01/default17-96.png',
'https://wipi.oss-cn-shanghai.aliyuncs.com/2022-02-01/default18-96.png',
];
+
+export const EMPTY_DOCUMNENT = {
+ content: JSON.stringify({
+ default: {
+ type: 'doc',
+ content: [{ type: 'title', content: [{ type: 'text', text: '未命名文档' }] }],
+ },
+ }),
+ state: Buffer.from(
+ new Uint8Array([
+ 1, 14, 204, 224, 154, 225, 13, 0, 7, 1, 7, 100, 101, 102, 97, 117, 108, 116, 3, 5, 116, 105, 116, 108, 101, 1, 0,
+ 204, 224, 154, 225, 13, 0, 1, 0, 1, 135, 204, 224, 154, 225, 13, 0, 3, 9, 112, 97, 114, 97, 103, 114, 97, 112,
+ 104, 40, 0, 204, 224, 154, 225, 13, 3, 6, 105, 110, 100, 101, 110, 116, 1, 125, 0, 40, 0, 204, 224, 154, 225, 13,
+ 3, 9, 116, 101, 120, 116, 65, 108, 105, 103, 110, 1, 119, 4, 108, 101, 102, 116, 0, 4, 71, 204, 224, 154, 225, 13,
+ 1, 6, 1, 0, 204, 224, 154, 225, 13, 10, 3, 132, 204, 224, 154, 225, 13, 13, 3, 230, 156, 170, 129, 204, 224, 154,
+ 225, 13, 14, 6, 132, 204, 224, 154, 225, 13, 20, 6, 229, 145, 189, 229, 144, 141, 129, 204, 224, 154, 225, 13, 22,
+ 5, 132, 204, 224, 154, 225, 13, 27, 6, 230, 150, 135, 230, 161, 163, 1, 204, 224, 154, 225, 13, 5, 1, 2, 6, 4, 11,
+ 3, 15, 6, 23, 5,
+ ])
+ ),
+};
diff --git a/packages/server/src/controllers/document.controller.ts b/packages/server/src/controllers/document.controller.ts
index 93dc5eef..338e9bf5 100644
--- a/packages/server/src/controllers/document.controller.ts
+++ b/packages/server/src/controllers/document.controller.ts
@@ -14,6 +14,9 @@ import {
Delete,
} from '@nestjs/common';
import { JwtGuard } from '@guard/jwt.guard';
+import { DocumentAuthorityGuard, CheckDocumentAuthority } from '@guard/document-auth.guard';
+import { DocumentStatusGuard, CheckDocumentStatus } from '@guard/document-status.guard';
+import { DocumentStatus } from '@think/domains';
import { DocumentService } from '@services/document.service';
import { DocAuthDto } from '@dtos/doc-auth.dto';
import { CreateDocumentDto } from '@dtos/create-document.dto';
@@ -21,6 +24,8 @@ import { UpdateDocumentDto } from '@dtos/update-document.dto';
import { ShareDocumentDto } from '@dtos/share-document.dto';
@Controller('document')
+@UseGuards(DocumentAuthorityGuard)
+@UseGuards(DocumentStatusGuard)
export class DocumentController {
constructor(private readonly documentService: DocumentService) {}
@@ -32,54 +37,104 @@ export class DocumentController {
return await this.documentService.createDocument(req.user, dto);
}
+ /**
+ * 获取文档详情
+ * @param req
+ * @param documentId
+ * @returns
+ */
@UseInterceptors(ClassSerializerInterceptor)
@Get('detail/:id')
@HttpCode(HttpStatus.OK)
+ @CheckDocumentAuthority('readable')
@UseGuards(JwtGuard)
async getDocumentDetail(@Request() req, @Param('id') documentId) {
return await this.documentService.getDocumentDetail(req.user, documentId, req.headers['user-agent']);
}
+ /**
+ * 更新文档
+ * @param req
+ * @param documentId
+ * @param dto
+ * @returns
+ */
@UseInterceptors(ClassSerializerInterceptor)
@Post('update/:id')
@HttpCode(HttpStatus.OK)
+ @CheckDocumentAuthority('editable')
@UseGuards(JwtGuard)
async updateDocument(@Request() req, @Param('id') documentId, @Body() dto: UpdateDocumentDto) {
return await this.documentService.updateDocument(req.user, documentId, dto);
}
+ /**
+ * 获取文档版本记录
+ * @param req
+ * @param documentId
+ * @returns
+ */
@UseInterceptors(ClassSerializerInterceptor)
@Get('version/:id')
@HttpCode(HttpStatus.OK)
+ @CheckDocumentAuthority('readable')
@UseGuards(JwtGuard)
async getDocumentVersion(@Request() req, @Param('id') documentId) {
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)
@@ -88,52 +143,74 @@ export class DocumentController {
return await this.documentService.search(req.user, keyword);
}
+ /**
+ * 获取文档成员
+ * @param req
+ * @param documentId
+ * @returns
+ */
@UseInterceptors(ClassSerializerInterceptor)
@Get('user/:id')
@HttpCode(HttpStatus.OK)
+ @CheckDocumentAuthority('readable')
@UseGuards(JwtGuard)
async getDocUsers(@Request() req, @Param('id') documentId) {
return await this.documentService.getDocUsers(req.user, documentId);
}
+ /**
+ * 添加文档成员
+ * 只有文档创建着才可操作
+ * @param req
+ * @param dto
+ * @returns
+ */
@UseInterceptors(ClassSerializerInterceptor)
@Post('user/:id/add')
@HttpCode(HttpStatus.OK)
+ @CheckDocumentAuthority('createUser')
@UseGuards(JwtGuard)
async addDocUser(@Request() req, @Body() dto: DocAuthDto) {
return await this.documentService.addDocUser(req.user, dto);
}
+ /**
+ * 修改文档成员(一般是权限操作)
+ * 只有文档创建着才可操作
+ * @param req
+ * @param dto
+ * @returns
+ */
@UseInterceptors(ClassSerializerInterceptor)
@Post('user/:id/update')
@HttpCode(HttpStatus.OK)
+ @CheckDocumentAuthority('createUser')
@UseGuards(JwtGuard)
async updateDocUser(@Request() req, @Body() dto: DocAuthDto) {
return await this.documentService.updateDocUser(req.user, dto);
}
+ /**
+ * 删除文档成员
+ * 只有文档创建着才可操作
+ * @param req
+ * @param dto
+ * @returns
+ */
@UseInterceptors(ClassSerializerInterceptor)
@Post('user/:id/delete')
@HttpCode(HttpStatus.OK)
+ @CheckDocumentAuthority('createUser')
@UseGuards(JwtGuard)
async deleteDocUser(@Request() req, @Body() dto: DocAuthDto) {
return await this.documentService.deleteDocUser(req.user, dto);
}
- @UseInterceptors(ClassSerializerInterceptor)
- @Post('public/detail/:id')
- @HttpCode(HttpStatus.OK)
- async getShareDocumentDetail(@Request() req, @Param('id') documentId, @Body() dto: ShareDocumentDto) {
- return await this.documentService.getPublicDocumentDetail(documentId, dto, req.headers['user-agent']);
- }
-
- @UseInterceptors(ClassSerializerInterceptor)
- @Post('public/children')
- @HttpCode(HttpStatus.OK)
- async getShareChildrenDocuments(@Body() data) {
- return await this.documentService.getShareChildrenDocuments(data);
- }
-
+ /**
+ * 获取用户最近访问的文档
+ * @param req
+ * @returns
+ */
@UseInterceptors(ClassSerializerInterceptor)
@Get('recent')
@HttpCode(HttpStatus.OK)
@@ -141,4 +218,32 @@ export class DocumentController {
async getWorkspaceDocuments(@Request() req) {
return await this.documentService.getRecentDocuments(req.user);
}
+
+ /**
+ * 获取公开文档详情
+ * @param req
+ * @param documentId
+ * @param dto
+ * @returns
+ */
+ @UseInterceptors(ClassSerializerInterceptor)
+ @Post('public/detail/:id')
+ @CheckDocumentStatus(DocumentStatus.public)
+ @HttpCode(HttpStatus.OK)
+ async getShareDocumentDetail(@Request() req, @Param('id') documentId, @Body() dto: ShareDocumentDto) {
+ return await this.documentService.getPublicDocumentDetail(documentId, dto, req.headers['user-agent']);
+ }
+
+ /**
+ * 获取公开文档的子文档
+ * @param data
+ * @returns
+ */
+ @UseInterceptors(ClassSerializerInterceptor)
+ @Post('public/children')
+ @CheckDocumentStatus(DocumentStatus.public)
+ @HttpCode(HttpStatus.OK)
+ async getShareChildrenDocuments(@Body() data) {
+ return await this.documentService.getShareChildrenDocuments(data);
+ }
}
diff --git a/packages/server/src/controllers/wiki.controller.ts b/packages/server/src/controllers/wiki.controller.ts
index e1f1f8a8..f5de2ac4 100644
--- a/packages/server/src/controllers/wiki.controller.ts
+++ b/packages/server/src/controllers/wiki.controller.ts
@@ -15,7 +15,9 @@ import {
Delete,
} from '@nestjs/common';
import { JwtGuard } from '@guard/jwt.guard';
-import { IPagination } from '@think/domains';
+import { IPagination, WikiUserRole, WikiStatus } from '@think/domains';
+import { WikiUserRoleGuard, CheckWikiUserRole } from '@guard/wiki-user.guard';
+import { WikiStatusGuard, CheckWikiStatus } from '@guard/wiki-status.guard';
import { WikiService } from '@services/wiki.service';
import { WikiUserDto } from '@dtos/wiki-user.dto';
import { CreateWikiDto } from '@dtos/create-wiki.dto';
@@ -23,9 +25,17 @@ import { UpdateWikiDto } from '@dtos/update-wiki.dto';
import { ShareWikiDto } from '@dtos/share-wiki.dto';
@Controller('wiki')
+@UseGuards(WikiUserRoleGuard)
+@UseGuards(WikiStatusGuard)
export class WikiController {
constructor(private readonly wikiService: WikiService) {}
+ /**
+ * 新建知识库
+ * @param req
+ * @param dto
+ * @returns
+ */
@UseInterceptors(ClassSerializerInterceptor)
@Post('create')
@HttpCode(HttpStatus.CREATED)
@@ -34,6 +44,12 @@ export class WikiController {
return await this.wikiService.createWiki(req.user, dto);
}
+ /**
+ * 获取用户所有知识库(创建的、参与的)
+ * @param req
+ * @param pagination
+ * @returns
+ */
@UseInterceptors(ClassSerializerInterceptor)
@Get('list/all')
@HttpCode(HttpStatus.OK)
@@ -42,6 +58,12 @@ export class WikiController {
return await this.wikiService.getAllWikis(req.user, pagination);
}
+ /**
+ * 获取用户拥有的知识库(一般是创建的,尚未实现知识库转移)
+ * @param req
+ * @param pagination
+ * @returns
+ */
@UseInterceptors(ClassSerializerInterceptor)
@Get('list/own')
@HttpCode(HttpStatus.OK)
@@ -50,6 +72,12 @@ export class WikiController {
return await this.wikiService.getOwnWikis(req.user, pagination);
}
+ /**
+ * 获取用户参与的知识库
+ * @param req
+ * @param pagination
+ * @returns
+ */
@UseInterceptors(ClassSerializerInterceptor)
@Get('list/join')
@HttpCode(HttpStatus.OK)
@@ -58,123 +86,244 @@ export class WikiController {
return await this.wikiService.getJoinWikis(req.user, pagination);
}
+ /**
+ * 获取知识库详情
+ * @param req
+ * @param wikiId
+ * @returns
+ */
@UseInterceptors(ClassSerializerInterceptor)
@Get('detail/:id')
@HttpCode(HttpStatus.OK)
+ @CheckWikiUserRole()
@UseGuards(JwtGuard)
async getWikiDetail(@Request() req, @Param('id') wikiId) {
return await this.wikiService.getWikiDetail(req.user, wikiId);
}
+ /**
+ * 获取知识库首页文档(首页文档为自动创建)
+ * @param req
+ * @param wikiId
+ * @returns
+ */
@UseInterceptors(ClassSerializerInterceptor)
@Get('homedoc/:id')
@HttpCode(HttpStatus.OK)
+ @CheckWikiUserRole()
@UseGuards(JwtGuard)
async getWikiHomeDocument(@Request() req, @Param('id') wikiId) {
return await this.wikiService.getWikiHomeDocument(req.user, wikiId);
}
+ /**
+ * 修改知识库
+ * 只有管理员可操作
+ * @param req
+ * @param wikiId
+ * @param dto
+ * @returns
+ */
@UseInterceptors(ClassSerializerInterceptor)
@Patch('update/:id')
@HttpCode(HttpStatus.OK)
+ @CheckWikiUserRole(WikiUserRole.admin)
@UseGuards(JwtGuard)
async updateWiki(@Request() req, @Param('id') wikiId, @Body() dto: UpdateWikiDto) {
return await this.wikiService.updateWiki(req.user, wikiId, dto);
}
+ /**
+ * 删除知识库
+ * 只有管理员可操作
+ * @param req
+ * @param wikiId
+ * @returns
+ */
@UseInterceptors(ClassSerializerInterceptor)
@Delete('delete/:id')
@HttpCode(HttpStatus.OK)
+ @CheckWikiUserRole(WikiUserRole.admin)
@UseGuards(JwtGuard)
async deleteWiki(@Request() req, @Param('id') wikiId) {
return await this.wikiService.deleteWiki(req.user, wikiId);
}
+ /**
+ * 查看知识库成员
+ * 只有管理员可操作
+ * @param req
+ * @param wikiId
+ * @returns
+ */
@UseInterceptors(ClassSerializerInterceptor)
@Get('user/:id')
@HttpCode(HttpStatus.OK)
+ @CheckWikiUserRole(WikiUserRole.admin)
@UseGuards(JwtGuard)
- async getWikiUsers(@Request() req, @Param('id') wikiId) {
- return await this.wikiService.getWikiUsers({ userId: req.user.id, wikiId });
+ async getWikiUsers(@Param('id') wikiId) {
+ return await this.wikiService.getWikiUsers(wikiId);
}
+ /**
+ * 添加知识库成员
+ * 只有管理员可操作
+ * @param req
+ * @param wikiId
+ * @param dto
+ * @returns
+ */
@UseInterceptors(ClassSerializerInterceptor)
@Post('user/:id/add')
@HttpCode(HttpStatus.OK)
+ @CheckWikiUserRole(WikiUserRole.admin)
@UseGuards(JwtGuard)
async addWikiUser(@Request() req, @Param('id') wikiId, @Body() dto: WikiUserDto) {
return await this.wikiService.addWikiUser(req.user, wikiId, dto);
}
+ /**
+ * 更新知识库成员(一般为角色操作)
+ * 只有管理员可操作
+ * @param req
+ * @param wikiId
+ * @param dto
+ * @returns
+ */
@UseInterceptors(ClassSerializerInterceptor)
@Post('user/:id/update')
@HttpCode(HttpStatus.OK)
+ @CheckWikiUserRole(WikiUserRole.admin)
@UseGuards(JwtGuard)
async updateWikiUser(@Request() req, @Param('id') wikiId, @Body() dto: WikiUserDto) {
return await this.wikiService.updateWikiUser(req.user, wikiId, dto);
}
+ /**
+ * 删除知识库成员
+ * 只有管理员可操作
+ * @param req
+ * @param wikiId
+ * @param dto
+ * @returns
+ */
@UseInterceptors(ClassSerializerInterceptor)
@Post('user/:id/delete')
@HttpCode(HttpStatus.OK)
+ @CheckWikiUserRole(WikiUserRole.admin)
@UseGuards(JwtGuard)
async deleteWikiUser(@Request() req, @Param('id') wikiId, @Body() dto: WikiUserDto) {
return await this.wikiService.deleteWikiUser(req.user, wikiId, dto);
}
+ /**
+ * 分享(或关闭分享)知识库
+ * 只有管理员可操作
+ * @param req
+ * @param wikiId
+ * @param dto
+ * @returns
+ */
@UseInterceptors(ClassSerializerInterceptor)
@Post('share/:id')
@HttpCode(HttpStatus.OK)
+ @CheckWikiUserRole(WikiUserRole.admin)
@UseGuards(JwtGuard)
async toggleWorkspaceStatus(@Request() req, @Param('id') wikiId, @Body() dto: ShareWikiDto) {
return await this.wikiService.shareWiki(req.user, wikiId, dto);
}
+ /**
+ * 获取知识库目录
+ * @param req
+ * @param wikiId
+ * @returns
+ */
@UseInterceptors(ClassSerializerInterceptor)
@Get('tocs/:id')
@HttpCode(HttpStatus.OK)
+ @CheckWikiUserRole()
@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(JwtGuard)
- async orderWikiTocs(@Request() req, @Param('id') wikiId, @Body() relations) {
- return await this.wikiService.orderWikiTocs(req.user, wikiId, relations);
+ 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(JwtGuard)
async getWikiDocs(@Request() req, @Param('id') wikiId) {
return await this.wikiService.getWikiDocs(req.user, wikiId);
}
+ /**
+ * 获取公开知识库首页文档
+ * @param req
+ * @param wikiId
+ * @returns
+ */
@UseInterceptors(ClassSerializerInterceptor)
@Get('public/homedoc/:id')
+ @CheckWikiStatus(WikiStatus.public)
@HttpCode(HttpStatus.OK)
async getWikiPublicHomeDocument(@Request() req, @Param('id') wikiId) {
- return await this.wikiService.getWikiPublicHomeDocument(wikiId, req.headers['user-agent']);
- }
-
- @UseInterceptors(ClassSerializerInterceptor)
- @Post('public/tocs/:id')
- @HttpCode(HttpStatus.OK)
- async getPublicWikiTocs(@Param('id') wikiId) {
- return await this.wikiService.getPublicWikiTocs(wikiId);
+ return await this.wikiService.getPublicWikiHomeDocument(wikiId, req.headers['user-agent']);
}
+ /**
+ * 获取公开知识库详情
+ * @param wikiId
+ * @returns
+ */
@UseInterceptors(ClassSerializerInterceptor)
@Post('public/detail/:id')
+ @CheckWikiStatus(WikiStatus.public)
@HttpCode(HttpStatus.OK)
async getPublicWorkspaceDetail(@Param('id') wikiId) {
return await this.wikiService.getPublicWikiDetail(wikiId);
}
+ /**
+ * 获取公开知识库目录
+ * @param wikiId
+ * @returns
+ */
+ @UseInterceptors(ClassSerializerInterceptor)
+ @Post('public/tocs/:id')
+ @CheckWikiStatus(WikiStatus.public)
+ @HttpCode(HttpStatus.OK)
+ async getPublicWikiTocs(@Param('id') wikiId) {
+ return await this.wikiService.getPublicWikiTocs(wikiId);
+ }
+
+ /**
+ * 获取所有公开知识库
+ * @param pagination
+ * @returns
+ */
@UseInterceptors(ClassSerializerInterceptor)
@Get('public/wikis')
@HttpCode(HttpStatus.OK)
diff --git a/packages/server/src/guard/document-auth.guard.ts b/packages/server/src/guard/document-auth.guard.ts
new file mode 100644
index 00000000..ab600cb2
--- /dev/null
+++ b/packages/server/src/guard/document-auth.guard.ts
@@ -0,0 +1,63 @@
+import { CanActivate, ExecutionContext, Injectable, SetMetadata, HttpException, HttpStatus } from '@nestjs/common';
+import { JwtService } from '@nestjs/jwt';
+import { Reflector } from '@nestjs/core';
+import { IUser } from '@think/domains';
+import { DocumentService } from '@services/document.service';
+
+const KEY = 'DocumentAuthority';
+export const CheckDocumentAuthority = (auth: 'readable' | 'editable' | 'createUser' | null) => SetMetadata(KEY, auth);
+
+@Injectable()
+export class DocumentAuthorityGuard implements CanActivate {
+ constructor(
+ private readonly reflector: Reflector,
+ private readonly jwtService: JwtService,
+ private readonly documentService: DocumentService
+ ) {}
+
+ async canActivate(context: ExecutionContext): Promise {
+ const needAuth = this.reflector.get(KEY, context.getHandler());
+
+ if (!needAuth) {
+ return true;
+ }
+
+ const request = context.switchToHttp().getRequest();
+
+ let token = request.headers.authorization;
+
+ if (/Bearer/.test(token)) {
+ token = token.split(' ').pop();
+ }
+
+ 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;
+ let document = null;
+
+ if (documentId) {
+ document = await this.documentService.findById(documentId);
+ } else {
+ if (body.wikiId) {
+ document = await this.documentService.findWikiHomeDocument(body.wikiId);
+ }
+ }
+
+ if (!document) {
+ throw new HttpException('文档不存在', HttpStatus.NOT_FOUND);
+ }
+
+ if (needAuth === 'createUser') {
+ if (document.createUserId !== user.id) {
+ throw new HttpException('您不是该文档的创建者,无法删除', HttpStatus.FORBIDDEN);
+ }
+ } else if (needAuth) {
+ const authority = await this.documentService.getDocumentAuthority(documentId, user.id);
+ if (!authority || !authority[needAuth]) {
+ throw new HttpException('您无权查看此文档', HttpStatus.FORBIDDEN);
+ }
+ }
+
+ return true;
+ }
+}
diff --git a/packages/server/src/guard/document-status.guard.ts b/packages/server/src/guard/document-status.guard.ts
new file mode 100644
index 00000000..2efcc14a
--- /dev/null
+++ b/packages/server/src/guard/document-status.guard.ts
@@ -0,0 +1,49 @@
+import { CanActivate, ExecutionContext, Injectable, SetMetadata, HttpException, HttpStatus } from '@nestjs/common';
+import { Reflector } from '@nestjs/core';
+import { DocumentStatus } from '@think/domains';
+import { DocumentService } from '@services/document.service';
+
+const KEY = 'DocumentStatus';
+export const CheckDocumentStatus = (status: DocumentStatus) => SetMetadata(KEY, status);
+
+@Injectable()
+export class DocumentStatusGuard implements CanActivate {
+ constructor(private readonly reflector: Reflector, private readonly documentService: DocumentService) {}
+
+ async canActivate(context: ExecutionContext): Promise {
+ const targetStatus = this.reflector.get(KEY, context.getHandler());
+
+ if (!targetStatus) {
+ return true;
+ }
+
+ const request = context.switchToHttp().getRequest();
+ const { params, query, body } = request;
+ const documentId = params.id || params.documentId || query.id || query.documentId || body.documentId;
+
+ let document = null;
+
+ if (documentId) {
+ document = await this.documentService.findById(documentId);
+ } else {
+ if (body.wikiId) {
+ document = await this.documentService.findWikiHomeDocument(body.wikiId);
+ }
+ }
+
+ if (!document) {
+ throw new HttpException('文档不存在', HttpStatus.NOT_FOUND);
+ }
+
+ if (document.status !== targetStatus) {
+ throw new HttpException(
+ targetStatus === DocumentStatus.private
+ ? '私有文档,无法查看内容'
+ : '公共文档,无法查看内容,请提 issue 到 GitHub 仓库反馈',
+ HttpStatus.FORBIDDEN
+ );
+ }
+
+ return true;
+ }
+}
diff --git a/packages/server/src/guard/wiki-status.guard.ts b/packages/server/src/guard/wiki-status.guard.ts
new file mode 100644
index 00000000..69ae6689
--- /dev/null
+++ b/packages/server/src/guard/wiki-status.guard.ts
@@ -0,0 +1,40 @@
+import { CanActivate, ExecutionContext, Injectable, SetMetadata, HttpException, HttpStatus } from '@nestjs/common';
+import { Reflector } from '@nestjs/core';
+import { WikiStatus } from '@think/domains';
+import { WikiService } from '@services/wiki.service';
+
+const KEY = 'WikiStatus';
+export const CheckWikiStatus = (status: WikiStatus) => SetMetadata(KEY, status);
+
+@Injectable()
+export class WikiStatusGuard implements CanActivate {
+ constructor(private readonly reflector: Reflector, private readonly wikiService: WikiService) {}
+
+ async canActivate(context: ExecutionContext): Promise {
+ const targetStatus = this.reflector.get(KEY, context.getHandler());
+
+ if (!targetStatus) {
+ return true;
+ }
+
+ const request = context.switchToHttp().getRequest();
+ const { params, query, body } = request;
+ const wikiId = params.id || params.wikiId || query.id || query.wikiId || body.wikiId;
+
+ const wiki = await this.wikiService.findById(wikiId);
+
+ if (!wiki) {
+ throw new HttpException('目标知识库不存在', HttpStatus.NOT_FOUND);
+ }
+ if (wiki.status !== targetStatus) {
+ throw new HttpException(
+ targetStatus === WikiStatus.private
+ ? '私有知识库,无法查看内容'
+ : '公共知识库,无法查看内容,请提 issue 到 GitHub 仓库反馈',
+ HttpStatus.FORBIDDEN
+ );
+ }
+
+ return true;
+ }
+}
diff --git a/packages/server/src/guard/wiki-user.guard.ts b/packages/server/src/guard/wiki-user.guard.ts
new file mode 100644
index 00000000..cf7655ae
--- /dev/null
+++ b/packages/server/src/guard/wiki-user.guard.ts
@@ -0,0 +1,54 @@
+import { CanActivate, ExecutionContext, Injectable, SetMetadata, HttpException, HttpStatus } from '@nestjs/common';
+import { JwtService } from '@nestjs/jwt';
+import { Reflector } from '@nestjs/core';
+import { IUser, WikiUserRole } from '@think/domains';
+import { WikiService } from '@services/wiki.service';
+
+const KEY = 'WIKI_USER_ROLE';
+
+/**
+ * 知识库成员角色检测
+ * @param role 不传意味只要是成员即可
+ * @returns
+ */
+export const CheckWikiUserRole = (role: WikiUserRole | null = null) => SetMetadata(KEY, role);
+
+@Injectable()
+export class WikiUserRoleGuard implements CanActivate {
+ constructor(
+ private readonly reflector: Reflector,
+ private readonly jwtService: JwtService,
+ private readonly wikiService: WikiService
+ ) {}
+
+ async canActivate(context: ExecutionContext): Promise {
+ const targetUserRole = this.reflector.get(KEY, context.getHandler());
+
+ const request = context.switchToHttp().getRequest();
+
+ let token = request.headers.authorization;
+
+ if (/Bearer/.test(token)) {
+ token = token.split(' ').pop();
+ }
+
+ const user = this.jwtService.decode(token) as IUser;
+
+ const { params, query, body } = request;
+ const wikiId = params.id || params.wikiId || query.id || query.wikiId || body.wikiId;
+
+ const wiki = await this.wikiService.findById(wikiId);
+
+ if (!wiki) {
+ throw new HttpException('目标知识库不存在', HttpStatus.NOT_FOUND);
+ }
+
+ const wikiUser = await this.wikiService.findWikiUser(wikiId, user.id);
+
+ if (!wikiUser && targetUserRole && wikiUser.userRole !== targetUserRole) {
+ throw new HttpException('您无权查看该知识库', HttpStatus.FORBIDDEN);
+ }
+
+ return true;
+ }
+}
diff --git a/packages/server/src/modules/wiki.module.ts b/packages/server/src/modules/wiki.module.ts
index 7608926e..90d60bcc 100644
--- a/packages/server/src/modules/wiki.module.ts
+++ b/packages/server/src/modules/wiki.module.ts
@@ -4,6 +4,7 @@ import { UserModule } from '@modules/user.module';
import { DocumentModule } from '@modules/document.module';
import { MessageModule } from '@modules/message.module';
import { CollectorModule } from '@modules/collector.module';
+import { ViewModule } from '@modules/view.module';
import { WikiEntity } from '@entities/wiki.entity';
import { WikiUserEntity } from '@entities/wiki-user.entity';
import { WikiController } from '@controllers/wiki.controller';
@@ -15,6 +16,7 @@ import { WikiService } from '@services/wiki.service';
forwardRef(() => UserModule),
forwardRef(() => DocumentModule),
forwardRef(() => MessageModule),
+ forwardRef(() => ViewModule),
forwardRef(() => CollectorModule),
],
providers: [WikiService],
diff --git a/packages/server/src/services/collaboration.service.ts b/packages/server/src/services/collaboration.service.ts
index b3509bd1..f21be713 100644
--- a/packages/server/src/services/collaboration.service.ts
+++ b/packages/server/src/services/collaboration.service.ts
@@ -1,5 +1,4 @@
import { Injectable, HttpException, HttpStatus, Inject, forwardRef } from '@nestjs/common';
-import { DocumentStatus } from '@think/domains';
import { getConfig } from '@think/config';
import * as Y from 'yjs';
import { TiptapTransformer } from '@hocuspocus/transformer';
@@ -77,57 +76,31 @@ export class CollaborationService {
async onAuthenticate({ connection, token, requestParameters }: onAuthenticatePayload) {
const targetId = requestParameters.get('targetId');
const docType = requestParameters.get('docType');
+ const user = await this.userService.decodeToken(token);
+
+ if (!user || !user.id) {
+ throw new HttpException('您无权查看', HttpStatus.UNAUTHORIZED);
+ }
switch (docType) {
case 'document': {
- const documentId = targetId;
- if (token === 'anoy') {
- const document = await this.documentService.findById(documentId);
- if (document.status === DocumentStatus.public) {
- connection.readOnly = true;
- return {
- user: { name: 'anoymouse' },
- };
- }
- } else {
- const user = await this.userService.decodeToken(token);
-
- if (!user || !user.id) {
- throw new HttpException('您无权查看此文档', HttpStatus.UNAUTHORIZED);
- }
-
- const authority = await this.documentService.getDocumentAuthority(documentId, user.id);
-
- if (!authority.readable) {
- throw new HttpException('您无权查看此文档', HttpStatus.FORBIDDEN);
- }
-
- if (!authority.editable) {
- connection.readOnly = true;
- }
-
- return {
- user,
- };
+ const authority = await this.documentService.getDocumentAuthority(targetId, user.id);
+ if (!authority.readable) {
+ throw new HttpException('您无权查看此文档', HttpStatus.FORBIDDEN);
}
- break;
+ if (!authority.editable) {
+ connection.readOnly = true;
+ }
+ return {
+ user,
+ };
}
case 'template': {
- const templateId = targetId;
-
- const user = await this.userService.decodeToken(token);
-
- if (!user || !user.id) {
- throw new HttpException('您无权查看此模板', HttpStatus.UNAUTHORIZED);
- }
-
- const template = await this.templateService.findById(templateId);
-
+ const template = await this.templateService.findById(targetId);
if (template.createUserId !== user.id) {
throw new HttpException('您无权查看此模板', HttpStatus.FORBIDDEN);
}
-
return {
user,
};
@@ -148,34 +121,32 @@ export class CollaborationService {
const targetId = requestParameters.get('targetId');
const docType = requestParameters.get('docType');
+ let state = null;
+
switch (docType) {
case 'document': {
- const documentId = targetId;
- const { state } = await this.documentService.findById(documentId);
- const unit8 = new Uint8Array(state);
-
- if (unit8.byteLength) {
- Y.applyUpdate(document, unit8);
- }
-
- return document;
+ const res = await this.documentService.findById(targetId);
+ state = res.state;
+ break;
}
case 'template': {
- const templateId = targetId;
- const { state } = await this.templateService.findById(templateId);
- const unit8 = new Uint8Array(state);
-
- if (unit8.byteLength) {
- Y.applyUpdate(document, unit8);
- }
-
- return document;
+ const res = await this.templateService.findById(targetId);
+ state = res.state;
+ break;
}
default:
throw new Error('未知类型');
}
+
+ const unit8 = new Uint8Array(state);
+
+ if (unit8.byteLength) {
+ Y.applyUpdate(document, unit8);
+ }
+
+ return document;
}
async onChange(data: onChangePayload) {
@@ -194,9 +165,9 @@ export class CollaborationService {
this.debounceTime * 2
);
};
+ const updateTemplate = this.templateService.updateTemplate.bind(this.templateService);
- const updateHandler =
- docType === 'document' ? updateDocument : this.templateService.updateTemplate.bind(this.templateService);
+ const updateHandler = docType === 'document' ? updateDocument : updateTemplate;
this.debounce(`onStoreDocument-${targetId}`, () => {
this.onStoreDocument(updateHandler, data).catch((error) => {
@@ -208,13 +179,12 @@ export class CollaborationService {
}
async onStoreDocument(updateHandler, data: onChangePayload) {
- const { requestParameters, context } = data;
+ const { requestParameters } = data;
const targetId = requestParameters.get('targetId');
const userId = requestParameters.get('userId');
if (!userId) {
- console.error('COLLABORATION: can not get user info');
- return;
+ throw new HttpException('无用户信息,拒绝存储文档数据变更', HttpStatus.FORBIDDEN);
}
const node = TiptapTransformer.fromYdoc(data.document);
@@ -233,33 +203,24 @@ export class CollaborationService {
const docType = requestParameters.get('docType');
const userId = requestParameters.get('userId');
- switch (docType) {
- case 'document': {
- const documentId = targetId;
- const ret = await this.documentService.findById(documentId);
-
- if (ret && !ret.title) {
- await this.documentService.updateDocument({ id: userId } as OutUser, targetId, {
- title: '未命名文档',
- });
- }
- break;
+ if (docType === 'document') {
+ const data = await this.documentService.findById(targetId);
+ if (data && !data.title) {
+ await this.documentService.updateDocument({ id: userId } as OutUser, targetId, {
+ title: '未命名文档',
+ });
}
+ return;
+ }
- case 'template': {
- const templateId = targetId;
- const ret = await this.templateService.findById(templateId);
-
- if (ret && !ret.title) {
- await this.templateService.updateTemplate({ id: userId } as OutUser, targetId, {
- title: '未命名模板',
- });
- }
- break;
+ if (docType === 'template') {
+ const data = await this.templateService.findById(targetId);
+ if (data && !data.title) {
+ await this.templateService.updateTemplate({ id: userId } as OutUser, targetId, {
+ title: '未命名模板',
+ });
}
-
- default:
- throw new Error('未知类型');
+ return;
}
}
}
diff --git a/packages/server/src/services/document-version.service.ts b/packages/server/src/services/document-version.service.ts
index c77f5086..c7e16e4a 100644
--- a/packages/server/src/services/document-version.service.ts
+++ b/packages/server/src/services/document-version.service.ts
@@ -4,6 +4,8 @@ import { IDocument } from '@think/domains';
import { getConfig } from '@think/config';
import * as lodash from 'lodash';
+type VerisonDataItem = { version: string; data: string };
+
@Injectable()
export class DocumentVersionService {
private redis: Redis;
@@ -14,7 +16,7 @@ export class DocumentVersionService {
this.init();
}
- private versionDataToArray(data: Record): Array<{ version: string; data: string }> {
+ private versionDataToArray(data: Record): Array {
return Object.keys(data)
.sort((a, b) => +b - +a)
.map((key) => ({ version: key, data: data[key] }));
@@ -88,7 +90,7 @@ export class DocumentVersionService {
* @param documentId
* @returns
*/
- public async getDocumentVersions(documentId: IDocument['id']): Promise> {
+ public async getDocumentVersions(documentId: IDocument['id']): Promise> {
if (this.error || !this.redis) {
throw new HttpException(this.error, HttpStatus.NOT_IMPLEMENTED);
}
diff --git a/packages/server/src/services/document.service.ts b/packages/server/src/services/document.service.ts
index a602066e..28c04859 100644
--- a/packages/server/src/services/document.service.ts
+++ b/packages/server/src/services/document.service.ts
@@ -2,6 +2,7 @@ import { Injectable, HttpException, HttpStatus, Inject, forwardRef } from '@nest
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { instanceToPlain } from 'class-transformer';
+import { EMPTY_DOCUMNENT } from '@think/constants';
import { DocumentStatus, IDocument, WikiUserRole } from '@think/domains';
import { DocumentAuthorityEntity } from '@entities/document-authority.entity';
import { DocumentEntity } from '@entities/document.entity';
@@ -12,33 +13,11 @@ import { CollaborationService } from '@services/collaboration.service';
import { DocumentVersionService } from '@services/document-version.service';
import { TemplateService } from '@services/template.service';
import { ViewService } from '@services/view.service';
-import { array2tree } from '@helpers/tree.helper';
import { DocAuthDto } from '@dtos/doc-auth.dto';
import { CreateDocumentDto } from '@dtos/create-document.dto';
import { UpdateDocumentDto } from '@dtos/update-document.dto';
import { ShareDocumentDto } from '@dtos/share-document.dto';
-const EMPTY_DOCUMNENT = {
- content: JSON.stringify({
- default: {
- type: 'doc',
- content: [{ type: 'title', content: [{ type: 'text', text: '未命名文档' }] }],
- },
- }),
- state: Buffer.from(
- new Uint8Array([
- 1, 14, 204, 224, 154, 225, 13, 0, 7, 1, 7, 100, 101, 102, 97, 117, 108, 116, 3, 5, 116, 105, 116, 108, 101, 1, 0,
- 204, 224, 154, 225, 13, 0, 1, 0, 1, 135, 204, 224, 154, 225, 13, 0, 3, 9, 112, 97, 114, 97, 103, 114, 97, 112,
- 104, 40, 0, 204, 224, 154, 225, 13, 3, 6, 105, 110, 100, 101, 110, 116, 1, 125, 0, 40, 0, 204, 224, 154, 225, 13,
- 3, 9, 116, 101, 120, 116, 65, 108, 105, 103, 110, 1, 119, 4, 108, 101, 102, 116, 0, 4, 71, 204, 224, 154, 225, 13,
- 1, 6, 1, 0, 204, 224, 154, 225, 13, 10, 3, 132, 204, 224, 154, 225, 13, 13, 3, 230, 156, 170, 129, 204, 224, 154,
- 225, 13, 14, 6, 132, 204, 224, 154, 225, 13, 20, 6, 229, 145, 189, 229, 144, 141, 129, 204, 224, 154, 225, 13, 22,
- 5, 132, 204, 224, 154, 225, 13, 27, 6, 230, 150, 135, 230, 161, 163, 1, 204, 224, 154, 225, 13, 5, 1, 2, 6, 4, 11,
- 3, 15, 6, 23, 5,
- ])
- ),
-};
-
@Injectable()
export class DocumentService {
private collaborationService: CollaborationService;
@@ -46,17 +25,23 @@ export class DocumentService {
constructor(
@InjectRepository(DocumentAuthorityEntity)
- private readonly documentAuthorityRepo: Repository,
+ public readonly documentAuthorityRepo: Repository,
+
@InjectRepository(DocumentEntity)
- private readonly documentRepo: Repository,
+ public readonly documentRepo: Repository,
+
@Inject(forwardRef(() => MessageService))
private readonly messageService: MessageService,
+
@Inject(forwardRef(() => UserService))
private readonly userService: UserService,
+
@Inject(forwardRef(() => WikiService))
private readonly wikiService: WikiService,
+
@Inject(forwardRef(() => TemplateService))
private readonly templateService: TemplateService,
+
@Inject(forwardRef(() => ViewService))
private readonly viewService: ViewService
) {
@@ -91,6 +76,15 @@ export class DocumentService {
return documents.map((doc) => instanceToPlain(doc));
}
+ /**
+ * 获取知识库首页文档
+ * @param wikiId
+ * @returns
+ */
+ public async findWikiHomeDocument(wikiId) {
+ return await this.documentRepo.findOne({ wikiId, isWikiHome: true });
+ }
+
/**
* 获取用户在指定文档的权限
* @param documentId
@@ -176,11 +170,8 @@ export class DocumentService {
*/
async addDocUser(user: OutUser, dto: DocAuthDto) {
const doc = await this.documentRepo.findOne(dto.documentId);
- if (!doc) {
- throw new HttpException('文档不存在', HttpStatus.BAD_REQUEST);
- }
-
const targetUser = await this.userService.findOne({ name: dto.userName });
+
if (!targetUser) {
throw new HttpException('用户不存在', HttpStatus.BAD_REQUEST);
}
@@ -226,16 +217,6 @@ export class DocumentService {
*/
async deleteDocUser(user: OutUser, dto: DocAuthDto): Promise {
const doc = await this.documentRepo.findOne({ id: dto.documentId });
- if (!doc) {
- throw new HttpException('文档不存在', HttpStatus.BAD_REQUEST);
- }
-
- const isCurrentUserCreator = user.id === doc.createUserId;
-
- if (!isCurrentUserCreator) {
- throw new HttpException('您无权限进行该操作', HttpStatus.FORBIDDEN);
- }
-
const targetUser = await this.userService.findOne({ name: dto.userName });
if (targetUser.id === doc.createUserId) {
@@ -249,7 +230,7 @@ export class DocumentService {
await this.messageService.notify(targetUser, {
title: `您已被移出文档「${doc.title}」`,
- message: `管理员已将您从文档「${doc.title}」移出!`,
+ message: `${user.name}已将您从文档「${doc.title}」移出!`,
url: `/wiki/${doc.wikiId}/document/${doc.id}`,
});
@@ -332,7 +313,7 @@ export class DocumentService {
const document = await this.documentRepo.save(res);
// 知识库成员权限继承
- const wikiUsers = await this.wikiService.getWikiUsers({ userId: user.id, wikiId: dto.wikiId }, true);
+ const wikiUsers = await this.wikiService.getWikiUsers(dto.wikiId);
await Promise.all([
await this.operateDocumentAuth({
@@ -376,12 +357,6 @@ export class DocumentService {
*/
async deleteDocument(user: OutUser, documentId) {
const document = await this.documentRepo.findOne(documentId);
- if (!document) {
- throw new HttpException('文档不存在', HttpStatus.NOT_FOUND);
- }
- if (document.createUserId !== user.id) {
- throw new HttpException('您不是该文档的创建者,无法删除', HttpStatus.FORBIDDEN);
- }
if (document.isWikiHome) {
throw new HttpException('该文档作为知识库首页使用,无法删除', HttpStatus.FORBIDDEN);
}
@@ -414,49 +389,11 @@ export class DocumentService {
*/
public async updateDocument(user: OutUser, documentId: string, dto: UpdateDocumentDto) {
const document = await this.documentRepo.findOne(documentId);
- if (!document) {
- throw new HttpException('文档不存在', HttpStatus.NOT_FOUND);
- }
- const authority = await this.documentAuthorityRepo.findOne({
- documentId,
- userId: user.id,
- });
- if (!authority || !authority.editable) {
- throw new HttpException('您无权编辑此文档', HttpStatus.FORBIDDEN);
- }
const res = await this.documentRepo.create({ ...document, ...dto });
const ret = await this.documentRepo.save(res);
return instanceToPlain(ret);
}
- /**
- * 获取知识库首页文档
- * @param user
- * @param wikiId
- * @returns
- */
- async getWikiHomeDocument(user: OutUser, wikiId) {
- const res = await this.documentRepo.findOne({ wikiId, isWikiHome: true });
- return instanceToPlain(res);
- }
-
- /**
- * 获取公开知识库首页文档
- * @param user
- * @param wikiId
- * @returns
- */
- async getWikiPublicHomeDocument(wikiId, userAgent) {
- const res = await this.documentRepo.findOne({ wikiId, isWikiHome: true });
- await this.viewService.create({
- userId: 'public',
- documentId: res.id,
- userAgent,
- });
- const views = await this.viewService.getDocumentTotalViews(res.id);
- return { ...instanceToPlain(res), views };
- }
-
/**
* 获取文档详情
* @param user
@@ -464,27 +401,20 @@ export class DocumentService {
* @returns
*/
public async getDocumentDetail(user: OutUser, documentId: string, userAgent) {
+ // 1. 记录访问
+ await this.viewService.create({ userId: user.id, documentId, userAgent });
+ // 2. 查询文档
const document = await this.documentRepo.findOne(documentId);
-
- if (!document) {
- throw new HttpException('文档不存在', HttpStatus.NOT_FOUND);
- }
-
+ // 3. 查询权限
const authority = await this.documentAuthorityRepo.findOne({
documentId,
userId: user.id,
});
-
- if (!authority || !authority.readable) {
- throw new HttpException('您无权查看此文档', HttpStatus.FORBIDDEN);
- }
-
- await this.viewService.create({ userId: user.id, documentId, userAgent });
+ // 4. 查询访问
const views = await this.viewService.getDocumentTotalViews(documentId);
-
+ // 5. 生成响应
const doc = instanceToPlain(document);
const createUser = await this.userService.findById(doc.createUserId);
-
return { document: { ...doc, views, createUser }, authority };
}
@@ -495,21 +425,6 @@ export class DocumentService {
* @returns
*/
public async getDocumentVersion(user: OutUser, documentId: string) {
- const document = await this.documentRepo.findOne(documentId);
-
- if (!document) {
- throw new HttpException('文档不存在', HttpStatus.NOT_FOUND);
- }
-
- const authority = await this.documentAuthorityRepo.findOne({
- documentId,
- userId: user.id,
- });
-
- if (!authority || !authority.readable) {
- throw new HttpException('您无权查看此文档', HttpStatus.FORBIDDEN);
- }
-
const data = await this.documentVersionService.getDocumentVersions(documentId);
return data;
}
@@ -520,19 +435,6 @@ export class DocumentService {
*/
async shareDocument(user: OutUser, documentId, dto: ShareDocumentDto, nextStatus = null) {
const document = await this.documentRepo.findOne(documentId);
- if (!document) {
- throw new HttpException('文档不存在', HttpStatus.NOT_FOUND);
- }
-
- const authority = await this.documentAuthorityRepo.findOne({
- documentId,
- userId: user.id,
- });
-
- if (!authority || !authority.editable) {
- throw new HttpException('您无权编辑此文档', HttpStatus.FORBIDDEN);
- }
-
nextStatus = !nextStatus
? document.status === DocumentStatus.private
? DocumentStatus.public
@@ -553,14 +455,6 @@ export class DocumentService {
async getPublicDocumentDetail(documentId, dto: ShareDocumentDto, userAgent) {
const document = await this.documentRepo.findOne(documentId);
- if (!document) {
- throw new HttpException('文档不存在', HttpStatus.NOT_FOUND);
- }
-
- if (document.status !== DocumentStatus.public) {
- throw new HttpException('私有文档,无法查看内容', HttpStatus.FORBIDDEN);
- }
-
if (document.sharePassword && !dto.sharePassword) {
throw new HttpException('输入密码后查看内容', HttpStatus.BAD_REQUEST);
}
@@ -596,21 +490,8 @@ export class DocumentService {
? await this.documentRepo.findOne(documentId)
: await this.documentRepo.findOne({ wikiId, isWikiHome: true });
- if (!document) {
- throw new HttpException('文档不存在', HttpStatus.NOT_FOUND);
- }
-
let unSortDocuments = [];
- const authority = await this.documentAuthorityRepo.findOne({
- documentId: document.id,
- userId: user.id,
- });
-
- if (!authority || !authority.readable) {
- throw new HttpException('您无权查看该文档下的子文档', HttpStatus.FORBIDDEN);
- }
-
if (document.isWikiHome) {
unSortDocuments = await this.documentRepo.find({
wikiId: document.wikiId,
@@ -661,16 +542,8 @@ export class DocumentService {
? await this.documentRepo.findOne(documentId)
: await this.documentRepo.findOne({ wikiId, isWikiHome: true });
- if (!document) {
- throw new HttpException('文档不存在', HttpStatus.NOT_FOUND);
- }
-
let unSortDocuments = [];
- if (document.status !== DocumentStatus.public) {
- throw new HttpException('私有文档,无法查看内容', HttpStatus.FORBIDDEN);
- }
-
if (document.isWikiHome) {
unSortDocuments = await this.documentRepo.find({
wikiId: document.wikiId,
@@ -717,153 +590,6 @@ export class DocumentService {
return docsWithCreateUser;
}
- /**
- * 获取知识库的文档目录
- * @param user
- * @param dto
- * @returns
- */
- public async getWikiTocs(user: OutUser, wikiId: string) {
- const ONE_DAY_TIME = 24 * 60 * 60 * 1000;
- await this.wikiService.getWikiDetail(user, wikiId);
- // @ts-ignore
- const records = await this.documentAuthorityRepo.find({
- userId: user.id,
- wikiId,
- });
-
- const ids = records.map((record) => record.documentId);
- const documents = await this.documentRepo.findByIds(ids, {
- order: { createdAt: 'ASC' },
- });
- documents.sort((a, b) => a.index - b.index);
-
- documents.forEach((doc) => {
- delete doc.state;
- });
-
- const docs = documents
- .filter((doc) => !doc.isWikiHome)
- .map((doc) => {
- const res = instanceToPlain(doc);
- res.key = res.id;
- res.label = res.title;
- return res;
- });
-
- const docsWithCreateUser = await Promise.all(
- docs.map(async (doc) => {
- const createUser = await this.userService.findById(doc.createUserId);
- return { ...doc, createUser };
- })
- );
-
- return array2tree(docsWithCreateUser);
- }
-
- /**
- * 重排知识库目录
- * @param user
- * @param wikiId
- * @param relations
- */
- public async orderWikiTocs(
- user: OutUser,
- wikiId: string,
- relations: Array<{ id: string; parentDocumentId?: string; index: number }>
- ) {
- await this.wikiService.getWikiDetail(user, wikiId);
- await Promise.all(
- relations.map(async (relation) => {
- const { id, parentDocumentId, index } = relation;
- const doc = await this.documentRepo.findOne(id);
-
- if (doc) {
- const newData = await this.documentRepo.merge(doc, {
- parentDocumentId,
- index,
- });
- await this.documentRepo.save(newData);
- }
- })
- );
- }
-
- /**
- * 获取知识库公开的文档目录
- * @param user
- * @param dto
- * @returns
- */
- public async getPublicWikiTocs(wikiId: string) {
- await this.wikiService.getPublicWikiDetail(wikiId);
- // @ts-ignore
- const unSortDocuments = await this.documentRepo.find({
- wikiId,
- status: DocumentStatus.public,
- });
-
- const documents = await this.documentRepo.findByIds(
- unSortDocuments.map((d) => d.id),
- {
- order: { createdAt: 'ASC' },
- }
- );
-
- documents.sort((a, b) => a.index - b.index);
-
- const docs = documents
- .filter((doc) => !doc.isWikiHome)
- .map((doc) => {
- const res = instanceToPlain(doc);
- res.key = res.id;
- res.label = res.title;
- return res;
- });
-
- docs.forEach((doc) => {
- delete doc.state;
- });
-
- return array2tree(docs);
- }
-
- /**
- * 获取知识库所有的文档
- * @param user
- * @param dto
- * @returns
- */
- public async getWikiDocs(user: OutUser, wikiId: string) {
- this.wikiService.getWikiDetail(user, wikiId);
- const records = await this.documentAuthorityRepo.find({
- userId: user.id,
- wikiId,
- });
- const ids = records.map((record) => record.documentId);
- const documents = await this.documentRepo.findByIds(ids);
- documents.forEach((doc) => {
- delete doc.state;
- });
-
- const docs = documents
- .filter((doc) => !doc.isWikiHome)
- .map((doc) => {
- const res = instanceToPlain(doc);
- res.key = res.id;
- return res;
- });
-
- const docsWithCreateUser = await Promise.all(
- docs.map(async (doc) => {
- const createUser = await this.userService.findById(doc.createUserId);
- return { ...doc, createUser };
- })
- );
-
- return docsWithCreateUser;
- }
-
/**
* 获取用户最近更新的10篇文档
* @param user
diff --git a/packages/server/src/services/file.service.ts b/packages/server/src/services/file.service.ts
index bb2b9b8a..4affd9ba 100644
--- a/packages/server/src/services/file.service.ts
+++ b/packages/server/src/services/file.service.ts
@@ -17,8 +17,7 @@ export class FileService {
* @param file
*/
async uploadFile(file) {
- console.log('upload', file);
- const { originalname, mimetype, size, buffer } = file;
+ const { originalname, buffer } = file;
const filename = `/${dateFormat(new Date(), 'yyyy-MM-dd')}/${uniqueid()}/${originalname}`;
const url = await this.ossClient.putFile(filename, buffer);
return url;
diff --git a/packages/server/src/services/template.service.ts b/packages/server/src/services/template.service.ts
index ff1746ba..5c414be7 100644
--- a/packages/server/src/services/template.service.ts
+++ b/packages/server/src/services/template.service.ts
@@ -12,6 +12,7 @@ export class TemplateService {
constructor(
@InjectRepository(TemplateEntity)
private readonly templateRepo: Repository,
+
@Inject(forwardRef(() => UserService))
private readonly userService: UserService
) {}
diff --git a/packages/server/src/services/user.service.ts b/packages/server/src/services/user.service.ts
index 7378701f..9c70f055 100644
--- a/packages/server/src/services/user.service.ts
+++ b/packages/server/src/services/user.service.ts
@@ -20,32 +20,21 @@ export class UserService {
constructor(
@InjectRepository(UserEntity)
private readonly userRepo: Repository,
+
private readonly confifgService: ConfigService,
+
@Inject(forwardRef(() => JwtService))
private readonly jwtService: JwtService,
+
@Inject(forwardRef(() => MessageService))
private readonly messageService: MessageService,
+
@Inject(forwardRef(() => CollectorService))
private readonly collectorService: CollectorService,
+
@Inject(forwardRef(() => WikiService))
private readonly wikiService: WikiService
- ) {
- this.createSuperAdmin();
- }
-
- private async createSuperAdmin() {
- const superadmin = this.confifgService.get('superadmin');
- if (!superadmin) return;
- if (!(await this.userRepo.findOne({ name: superadmin.name }))) {
- const res = await this.userRepo.create({
- ...superadmin,
- confirmPassword: superadmin.password,
- role: UserRole.superadmin,
- });
- await this.userRepo.save(res);
- }
- console.info('已注册超管用户,请及时修改默认密码');
- }
+ ) {}
/**
* 根据 id 查询用户
diff --git a/packages/server/src/services/wiki.service.ts b/packages/server/src/services/wiki.service.ts
index 14c3a19e..82ded160 100644
--- a/packages/server/src/services/wiki.service.ts
+++ b/packages/server/src/services/wiki.service.ts
@@ -1,13 +1,16 @@
import { Injectable, HttpException, HttpStatus, Inject, forwardRef } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
+import { instanceToPlain } from 'class-transformer';
import { WikiStatus, WikiUserRole, DocumentStatus, IPagination } from '@think/domains';
+import { array2tree } from '@helpers/tree.helper';
import { WikiEntity } from '@entities/wiki.entity';
import { WikiUserEntity } from '@entities/wiki-user.entity';
import { UserService } from '@services/user.service';
import { MessageService } from '@services/message.service';
import { CollectorService } from '@services/collector.service';
import { OutUser } from '@services/user.service';
+import { ViewService } from '@services/view.service';
import { DocumentService } from '@services/document.service';
import { WikiUserDto } from '@dtos/wiki-user.dto';
import { CreateWikiDto } from '@dtos/create-wiki.dto';
@@ -19,18 +22,36 @@ export class WikiService {
constructor(
@InjectRepository(WikiEntity)
private readonly wikiRepo: Repository,
+
@InjectRepository(WikiUserEntity)
private readonly wikiUserRepo: Repository,
+
@Inject(forwardRef(() => MessageService))
private readonly messageService: MessageService,
+
@Inject(forwardRef(() => CollectorService))
private readonly collectorService: CollectorService,
+
@Inject(forwardRef(() => DocumentService))
private readonly documentService: DocumentService,
+
@Inject(forwardRef(() => UserService))
- private readonly userService: UserService
+ private readonly userService: UserService,
+
+ @Inject(forwardRef(() => ViewService))
+ private readonly viewService: ViewService
) {}
+ /**
+ * 按 id 查取知识库
+ * @param user
+ * @param dto
+ * @returns
+ */
+ public async findById(id: string) {
+ return await this.wikiRepo.findOne(id);
+ }
+
/**
* 按 id 查取一组知识库
* @param user
@@ -42,6 +63,19 @@ export class WikiService {
return ret;
}
+ /**
+ * 获取知识库成员信息
+ * @param wikiId
+ * @param userId
+ * @returns
+ */
+ public async findWikiUser(wikiId: string, userId: string) {
+ return await this.wikiUserRepo.findOne({
+ userId,
+ wikiId,
+ });
+ }
+
/**
* 操作知识库成员(添加、修改角色)
* @param param0
@@ -133,6 +167,7 @@ export class WikiService {
}
const homeDoc = await this.getWikiHomeDocument(user, wikiId);
+
await this.documentService.operateDocumentAuth({
currentUserId: user.id,
documentId: homeDoc.id,
@@ -140,6 +175,7 @@ export class WikiService {
readable: true,
editable: dto.userRole === WikiUserRole.admin,
});
+
return this.operateWikiUser({
wikiId,
currentUserId: user.id,
@@ -177,17 +213,6 @@ export class WikiService {
* @returns
*/
async deleteWikiUser(user: OutUser, wikiId, dto: WikiUserDto): Promise {
- const currentWikiUserRole = (
- await this.wikiUserRepo.findOne({
- wikiId,
- userId: user.id,
- })
- ).userRole;
-
- if (currentWikiUserRole !== WikiUserRole.admin) {
- throw new HttpException('您无权限进行该操作', HttpStatus.FORBIDDEN);
- }
-
const targetUser = await this.userService.findOne({ name: dto.userName });
if (!targetUser) {
@@ -207,7 +232,7 @@ export class WikiService {
await this.messageService.notify(targetUser, {
title: `您已被移出知识库「${wiki.name}」`,
- message: `管理员已将您从知识库「${wiki.name}」移出!`,
+ message: `${user.name}已将您从知识库「${wiki.name}」移出!`,
url: `/wiki/${wiki.id}`,
});
@@ -218,15 +243,8 @@ export class WikiService {
* 获取知识库成员
* @param userId
* @param wikiId
- * @param internalInvoke 是否为内部调用,内部调用忽略权限检查
*/
- async getWikiUsers({ userId, wikiId }, internalInvoke = false) {
- const currenWikiUser = await this.getWikiUserDetail({ userId, wikiId });
-
- if (currenWikiUser.userRole !== WikiUserRole.admin && !internalInvoke) {
- throw new HttpException('您无权限查看', HttpStatus.FORBIDDEN);
- }
-
+ async getWikiUsers(wikiId) {
const records = await this.wikiUserRepo.find({ wikiId });
const ids = records.map((record) => record.userId);
const users = await this.userService.findByIds(ids);
@@ -382,21 +400,7 @@ export class WikiService {
* @returns
*/
async getWikiDetail(user: OutUser, wikiId: string) {
- const wikiUser = await this.wikiUserRepo.findOne({
- userId: user.id,
- wikiId,
- });
-
- if (!wikiUser) {
- throw new HttpException('您无权查看该知识库', HttpStatus.FORBIDDEN);
- }
-
const wiki = await this.wikiRepo.findOne(wikiId);
-
- if (!wiki) {
- throw new HttpException('访问的知识库不存在', HttpStatus.NOT_FOUND);
- }
-
const createUser = await this.userService.findById(wiki.createUserId);
return { ...wiki, createUser };
}
@@ -408,48 +412,8 @@ export class WikiService {
* @returns
*/
async getWikiHomeDocument(user: OutUser, wikiId) {
- await this.getWikiUserDetail({ wikiId, userId: user.id });
- return this.documentService.getWikiHomeDocument(user, wikiId);
- }
-
- /**
- * 获取公开知识库首页文档(首页文档由系统自动创建)
- * @param user
- * @param wikiId
- * @returns
- */
- async getWikiPublicHomeDocument(wikiId, userAgent) {
- const wiki = await this.wikiRepo.findOne(wikiId);
-
- if (!wiki) {
- throw new HttpException('目标知识库不存在', HttpStatus.NOT_FOUND);
- }
-
- if (wiki.status !== WikiStatus.public) {
- throw new HttpException('私有文档,无法查看内容', HttpStatus.FORBIDDEN);
- }
-
- return this.documentService.getWikiPublicHomeDocument(wikiId, userAgent);
- }
-
- /**
- * 获取公开知识库详情
- * @param wikiId
- * @returns
- */
- async getPublicWikiDetail(wikiId: string) {
- const wiki = await this.wikiRepo.findOne(wikiId);
-
- if (!wiki) {
- throw new HttpException('目标知识库不存在', HttpStatus.NOT_FOUND);
- }
-
- if (wiki.status !== WikiStatus.public) {
- throw new HttpException('私有文档,无法查看内容', HttpStatus.FORBIDDEN);
- }
-
- const createUser = await this.userService.findById(wiki.createUserId);
- return { ...wiki, createUser };
+ const res = await this.documentService.documentRepo.findOne({ wikiId, isWikiHome: true });
+ return instanceToPlain(res);
}
/**
@@ -460,15 +424,6 @@ export class WikiService {
* @returns
*/
async updateWiki(user: OutUser, wikiId, dto: UpdateWikiDto) {
- const workspaceUser = await this.getWikiUserDetail({
- wikiId,
- userId: user.id,
- });
-
- if (workspaceUser.userRole !== WikiUserRole.admin) {
- throw new HttpException('您没有权限更新该知识库信息', HttpStatus.FORBIDDEN);
- }
-
const oldData = await this.wikiRepo.findOne(wikiId);
if (!oldData) {
throw new HttpException('目标知识库不存在', HttpStatus.NOT_FOUND);
@@ -488,10 +443,6 @@ export class WikiService {
* @returns
*/
async deleteWiki(user: OutUser, wikiId) {
- const wikiUser = await this.getWikiUserDetail({ wikiId, userId: user.id });
- if (wikiUser.userRole !== WikiUserRole.admin) {
- throw new HttpException('您没有权限操作该知识库', HttpStatus.FORBIDDEN);
- }
const wiki = await this.wikiRepo.findOne(wikiId);
if (user.id !== wiki.createUserId) {
throw new HttpException('您不是创建者,无法删除该知识库', HttpStatus.FORBIDDEN);
@@ -565,7 +516,38 @@ export class WikiService {
* @returns
*/
async getWikiTocs(user: OutUser, wikiId) {
- return await this.documentService.getWikiTocs(user, wikiId);
+ const records = await this.documentService.documentAuthorityRepo.find({
+ userId: user.id,
+ wikiId,
+ });
+
+ const ids = records.map((record) => record.documentId);
+ const documents = await this.documentService.documentRepo.findByIds(ids, {
+ order: { createdAt: 'ASC' },
+ });
+ documents.sort((a, b) => a.index - b.index);
+
+ documents.forEach((doc) => {
+ delete doc.state;
+ });
+
+ const docs = documents
+ .filter((doc) => !doc.isWikiHome)
+ .map((doc) => {
+ const res = instanceToPlain(doc);
+ res.key = res.id;
+ res.label = res.title;
+ return res;
+ });
+
+ const docsWithCreateUser = await Promise.all(
+ docs.map(async (doc) => {
+ const createUser = await this.userService.findById(doc.createUserId);
+ return { ...doc, createUser };
+ })
+ );
+
+ return array2tree(docsWithCreateUser);
}
/**
@@ -574,22 +556,59 @@ export class WikiService {
* @param wikiId
* @param relations
*/
- public async orderWikiTocs(
- user: OutUser,
- wikiId: string,
- relations: Array<{ id: string; parentDocumentId?: string; index: number }>
- ) {
- return await this.documentService.orderWikiTocs(user, wikiId, relations);
+ public async orderWikiTocs(relations: Array<{ id: string; parentDocumentId?: string; index: number }>) {
+ await Promise.all(
+ relations.map(async (relation) => {
+ const { id, parentDocumentId, index } = relation;
+ const doc = await this.documentService.documentRepo.findOne(id);
+
+ if (doc) {
+ const newData = await this.documentService.documentRepo.merge(doc, {
+ parentDocumentId,
+ index,
+ });
+ await this.documentService.documentRepo.save(newData);
+ }
+ })
+ );
}
/**
- * 获取知识库目录
+ * 获取知识库所有文档(无结构嵌套)
* @param user
* @param wikiId
* @returns
*/
async getWikiDocs(user: OutUser, wikiId) {
- return await this.documentService.getWikiDocs(user, wikiId);
+ // 通过文档成员表获取当前用户可查阅的所有文档
+ const records = await this.documentService.documentAuthorityRepo.find({
+ userId: user.id,
+ wikiId,
+ });
+
+ const ids = records.map((record) => record.documentId);
+
+ const documents = await this.documentService.documentRepo.findByIds(ids);
+ documents.forEach((doc) => {
+ delete doc.state;
+ });
+
+ const docs = documents
+ .filter((doc) => !doc.isWikiHome)
+ .map((doc) => {
+ const res = instanceToPlain(doc);
+ res.key = res.id;
+ return res;
+ });
+
+ const docsWithCreateUser = await Promise.all(
+ docs.map(async (doc) => {
+ const createUser = await this.userService.findById(doc.createUserId);
+ return { ...doc, createUser };
+ })
+ );
+
+ return docsWithCreateUser;
}
/**
@@ -598,11 +617,67 @@ export class WikiService {
* @returns
*/
async getPublicWikiTocs(wikiId) {
- return await this.documentService.getPublicWikiTocs(wikiId);
+ const unSortDocuments = await this.documentService.documentRepo.find({
+ wikiId,
+ status: DocumentStatus.public,
+ });
+
+ const documents = await this.documentService.documentRepo.findByIds(
+ unSortDocuments.map((d) => d.id),
+ {
+ order: { createdAt: 'ASC' },
+ }
+ );
+
+ documents.sort((a, b) => a.index - b.index);
+
+ const docs = documents
+ .filter((doc) => !doc.isWikiHome)
+ .map((doc) => {
+ const res = instanceToPlain(doc);
+ res.key = res.id;
+ res.label = res.title;
+ return res;
+ });
+
+ docs.forEach((doc) => {
+ delete doc.state;
+ });
+
+ return array2tree(docs);
}
/**
- * 获取当前用户所有知识库
+ * 获取公开知识库首页文档(首页文档由系统自动创建)
+ * @param user
+ * @param wikiId
+ * @returns
+ */
+ async getPublicWikiHomeDocument(wikiId, userAgent) {
+ const res = await this.documentService.documentRepo.findOne({ wikiId, isWikiHome: true });
+
+ await this.viewService.create({
+ userId: 'public',
+ documentId: res.id,
+ userAgent,
+ });
+ const views = await this.viewService.getDocumentTotalViews(res.id);
+ return { ...instanceToPlain(res), views };
+ }
+
+ /**
+ * 获取公开知识库详情
+ * @param wikiId
+ * @returns
+ */
+ async getPublicWikiDetail(wikiId: string) {
+ const wiki = await this.wikiRepo.findOne(wikiId);
+ const createUser = await this.userService.findById(wiki.createUserId);
+ return { ...wiki, createUser };
+ }
+
+ /**
+ * 获取所有公开知识库
* @param user
* @param pagination
* @returns