feat: note module
This commit is contained in:
@@ -5,7 +5,7 @@ import {
|
||||
RequestMethod,
|
||||
} from '@nestjs/common'
|
||||
import { ConfigModule } from '@nestjs/config'
|
||||
import { APP_FILTER, APP_INTERCEPTOR } from '@nestjs/core'
|
||||
import { APP_FILTER, APP_GUARD, APP_INTERCEPTOR } from '@nestjs/core'
|
||||
import { AppController } from './app.controller'
|
||||
import { AllExceptionsFilter } from './common/filters/any-exception.filter'
|
||||
import { HttpCacheInterceptor } from './common/interceptors/cache.interceptor'
|
||||
@@ -18,6 +18,7 @@ import { AnalyzeMiddleware } from './common/middlewares/analyze.middleware'
|
||||
import { SkipBrowserDefaultRequestMiddleware } from './common/middlewares/favicon.middleware'
|
||||
import { SecurityMiddleware } from './common/middlewares/security.middleware'
|
||||
import { AuthModule } from './modules/auth/auth.module'
|
||||
import { RolesGuard } from './modules/auth/roles.guard'
|
||||
import { CategoryModule } from './modules/category/category.module'
|
||||
import { CommentModule } from './modules/comment/comment.module'
|
||||
import { ConfigsModule } from './modules/configs/configs.module'
|
||||
@@ -78,11 +79,14 @@ import { HelperModule } from './processors/helper/helper.module'
|
||||
provide: APP_INTERCEPTOR,
|
||||
useClass: ResponseInterceptor,
|
||||
},
|
||||
|
||||
{
|
||||
provide: APP_FILTER,
|
||||
useClass: AllExceptionsFilter,
|
||||
},
|
||||
{
|
||||
provide: APP_GUARD,
|
||||
useClass: RolesGuard,
|
||||
},
|
||||
],
|
||||
})
|
||||
export class AppModule implements NestModule {
|
||||
|
||||
@@ -15,6 +15,7 @@ import { map } from 'rxjs'
|
||||
import { ArticleType } from '~/constants/article.constant'
|
||||
import { RedisKeys } from '~/constants/cache.constant'
|
||||
import { HTTP_RES_UPDATE_DOC_COUNT_TYPE } from '~/constants/meta.constant'
|
||||
import { NoteModel } from '~/modules/note/note.model'
|
||||
import { PostModel } from '~/modules/post/post.model'
|
||||
import { CacheService } from '~/processors/cache/cache.service'
|
||||
import { getIp } from '~/utils/ip.util'
|
||||
@@ -27,6 +28,8 @@ export class CountingInterceptor<T> implements NestInterceptor<T, any> {
|
||||
private readonly reflector: Reflector,
|
||||
@InjectModel(PostModel)
|
||||
private readonly postModel: MongooseModel<PostModel>,
|
||||
@InjectModel(NoteModel)
|
||||
private readonly noteModel: MongooseModel<NoteModel>,
|
||||
private readonly redis: CacheService,
|
||||
) {
|
||||
this.logger = new Logger(CountingInterceptor.name)
|
||||
@@ -69,6 +72,7 @@ export class CountingInterceptor<T> implements NestInterceptor<T, any> {
|
||||
}
|
||||
const modelMap = {
|
||||
Post: this.postModel,
|
||||
Note: this.noteModel,
|
||||
} as const
|
||||
|
||||
const model = modelMap[type]
|
||||
|
||||
@@ -6,7 +6,6 @@ import {
|
||||
Post,
|
||||
Query,
|
||||
Scope,
|
||||
UseGuards,
|
||||
} from '@nestjs/common'
|
||||
import { ApiBearerAuth, ApiOperation } from '@nestjs/swagger'
|
||||
import { Transform } from 'class-transformer'
|
||||
@@ -23,7 +22,6 @@ import { IsMaster as Master } from '~/common/decorator/role.decorator'
|
||||
import { MongoIdDto } from '~/shared/dto/id.dto'
|
||||
import { AdminEventsGateway } from '../../processors/gateway/admin/events.gateway'
|
||||
import { AuthService } from './auth.service'
|
||||
import { RolesGuard } from './roles.guard'
|
||||
|
||||
export class TokenDto {
|
||||
@IsDate()
|
||||
@@ -50,7 +48,6 @@ export class AuthController {
|
||||
@Get()
|
||||
@ApiOperation({ summary: '判断当前 Token 是否有效 ' })
|
||||
@ApiBearerAuth()
|
||||
@UseGuards(RolesGuard)
|
||||
checkLogged(@Master() isMaster: boolean) {
|
||||
return { ok: ~~isMaster, isGuest: !isMaster }
|
||||
}
|
||||
|
||||
@@ -1,9 +1,29 @@
|
||||
import { Controller, Get } from '@nestjs/common'
|
||||
import {
|
||||
Body,
|
||||
Controller,
|
||||
ForbiddenException,
|
||||
Get,
|
||||
HttpCode,
|
||||
Param,
|
||||
Post,
|
||||
Put,
|
||||
Query,
|
||||
} from '@nestjs/common'
|
||||
import { ApiOperation } from '@nestjs/swagger'
|
||||
import { Auth } from '~/common/decorator/auth.decorator'
|
||||
import { Paginator } from '~/common/decorator/http.decorator'
|
||||
import { IpLocation, IpRecord } from '~/common/decorator/ip.decorator'
|
||||
import { ApiName } from '~/common/decorator/openapi.decorator'
|
||||
import { IsMaster } from '~/common/decorator/role.decorator'
|
||||
import { addConditionToSeeHideContent } from '~/utils/query.util'
|
||||
import { UpdateDocumentCount } from '~/common/decorator/update-count.decorator'
|
||||
import { CannotFindException } from '~/common/exceptions/cant-find.exception'
|
||||
import { MongoIdDto } from '~/shared/dto/id.dto'
|
||||
import {
|
||||
addConditionToSeeHideContent,
|
||||
addYearCondition,
|
||||
} from '~/utils/query.util'
|
||||
import { ListQueryDto, NoteQueryDto, PasswordQueryDto } from './note.dto'
|
||||
import { NoteModel, PartialNoteModel } from './note.model'
|
||||
import { NoteService } from './note.service'
|
||||
|
||||
@ApiName
|
||||
@@ -13,6 +33,7 @@ export class NoteController {
|
||||
|
||||
@Get('latest')
|
||||
@ApiOperation({ summary: '获取最新发布一篇记录' })
|
||||
@UpdateDocumentCount('Note')
|
||||
async getLatestOne(
|
||||
@IsMaster() isMaster: boolean,
|
||||
@IpLocation() location: IpRecord,
|
||||
@@ -27,4 +48,164 @@ export class NoteController {
|
||||
// this.noteService.shouldAddReadCount(latest, location.ip)
|
||||
return { data: latest.toObject(), next: next.toObject() }
|
||||
}
|
||||
|
||||
@Get('/')
|
||||
@Paginator
|
||||
@ApiOperation({ summary: '获取记录带分页器' })
|
||||
async getNotes(@IsMaster() isMaster: boolean, @Query() query: NoteQueryDto) {
|
||||
const { size, select, page, sortBy, sortOrder, year } = query
|
||||
const condition = {
|
||||
...addConditionToSeeHideContent(isMaster),
|
||||
...addYearCondition(year),
|
||||
}
|
||||
return await this.noteService.model.paginate(condition, {
|
||||
limit: size,
|
||||
page,
|
||||
select: isMaster
|
||||
? select
|
||||
: select?.replace(/[+-]?(coordinates|location|password)/g, ''),
|
||||
sort: sortBy ? { [sortBy]: sortOrder || -1 } : { created: -1 },
|
||||
})
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
@UpdateDocumentCount('Note')
|
||||
async getOneNote(
|
||||
@Param() params: MongoIdDto,
|
||||
@IsMaster() isMaster: boolean,
|
||||
@Query() query: PasswordQueryDto,
|
||||
@Query('single') isSingle?: boolean,
|
||||
) {
|
||||
const { id } = params
|
||||
const { password } = query
|
||||
const condition = addConditionToSeeHideContent(isMaster)
|
||||
const current = await this.noteService.model
|
||||
.findOne({
|
||||
_id: id,
|
||||
...condition,
|
||||
})
|
||||
.select('+password ' + (isMaster ? '+location +coordinates' : ''))
|
||||
if (!current) {
|
||||
throw new CannotFindException()
|
||||
}
|
||||
if (
|
||||
!this.noteService.checkPasswordToAccess(current, password) &&
|
||||
!isMaster
|
||||
) {
|
||||
throw new ForbiddenException('不要偷看人家的小心思啦~')
|
||||
}
|
||||
if (isSingle) {
|
||||
return current
|
||||
}
|
||||
|
||||
const select = '_id title nid id created modified'
|
||||
const prev = await this.noteService.model
|
||||
.findOne({
|
||||
...condition,
|
||||
created: {
|
||||
$gt: current.created,
|
||||
},
|
||||
})
|
||||
.sort({ created: 1 })
|
||||
.select(select)
|
||||
.lean()
|
||||
const next = await this.noteService.model
|
||||
.findOne({
|
||||
...condition,
|
||||
created: {
|
||||
$lt: current.created,
|
||||
},
|
||||
})
|
||||
.sort({ created: -1 })
|
||||
.select(select)
|
||||
.lean()
|
||||
|
||||
return { data: current, next, prev }
|
||||
}
|
||||
|
||||
@Get('/list/:id')
|
||||
@ApiOperation({ summary: '以一篇记录为基准的中间 10 篇记录' })
|
||||
async getNoteList(
|
||||
@Query() query: ListQueryDto,
|
||||
@Param() params: MongoIdDto,
|
||||
@IsMaster() isMaster: boolean,
|
||||
) {
|
||||
const { size = 10 } = query
|
||||
const half = size >> 1
|
||||
const { id } = params
|
||||
const select = 'nid _id title created'
|
||||
const condition = addConditionToSeeHideContent(isMaster)
|
||||
const currentDocument = await this.noteService.model
|
||||
.findOne(
|
||||
{
|
||||
_id: id,
|
||||
...condition,
|
||||
},
|
||||
select,
|
||||
)
|
||||
.lean()
|
||||
|
||||
if (!currentDocument) {
|
||||
return { data: [], size: 0 }
|
||||
}
|
||||
const prevList =
|
||||
half - 1 === 0
|
||||
? []
|
||||
: await this.noteService.model
|
||||
.find(
|
||||
{
|
||||
created: {
|
||||
$gt: currentDocument.created,
|
||||
},
|
||||
...condition,
|
||||
},
|
||||
select,
|
||||
)
|
||||
.limit(half - 1)
|
||||
.sort({ created: -1 })
|
||||
.lean()
|
||||
const nextList = !half
|
||||
? []
|
||||
: await this.noteService.model
|
||||
.find(
|
||||
{
|
||||
created: {
|
||||
$lt: currentDocument.created,
|
||||
},
|
||||
...condition,
|
||||
},
|
||||
select,
|
||||
)
|
||||
.limit(half - 1)
|
||||
.sort({ created: -1 })
|
||||
.lean()
|
||||
const data = [...prevList, ...nextList, currentDocument].sort(
|
||||
(a: any, b: any) => b.created - a.created,
|
||||
)
|
||||
|
||||
return { data, size: data.length }
|
||||
}
|
||||
|
||||
@Post('/')
|
||||
@Auth()
|
||||
async create(@Body() body: NoteModel) {
|
||||
// TODO clean cache
|
||||
// refreshKeyedCache(this.cacheManager)
|
||||
return await this.noteService.create(body)
|
||||
}
|
||||
|
||||
@Put('/:id')
|
||||
@Auth()
|
||||
async modify(@Body() body: NoteModel, @Param() params: MongoIdDto) {
|
||||
await this.noteService.updateById(params.id, body)
|
||||
return await this.noteService.model.findById(params.id)
|
||||
}
|
||||
|
||||
@Put('/:id')
|
||||
@HttpCode(204)
|
||||
@Auth()
|
||||
async patch(@Body() body: PartialNoteModel, @Param() params: MongoIdDto) {
|
||||
await this.noteService.updateById(params.id, body)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
59
src/modules/note/note.dto.ts
Normal file
59
src/modules/note/note.dto.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import { Transform } from 'class-transformer'
|
||||
import {
|
||||
IsDefined,
|
||||
IsEnum,
|
||||
IsInt,
|
||||
IsNotEmpty,
|
||||
IsNumber,
|
||||
IsOptional,
|
||||
IsString,
|
||||
Max,
|
||||
Min,
|
||||
ValidateIf,
|
||||
} from 'class-validator'
|
||||
import { PagerDto } from '~/shared/dto/pager.dto'
|
||||
|
||||
export class NoteQueryDto extends PagerDto {
|
||||
@IsOptional()
|
||||
@IsEnum(['title', 'created', 'modified', 'weather', 'mood'])
|
||||
sortBy?: string
|
||||
|
||||
@IsOptional()
|
||||
@IsEnum([1, -1])
|
||||
@ValidateIf((o) => o.sortBy)
|
||||
@Transform(({ value: v }) => v | 0)
|
||||
sortOrder?: 1 | -1
|
||||
}
|
||||
|
||||
export class PasswordQueryDto {
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
@IsNotEmpty()
|
||||
password?: string
|
||||
}
|
||||
|
||||
export class NoteMusicDto {
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
type: string
|
||||
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
id: string
|
||||
}
|
||||
export class ListQueryDto {
|
||||
@IsNumber()
|
||||
@Max(20)
|
||||
@Min(1)
|
||||
@Transform(({ value: v }) => parseInt(v))
|
||||
@IsOptional()
|
||||
size: number
|
||||
}
|
||||
|
||||
export class NidType {
|
||||
@IsInt()
|
||||
@Min(1)
|
||||
@IsDefined()
|
||||
@Transform(({ value: val }) => parseInt(val))
|
||||
nid: number
|
||||
}
|
||||
@@ -6,10 +6,21 @@
|
||||
* @FilePath: /server/libs/db/src/models/note.model.ts
|
||||
* Mark: Coding with Love
|
||||
*/
|
||||
import { PartialType } from '@nestjs/mapped-types'
|
||||
import { AutoIncrementID } from '@typegoose/auto-increment'
|
||||
import { index, modelOptions, plugin, prop } from '@typegoose/typegoose'
|
||||
import { IsNumber } from 'class-validator'
|
||||
import { Transform, Type } from 'class-transformer'
|
||||
import {
|
||||
IsBoolean,
|
||||
IsDate,
|
||||
IsNotEmpty,
|
||||
IsNumber,
|
||||
IsOptional,
|
||||
IsString,
|
||||
ValidateNested,
|
||||
} from 'class-validator'
|
||||
import { CountMixed, WriteBaseModel } from '~/shared/model/base.model'
|
||||
import { NoteMusicDto } from './note.dto'
|
||||
|
||||
@modelOptions({ schemaOptions: { id: false, _id: false } })
|
||||
export class Coordinate {
|
||||
@@ -47,38 +58,71 @@ export class NoteMusic {
|
||||
@index({ modified: -1 })
|
||||
@index({ nid: -1 })
|
||||
export class NoteModel extends WriteBaseModel {
|
||||
@prop()
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
@Transform(({ value: title }) => (title.length === 0 ? '无题' : title))
|
||||
title: string
|
||||
@prop({ required: false, unique: true })
|
||||
public nid: number
|
||||
|
||||
@prop({ default: false })
|
||||
@IsBoolean()
|
||||
@IsOptional()
|
||||
hide: boolean
|
||||
|
||||
@prop({
|
||||
select: false,
|
||||
})
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
@IsNotEmpty()
|
||||
@Transform(({ value: val }) => (String(val).length === 0 ? null : val))
|
||||
password?: string
|
||||
|
||||
@prop()
|
||||
@IsOptional()
|
||||
@IsDate()
|
||||
@Transform(({ value }) => (value ? new Date(value) : null))
|
||||
secret?: Date
|
||||
|
||||
@prop()
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
mood?: string
|
||||
|
||||
@prop()
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
weather?: string
|
||||
|
||||
@prop()
|
||||
@IsBoolean()
|
||||
@IsOptional()
|
||||
hasMemory?: boolean
|
||||
|
||||
@prop({ select: false, type: Coordinate })
|
||||
@ValidateNested()
|
||||
@Type(() => Coordinate)
|
||||
@IsOptional()
|
||||
coordinates?: Coordinate
|
||||
|
||||
@prop({ select: false })
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
location?: string
|
||||
|
||||
@prop({ type: CountMixed, default: { read: 0, like: 0 }, _id: false })
|
||||
count?: CountMixed
|
||||
|
||||
@prop({ type: [NoteMusic] })
|
||||
@ValidateNested({ each: true })
|
||||
@IsOptional()
|
||||
@Type(() => NoteMusicDto)
|
||||
music?: NoteMusic[]
|
||||
static get protectedKeys() {
|
||||
return ['nid', 'count'].concat(super.protectedKeys)
|
||||
}
|
||||
}
|
||||
|
||||
export class PartialNoteModel extends PartialType(NoteModel) {}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Module } from '@nestjs/common'
|
||||
import { GatewayModule } from '~/processors/gateway/gateway.module'
|
||||
import { NoteController } from './note.controller'
|
||||
import { NoteService } from './note.service'
|
||||
|
||||
@@ -6,5 +7,6 @@ import { NoteService } from './note.service'
|
||||
controllers: [NoteController],
|
||||
providers: [NoteService],
|
||||
exports: [NoteService],
|
||||
imports: [GatewayModule],
|
||||
})
|
||||
export class NoteModule {}
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
import { Injectable } from '@nestjs/common'
|
||||
import { DocumentType } from '@typegoose/typegoose'
|
||||
import { compareSync } from 'bcrypt'
|
||||
import { FilterQuery } from 'mongoose'
|
||||
import { InjectModel } from 'nestjs-typegoose'
|
||||
import { CannotFindException } from '~/common/exceptions/cant-find.exception'
|
||||
import { EventTypes } from '~/processors/gateway/events.types'
|
||||
import { WebEventsGateway } from '~/processors/gateway/web/events.gateway'
|
||||
import { ImageService } from '~/processors/helper/helper.image.service'
|
||||
import { deleteKeys } from '~/utils/index.util'
|
||||
import { NoteModel } from './note.model'
|
||||
|
||||
@Injectable()
|
||||
@@ -10,6 +15,8 @@ export class NoteService {
|
||||
constructor(
|
||||
@InjectModel(NoteModel)
|
||||
private readonly noteModel: MongooseModel<NoteModel>,
|
||||
private readonly imageService: ImageService,
|
||||
private readonly webGateway: WebEventsGateway,
|
||||
) {
|
||||
this.needCreateDefult()
|
||||
}
|
||||
@@ -54,6 +61,60 @@ export class NoteService {
|
||||
}
|
||||
}
|
||||
|
||||
checkPasswordToAccess(
|
||||
doc: DocumentType<NoteModel>,
|
||||
password: string,
|
||||
): boolean {
|
||||
const hasPassword = doc.password
|
||||
if (!hasPassword) {
|
||||
return true
|
||||
}
|
||||
if (!password) {
|
||||
return false
|
||||
}
|
||||
const isValid = compareSync(password, doc.password)
|
||||
return isValid
|
||||
}
|
||||
|
||||
public async create(document: NoteModel) {
|
||||
const doc = await this.noteModel.create(document)
|
||||
process.nextTick(async () => {
|
||||
await Promise.all([
|
||||
this.imageService.recordImageDimensions(this.noteModel, doc._id),
|
||||
doc.hide || doc.password
|
||||
? null
|
||||
: this.webGateway.broadcast(EventTypes.NOTE_CREATE, doc.toJSON()),
|
||||
])
|
||||
})
|
||||
|
||||
return doc
|
||||
}
|
||||
|
||||
public async updateById(id: string, doc: Partial<NoteModel>) {
|
||||
console.log(NoteModel.protectedKeys)
|
||||
|
||||
deleteKeys(doc, NoteModel.protectedKeys as any)
|
||||
await this.noteModel.updateOne(
|
||||
{
|
||||
_id: id,
|
||||
},
|
||||
{ ...doc, modified: new Date() },
|
||||
)
|
||||
process.nextTick(async () => {
|
||||
Promise.all([
|
||||
this.imageService.recordImageDimensions(this.noteModel, id),
|
||||
this.model.findById(id).then((doc) => {
|
||||
if (doc.hide || doc.password) {
|
||||
return this.webGateway.broadcast(EventTypes.NOTE_UPDATE, doc)
|
||||
}
|
||||
}),
|
||||
])
|
||||
|
||||
// TODO clean cache
|
||||
// refreshKeyedCache(this.cacheManager)
|
||||
})
|
||||
}
|
||||
|
||||
async needCreateDefult() {
|
||||
await this.noteModel.countDocuments({}).then((count) => {
|
||||
if (!count) {
|
||||
|
||||
@@ -10,7 +10,6 @@ import {
|
||||
Post,
|
||||
Put,
|
||||
Query,
|
||||
UseGuards,
|
||||
} from '@nestjs/common'
|
||||
import { ApiOperation } from '@nestjs/swagger'
|
||||
import { Types } from 'mongoose'
|
||||
@@ -26,14 +25,12 @@ import {
|
||||
addConditionToSeeHideContent,
|
||||
addYearCondition,
|
||||
} from '~/utils/query.util'
|
||||
import { RolesGuard } from '../auth/roles.guard'
|
||||
import { CategoryAndSlug, PostQueryDto } from './post.dto'
|
||||
import { PartialPostModel, PostModel } from './post.model'
|
||||
import { PostService } from './post.service'
|
||||
|
||||
@Controller('posts')
|
||||
@ApiName
|
||||
@UseGuards(RolesGuard)
|
||||
export class PostController {
|
||||
constructor(private readonly postService: PostService) {}
|
||||
|
||||
|
||||
@@ -1,13 +1,6 @@
|
||||
import { PartialType } from '@nestjs/mapped-types'
|
||||
import { ApiHideProperty, ApiProperty } from '@nestjs/swagger'
|
||||
import {
|
||||
index,
|
||||
modelOptions,
|
||||
plugin,
|
||||
prop,
|
||||
Ref,
|
||||
Severity,
|
||||
} from '@typegoose/typegoose'
|
||||
import { index, modelOptions, prop, Ref, Severity } from '@typegoose/typegoose'
|
||||
import {
|
||||
ArrayUnique,
|
||||
IsBoolean,
|
||||
@@ -16,7 +9,6 @@ import {
|
||||
IsOptional,
|
||||
IsString,
|
||||
} from 'class-validator'
|
||||
import Paginate from 'mongoose-paginate-v2'
|
||||
import { CountMixed as Count, WriteBaseModel } from '~/shared/model/base.model'
|
||||
import { CategoryModel as Category } from '../category/category.model'
|
||||
|
||||
@@ -24,7 +16,6 @@ import { CategoryModel as Category } from '../category/category.model'
|
||||
@index({ modified: -1 })
|
||||
@index({ text: 'text' })
|
||||
@modelOptions({ options: { customName: 'Post', allowMixed: Severity.ALLOW } })
|
||||
@plugin(Paginate)
|
||||
export class PostModel extends WriteBaseModel {
|
||||
@prop({ trim: true, unique: true, required: true })
|
||||
@IsString()
|
||||
@@ -72,6 +63,10 @@ export class PostModel extends WriteBaseModel {
|
||||
@prop({ type: Count, default: { read: 0, like: 0 }, _id: false })
|
||||
@ApiHideProperty()
|
||||
count?: Count
|
||||
|
||||
static get protectedKeys() {
|
||||
return ['count'].concat(super.protectedKeys)
|
||||
}
|
||||
}
|
||||
|
||||
export class PartialPostModel extends PartialType(PostModel) {}
|
||||
|
||||
@@ -111,7 +111,7 @@ export class PostService {
|
||||
{
|
||||
_id: id,
|
||||
},
|
||||
omit(data, ['id', '_id', 'created']),
|
||||
omit(data, PostModel.protectedKeys),
|
||||
)
|
||||
process.nextTick(async () => {
|
||||
// 更新图片信息缓存
|
||||
|
||||
@@ -17,7 +17,6 @@ import { ApiName } from '~/common/decorator/openapi.decorator'
|
||||
import { IsMaster } from '~/common/decorator/role.decorator'
|
||||
import { getAvatar } from '~/utils/index.util'
|
||||
import { AuthService } from '../auth/auth.service'
|
||||
import { RolesGuard } from '../auth/roles.guard'
|
||||
import { LoginDto, UserDto, UserPatchDto } from './user.dto'
|
||||
import { UserDocument, UserModel } from './user.model'
|
||||
import { UserService } from './user.service'
|
||||
@@ -32,7 +31,6 @@ export class UserController {
|
||||
|
||||
@Get()
|
||||
@ApiOperation({ summary: '获取主人信息' })
|
||||
@UseGuards(RolesGuard)
|
||||
async getMasterInfo(@IsMaster() isMaster: boolean) {
|
||||
return await this.userService.getMasterInfo(isMaster)
|
||||
}
|
||||
@@ -73,7 +71,6 @@ export class UserController {
|
||||
@Get('check_logged')
|
||||
@ApiOperation({ summary: '判断当前 Token 是否有效 ' })
|
||||
@ApiBearerAuth()
|
||||
@UseGuards(RolesGuard)
|
||||
@HttpCache({ disable: true })
|
||||
checkLogged(@IsMaster() isMaster: boolean) {
|
||||
return { ok: +isMaster, isGuest: !isMaster }
|
||||
|
||||
@@ -1,10 +1,18 @@
|
||||
import { ApiHideProperty } from '@nestjs/swagger'
|
||||
import { modelOptions, prop } from '@typegoose/typegoose'
|
||||
import { modelOptions, plugin, prop } from '@typegoose/typegoose'
|
||||
import { IsBoolean, IsNotEmpty, IsOptional, IsString } from 'class-validator'
|
||||
import mongooseLeanVirtuals from 'mongoose-lean-virtuals'
|
||||
import Paginate from 'mongoose-paginate-v2'
|
||||
|
||||
@plugin(mongooseLeanVirtuals)
|
||||
@plugin(Paginate)
|
||||
export class BaseModel {
|
||||
@ApiHideProperty()
|
||||
created?: Date
|
||||
|
||||
static get protectedKeys() {
|
||||
return ['created']
|
||||
}
|
||||
}
|
||||
|
||||
export interface Paginator {
|
||||
@@ -57,6 +65,10 @@ export abstract class BaseCommentIndexModel extends BaseModel {
|
||||
@IsBoolean()
|
||||
@IsOptional()
|
||||
allowComment: boolean
|
||||
|
||||
static get protectedKeys() {
|
||||
return ['commentsIndex'].concat(super.protectedKeys)
|
||||
}
|
||||
}
|
||||
|
||||
@modelOptions({
|
||||
@@ -84,6 +96,10 @@ export abstract class WriteBaseModel extends BaseCommentIndexModel {
|
||||
@prop({ default: null })
|
||||
@ApiHideProperty()
|
||||
modified: Date | null
|
||||
|
||||
static get protectedKeys() {
|
||||
return super.protectedKeys
|
||||
}
|
||||
}
|
||||
|
||||
@modelOptions({
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { isObject } from 'lodash'
|
||||
|
||||
export * from './ip.util'
|
||||
export const isDev = process.env.NODE_ENV == 'development'
|
||||
|
||||
@@ -44,3 +46,17 @@ export function arrDifference(a1: string[], a2: string[]) {
|
||||
|
||||
return diff
|
||||
}
|
||||
|
||||
export const deleteKeys = <T extends KV>(
|
||||
target: T,
|
||||
keys: readonly (keyof T)[],
|
||||
): Partial<T> => {
|
||||
if (!isObject(target)) {
|
||||
throw new TypeError('target must be Object, got ' + target)
|
||||
}
|
||||
|
||||
for (const key of keys) {
|
||||
Reflect.deleteProperty(target, key)
|
||||
}
|
||||
return target
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user