From 49141c6566f3afd6141e1d556eece1058ce949f0 Mon Sep 17 00:00:00 2001 From: fantasticit Date: Sun, 22 May 2022 23:40:22 +0800 Subject: [PATCH 1/6] feat: use cookie to authentication --- packages/server/package.json | 2 ++ .../server/src/controllers/user.controller.ts | 16 ++++++++++++--- .../server/src/controllers/wiki.controller.ts | 4 ++++ .../server/src/guard/document-auth.guard.ts | 8 +------- packages/server/src/guard/wiki-user.guard.ts | 9 +-------- packages/server/src/main.ts | 9 ++++++++- packages/server/src/modules/user.module.ts | 9 ++++++++- packages/server/src/services/user.service.ts | 4 ++-- .../src/transforms/http-response.transform.ts | 20 +++++++++---------- 9 files changed, 49 insertions(+), 32 deletions(-) diff --git a/packages/server/package.json b/packages/server/package.json index 35ca5336..04def600 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -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,6 +65,7 @@ "@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", diff --git a/packages/server/src/controllers/user.controller.ts b/packages/server/src/controllers/user.controller.ts index c39b1f5e..9b7cdf36 100644 --- a/packages/server/src/controllers/user.controller.ts +++ b/packages/server/src/controllers/user.controller.ts @@ -12,10 +12,13 @@ import { Patch, Post, Request, + Res, UseGuards, UseInterceptors, } from '@nestjs/common'; import { UserService } from '@services/user.service'; +import { wrapResponse } from '@transforms/http-response.transform'; +import { Response as ExpressResponse } from 'express'; @Controller('user') export class UserController { @@ -31,9 +34,16 @@ export class UserController { @UseInterceptors(ClassSerializerInterceptor) @Post('login') @HttpCode(HttpStatus.OK) - async login(@Body() user: LoginUserDto) { - const res = await this.userService.login(user); - return res; + async login(@Body() user: LoginUserDto, @Res({ passthrough: true }) response: ExpressResponse) { + const { user: data, token } = await this.userService.login(user); + response.cookie('token', token, { httpOnly: true, sameSite: 'none', secure: true }); + return response.send(wrapResponse({ data: { ...data, token }, statusCode: HttpStatus.OK })); + } + + @Get('logout') + async logout(@Res({ passthrough: true }) response: ExpressResponse) { + response.cookie('token', '', { expires: new Date() }); + return; } @UseInterceptors(ClassSerializerInterceptor) diff --git a/packages/server/src/controllers/wiki.controller.ts b/packages/server/src/controllers/wiki.controller.ts index 8955fa9f..feee4c08 100644 --- a/packages/server/src/controllers/wiki.controller.ts +++ b/packages/server/src/controllers/wiki.controller.ts @@ -253,6 +253,10 @@ export class WikiController { @UseGuards(WikiUserRoleGuard) @UseGuards(JwtGuard) async getWikiTocs(@Request() req, @Param('id') wikiId) { + const sleep = (v) => { + return new Promise((r) => setTimeout(r, v * 1000)); + }; + await sleep(4); return await this.wikiService.getWikiTocs(req.user, wikiId); } diff --git a/packages/server/src/guard/document-auth.guard.ts b/packages/server/src/guard/document-auth.guard.ts index 399eff43..c9278833 100644 --- a/packages/server/src/guard/document-auth.guard.ts +++ b/packages/server/src/guard/document-auth.guard.ts @@ -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; diff --git a/packages/server/src/guard/wiki-user.guard.ts b/packages/server/src/guard/wiki-user.guard.ts index 14124254..83f7ef60 100644 --- a/packages/server/src/guard/wiki-user.guard.ts +++ b/packages/server/src/guard/wiki-user.guard.ts @@ -23,15 +23,8 @@ export class WikiUserRoleGuard implements CanActivate { 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 token = request?.cookies['token']; const user = this.jwtService.decode(token) as IUser; if (!user) { diff --git a/packages/server/src/main.ts b/packages/server/src/main.ts index ea84695b..c0584762 100644 --- a/packages/server/src/main.ts +++ b/packages/server/src/main.ts @@ -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'; @@ -18,7 +19,13 @@ async function bootstrap() { const config = app.get(ConfigService); const port = config.get('server.port') || 5002; - app.enableCors(); + app.enableCors({ + // TODO: fixme + origin: 'http://localhost:5001', + methods: 'GET,HEAD,PUT,PATCH,POST,DELETE,OPTIONS', + credentials: true, + }); + app.use(cookieParser()); app.use(compression()); app.use(helmet()); app.use(express.json()); diff --git a/packages/server/src/modules/user.module.ts b/packages/server/src/modules/user.module.ts index 9765e76f..3ac5e6bd 100644 --- a/packages/server/src/modules/user.module.ts +++ b/packages/server/src/modules/user.module.ts @@ -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; + }, + ]), }); } diff --git a/packages/server/src/services/user.service.ts b/packages/server/src/services/user.service.ts index 6d70baf1..809b041f 100644 --- a/packages/server/src/services/user.service.ts +++ b/packages/server/src/services/user.service.ts @@ -111,7 +111,7 @@ export class UserService { * @param user * @returns */ - async login(user: LoginUserDto): Promise { + async login(user: LoginUserDto): Promise<{ user: OutUser; token: string }> { const { name, password } = user; const existUser = await this.userRepo.findOne({ where: { name } }); @@ -125,7 +125,7 @@ export class UserService { const res = instanceToPlain(existUser) as OutUser; const token = this.jwtService.sign(res); - return Object.assign(res, { token }); + return { user: res, token }; } async validateUser(user: UserEntity) { diff --git a/packages/server/src/transforms/http-response.transform.ts b/packages/server/src/transforms/http-response.transform.ts index 9042398a..bb55292f 100644 --- a/packages/server/src/transforms/http-response.transform.ts +++ b/packages/server/src/transforms/http-response.transform.ts @@ -6,6 +6,15 @@ interface Response { data: T; } +export function wrapResponse({ statusCode, data }) { + return { + statusCode, + message: null, + success: true, + data, + }; +} + @Injectable() export class HttpResponseTransformInterceptor implements NestInterceptor> { intercept(context: ExecutionContext, next: CallHandler): Observable> { @@ -13,17 +22,8 @@ export class HttpResponseTransformInterceptor implements NestInterceptor { 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 }); }) ); } From 132c8ccd049591e17f1d209fbcb0036c3d9f04a1 Mon Sep 17 00:00:00 2001 From: fantasticit Date: Sun, 22 May 2022 23:40:22 +0800 Subject: [PATCH 2/6] feat: use cookie to authentication --- packages/server/package.json | 2 ++ .../server/src/controllers/user.controller.ts | 16 ++++++++++++--- .../server/src/controllers/wiki.controller.ts | 4 ++++ .../server/src/guard/document-auth.guard.ts | 8 +------- packages/server/src/guard/wiki-user.guard.ts | 9 +-------- packages/server/src/main.ts | 9 ++++++++- packages/server/src/modules/user.module.ts | 9 ++++++++- packages/server/src/services/user.service.ts | 4 ++-- .../src/transforms/http-response.transform.ts | 20 +++++++++---------- 9 files changed, 49 insertions(+), 32 deletions(-) diff --git a/packages/server/package.json b/packages/server/package.json index 35ca5336..04def600 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -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,6 +65,7 @@ "@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", diff --git a/packages/server/src/controllers/user.controller.ts b/packages/server/src/controllers/user.controller.ts index c39b1f5e..9b7cdf36 100644 --- a/packages/server/src/controllers/user.controller.ts +++ b/packages/server/src/controllers/user.controller.ts @@ -12,10 +12,13 @@ import { Patch, Post, Request, + Res, UseGuards, UseInterceptors, } from '@nestjs/common'; import { UserService } from '@services/user.service'; +import { wrapResponse } from '@transforms/http-response.transform'; +import { Response as ExpressResponse } from 'express'; @Controller('user') export class UserController { @@ -31,9 +34,16 @@ export class UserController { @UseInterceptors(ClassSerializerInterceptor) @Post('login') @HttpCode(HttpStatus.OK) - async login(@Body() user: LoginUserDto) { - const res = await this.userService.login(user); - return res; + async login(@Body() user: LoginUserDto, @Res({ passthrough: true }) response: ExpressResponse) { + const { user: data, token } = await this.userService.login(user); + response.cookie('token', token, { httpOnly: true, sameSite: 'none', secure: true }); + return response.send(wrapResponse({ data: { ...data, token }, statusCode: HttpStatus.OK })); + } + + @Get('logout') + async logout(@Res({ passthrough: true }) response: ExpressResponse) { + response.cookie('token', '', { expires: new Date() }); + return; } @UseInterceptors(ClassSerializerInterceptor) diff --git a/packages/server/src/controllers/wiki.controller.ts b/packages/server/src/controllers/wiki.controller.ts index 8955fa9f..feee4c08 100644 --- a/packages/server/src/controllers/wiki.controller.ts +++ b/packages/server/src/controllers/wiki.controller.ts @@ -253,6 +253,10 @@ export class WikiController { @UseGuards(WikiUserRoleGuard) @UseGuards(JwtGuard) async getWikiTocs(@Request() req, @Param('id') wikiId) { + const sleep = (v) => { + return new Promise((r) => setTimeout(r, v * 1000)); + }; + await sleep(4); return await this.wikiService.getWikiTocs(req.user, wikiId); } diff --git a/packages/server/src/guard/document-auth.guard.ts b/packages/server/src/guard/document-auth.guard.ts index 399eff43..c9278833 100644 --- a/packages/server/src/guard/document-auth.guard.ts +++ b/packages/server/src/guard/document-auth.guard.ts @@ -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; diff --git a/packages/server/src/guard/wiki-user.guard.ts b/packages/server/src/guard/wiki-user.guard.ts index 14124254..83f7ef60 100644 --- a/packages/server/src/guard/wiki-user.guard.ts +++ b/packages/server/src/guard/wiki-user.guard.ts @@ -23,15 +23,8 @@ export class WikiUserRoleGuard implements CanActivate { 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 token = request?.cookies['token']; const user = this.jwtService.decode(token) as IUser; if (!user) { diff --git a/packages/server/src/main.ts b/packages/server/src/main.ts index ea84695b..c0584762 100644 --- a/packages/server/src/main.ts +++ b/packages/server/src/main.ts @@ -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'; @@ -18,7 +19,13 @@ async function bootstrap() { const config = app.get(ConfigService); const port = config.get('server.port') || 5002; - app.enableCors(); + app.enableCors({ + // TODO: fixme + origin: 'http://localhost:5001', + methods: 'GET,HEAD,PUT,PATCH,POST,DELETE,OPTIONS', + credentials: true, + }); + app.use(cookieParser()); app.use(compression()); app.use(helmet()); app.use(express.json()); diff --git a/packages/server/src/modules/user.module.ts b/packages/server/src/modules/user.module.ts index 9765e76f..3ac5e6bd 100644 --- a/packages/server/src/modules/user.module.ts +++ b/packages/server/src/modules/user.module.ts @@ -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; + }, + ]), }); } diff --git a/packages/server/src/services/user.service.ts b/packages/server/src/services/user.service.ts index 6d70baf1..809b041f 100644 --- a/packages/server/src/services/user.service.ts +++ b/packages/server/src/services/user.service.ts @@ -111,7 +111,7 @@ export class UserService { * @param user * @returns */ - async login(user: LoginUserDto): Promise { + async login(user: LoginUserDto): Promise<{ user: OutUser; token: string }> { const { name, password } = user; const existUser = await this.userRepo.findOne({ where: { name } }); @@ -125,7 +125,7 @@ export class UserService { const res = instanceToPlain(existUser) as OutUser; const token = this.jwtService.sign(res); - return Object.assign(res, { token }); + return { user: res, token }; } async validateUser(user: UserEntity) { diff --git a/packages/server/src/transforms/http-response.transform.ts b/packages/server/src/transforms/http-response.transform.ts index 9042398a..bb55292f 100644 --- a/packages/server/src/transforms/http-response.transform.ts +++ b/packages/server/src/transforms/http-response.transform.ts @@ -6,6 +6,15 @@ interface Response { data: T; } +export function wrapResponse({ statusCode, data }) { + return { + statusCode, + message: null, + success: true, + data, + }; +} + @Injectable() export class HttpResponseTransformInterceptor implements NestInterceptor> { intercept(context: ExecutionContext, next: CallHandler): Observable> { @@ -13,17 +22,8 @@ export class HttpResponseTransformInterceptor implements NestInterceptor { 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 }); }) ); } From a3cce12243b575a93b9088ad297397ebe4c5dc92 Mon Sep 17 00:00:00 2001 From: fantasticit Date: Mon, 23 May 2022 11:37:22 +0800 Subject: [PATCH 3/6] server: fix set cookie --- .../server/src/controllers/user.controller.ts | 5 ++- packages/server/src/modules/user.module.ts | 2 +- pnpm-lock.yaml | 36 ++++++++++--------- 3 files changed, 22 insertions(+), 21 deletions(-) diff --git a/packages/server/src/controllers/user.controller.ts b/packages/server/src/controllers/user.controller.ts index 9b7cdf36..9fe782b1 100644 --- a/packages/server/src/controllers/user.controller.ts +++ b/packages/server/src/controllers/user.controller.ts @@ -17,7 +17,6 @@ import { UseInterceptors, } from '@nestjs/common'; import { UserService } from '@services/user.service'; -import { wrapResponse } from '@transforms/http-response.transform'; import { Response as ExpressResponse } from 'express'; @Controller('user') @@ -36,8 +35,8 @@ export class UserController { @HttpCode(HttpStatus.OK) async login(@Body() user: LoginUserDto, @Res({ passthrough: true }) response: ExpressResponse) { const { user: data, token } = await this.userService.login(user); - response.cookie('token', token, { httpOnly: true, sameSite: 'none', secure: true }); - return response.send(wrapResponse({ data: { ...data, token }, statusCode: HttpStatus.OK })); + response.cookie('token', token, { httpOnly: true, secure: true, sameSite: 'lax' }); + return { ...data, token }; } @Get('logout') diff --git a/packages/server/src/modules/user.module.ts b/packages/server/src/modules/user.module.ts index 3ac5e6bd..50bde5be 100644 --- a/packages/server/src/modules/user.module.ts +++ b/packages/server/src/modules/user.module.ts @@ -30,7 +30,7 @@ export class JwtStrategy extends PassportStrategy(Strategy) { secretOrKey: jwtConfig.secretkey, jwtFromRequest: ExtractJwt.fromExtractors([ (request: RequestType) => { - const token = request?.cookies['token']; + const token = request?.cookies?.token; return token; }, ]), diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 22cb56a2..952e6b75 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -236,7 +236,7 @@ importers: eslint: 8.14.0 eslint-config-prettier: 8.5.0_eslint@8.14.0 eslint-plugin-import: 2.26.0_eslint@8.14.0 - eslint-plugin-prettier: 4.0.0_74ebb802163a9b4fa8f89d76ed02f62a + eslint-plugin-prettier: 4.0.0_740be41c8168d0cc214a306089357ad0 eslint-plugin-react: 7.29.4_eslint@8.14.0 eslint-plugin-react-hooks: 4.5.0_eslint@8.14.0 eslint-plugin-simple-import-sort: 7.0.0_eslint@8.14.0 @@ -280,6 +280,7 @@ 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 @@ -292,6 +293,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 +350,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,6 +378,7 @@ 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 @@ -3101,6 +3105,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 @@ -4684,6 +4694,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 @@ -5397,22 +5415,6 @@ packages: prettier-linter-helpers: 1.0.0 dev: true - /eslint-plugin-prettier/4.0.0_74ebb802163a9b4fa8f89d76ed02f62a: - resolution: {integrity: sha512-98MqmCJ7vJodoQK359bqQWaxOE0CS8paAz/GgjaZLyex4TTk3g9HugoO89EqWCrFiOqn9EVvcoo7gZzONCWVwQ==} - engines: {node: '>=6.0.0'} - peerDependencies: - eslint: '>=7.28.0' - eslint-config-prettier: '*' - prettier: '>=2.0.0' - peerDependenciesMeta: - eslint-config-prettier: - optional: true - dependencies: - eslint: 8.14.0 - eslint-config-prettier: 8.5.0_eslint@8.14.0 - prettier-linter-helpers: 1.0.0 - dev: true - /eslint-plugin-react-hooks/4.5.0_eslint@8.14.0: resolution: {integrity: sha512-8k1gRt7D7h03kd+SAAlzXkQwWK22BnK6GKZG+FJA6BAGy22CFvl8kCIXKpVux0cCxMWDQUPqSok0LKaZ0aOcCw==} engines: {node: '>=10'} From 55f227fae0ea645ea6fc9e794137cba5aa7007ad Mon Sep 17 00:00:00 2001 From: fantasticit Date: Mon, 23 May 2022 13:05:59 +0800 Subject: [PATCH 4/6] refactor: add api definition in domain --- packages/domains/lib/api/collector.d.ts | 49 +++++ packages/domains/lib/api/collector.js | 37 ++++ packages/domains/lib/api/comment.d.ts | 35 +++ packages/domains/lib/api/comment.js | 37 ++++ packages/domains/lib/api/document.d.ts | 123 +++++++++++ packages/domains/lib/api/document.js | 125 +++++++++++ packages/domains/lib/api/file.d.ts | 10 + packages/domains/lib/api/file.js | 13 ++ packages/domains/lib/api/index.d.ts | 8 + packages/domains/lib/api/index.js | 20 ++ packages/domains/lib/api/message.d.ts | 35 +++ packages/domains/lib/api/message.js | 37 ++++ packages/domains/lib/api/template.d.ts | 51 +++++ packages/domains/lib/api/template.js | 53 +++++ packages/domains/lib/api/user.d.ts | 42 ++++ packages/domains/lib/api/user.js | 45 ++++ packages/domains/lib/api/wiki.d.ts | 163 ++++++++++++++ packages/domains/lib/api/wiki.js | 165 ++++++++++++++ packages/domains/lib/index.d.ts | 1 + packages/domains/lib/index.js | 1 + packages/domains/src/api/collector.ts | 54 +++++ packages/domains/src/api/comment.ts | 39 ++++ packages/domains/src/api/document.ts | 138 ++++++++++++ packages/domains/src/api/file.ts | 10 + packages/domains/src/api/index.ts | 8 + packages/domains/src/api/message.ts | 39 ++++ packages/domains/src/api/template.ts | 57 +++++ packages/domains/src/api/user.ts | 48 +++++ packages/domains/src/api/wiki.ts | 183 ++++++++++++++++ packages/domains/src/index.ts | 1 + .../src/controllers/collector.controller.ts | 36 +++- .../src/controllers/comment.controller.ts | 23 +- .../src/controllers/document.controller.ts | 154 ++++++------- .../server/src/controllers/file.controller.ts | 3 +- .../src/controllers/message.controller.ts | 21 +- .../src/controllers/template.controller.ts | 63 ++++-- .../server/src/controllers/user.controller.ts | 40 ++-- .../server/src/controllers/wiki.controller.ts | 204 +++++++++--------- pnpm-lock.yaml | 18 +- 39 files changed, 1956 insertions(+), 233 deletions(-) create mode 100644 packages/domains/lib/api/collector.d.ts create mode 100644 packages/domains/lib/api/collector.js create mode 100644 packages/domains/lib/api/comment.d.ts create mode 100644 packages/domains/lib/api/comment.js create mode 100644 packages/domains/lib/api/document.d.ts create mode 100644 packages/domains/lib/api/document.js create mode 100644 packages/domains/lib/api/file.d.ts create mode 100644 packages/domains/lib/api/file.js create mode 100644 packages/domains/lib/api/index.d.ts create mode 100644 packages/domains/lib/api/index.js create mode 100644 packages/domains/lib/api/message.d.ts create mode 100644 packages/domains/lib/api/message.js create mode 100644 packages/domains/lib/api/template.d.ts create mode 100644 packages/domains/lib/api/template.js create mode 100644 packages/domains/lib/api/user.d.ts create mode 100644 packages/domains/lib/api/user.js create mode 100644 packages/domains/lib/api/wiki.d.ts create mode 100644 packages/domains/lib/api/wiki.js create mode 100644 packages/domains/src/api/collector.ts create mode 100644 packages/domains/src/api/comment.ts create mode 100644 packages/domains/src/api/document.ts create mode 100644 packages/domains/src/api/file.ts create mode 100644 packages/domains/src/api/index.ts create mode 100644 packages/domains/src/api/message.ts create mode 100644 packages/domains/src/api/template.ts create mode 100644 packages/domains/src/api/user.ts create mode 100644 packages/domains/src/api/wiki.ts diff --git a/packages/domains/lib/api/collector.d.ts b/packages/domains/lib/api/collector.d.ts new file mode 100644 index 00000000..09126142 --- /dev/null +++ b/packages/domains/lib/api/collector.d.ts @@ -0,0 +1,49 @@ +import { IDocument, IWiki, CollectType } from '../models'; +export declare type CollectorApiTypeDefinition = { + toggle: { + request: { + targetId: IDocument['id'] | IWiki['id']; + type: CollectType; + }; + }; + check: { + request: { + targetId: IDocument['id'] | IWiki['id']; + type: CollectType; + }; + }; +}; +export declare const CollectorApiDefinition: { + /** + * 收藏(或取消收藏) + */ + toggle: { + method: "Post"; + server: "toggle"; + client: () => string; + }; + /** + * 检测是否收藏 + */ + check: { + method: "Post"; + server: "check"; + client: () => string; + }; + /** + * 获取收藏的知识库 + */ + wikis: { + method: "Post"; + server: "wikis"; + client: () => string; + }; + /** + * 获取收藏的文档 + */ + documents: { + method: "Post"; + server: "documents"; + client: () => string; + }; +}; diff --git a/packages/domains/lib/api/collector.js b/packages/domains/lib/api/collector.js new file mode 100644 index 00000000..6829e4dd --- /dev/null +++ b/packages/domains/lib/api/collector.js @@ -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: 'Post', + server: 'wikis', + client: function () { return '/collector/wikis'; } + }, + /** + * 获取收藏的文档 + */ + documents: { + method: 'Post', + server: 'documents', + client: function () { return '/collector/documents'; } + } +}; diff --git a/packages/domains/lib/api/comment.d.ts b/packages/domains/lib/api/comment.d.ts new file mode 100644 index 00000000..9e78ee33 --- /dev/null +++ b/packages/domains/lib/api/comment.d.ts @@ -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; + }; +}; diff --git a/packages/domains/lib/api/comment.js b/packages/domains/lib/api/comment.js new file mode 100644 index 00000000..0522cf52 --- /dev/null +++ b/packages/domains/lib/api/comment.js @@ -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); } + } +}; diff --git a/packages/domains/lib/api/document.d.ts b/packages/domains/lib/api/document.d.ts new file mode 100644 index 00000000..902fb6c1 --- /dev/null +++ b/packages/domains/lib/api/document.d.ts @@ -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: "Get"; + 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: "Get"; + server: "public/detail/:id"; + client: (id: IDocument['id']) => string; + }; + /** + * 获取公开文档的子文档 + */ + getPublicChildren: { + method: "Get"; + server: "public/children"; + client: () => string; + }; +}; diff --git a/packages/domains/lib/api/document.js b/packages/domains/lib/api/document.js new file mode 100644 index 00000000..e99e3dba --- /dev/null +++ b/packages/domains/lib/api/document.js @@ -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: 'Get', + 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: 'Get', + server: 'public/detail/:id', + client: function (id) { return "/document/public/detail/".concat(id); } + }, + /** + * 获取公开文档的子文档 + */ + getPublicChildren: { + method: 'Get', + server: 'public/children', + client: function () { return "/document/public/children"; } + } +}; diff --git a/packages/domains/lib/api/file.d.ts b/packages/domains/lib/api/file.d.ts new file mode 100644 index 00000000..ad1e5493 --- /dev/null +++ b/packages/domains/lib/api/file.d.ts @@ -0,0 +1,10 @@ +export declare const FileApiDefinition: { + /** + * 上传文件 + */ + upload: { + method: "Post"; + server: "upload"; + client: () => string; + }; +}; diff --git a/packages/domains/lib/api/file.js b/packages/domains/lib/api/file.js new file mode 100644 index 00000000..e0d8e992 --- /dev/null +++ b/packages/domains/lib/api/file.js @@ -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'; } + } +}; diff --git a/packages/domains/lib/api/index.d.ts b/packages/domains/lib/api/index.d.ts new file mode 100644 index 00000000..8cc7ffad --- /dev/null +++ b/packages/domains/lib/api/index.d.ts @@ -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'; diff --git a/packages/domains/lib/api/index.js b/packages/domains/lib/api/index.js new file mode 100644 index 00000000..2df03c3a --- /dev/null +++ b/packages/domains/lib/api/index.js @@ -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); diff --git a/packages/domains/lib/api/message.d.ts b/packages/domains/lib/api/message.d.ts new file mode 100644 index 00000000..0a36c842 --- /dev/null +++ b/packages/domains/lib/api/message.d.ts @@ -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; + }; +}; diff --git a/packages/domains/lib/api/message.js b/packages/domains/lib/api/message.js new file mode 100644 index 00000000..1e020045 --- /dev/null +++ b/packages/domains/lib/api/message.js @@ -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); } + } +}; diff --git a/packages/domains/lib/api/template.d.ts b/packages/domains/lib/api/template.d.ts new file mode 100644 index 00000000..e62917cc --- /dev/null +++ b/packages/domains/lib/api/template.d.ts @@ -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; + }; +}; diff --git a/packages/domains/lib/api/template.js b/packages/domains/lib/api/template.js new file mode 100644 index 00000000..6a6a41d5 --- /dev/null +++ b/packages/domains/lib/api/template.js @@ -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); } + } +}; diff --git a/packages/domains/lib/api/user.d.ts b/packages/domains/lib/api/user.d.ts new file mode 100644 index 00000000..5b9d6cff --- /dev/null +++ b/packages/domains/lib/api/user.d.ts @@ -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; + }; +}; diff --git a/packages/domains/lib/api/user.js b/packages/domains/lib/api/user.js new file mode 100644 index 00000000..7d0648e6 --- /dev/null +++ b/packages/domains/lib/api/user.js @@ -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"; } + } +}; diff --git a/packages/domains/lib/api/wiki.d.ts b/packages/domains/lib/api/wiki.d.ts new file mode 100644 index 00000000..42d630e4 --- /dev/null +++ b/packages/domains/lib/api/wiki.d.ts @@ -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: (id: IWiki['id']) => string; + }; +}; diff --git a/packages/domains/lib/api/wiki.js b/packages/domains/lib/api/wiki.js new file mode 100644 index 00000000..cecaea48 --- /dev/null +++ b/packages/domains/lib/api/wiki.js @@ -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 (id) { return "/wiki/public/wikis"; } + } +}; diff --git a/packages/domains/lib/index.d.ts b/packages/domains/lib/index.d.ts index beca895c..60ab18b1 100644 --- a/packages/domains/lib/index.d.ts +++ b/packages/domains/lib/index.d.ts @@ -1,2 +1,3 @@ export * from './models'; export * from './util'; +export * from './api'; diff --git a/packages/domains/lib/index.js b/packages/domains/lib/index.js index f4a7e2ca..7cb1843b 100644 --- a/packages/domains/lib/index.js +++ b/packages/domains/lib/index.js @@ -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); diff --git a/packages/domains/src/api/collector.ts b/packages/domains/src/api/collector.ts new file mode 100644 index 00000000..d9862546 --- /dev/null +++ b/packages/domains/src/api/collector.ts @@ -0,0 +1,54 @@ +import { IDocument, IWiki, CollectType } from '../models'; + +export type CollectorApiTypeDefinition = { + toggle: { + request: { + targetId: IDocument['id'] | IWiki['id']; + type: CollectType; + }; + }; + check: { + request: { + targetId: IDocument['id'] | IWiki['id']; + type: CollectType; + }; + }; +}; + +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: 'Post' as const, + server: 'wikis' as const, + client: () => '/collector/wikis', + }, + + /** + * 获取收藏的文档 + */ + documents: { + method: 'Post' as const, + server: 'documents' as const, + client: () => '/collector/documents', + }, +}; diff --git a/packages/domains/src/api/comment.ts b/packages/domains/src/api/comment.ts new file mode 100644 index 00000000..40b8842d --- /dev/null +++ b/packages/domains/src/api/comment.ts @@ -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}`, + }, +}; diff --git a/packages/domains/src/api/document.ts b/packages/domains/src/api/document.ts new file mode 100644 index 00000000..21e7ac6a --- /dev/null +++ b/packages/domains/src/api/document.ts @@ -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: 'Get' 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: 'Get' as const, + server: 'public/detail/:id' as const, + client: (id: IDocument['id']) => `/document/public/detail/${id}`, + }, + + /** + * 获取公开文档的子文档 + */ + getPublicChildren: { + method: 'Get' as const, + server: 'public/children' as const, + client: () => `/document/public/children`, + }, +}; diff --git a/packages/domains/src/api/file.ts b/packages/domains/src/api/file.ts new file mode 100644 index 00000000..6a7e28c1 --- /dev/null +++ b/packages/domains/src/api/file.ts @@ -0,0 +1,10 @@ +export const FileApiDefinition = { + /** + * 上传文件 + */ + upload: { + method: 'Post' as const, + server: 'upload' as const, + client: () => '/file/upload', + }, +}; diff --git a/packages/domains/src/api/index.ts b/packages/domains/src/api/index.ts new file mode 100644 index 00000000..8cc7ffad --- /dev/null +++ b/packages/domains/src/api/index.ts @@ -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'; diff --git a/packages/domains/src/api/message.ts b/packages/domains/src/api/message.ts new file mode 100644 index 00000000..821ccace --- /dev/null +++ b/packages/domains/src/api/message.ts @@ -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}`, + }, +}; diff --git a/packages/domains/src/api/template.ts b/packages/domains/src/api/template.ts new file mode 100644 index 00000000..ca08ada6 --- /dev/null +++ b/packages/domains/src/api/template.ts @@ -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}`, + }, +}; diff --git a/packages/domains/src/api/user.ts b/packages/domains/src/api/user.ts new file mode 100644 index 00000000..b6f8332b --- /dev/null +++ b/packages/domains/src/api/user.ts @@ -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`, + }, +}; diff --git a/packages/domains/src/api/wiki.ts b/packages/domains/src/api/wiki.ts new file mode 100644 index 00000000..7ecbc3cc --- /dev/null +++ b/packages/domains/src/api/wiki.ts @@ -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: (id: IWiki['id']) => `/wiki/public/wikis`, + }, +}; diff --git a/packages/domains/src/index.ts b/packages/domains/src/index.ts index beca895c..60ab18b1 100644 --- a/packages/domains/src/index.ts +++ b/packages/domains/src/index.ts @@ -1,2 +1,3 @@ export * from './models'; export * from './util'; +export * from './api'; diff --git a/packages/server/src/controllers/collector.controller.ts b/packages/server/src/controllers/collector.controller.ts index c0bff6c5..add73a0d 100644 --- a/packages/server/src/controllers/collector.controller.ts +++ b/packages/server/src/controllers/collector.controller.ts @@ -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.toggle.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); + } } diff --git a/packages/server/src/controllers/comment.controller.ts b/packages/server/src/controllers/comment.controller.ts index 3378716d..8a227e04 100644 --- a/packages/server/src/controllers/comment.controller.ts +++ b/packages/server/src/controllers/comment.controller.ts @@ -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,24 +35,33 @@ 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) { diff --git a/packages/server/src/controllers/document.controller.ts b/packages/server/src/controllers/document.controller.ts index cd1a6599..8e915957 100644 --- a/packages/server/src/controllers/document.controller.ts +++ b/packages/server/src/controllers/document.controller.ts @@ -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) { diff --git a/packages/server/src/controllers/file.controller.ts b/packages/server/src/controllers/file.controller.ts index 347d77f3..d9bab8e3 100644 --- a/packages/server/src/controllers/file.controller.ts +++ b/packages/server/src/controllers/file.controller.ts @@ -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: { diff --git a/packages/server/src/controllers/message.controller.ts b/packages/server/src/controllers/message.controller.ts index adf38aee..be5b4ff9 100644 --- a/packages/server/src/controllers/message.controller.ts +++ b/packages/server/src/controllers/message.controller.ts @@ -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) { diff --git a/packages/server/src/controllers/template.controller.ts b/packages/server/src/controllers/template.controller.ts index 34cd5e30..748bac46 100644 --- a/packages/server/src/controllers/template.controller.ts +++ b/packages/server/src/controllers/template.controller.ts @@ -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); } } diff --git a/packages/server/src/controllers/user.controller.ts b/packages/server/src/controllers/user.controller.ts index 9fe782b1..7d132d85 100644 --- a/packages/server/src/controllers/user.controller.ts +++ b/packages/server/src/controllers/user.controller.ts @@ -17,21 +17,39 @@ import { 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') + @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('login') + @Post(UserApiDefinition.login.server) @HttpCode(HttpStatus.OK) async login(@Body() user: LoginUserDto, @Res({ passthrough: true }) response: ExpressResponse) { const { user: data, token } = await this.userService.login(user); @@ -39,25 +57,23 @@ export class UserController { return { ...data, token }; } - @Get('logout') + /** + * 登出 + */ + @Get(UserApiDefinition.logout.server) async logout(@Res({ passthrough: true }) response: ExpressResponse) { response.cookie('token', '', { expires: new Date() }); return; } + /** + * 更新 + */ @UseInterceptors(ClassSerializerInterceptor) - @Patch('update') + @Patch(UserApiDefinition.update.server) @HttpCode(HttpStatus.OK) @UseGuards(JwtGuard) async updateUser(@Request() req, @Body() dto: UpdateUserDto) { return await this.userService.updateUser(req.user, dto); } - - @UseInterceptors(ClassSerializerInterceptor) - @Get('/') - @HttpCode(HttpStatus.OK) - @UseGuards(JwtGuard) - async getUsers() { - return this.userService.getUsers(); - } } diff --git a/packages/server/src/controllers/wiki.controller.ts b/packages/server/src/controllers/wiki.controller.ts index feee4c08..ed76b00d 100644 --- a/packages/server/src/controllers/wiki.controller.ts +++ b/packages/server/src/controllers/wiki.controller.ts @@ -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,59 +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) { - const sleep = (v) => { - return new Promise((r) => setTimeout(r, v * 1000)); - }; - await sleep(4); - 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 @@ -300,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) @@ -308,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 @@ -329,13 +311,27 @@ export class WikiController { */ @UseInterceptors(ClassSerializerInterceptor) @HttpCode(HttpStatus.OK) - @Post('public/tocs/:id') + @Post(WikiApiDefinition.getPublicTocsById.server) @CheckWikiStatus(WikiStatus.public) @UseGuards(WikiStatusGuard) async getPublicWikiTocs(@Param('id') wikiId) { return await this.wikiService.getPublicWikiTocs(wikiId); } + /** + * 获取公开知识库详情 + * @param wikiId + * @returns + */ + @UseInterceptors(ClassSerializerInterceptor) + @Post(WikiApiDefinition.getPublicDetailById.server) + @CheckWikiStatus(WikiStatus.public) + @UseGuards(WikiStatusGuard) + @HttpCode(HttpStatus.OK) + async getPublicWorkspaceDetail(@Param('id') wikiId) { + return await this.wikiService.getPublicWikiDetail(wikiId); + } + /** * 获取所有公开知识库 * @param pagination @@ -343,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); } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 952e6b75..e73454d6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -236,7 +236,7 @@ importers: eslint: 8.14.0 eslint-config-prettier: 8.5.0_eslint@8.14.0 eslint-plugin-import: 2.26.0_eslint@8.14.0 - eslint-plugin-prettier: 4.0.0_740be41c8168d0cc214a306089357ad0 + eslint-plugin-prettier: 4.0.0_74ebb802163a9b4fa8f89d76ed02f62a eslint-plugin-react: 7.29.4_eslint@8.14.0 eslint-plugin-react-hooks: 4.5.0_eslint@8.14.0 eslint-plugin-simple-import-sort: 7.0.0_eslint@8.14.0 @@ -5415,6 +5415,22 @@ packages: prettier-linter-helpers: 1.0.0 dev: true + /eslint-plugin-prettier/4.0.0_74ebb802163a9b4fa8f89d76ed02f62a: + resolution: {integrity: sha512-98MqmCJ7vJodoQK359bqQWaxOE0CS8paAz/GgjaZLyex4TTk3g9HugoO89EqWCrFiOqn9EVvcoo7gZzONCWVwQ==} + engines: {node: '>=6.0.0'} + peerDependencies: + eslint: '>=7.28.0' + eslint-config-prettier: '*' + prettier: '>=2.0.0' + peerDependenciesMeta: + eslint-config-prettier: + optional: true + dependencies: + eslint: 8.14.0 + eslint-config-prettier: 8.5.0_eslint@8.14.0 + prettier-linter-helpers: 1.0.0 + dev: true + /eslint-plugin-react-hooks/4.5.0_eslint@8.14.0: resolution: {integrity: sha512-8k1gRt7D7h03kd+SAAlzXkQwWK22BnK6GKZG+FJA6BAGy22CFvl8kCIXKpVux0cCxMWDQUPqSok0LKaZ0aOcCw==} engines: {node: '>=10'} From b3c7a375ce58f80bd9137cdf82896fa3ad102424 Mon Sep 17 00:00:00 2001 From: fantasticit Date: Mon, 23 May 2022 16:20:56 +0800 Subject: [PATCH 5/6] =?UTF-8?q?chore:=20ssr=20=E9=98=B6=E6=AE=B51?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/client/package.json | 1 + .../src/components/data-render/index.tsx | 2 +- .../src/components/document/star/index.tsx | 4 +- .../client/src/components/message/index.tsx | 3 +- .../src/components/template/editor/editor.tsx | 4 +- .../src/components/template/editor/index.tsx | 10 +- packages/client/src/components/user/index.tsx | 2 +- .../client/src/components/wiki/card/index.tsx | 2 +- .../src/components/wiki/create/index.tsx | 2 +- .../client/src/components/wiki/star/index.tsx | 8 +- .../client/src/data/refactor/collector.tsx | 167 +++++++++++++++++ .../client/src/data/refactor/document.tsx | 40 +++++ packages/client/src/data/refactor/template.ts | 146 +++++++++++++++ packages/client/src/data/refactor/wiki.tsx | 170 ++++++++++++++++++ .../src/layouts/router-header/index.tsx | 2 +- .../src/layouts/router-header/recent.tsx | 1 - .../client/src/layouts/router-header/wiki.tsx | 5 +- packages/client/src/pages/_app.tsx | 20 ++- packages/client/src/pages/find/index.tsx | 11 +- packages/client/src/pages/index.tsx | 17 +- packages/client/src/pages/star/index.tsx | 22 ++- packages/client/src/pages/template/index.tsx | 2 +- packages/client/src/pages/wiki/index.tsx | 13 +- packages/client/src/services/http-client.tsx | 15 +- .../client/src/services/server-prefetcher.ts | 25 +++ packages/domains/lib/api/collector.d.ts | 8 +- packages/domains/lib/api/collector.js | 8 +- packages/domains/lib/api/comment.d.ts | 8 +- packages/domains/lib/api/comment.js | 8 +- packages/domains/lib/api/document.d.ts | 30 ++-- packages/domains/lib/api/document.js | 30 ++-- packages/domains/lib/api/file.d.ts | 2 +- packages/domains/lib/api/file.js | 2 +- packages/domains/lib/api/message.d.ts | 8 +- packages/domains/lib/api/message.js | 8 +- packages/domains/lib/api/template.d.ts | 12 +- packages/domains/lib/api/template.js | 12 +- packages/domains/lib/api/user.d.ts | 10 +- packages/domains/lib/api/user.js | 10 +- packages/domains/lib/api/wiki.d.ts | 42 ++--- packages/domains/lib/api/wiki.js | 42 ++--- packages/domains/src/api/collector.ts | 23 +-- packages/domains/src/api/comment.ts | 8 +- packages/domains/src/api/document.ts | 30 ++-- packages/domains/src/api/file.ts | 2 +- packages/domains/src/api/message.ts | 8 +- packages/domains/src/api/template.ts | 12 +- packages/domains/src/api/user.ts | 10 +- packages/domains/src/api/wiki.ts | 42 ++--- packages/server/package.json | 1 + .../src/controllers/collector.controller.ts | 2 +- .../server/src/services/collector.service.ts | 8 +- .../src/services/document-version.service.ts | 2 +- .../server/src/services/document.service.ts | 7 +- .../server/src/services/template.service.ts | 5 +- pnpm-lock.yaml | 103 +++++++++-- 56 files changed, 930 insertions(+), 267 deletions(-) create mode 100644 packages/client/src/data/refactor/collector.tsx create mode 100644 packages/client/src/data/refactor/document.tsx create mode 100644 packages/client/src/data/refactor/template.ts create mode 100644 packages/client/src/data/refactor/wiki.tsx create mode 100644 packages/client/src/services/server-prefetcher.ts diff --git a/packages/client/package.json b/packages/client/package.json index e0783866..78ca2e87 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -86,6 +86,7 @@ "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", diff --git a/packages/client/src/components/data-render/index.tsx b/packages/client/src/components/data-render/index.tsx index acf4575a..4b3c5e3f 100644 --- a/packages/client/src/components/data-render/index.tsx +++ b/packages/client/src/components/data-render/index.tsx @@ -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; diff --git a/packages/client/src/components/document/star/index.tsx b/packages/client/src/components/document/star/index.tsx index 39dc0946..f0585d4a 100644 --- a/packages/client/src/components/document/star/index.tsx +++ b/packages/client/src/components/document/star/index.tsx @@ -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/refactor/collector'; import React from 'react'; interface IProps { @@ -9,7 +9,7 @@ interface IProps { } export const DocumentStar: React.FC = ({ documentId, render }) => { - const { data, toggleStar } = useDocumentStar(documentId); + const { data, toggle: toggleStar } = useDocumentCollectToggle(documentId); const text = data ? '取消收藏' : '收藏文档'; return ( diff --git a/packages/client/src/components/message/index.tsx b/packages/client/src/components/message/index.tsx index d7fe5c98..caded197 100644 --- a/packages/client/src/components/message/index.tsx +++ b/packages/client/src/components/message/index.tsx @@ -250,5 +250,6 @@ const MessageBox = () => { export const Message = () => { const { loading, error } = useUser(); - return } />; + return null; + // return } />; }; diff --git a/packages/client/src/components/template/editor/editor.tsx b/packages/client/src/components/template/editor/editor.tsx index 069b0bb2..d5ed712e 100644 --- a/packages/client/src/components/template/editor/editor.tsx +++ b/packages/client/src/components/template/editor/editor.tsx @@ -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'; diff --git a/packages/client/src/components/template/editor/index.tsx b/packages/client/src/components/template/editor/index.tsx index f2cc8bef..8b10f9d0 100644 --- a/packages/client/src/components/template/editor/index.tsx +++ b/packages/client/src/components/template/editor/index.tsx @@ -1,12 +1,12 @@ -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'; import { Seo } from 'components/seo'; import { Theme } from 'components/theme'; import { User } from 'components/user'; -import { useTemplate } from 'data/template'; +import { useTemplate } from 'data/refactor/template'; import { useUser } from 'data/user'; import { useDocumentStyle } from 'hooks/use-document-style'; import { useWindowSize } from 'hooks/use-window-size'; @@ -25,9 +25,8 @@ const { Text } = Typography; export const TemplateEditor: React.FC = ({ templateId }) => { const { user } = useUser(); const { data, loading, error, updateTemplate, deleteTemplate } = useTemplate(templateId); - const { width: windowWidth } = useWindowSize(); - const [title, setTitle] = useState(data.title); + const [title, setTitle] = useState(data && data.title); const [isPublic, setPublic] = useState(false); const { width, fontSize } = useDocumentStyle(); const editorWrapClassNames = useMemo(() => { @@ -47,6 +46,7 @@ export const TemplateEditor: React.FC = ({ templateId }) => { useEffect(() => { if (!data) return; setPublic(data.isPublic); + setTitle(data.title); }, [data]); return ( diff --git a/packages/client/src/components/user/index.tsx b/packages/client/src/components/user/index.tsx index eda58d5f..e008f432 100644 --- a/packages/client/src/components/user/index.tsx +++ b/packages/client/src/components/user/index.tsx @@ -45,7 +45,7 @@ export const User: React.FC = () => { ) : ( - {user && user.name[0]} + {user && user.name && user.name[0]} ) } diff --git a/packages/client/src/components/wiki/card/index.tsx b/packages/client/src/components/wiki/card/index.tsx index 0daece69..386ca8b5 100644 --- a/packages/client/src/components/wiki/card/index.tsx +++ b/packages/client/src/components/wiki/card/index.tsx @@ -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/refactor/collector'; import Link from 'next/link'; import styles from './index.module.scss'; diff --git a/packages/client/src/components/wiki/create/index.tsx b/packages/client/src/components/wiki/create/index.tsx index c794428b..4c825471 100644 --- a/packages/client/src/components/wiki/create/index.tsx +++ b/packages/client/src/components/wiki/create/index.tsx @@ -1,7 +1,7 @@ import { Form, Modal } from '@douyinfe/semi-ui'; import { FormApi } from '@douyinfe/semi-ui/lib/es/form'; import type { IWiki } from '@think/domains'; -import { ICreateWiki, useOwnWikis } from 'data/wiki'; +import { ICreateWiki, useOwnWikis } from 'data/refactor/wiki'; import Router from 'next/router'; import { Dispatch, SetStateAction, useRef } from 'react'; diff --git a/packages/client/src/components/wiki/star/index.tsx b/packages/client/src/components/wiki/star/index.tsx index 4edf800a..a7e49f73 100644 --- a/packages/client/src/components/wiki/star/index.tsx +++ b/packages/client/src/components/wiki/star/index.tsx @@ -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/refactor/collector'; import React from 'react'; interface IProps { @@ -10,13 +10,13 @@ interface IProps { } export const WikiStar: React.FC = ({ 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 }) ) : ( @@ -95,14 +97,16 @@ export const TemplateEditor: React.FC = ({ templateId }) => {
- + {mounted && ( + + )}
diff --git a/packages/client/src/components/template/editor/editor.tsx b/packages/client/src/components/template/editor/jhuhhkh.tsx similarity index 100% rename from packages/client/src/components/template/editor/editor.tsx rename to packages/client/src/components/template/editor/jhuhhkh.tsx diff --git a/packages/client/src/components/wiki/card/index.tsx b/packages/client/src/components/wiki/card/index.tsx index 386ca8b5..0d4b453d 100644 --- a/packages/client/src/components/wiki/card/index.tsx +++ b/packages/client/src/components/wiki/card/index.tsx @@ -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/refactor/collector'; +import { IWikiWithIsMember } from 'data/collector'; import Link from 'next/link'; import styles from './index.module.scss'; diff --git a/packages/client/src/components/wiki/create/index.tsx b/packages/client/src/components/wiki/create/index.tsx index 4c825471..c794428b 100644 --- a/packages/client/src/components/wiki/create/index.tsx +++ b/packages/client/src/components/wiki/create/index.tsx @@ -1,7 +1,7 @@ import { Form, Modal } from '@douyinfe/semi-ui'; import { FormApi } from '@douyinfe/semi-ui/lib/es/form'; import type { IWiki } from '@think/domains'; -import { ICreateWiki, useOwnWikis } from 'data/refactor/wiki'; +import { ICreateWiki, useOwnWikis } from 'data/wiki'; import Router from 'next/router'; import { Dispatch, SetStateAction, useRef } from 'react'; diff --git a/packages/client/src/components/wiki/setting/users/index.tsx b/packages/client/src/components/wiki/setting/users/index.tsx index c3d1e860..1dedd48e 100644 --- a/packages/client/src/components/wiki/setting/users/index.tsx +++ b/packages/client/src/components/wiki/setting/users/index.tsx @@ -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 = ({ 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); diff --git a/packages/client/src/components/wiki/star/index.tsx b/packages/client/src/components/wiki/star/index.tsx index a7e49f73..76b6ae23 100644 --- a/packages/client/src/components/wiki/star/index.tsx +++ b/packages/client/src/components/wiki/star/index.tsx @@ -1,6 +1,6 @@ import { IconStar } from '@douyinfe/semi-icons'; import { Button, Tooltip } from '@douyinfe/semi-ui'; -import { useWikiCollectToggle } from 'data/refactor/collector'; +import { useWikiCollectToggle } from 'data/collector'; import React from 'react'; interface IProps { diff --git a/packages/client/src/data/refactor/collector.tsx b/packages/client/src/data/collector.tsx similarity index 91% rename from packages/client/src/data/refactor/collector.tsx rename to packages/client/src/data/collector.tsx index 97043da7..35180444 100644 --- a/packages/client/src/data/refactor/collector.tsx +++ b/packages/client/src/data/collector.tsx @@ -1,4 +1,4 @@ -import { CollectorApiDefinition, CollectorApiTypeDefinition, CollectType, IDocument, IWiki } from '@think/domains'; +import { CollectorApiDefinition, CollectType, IDocument, IWiki } from '@think/domains'; import { useCallback } from 'react'; import { useQuery } from 'react-query'; import { HttpClient } from 'services/http-client'; @@ -13,9 +13,7 @@ export const getCollectedWikis = (cookie = null): Promise = return HttpClient.request({ method: CollectorApiDefinition.wikis.method, url: CollectorApiDefinition.wikis.client(), - headers: { - cookie, - }, + cookie, }); }; @@ -37,9 +35,7 @@ export const getWikiIsCollected = (wikiId, cookie = null): Promise => { return HttpClient.request({ method: CollectorApiDefinition.check.method, url: CollectorApiDefinition.check.client(), - headers: { - cookie, - }, + cookie, data: { type: CollectType.wiki, targetId: wikiId, @@ -56,9 +52,7 @@ export const toggleCollectWiki = (wikiId, cookie = null): Promise => { return HttpClient.request({ method: CollectorApiDefinition.toggle.method, url: CollectorApiDefinition.toggle.client(), - headers: { - cookie, - }, + cookie, data: { type: CollectType.wiki, targetId: wikiId, @@ -92,9 +86,7 @@ export const getCollectedDocuments = (cookie = null): Promise => { return HttpClient.request({ method: CollectorApiDefinition.documents.method, url: CollectorApiDefinition.documents.client(), - headers: { - cookie, - }, + cookie, }); }; @@ -119,9 +111,7 @@ export const getDocumentIsCollected = (documentId, cookie = null): Promise; - export type UpdateCommentDto = Pick; +export const getComments = ( + documentId, + page = 1, + cookie = null +): Promise<{ + data: Array; + 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; */ export const useComments = (documentId) => { const [page, setPage] = useState(1); - const { data, error, mutate } = useSWR<{ - data: Array; - 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, diff --git a/packages/client/src/data/document.ts b/packages/client/src/data/document.ts deleted file mode 100644 index 78d06217..00000000 --- a/packages/client/src/data/document.ts +++ /dev/null @@ -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>; -type IDocumentWithAuth = { document: IDocument; authority: IAuthority }; -type IUpdateDocument = Partial>; - -/** - * 创建文档 - * @returns - */ -export const useCreateDocument = () => { - const [create, loading] = useAsyncLoading((data: ICreateDocument): Promise => { - 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 => { - return HttpClient.delete('/document/delete/' + id); - }); - return { deleteDocument, loading }; -}; - -/** - * 获取文档详情 - * @param documentId - * @returns - */ -export const useDocumentDetail = (documentId, options = null) => { - const { data, error, mutate } = useSWR( - `/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>) => { - 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>( - `/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>('/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(`/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('/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>( - `/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>( - 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 }; -}; diff --git a/packages/client/src/data/document.tsx b/packages/client/src/data/document.tsx new file mode 100644 index 00000000..9efa5c6f --- /dev/null +++ b/packages/client/src/data/document.tsx @@ -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>; +type IDocumentWithAuth = { document: IDocument; authority: IAuthority }; +type IUpdateDocument = Partial>; + +/** + * 获取用户最近访问的文档 + * @returns + */ +export const getRecentVisitedDocuments = (cookie = null): Promise => { + 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> => { + 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 => { + 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> => { + 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 => { + 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 => { + 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 => { + 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> => { + 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 }; +}; diff --git a/packages/client/src/data/message.ts b/packages/client/src/data/message.ts index aec57715..509c23ba 100644 --- a/packages/client/src/data/message.ts +++ b/packages/client/src/data/message.ts @@ -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; + 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; - 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; - 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; - 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, }; }; diff --git a/packages/client/src/data/refactor/document.tsx b/packages/client/src/data/refactor/document.tsx deleted file mode 100644 index d255d8b1..00000000 --- a/packages/client/src/data/refactor/document.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import { - CollectorApiDefinition, - CollectorApiTypeDefinition, - CollectType, - DocumentApiDefinition, - IDocument, - IWiki, - WikiApiDefinition, -} from '@think/domains'; -import { useCallback } from 'react'; -import { useQuery } from 'react-query'; -import { HttpClient } from 'services/http-client'; - -type IDocumentWithVisitedAt = IDocument & { visitedAt: string }; - -/** - * 获取用户最近访问的文档 - * @returns - */ -export const getRecentVisitedDocuments = (cookie = null): Promise => { - return HttpClient.request({ - method: DocumentApiDefinition.recent.method, - url: DocumentApiDefinition.recent.client(), - headers: { - cookie, - }, - }); -}; - -/** - * 获取用户最近访问的文档 - * @returns - */ -export const useRecentDocuments = () => { - const { data, error, isLoading, refetch } = useQuery( - DocumentApiDefinition.recent.client(), - getRecentVisitedDocuments - ); - return { data, error, loading: isLoading, refresh: refetch }; -}; diff --git a/packages/client/src/data/refactor/template.ts b/packages/client/src/data/refactor/template.ts deleted file mode 100644 index dbe11fa0..00000000 --- a/packages/client/src/data/refactor/template.ts +++ /dev/null @@ -1,146 +0,0 @@ -import { ITemplate, TemplateApiDefinition } from '@think/domains'; -import { useCallback, useState } from 'react'; -import { useQuery } from 'react-query'; -import { HttpClient } from 'services/http-client'; - -export const getPublicTemplates = ( - page = 1, - cookie = null -): Promise<{ - data: Array; - total: number; -}> => { - return HttpClient.request({ - method: TemplateApiDefinition.public.method, - url: TemplateApiDefinition.public.client(), - headers: { - cookie, - }, - params: { - page, - }, - }); -}; - -export const usePublicTemplates = () => { - const [page, setPage] = useState(1); - const { data, error, isLoading } = useQuery(`${TemplateApiDefinition.public.client()}?page=${page}`, () => - getPublicTemplates(page) - ); - - return { - data, - loading: isLoading, - error, - setPage, - }; -}; - -export const getOwnTemplates = ( - page = 1, - cookie = null -): Promise<{ - data: Array; - total: number; -}> => { - return HttpClient.request({ - method: TemplateApiDefinition.own.method, - url: TemplateApiDefinition.own.client(), - headers: { - cookie, - }, - params: { - page, - }, - }); -}; - -/** - * 个人模板 - * @returns - */ -export const useOwnTemplates = () => { - const [page, setPage] = useState(1); - const { - data, - error, - isLoading, - refetch: mutate, - } = useQuery(`${TemplateApiDefinition.own.client()}?page=${page}`, () => getOwnTemplates(page)); - - const addTemplate = useCallback( - async (data): Promise => { - const ret = await HttpClient.post(TemplateApiDefinition.add.client(), data); - mutate(); - return ret as unknown as ITemplate; - }, - [mutate] - ); - - return { - data, - loading: isLoading, - error, - setPage, - addTemplate, - }; -}; - -/** - * 获取模板详情 - * @param templateId - * @param cookie - * @returns - */ -export const getTemplateDetail = (templateId, cookie = null): Promise => { - return HttpClient.request({ - method: TemplateApiDefinition.getDetailById.method, - url: TemplateApiDefinition.getDetailById.client(templateId), - headers: { - cookie, - }, - }); -}; - -/** - * 获取模板详情 - * @param templateId - * @returns - */ -export const useTemplate = (templateId) => { - const { data, error, refetch } = useQuery(TemplateApiDefinition.getDetailById.client(templateId), () => - getTemplateDetail(templateId) - ); - const loading = !data && !error; - - const updateTemplate = useCallback( - async (data): Promise => { - const ret = await HttpClient.request({ - method: TemplateApiDefinition.updateById.method, - url: TemplateApiDefinition.updateById.client(templateId), - data: { - id: templateId, - ...data, - }, - }); - refetch(); - return ret as unknown as ITemplate; - }, - [refetch, templateId] - ); - - const deleteTemplate = useCallback(async () => { - await HttpClient.request({ - method: TemplateApiDefinition.deleteById.method, - url: TemplateApiDefinition.deleteById.client(templateId), - }); - }, [templateId]); - - return { - data, - loading, - error, - updateTemplate, - deleteTemplate, - }; -}; diff --git a/packages/client/src/data/refactor/wiki.tsx b/packages/client/src/data/refactor/wiki.tsx deleted file mode 100644 index 9d92711b..00000000 --- a/packages/client/src/data/refactor/wiki.tsx +++ /dev/null @@ -1,170 +0,0 @@ -import { - CollectorApiDefinition, - CollectorApiTypeDefinition, - CollectType, - IUser, - IWiki, - IWikiUser, - WikiApiDefinition, -} from '@think/domains'; -import { useCallback, useState } from 'react'; -import { useQuery } from 'react-query'; -import { HttpClient } from 'services/http-client'; - -export type ICreateWiki = Pick; -export type IUpdateWiki = Partial; -export type IWikiUserOpeateData = { - userName: Pick; - userRole: Pick; -}; - -/** - * 获取用户所有知识库 - * @returns - */ -export const getAllWikis = (cookie = null): Promise<{ data: IWiki[]; total: number }> => { - return HttpClient.request({ - method: WikiApiDefinition.getAllWikis.method, - url: WikiApiDefinition.getAllWikis.client(), - headers: { - cookie, - }, - }); -}; - -/** - * 获取用户所有知识库 - * @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 }; -}; - -/** - * 获取用户参与的知识库 - * @returns - */ -export const getJoinWikis = (cookie = null): Promise<{ data: IWiki[]; total: number }> => { - return HttpClient.request({ - method: WikiApiDefinition.getJoinWikis.method, - url: WikiApiDefinition.getJoinWikis.client(), - headers: { - cookie, - }, - }); -}; - -/** - * 获取用户参与的知识库 - * @returns - */ -export const useJoinWikis = () => { - 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: isLoading }; -}; - -/** - * 获取用户创建的知识库 - * @returns - */ -export const getOwnWikis = (cookie = null): Promise<{ data: IWiki[]; total: number }> => { - return HttpClient.request({ - method: WikiApiDefinition.getOwnWikis.method, - url: WikiApiDefinition.getOwnWikis.client(), - headers: { - cookie, - }, - }); -}; - -/** - * 获取用户创建的知识库 - * @returns - */ -export const useOwnWikis = () => { - const { data, error, refetch } = useQuery(WikiApiDefinition.getOwnWikis.client(), getOwnWikis); - - const createWiki = useCallback( - async (data: ICreateWiki) => { - const res = await HttpClient.request({ - method: WikiApiDefinition.add.method, - url: WikiApiDefinition.add.client(), - data, - }); - refetch(); - return res; - }, - [refetch] - ); - - /** - * 删除文档 - * @param id - * @returns - */ - const deletWiki = useCallback( - async (id) => { - const res = await HttpClient.request({ - method: WikiApiDefinition.deleteById.method, - url: WikiApiDefinition.deleteById.client(id), - }); - refetch(); - return res; - }, - [refetch] - ); - - const loading = !data && !error; - const list = (data && data.data) || []; - const total = (data && data.total) || 0; - - return { data: list, total, error, loading, createWiki, deletWiki }; -}; - -/** - * 获取所有公开文档 - * @param documentId - * @returns - */ -export const getAllPublicWikis = ( - page = 1, - cookie = null -): Promise<{ - data: Array; - total: number; -}> => { - return HttpClient.request({ - method: WikiApiDefinition.getPublicWikis.method, - url: WikiApiDefinition.getPublicWikis.client(), - headers: { - 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, - }; -}; diff --git a/packages/client/src/data/template.ts b/packages/client/src/data/template.ts index 03101b1d..862cc8ce 100644 --- a/packages/client/src/data/template.ts +++ b/packages/client/src/data/template.ts @@ -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; + 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; - 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; + 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; - 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 => { - 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 => { + 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(`/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 => { - 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 { diff --git a/packages/client/src/data/user.tsx b/packages/client/src/data/user.tsx index e19186db..3d12f747 100644 --- a/packages/client/src/data/user.tsx +++ b/packages/client/src/data/user.tsx @@ -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('user', getStorage); + const { data, error, refetch } = useQuery('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('/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) => { 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, diff --git a/packages/client/src/data/wiki.tsx b/packages/client/src/data/wiki.tsx index 9c8bcf58..c5f01595 100644 --- a/packages/client/src/data/wiki.tsx +++ b/packages/client/src/data/wiki.tsx @@ -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; export type IUpdateWiki = Partial; @@ -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('/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('/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; + 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('/wiki/homedoc/' + wikiId, (url) => HttpClient.get(url)); - const loading = !data && !error; - return { data, error, loading }; +export const getWikiHomeDocument = (wikiId, cookie = null): Promise => { + 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>(`/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>(`/wiki/docs/${wikiId}`, (url) => - HttpClient.get(url) - ); - const loading = !data && !error; - return { data, loading, error, refresh: mutate }; +export const getWikiDetail = (wikiId, cookie = null): Promise => { + 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(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> => { + 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> => { + 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 => { + 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('/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(`/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('/collector/wikis', (url) => HttpClient.post(url), { - revalidateOnFocus: true, +export const getPublicWikiHomeDocument = (wikiId, cookie = null): Promise => { + 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('/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 => { + 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(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>(`/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> => { + 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; - 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 }; }; diff --git a/packages/client/src/helpers/url.tsx b/packages/client/src/helpers/url.tsx index a0d4c50c..62691af2 100644 --- a/packages/client/src/helpers/url.tsx +++ b/packages/client/src/helpers/url.tsx @@ -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; }; diff --git a/packages/client/src/hooks/use-document-style.ts b/packages/client/src/hooks/use-document-style.ts index c8e1e718..5a9ef5eb 100644 --- a/packages/client/src/hooks/use-document-style.ts +++ b/packages/client/src/hooks/use-document-style.ts @@ -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, diff --git a/packages/client/src/hooks/use-dragable-width.ts b/packages/client/src/hooks/use-dragable-width.ts index e4a549f6..663a4597 100644 --- a/packages/client/src/hooks/use-dragable-width.ts +++ b/packages/client/src/hooks/use-dragable-width.ts @@ -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(key, () => { + const { data: currentWidth, refetch } = useQuery(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, diff --git a/packages/client/src/layouts/router-header/wiki.tsx b/packages/client/src/layouts/router-header/wiki.tsx index 15636265..3d63cc49 100644 --- a/packages/client/src/layouts/router-header/wiki.tsx +++ b/packages/client/src/layouts/router-header/wiki.tsx @@ -3,7 +3,7 @@ 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 { useCollectedWikis } from 'data/refactor/collector'; +import { useCollectedWikis } from 'data/collector'; import { useWikiDetail } from 'data/wiki'; import Link from 'next/link'; import { useRouter } from 'next/router'; diff --git a/packages/client/src/pages/find/index.tsx b/packages/client/src/pages/find/index.tsx index f94c57b3..f1700e2e 100644 --- a/packages/client/src/pages/find/index.tsx +++ b/packages/client/src/pages/find/index.tsx @@ -4,7 +4,7 @@ import { DataRender } from 'components/data-render'; import { Empty } from 'components/empty'; import { Seo } from 'components/seo'; import { WikiCard, WikiCardPlaceholder } from 'components/wiki/card'; -import { getAllPublicWikis, useAllPublicWikis } from 'data/refactor/wiki'; +import { getAllPublicWikis, useAllPublicWikis } from 'data/wiki'; import { SingleColumnLayout } from 'layouts/single-column'; import type { NextPage } from 'next'; import React from 'react'; diff --git a/packages/client/src/pages/index.tsx b/packages/client/src/pages/index.tsx index 7bbff486..ab36eebf 100644 --- a/packages/client/src/pages/index.tsx +++ b/packages/client/src/pages/index.tsx @@ -7,8 +7,8 @@ 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 { getCollectedWikis, useCollectedWikis } from 'data/refactor/collector'; -import { getRecentVisitedDocuments, useRecentDocuments } from 'data/refactor/document'; +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'; diff --git a/packages/client/src/pages/share/document/[documentId]/index.tsx b/packages/client/src/pages/share/document/[documentId]/index.tsx index e2d8b312..b8136668 100644 --- a/packages/client/src/pages/share/document/[documentId]/index.tsx +++ b/packages/client/src/pages/share/document/[documentId]/index.tsx @@ -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 = ({ 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; diff --git a/packages/client/src/pages/share/wiki/[wikiId]/document/[documentId]/index.tsx b/packages/client/src/pages/share/wiki/[wikiId]/document/[documentId]/index.tsx index 3bdcc9ca..7aa989e6 100644 --- a/packages/client/src/pages/share/wiki/[wikiId]/document/[documentId]/index.tsx +++ b/packages/client/src/pages/share/wiki/[wikiId]/document/[documentId]/index.tsx @@ -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 = ({ 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; diff --git a/packages/client/src/pages/share/wiki/[wikiId]/index.tsx b/packages/client/src/pages/share/wiki/[wikiId]/index.tsx index 9538e8ba..40d6cb93 100644 --- a/packages/client/src/pages/share/wiki/[wikiId]/index.tsx +++ b/packages/client/src/pages/share/wiki/[wikiId]/index.tsx @@ -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 = ({ 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; diff --git a/packages/client/src/pages/star/index.tsx b/packages/client/src/pages/star/index.tsx index 9213840a..c020e5fa 100644 --- a/packages/client/src/pages/star/index.tsx +++ b/packages/client/src/pages/star/index.tsx @@ -5,12 +5,7 @@ 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 { - getCollectedDocuments, - getCollectedWikis, - useCollectedDocuments, - useCollectedWikis, -} from 'data/refactor/collector'; +import { getCollectedDocuments, getCollectedWikis, useCollectedDocuments, useCollectedWikis } from 'data/collector'; import { SingleColumnLayout } from 'layouts/single-column'; import type { NextPage } from 'next'; import React from 'react'; diff --git a/packages/client/src/pages/template/[templateId]/index.tsx b/packages/client/src/pages/template/[templateId]/index.tsx index 60706890..1a0097c0 100644 --- a/packages/client/src/pages/template/[templateId]/index.tsx +++ b/packages/client/src/pages/template/[templateId]/index.tsx @@ -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 = ({ 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; diff --git a/packages/client/src/pages/template/index.tsx b/packages/client/src/pages/template/index.tsx index bfcbdb30..520db141 100644 --- a/packages/client/src/pages/template/index.tsx +++ b/packages/client/src/pages/template/index.tsx @@ -1,7 +1,7 @@ import { Button, TabPane, Tabs, Typography } from '@douyinfe/semi-ui'; import { Seo } from 'components/seo'; import { TemplateList } from 'components/template/list'; -import { useOwnTemplates, usePublicTemplates } from 'data/refactor/template'; +import { useOwnTemplates, usePublicTemplates } from 'data/template'; import { SingleColumnLayout } from 'layouts/single-column'; import type { NextPage } from 'next'; import Router, { useRouter } from 'next/router'; diff --git a/packages/client/src/pages/wiki/[wikiId]/document/[documentId]/edit/index.tsx b/packages/client/src/pages/wiki/[wikiId]/document/[documentId]/edit/index.tsx index 254bc563..318c634b 100644 --- a/packages/client/src/pages/wiki/[wikiId]/document/[documentId]/edit/index.tsx +++ b/packages/client/src/pages/wiki/[wikiId]/document/[documentId]/edit/index.tsx @@ -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 = ({ 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; diff --git a/packages/client/src/pages/wiki/[wikiId]/document/[documentId]/index.tsx b/packages/client/src/pages/wiki/[wikiId]/document/[documentId]/index.tsx index 150636c7..80453c5a 100644 --- a/packages/client/src/pages/wiki/[wikiId]/document/[documentId]/index.tsx +++ b/packages/client/src/pages/wiki/[wikiId]/document/[documentId]/index.tsx @@ -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 = ({ 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; diff --git a/packages/client/src/pages/wiki/[wikiId]/documents/index.tsx b/packages/client/src/pages/wiki/[wikiId]/documents/index.tsx index cec67b1d..ce37b3aa 100644 --- a/packages/client/src/pages/wiki/[wikiId]/documents/index.tsx +++ b/packages/client/src/pages/wiki/[wikiId]/documents/index.tsx @@ -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 ( = ({ 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; diff --git a/packages/client/src/pages/wiki/[wikiId]/index.tsx b/packages/client/src/pages/wiki/[wikiId]/index.tsx index 0e151488..3d73e0cc 100644 --- a/packages/client/src/pages/wiki/[wikiId]/index.tsx +++ b/packages/client/src/pages/wiki/[wikiId]/index.tsx @@ -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 = ({ wikiId }) => { - const { data: doc, loading, error } = useWikiHomeDoc(wikiId); + const { data: doc, loading, error } = useWikiHomeDocument(wikiId); return ( = ({ 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; diff --git a/packages/client/src/pages/wiki/[wikiId]/setting/index.tsx b/packages/client/src/pages/wiki/[wikiId]/setting/index.tsx index 9eeebd8f..d73159f1 100644 --- a/packages/client/src/pages/wiki/[wikiId]/setting/index.tsx +++ b/packages/client/src/pages/wiki/[wikiId]/setting/index.tsx @@ -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 = ({ 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; diff --git a/packages/client/src/pages/wiki/index.tsx b/packages/client/src/pages/wiki/index.tsx index 4281ee4f..30bca788 100644 --- a/packages/client/src/pages/wiki/index.tsx +++ b/packages/client/src/pages/wiki/index.tsx @@ -5,7 +5,7 @@ import { Empty } from 'components/empty'; import { Seo } from 'components/seo'; import { WikiCard, WikiCardPlaceholder } from 'components/wiki/card'; import { WikiCreator } from 'components/wiki-creator'; -import { getAllWikis, getJoinWikis, getOwnWikis, useAllWikis, useJoinWikis, useOwnWikis } from 'data/refactor/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'; diff --git a/packages/client/src/services/http-client.tsx b/packages/client/src/services/http-client.tsx index 27705e6f..fade0194 100644 --- a/packages/client/src/services/http-client.tsx +++ b/packages/client/src/services/http-client.tsx @@ -2,8 +2,10 @@ import { Toast } from '@douyinfe/semi-ui'; import axios, { Axios, AxiosRequestConfig, AxiosResponse } from 'axios'; import Router from 'next/router'; +type WithCookieAxiosRequestConfig = AxiosRequestConfig & { cookie?: string }; + interface AxiosInstance extends Axios { - request>(config: AxiosRequestConfig): Promise; + request>(config: WithCookieAxiosRequestConfig): Promise; } export const HttpClient = axios.create({ @@ -15,7 +17,14 @@ export const HttpClient = axios.create({ const isBrowser = typeof window !== 'undefined'; HttpClient.interceptors.request.use( - (config) => { + (config: WithCookieAxiosRequestConfig) => { + const cookie = config.cookie; + if (cookie) { + if (typeof window === 'undefined' && !config.headers.cookie) { + config.headers.cookie = cookie; + } + delete config.cookie; + } return config; }, () => { diff --git a/packages/client/src/services/server-prefetcher.ts b/packages/client/src/services/server-prefetcher.ts index 14d949c9..a5e03836 100644 --- a/packages/client/src/services/server-prefetcher.ts +++ b/packages/client/src/services/server-prefetcher.ts @@ -4,12 +4,15 @@ 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) return {}; + if (!cookie && !actions.filter((action) => action.ignoreCookie === true).length) { + return {}; + } const queryClient = new QueryClient(); diff --git a/packages/domains/lib/api/collector.d.ts b/packages/domains/lib/api/collector.d.ts index dc67cd1a..92a4487e 100644 --- a/packages/domains/lib/api/collector.d.ts +++ b/packages/domains/lib/api/collector.d.ts @@ -1,18 +1,3 @@ -import { IDocument, IWiki, CollectType } from '../models'; -export declare type CollectorApiTypeDefinition = { - toggle: { - request: { - targetId: IDocument['id'] | IWiki['id']; - type: CollectType; - }; - }; - check: { - request: { - targetId: IDocument['id'] | IWiki['id']; - type: CollectType; - }; - }; -}; export declare const CollectorApiDefinition: { /** * 收藏(或取消收藏) diff --git a/packages/domains/lib/api/document.d.ts b/packages/domains/lib/api/document.d.ts index 43650160..6bcfecdd 100644 --- a/packages/domains/lib/api/document.d.ts +++ b/packages/domains/lib/api/document.d.ts @@ -84,7 +84,7 @@ export declare const DocumentApiDefinition: { * 获取子文档 */ getChildren: { - method: "get"; + method: "post"; server: "children"; client: () => string; }; @@ -108,7 +108,7 @@ export declare const DocumentApiDefinition: { * 获取公开文档详情 */ getPublicDetailById: { - method: "get"; + method: "post"; server: "public/detail/:id"; client: (id: IDocument['id']) => string; }; @@ -116,7 +116,7 @@ export declare const DocumentApiDefinition: { * 获取公开文档的子文档 */ getPublicChildren: { - method: "get"; + method: "post"; server: "public/children"; client: () => string; }; diff --git a/packages/domains/lib/api/document.js b/packages/domains/lib/api/document.js index 0ece5ff2..d1a535f8 100644 --- a/packages/domains/lib/api/document.js +++ b/packages/domains/lib/api/document.js @@ -86,7 +86,7 @@ exports.DocumentApiDefinition = { * 获取子文档 */ getChildren: { - method: 'get', + method: 'post', server: 'children', client: function () { return "/document/children"; } }, @@ -110,7 +110,7 @@ exports.DocumentApiDefinition = { * 获取公开文档详情 */ getPublicDetailById: { - method: 'get', + method: 'post', server: 'public/detail/:id', client: function (id) { return "/document/public/detail/".concat(id); } }, @@ -118,7 +118,7 @@ exports.DocumentApiDefinition = { * 获取公开文档的子文档 */ getPublicChildren: { - method: 'get', + method: 'post', server: 'public/children', client: function () { return "/document/public/children"; } } diff --git a/packages/domains/src/api/document.ts b/packages/domains/src/api/document.ts index 73ad51d3..56805c03 100644 --- a/packages/domains/src/api/document.ts +++ b/packages/domains/src/api/document.ts @@ -95,7 +95,7 @@ export const DocumentApiDefinition = { * 获取子文档 */ getChildren: { - method: 'get' as const, + method: 'post' as const, server: 'children' as const, client: () => `/document/children`, }, @@ -122,7 +122,7 @@ export const DocumentApiDefinition = { * 获取公开文档详情 */ getPublicDetailById: { - method: 'get' as const, + method: 'post' as const, server: 'public/detail/:id' as const, client: (id: IDocument['id']) => `/document/public/detail/${id}`, }, @@ -131,7 +131,7 @@ export const DocumentApiDefinition = { * 获取公开文档的子文档 */ getPublicChildren: { - method: 'get' as const, + method: 'post' as const, server: 'public/children' as const, client: () => `/document/public/children`, }, diff --git a/packages/server/src/controllers/comment.controller.ts b/packages/server/src/controllers/comment.controller.ts index 8a227e04..6b6d2a73 100644 --- a/packages/server/src/controllers/comment.controller.ts +++ b/packages/server/src/controllers/comment.controller.ts @@ -64,7 +64,7 @@ export class CommentController { @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); } } diff --git a/packages/server/src/controllers/user.controller.ts b/packages/server/src/controllers/user.controller.ts index 7d132d85..bd46a595 100644 --- a/packages/server/src/controllers/user.controller.ts +++ b/packages/server/src/controllers/user.controller.ts @@ -52,15 +52,21 @@ export class UserController { @Post(UserApiDefinition.login.server) @HttpCode(HttpStatus.OK) async login(@Body() user: LoginUserDto, @Res({ passthrough: true }) response: ExpressResponse) { - const { user: data, token } = await this.userService.login(user); - response.cookie('token', token, { httpOnly: true, secure: true, sameSite: 'lax' }); + 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 }; } /** * 登出 */ - @Get(UserApiDefinition.logout.server) + @Post(UserApiDefinition.logout.server) + @HttpCode(HttpStatus.OK) async logout(@Res({ passthrough: true }) response: ExpressResponse) { response.cookie('token', '', { expires: new Date() }); return; diff --git a/packages/server/src/controllers/wiki.controller.ts b/packages/server/src/controllers/wiki.controller.ts index ed76b00d..49deb8f7 100644 --- a/packages/server/src/controllers/wiki.controller.ts +++ b/packages/server/src/controllers/wiki.controller.ts @@ -311,7 +311,7 @@ export class WikiController { */ @UseInterceptors(ClassSerializerInterceptor) @HttpCode(HttpStatus.OK) - @Post(WikiApiDefinition.getPublicTocsById.server) + @Get(WikiApiDefinition.getPublicTocsById.server) @CheckWikiStatus(WikiStatus.public) @UseGuards(WikiStatusGuard) async getPublicWikiTocs(@Param('id') wikiId) { @@ -324,7 +324,7 @@ export class WikiController { * @returns */ @UseInterceptors(ClassSerializerInterceptor) - @Post(WikiApiDefinition.getPublicDetailById.server) + @Get(WikiApiDefinition.getPublicDetailById.server) @CheckWikiStatus(WikiStatus.public) @UseGuards(WikiStatusGuard) @HttpCode(HttpStatus.OK) diff --git a/packages/server/src/guard/document-status.guard.ts b/packages/server/src/guard/document-status.guard.ts index 0e1abd29..619944a6 100644 --- a/packages/server/src/guard/document-status.guard.ts +++ b/packages/server/src/guard/document-status.guard.ts @@ -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; diff --git a/packages/server/src/guard/wiki-status.guard.ts b/packages/server/src/guard/wiki-status.guard.ts index 3c2a0410..c25d8861 100644 --- a/packages/server/src/guard/wiki-status.guard.ts +++ b/packages/server/src/guard/wiki-status.guard.ts @@ -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; diff --git a/packages/server/src/main.ts b/packages/server/src/main.ts index c0584762..89d40720 100644 --- a/packages/server/src/main.ts +++ b/packages/server/src/main.ts @@ -13,15 +13,12 @@ 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({ - // TODO: fixme - origin: 'http://localhost:5001', + origin: config.get('client.siteUrl'), methods: 'GET,HEAD,PUT,PATCH,POST,DELETE,OPTIONS', credentials: true, }); diff --git a/packages/server/src/services/document.service.ts b/packages/server/src/services/document.service.ts index 072232b6..49d723cd 100644 --- a/packages/server/src/services/document.service.ts +++ b/packages/server/src/services/document.service.ts @@ -425,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 }; } /** @@ -453,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; @@ -469,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); } diff --git a/packages/server/src/services/user.service.ts b/packages/server/src/services/user.service.ts index 809b041f..17bef8fe 100644 --- a/packages/server/src/services/user.service.ts +++ b/packages/server/src/services/user.service.ts @@ -111,7 +111,7 @@ export class UserService { * @param user * @returns */ - async login(user: LoginUserDto): Promise<{ user: 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 { user: 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) { diff --git a/packages/server/src/services/wiki.service.ts b/packages/server/src/services/wiki.service.ts index 74ed373f..63c39236 100644 --- a/packages/server/src/services/wiki.service.ts +++ b/packages/server/src/services/wiki.service.ts @@ -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); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ff49d826..594877d9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -134,7 +134,6 @@ importers: 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 @@ -223,7 +222,6 @@ importers: 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 @@ -238,7 +236,7 @@ importers: eslint: 8.14.0 eslint-config-prettier: 8.5.0_eslint@8.14.0 eslint-plugin-import: 2.26.0_eslint@8.14.0 - eslint-plugin-prettier: 4.0.0_740be41c8168d0cc214a306089357ad0 + eslint-plugin-prettier: 4.0.0_74ebb802163a9b4fa8f89d76ed02f62a eslint-plugin-react: 7.29.4_eslint@8.14.0 eslint-plugin-react-hooks: 4.5.0_eslint@8.14.0 eslint-plugin-simple-import-sort: 7.0.0_eslint@8.14.0 @@ -5445,6 +5443,22 @@ packages: prettier-linter-helpers: 1.0.0 dev: true + /eslint-plugin-prettier/4.0.0_74ebb802163a9b4fa8f89d76ed02f62a: + resolution: {integrity: sha512-98MqmCJ7vJodoQK359bqQWaxOE0CS8paAz/GgjaZLyex4TTk3g9HugoO89EqWCrFiOqn9EVvcoo7gZzONCWVwQ==} + engines: {node: '>=6.0.0'} + peerDependencies: + eslint: '>=7.28.0' + eslint-config-prettier: '*' + prettier: '>=2.0.0' + peerDependenciesMeta: + eslint-config-prettier: + optional: true + dependencies: + eslint: 8.14.0 + eslint-config-prettier: 8.5.0_eslint@8.14.0 + prettier-linter-helpers: 1.0.0 + dev: true + /eslint-plugin-react-hooks/4.5.0_eslint@8.14.0: resolution: {integrity: sha512-8k1gRt7D7h03kd+SAAlzXkQwWK22BnK6GKZG+FJA6BAGy22CFvl8kCIXKpVux0cCxMWDQUPqSok0LKaZ0aOcCw==} engines: {node: '>=10'} @@ -10390,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'}