From c0966faafc3f0e72c1d6acf3902a1aca087256da Mon Sep 17 00:00:00 2001 From: Innei Date: Mon, 13 Sep 2021 20:46:04 +0800 Subject: [PATCH] refactor: shared database service --- src/modules/comment/comment.controller.ts | 16 +-- src/modules/comment/comment.dto.ts | 2 +- src/modules/comment/comment.model.ts | 1 + src/modules/comment/comment.service.ts | 74 ++++++++------ src/modules/markdown/markdown.controller.ts | 107 ++++++++++---------- src/modules/markdown/markdown.service.ts | 34 +++---- src/modules/sitemap/sitemap.controller.ts | 31 +++--- src/processors/database/database.module.ts | 5 +- src/processors/database/database.service.ts | 59 +++++++++++ 9 files changed, 192 insertions(+), 137 deletions(-) create mode 100644 src/processors/database/database.service.ts diff --git a/src/modules/comment/comment.controller.ts b/src/modules/comment/comment.controller.ts index 7152f301..ae76dc68 100644 --- a/src/modules/comment/comment.controller.ts +++ b/src/modules/comment/comment.controller.ts @@ -30,7 +30,7 @@ import { StateDto, TextOnlyDto, } from './comment.dto' -import { CommentModel, CommentRefTypes, CommentState } from './comment.model' +import { CommentModel, CommentState } from './comment.model' import { CommentService } from './comment.service' @Controller({ path: 'comments' }) @ApiName @@ -113,23 +113,13 @@ export class CommentController { const { ref } = query const id = params.id - if ( - !(await this.commentService.allowComment( - id, - ref || CommentRefTypes.Post, - )) && - !isMaster - ) { + if (!(await this.commentService.allowComment(id, ref)) && !isMaster) { throw new ForbiddenException('主人禁止了评论') } const model = { ...body, ...ipLocation } - const comment = await this.commentService.createComment( - id, - ref || CommentRefTypes.Post, - model, - ) + const comment = await this.commentService.createComment(id, model, ref) process.nextTick(async () => { if (await this.commentService.checkSpam(comment)) { diff --git a/src/modules/comment/comment.dto.ts b/src/modules/comment/comment.dto.ts index 8860a689..36a02162 100644 --- a/src/modules/comment/comment.dto.ts +++ b/src/modules/comment/comment.dto.ts @@ -50,7 +50,7 @@ export class CommentRefTypesDto { @IsOptional() @IsEnum(CommentRefTypes) @ApiProperty({ enum: CommentRefTypes, required: false }) - ref: CommentRefTypes + ref?: CommentRefTypes } export class StateDto { diff --git a/src/modules/comment/comment.model.ts b/src/modules/comment/comment.model.ts index 3ada81c1..be5a65f8 100644 --- a/src/modules/comment/comment.model.ts +++ b/src/modules/comment/comment.model.ts @@ -16,6 +16,7 @@ export enum CommentRefTypes { Note = 'Note', Page = 'Page', } + export enum CommentState { Unread, Read, diff --git a/src/modules/comment/comment.service.ts b/src/modules/comment/comment.service.ts index 2851fea1..48ec871d 100644 --- a/src/modules/comment/comment.service.ts +++ b/src/modules/comment/comment.service.ts @@ -1,17 +1,17 @@ import { BadRequestException, Injectable, Logger } from '@nestjs/common' -import { DocumentType, ReturnModelType } from '@typegoose/typegoose' -import { Types } from 'mongoose' +import { DocumentType } from '@typegoose/typegoose' +import { BeAnObject } from '@typegoose/typegoose/lib/types' +import { LeanDocument, Types } from 'mongoose' import { InjectModel } from 'nestjs-typegoose' import { CannotFindException } from '~/common/exceptions/cant-find.exception' +import { DatabaseService } from '~/processors/database/database.service' import { EmailService, ReplyMailType, } from '~/processors/helper/helper.email.service' +import { WriteBaseModel } from '~/shared/model/base.model' import { hasChinese, isDev } from '~/utils/index.util' import { ConfigsService } from '../configs/configs.service' -import { NoteModel } from '../note/note.model' -import { PageModel } from '../page/page.model' -import { PostModel } from '../post/post.model' import { UserService } from '../user/user.service' import BlockedKeywords from './block-keywords.json' import { CommentModel, CommentRefTypes } from './comment.model' @@ -22,12 +22,7 @@ export class CommentService { @InjectModel(CommentModel) private readonly commentModel: MongooseModel, - @InjectModel(PostModel) - private readonly postModel: ReturnModelType, - @InjectModel(NoteModel) - private readonly noteModel: ReturnModelType, - @InjectModel(PageModel) - private readonly pageModel: ReturnModelType, + private readonly databaseService: DatabaseService, private readonly configs: ConfigsService, private readonly userService: UserService, private readonly mailService: EmailService, @@ -38,16 +33,14 @@ export class CommentService { } private getModelByRefType(type: CommentRefTypes) { - const map = new Map( - Object.entries({ - Post: this.postModel, - Note: this.noteModel, - Page: this.pageModel, - }), - ) - return map.get(type) as any as ReturnModelType< - typeof NoteModel | typeof PostModel | typeof PageModel - > + switch (type) { + case CommentRefTypes.Note: + return this.databaseService.getModelByRefType('Note') + case CommentRefTypes.Page: + return this.databaseService.getModelByRefType('Page') + case CommentRefTypes.Post: + return this.databaseService.getModelByRefType('Post') + } } async checkSpam(doc: Partial) { @@ -95,11 +88,19 @@ export class CommentService { async createComment( id: string, - type: CommentRefTypes, doc: Partial, + type?: CommentRefTypes, ) { - const model = this.getModelByRefType(type) - const ref = await model.findById(id) + let ref: LeanDocument> + if (type) { + const model = this.getModelByRefType(type) + ref = await model.findById(id).lean() + } else { + const { type: type_, document } = + await this.databaseService.findGlobalById(id) + ref = document + type = type_ as any + } if (!ref) { throw new CannotFindException() } @@ -110,11 +111,15 @@ export class CommentService { ref: Types.ObjectId(id), refType: type, }) - await ref.updateOne({ - $inc: { - commentsIndex: 1, + + await this.databaseService.getModelByRefType(type as any).updateOne( + { _id: ref._id }, + { + $inc: { + commentsIndex: 1, + }, }, - }) + ) return comment } @@ -156,10 +161,15 @@ export class CommentService { return { message: '删除成功' } } - async allowComment(id: string, type: CommentRefTypes) { - const model = this.getModelByRefType(type) - const doc = await model.findById(id) - return doc.allowComment ?? true + async allowComment(id: string, type?: CommentRefTypes) { + if (type) { + const model = this.getModelByRefType(type) + const doc = await model.findById(id) + return doc.allowComment ?? true + } else { + const { document: doc } = await this.databaseService.findGlobalById(id) + return doc.allowComment ?? true + } } async getComments({ page, size, state } = { page: 1, size: 10, state: 0 }) { diff --git a/src/modules/markdown/markdown.controller.ts b/src/modules/markdown/markdown.controller.ts index 06f5dc5f..a353eea4 100644 --- a/src/modules/markdown/markdown.controller.ts +++ b/src/modules/markdown/markdown.controller.ts @@ -195,63 +195,62 @@ export class MarkdownController { } })() const url = new URL(relativePath, this.configs.get('url').webUrl) - reply.type('text/html').send( - minify( - ` - - - - - - - - - - ${document.title} - - -
-

${document.title}

- ${markdown} -
- - - - - - + + + - + }); + + - `, - { - removeAttributeQuotes: true, - removeComments: true, - minifyCSS: true, - collapseWhitespace: true, - }, - ), + `, + { + removeAttributeQuotes: true, + removeComments: true, + minifyCSS: true, + collapseWhitespace: true, + }, ) + reply.type('text/html').send(html) } } diff --git a/src/modules/markdown/markdown.service.ts b/src/modules/markdown/markdown.service.ts index 9a5e26a3..13fac275 100644 --- a/src/modules/markdown/markdown.service.ts +++ b/src/modules/markdown/markdown.service.ts @@ -7,6 +7,7 @@ import marked from 'marked' import { Types } from 'mongoose' import { InjectModel } from 'nestjs-typegoose' import xss from 'xss' +import { DatabaseService } from '~/processors/database/database.service' import { CategoryModel } from '../category/category.model' import { NoteModel } from '../note/note.model' import { PageModel } from '../page/page.model' @@ -25,6 +26,8 @@ export class MarkdownService { private readonly noteModel: ReturnModelType, @InjectModel(PageModel) private readonly pageModel: ReturnModelType, + + private readonly databaseService: DatabaseService, ) { this.logger = new Logger(MarkdownService.name) } @@ -208,20 +211,15 @@ ${text.trim()} } async renderArticle(id: string) { - const tasks = await Promise.all([ - this.postModel.findById(id).populate('category').lean(), - this.noteModel.findById(id).lean(), - this.pageModel.findById(id).lean(), - ]) - const index = tasks.findIndex(Boolean) - if (!~index) { + const doc = await this.databaseService.findGlobalById(id) + + if (!doc.document) { throw new BadRequestException('文档不存在') } - const document = tasks[index] + return { - html: this.render(document.text), - document, - type: ['post', 'note', 'page'][index], + html: this.render(doc.document.text), + ...doc, } } @@ -243,13 +241,12 @@ ${text.trim()} )}\n` }, tokenizer(src, tokens) { - const rule = /^\|\|([\s\S]+?)\|\|(?!\|)/ // Regex for the complete token + const rule = /^\|\|([\s\S]+?)\|\|(?!\|)/ const match = rule.exec(src) if (match) { return { - // Token to generate - type: 'spoiler', // Should match "name" above - raw: match[0], // Text to consume from the source + type: 'spoiler', + raw: match[0], // @ts-ignore text: this.lexer.inlineTokens(match[1].trim()), } @@ -269,13 +266,12 @@ ${text.trim()} return `@${username}\n` }, tokenizer(src, tokens) { - const rule = /^\((@(\w+\b))\)\s?(?!\[.*?\])/ // Regex for the complete token + const rule = /^\((@(\w+\b))\)\s?(?!\[.*?\])/ const match = rule.exec(src) if (match) { return { - // Token to generate - type: 'mention', // Should match "name" above - raw: match[0], // Text to consume from the source + type: 'mention', + raw: match[0], text: this.lexer.inlineTokens(match[1].trim(), []), } } diff --git a/src/modules/sitemap/sitemap.controller.ts b/src/modules/sitemap/sitemap.controller.ts index 73a1a8b8..77c692c6 100644 --- a/src/modules/sitemap/sitemap.controller.ts +++ b/src/modules/sitemap/sitemap.controller.ts @@ -13,22 +13,21 @@ export class SitemapController { async getSitemap(@Res() res: FastifyReply) { const content = await this.aggregateService.getSiteMapContent() - res.type('application/xml').send( - minify( - ` - - ${content - .map( - (item) => ` - ${item.url} - ${item.published_at.toISOString()} - `, - ) - .join('')} - - `, - { collapseWhitespace: true }, - ), + const xml = minify( + ` + + ${content + .map( + (item) => ` + ${item.url} + ${item.published_at.toISOString()} + `, ) + .join('')} + + `, + { collapseWhitespace: true }, + ) + res.type('application/xml').send(xml) } } diff --git a/src/processors/database/database.module.ts b/src/processors/database/database.module.ts index d9a0a01b..7e06bfe5 100644 --- a/src/processors/database/database.module.ts +++ b/src/processors/database/database.module.ts @@ -13,6 +13,7 @@ import { SayModel } from '~/modules/say/say.model' import { CategoryModel } from '../../modules/category/category.model' import { PostModel } from '../../modules/post/post.model' import { UserModel } from '../../modules/user/user.model' +import { DatabaseService } from './database.service' const models = TypegooseModule.forFeature([ AnalyzeModel, @@ -42,8 +43,8 @@ const models = TypegooseModule.forFeature([ }), models, ], - - exports: [models], + providers: [DatabaseService], + exports: [models, DatabaseService], }) @Global() export class DbModule {} diff --git a/src/processors/database/database.service.ts b/src/processors/database/database.service.ts new file mode 100644 index 00000000..8323eb75 --- /dev/null +++ b/src/processors/database/database.service.ts @@ -0,0 +1,59 @@ +import { Injectable } from '@nestjs/common' +import { ReturnModelType } from '@typegoose/typegoose' +import { InjectModel } from 'nestjs-typegoose' +import { NoteModel } from '~/modules/note/note.model' +import { PageModel } from '~/modules/page/page.model' +import { PostModel } from '~/modules/post/post.model' + +declare enum ModelRefTypes { + Post, + Note, + Page, +} + +@Injectable() +export class DatabaseService { + constructor( + @InjectModel(PostModel) + private readonly postModel: ReturnModelType, + @InjectModel(NoteModel) + private readonly noteModel: ReturnModelType, + @InjectModel(PageModel) + private readonly pageModel: ReturnModelType, + ) {} + + // @ts-ignore + public getModelByRefType(type: 'Post'): ReturnModelType + public getModelByRefType(type: 'Note'): ReturnModelType + public getModelByRefType(type: 'Page'): ReturnModelType + public getModelByRefType(type: keyof typeof ModelRefTypes) { + const map = new Map([ + ['Post', this.postModel], + ['Note', this.noteModel], + ['Page', this.pageModel], + ]) + return map.get(type) as any as ReturnModelType< + typeof NoteModel | typeof PostModel | typeof PageModel + > + } + + public async findGlobalById(id: string) { + const doc = await Promise.all([ + this.postModel.findById(id).populate('category').lean(), + this.noteModel.findById(id).lean(), + this.pageModel.findById(id).lean(), + ]) + const index = doc.findIndex(Boolean) + if (index == -1) { + return { + document: null, + type: null, + } + } + const document = doc[index] + return { + document, + type: ['Post', 'Note', 'Page'][index], + } + } +}