feat: add admin db query

This commit is contained in:
Innei
2022-03-06 18:24:45 +08:00
parent 331c192e12
commit d9cadc896f
9 changed files with 83 additions and 19 deletions

View File

@@ -119,6 +119,7 @@
"passport": "0.5.2", "passport": "0.5.2",
"passport-jwt": "4.0.0", "passport-jwt": "4.0.0",
"pluralize": "*", "pluralize": "*",
"qs": "6.10.3",
"reflect-metadata": "0.1.13", "reflect-metadata": "0.1.13",
"rxjs": "7.5.4", "rxjs": "7.5.4",
"snakecase-keys": "5.1.2", "snakecase-keys": "5.1.2",
@@ -148,12 +149,14 @@
"@types/node": "16.11.26", "@types/node": "16.11.26",
"@types/nodemailer": "6.4.4", "@types/nodemailer": "6.4.4",
"@types/passport-jwt": "3.0.6", "@types/passport-jwt": "3.0.6",
"@types/qs": "6.9.7",
"@types/ua-parser-js": "0.7.36", "@types/ua-parser-js": "0.7.36",
"@vercel/ncc": "0.33.3", "@vercel/ncc": "0.33.3",
"cron": "*", "cron": "*",
"cross-env": "7.0.3", "cross-env": "7.0.3",
"eslint": "*", "eslint": "*",
"husky": "7.0.4", "husky": "7.0.4",
"i": "0.3.7",
"ioredis": "4.28.5", "ioredis": "4.28.5",
"jest": "27.5.1", "jest": "27.5.1",
"lint-staged": "12.3.5", "lint-staged": "12.3.5",

18
pnpm-lock.yaml generated
View File

@@ -44,6 +44,7 @@ specifiers:
'@types/node': 16.11.26 '@types/node': 16.11.26
'@types/nodemailer': 6.4.4 '@types/nodemailer': 6.4.4
'@types/passport-jwt': 3.0.6 '@types/passport-jwt': 3.0.6
'@types/qs': 6.9.7
'@types/ua-parser-js': 0.7.36 '@types/ua-parser-js': 0.7.36
'@vercel/ncc': 0.33.3 '@vercel/ncc': 0.33.3
algoliasearch: 4.12.2 algoliasearch: 4.12.2
@@ -68,6 +69,7 @@ specifiers:
get-image-colors: 4.0.1 get-image-colors: 4.0.1
html-minifier: 4.0.0 html-minifier: 4.0.0
husky: 7.0.4 husky: 7.0.4
i: 0.3.7
image-size: 1.0.1 image-size: 1.0.1
inquirer: '*' inquirer: '*'
ioredis: 4.28.5 ioredis: 4.28.5
@@ -94,6 +96,7 @@ specifiers:
passport-jwt: 4.0.0 passport-jwt: 4.0.0
pluralize: '*' pluralize: '*'
prettier: 2.5.1 prettier: 2.5.1
qs: 6.10.3
redis-memory-server: 0.5.0 redis-memory-server: 0.5.0
reflect-metadata: 0.1.13 reflect-metadata: 0.1.13
rimraf: 3.0.2 rimraf: 3.0.2
@@ -169,6 +172,7 @@ dependencies:
passport: 0.5.2 passport: 0.5.2
passport-jwt: 4.0.0 passport-jwt: 4.0.0
pluralize: 8.0.0 pluralize: 8.0.0
qs: 6.10.3
reflect-metadata: 0.1.13 reflect-metadata: 0.1.13
rxjs: 7.5.4 rxjs: 7.5.4
snakecase-keys: 5.1.2 snakecase-keys: 5.1.2
@@ -202,12 +206,14 @@ devDependencies:
'@types/node': 16.11.26 '@types/node': 16.11.26
'@types/nodemailer': 6.4.4 '@types/nodemailer': 6.4.4
'@types/passport-jwt': 3.0.6 '@types/passport-jwt': 3.0.6
'@types/qs': 6.9.7
'@types/ua-parser-js': 0.7.36 '@types/ua-parser-js': 0.7.36
'@vercel/ncc': 0.33.3 '@vercel/ncc': 0.33.3
cron: 1.7.2 cron: 1.7.2
cross-env: 7.0.3 cross-env: 7.0.3
eslint: 8.10.0 eslint: 8.10.0
husky: 7.0.4 husky: 7.0.4
i: 0.3.7
ioredis: 4.28.5 ioredis: 4.28.5
jest: 27.5.1_ts-node@10.6.0 jest: 27.5.1_ts-node@10.6.0
lint-staged: 12.3.5 lint-staged: 12.3.5
@@ -4701,6 +4707,11 @@ packages:
hasBin: true hasBin: true
dev: true dev: true
/i/0.3.7:
resolution: {integrity: sha512-FYz4wlXgkQwIPqhzC5TdNMLSE5+GS1IIDJZY/1ZiEPCT2S3COUVZeT5OW4BmW4r5LHLQuOosSwsvnroG9GR59Q==}
engines: {node: '>=0.4'}
dev: true
/iconv-lite/0.4.24: /iconv-lite/0.4.24:
resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
@@ -6985,6 +6996,13 @@ packages:
resolution: {integrity: sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==} resolution: {integrity: sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==}
engines: {node: '>=6'} engines: {node: '>=6'}
/qs/6.10.3:
resolution: {integrity: sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==}
engines: {node: '>=0.6'}
dependencies:
side-channel: 1.0.4
dev: false
/qs/6.5.2: /qs/6.5.2:
resolution: {integrity: sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==} resolution: {integrity: sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==}
engines: {node: '>=0.6'} engines: {node: '>=0.6'}

View File

@@ -7,6 +7,7 @@ import { AnalyzeInterceptor } from './common/interceptors/analyze.interceptor'
import { HttpCacheInterceptor } from './common/interceptors/cache.interceptor' import { HttpCacheInterceptor } from './common/interceptors/cache.interceptor'
import { CountingInterceptor } from './common/interceptors/counting.interceptor' import { CountingInterceptor } from './common/interceptors/counting.interceptor'
import { JSONSerializeInterceptor } from './common/interceptors/json-serialize.interceptor' import { JSONSerializeInterceptor } from './common/interceptors/json-serialize.interceptor'
import { QueryInterceptor } from './common/interceptors/query.interceptor'
import { ResponseInterceptor } from './common/interceptors/response.interceptor' import { ResponseInterceptor } from './common/interceptors/response.interceptor'
import { AttachHeaderTokenMiddleware } from './common/middlewares/attach-auth.middleware' import { AttachHeaderTokenMiddleware } from './common/middlewares/attach-auth.middleware'
import { AggregateModule } from './modules/aggregate/aggregate.module' import { AggregateModule } from './modules/aggregate/aggregate.module'
@@ -85,7 +86,12 @@ import { LoggerModule } from './processors/logger/logger.module'
providers: [ providers: [
{ {
provide: APP_INTERCEPTOR, provide: APP_INTERCEPTOR,
useClass: HttpCacheInterceptor, useClass: QueryInterceptor,
},
{
provide: APP_INTERCEPTOR,
useClass: HttpCacheInterceptor, // 4
}, },
{ {
provide: APP_INTERCEPTOR, provide: APP_INTERCEPTOR,
@@ -93,15 +99,15 @@ import { LoggerModule } from './processors/logger/logger.module'
}, },
{ {
provide: APP_INTERCEPTOR, provide: APP_INTERCEPTOR,
useClass: CountingInterceptor, useClass: CountingInterceptor, // 3
}, },
{ {
provide: APP_INTERCEPTOR, provide: APP_INTERCEPTOR,
useClass: JSONSerializeInterceptor, useClass: JSONSerializeInterceptor, // 2
}, },
{ {
provide: APP_INTERCEPTOR, provide: APP_INTERCEPTOR,
useClass: ResponseInterceptor, useClass: ResponseInterceptor, // 1
}, },
{ {

View File

@@ -11,7 +11,6 @@ import {
NestInterceptor, NestInterceptor,
} from '@nestjs/common' } from '@nestjs/common'
import { ReturnModelType } from '@typegoose/typegoose' import { ReturnModelType } from '@typegoose/typegoose'
import { FastifyRequest } from 'fastify'
import isbot from 'isbot' import isbot from 'isbot'
import { InjectModel } from 'nestjs-typegoose' import { InjectModel } from 'nestjs-typegoose'
import { Observable } from 'rxjs' import { Observable } from 'rxjs'
@@ -21,6 +20,7 @@ import { RedisKeys } from '~/constants/cache.constant'
import { AnalyzeModel } from '~/modules/analyze/analyze.model' import { AnalyzeModel } from '~/modules/analyze/analyze.model'
import { OptionModel } from '~/modules/configs/configs.model' import { OptionModel } from '~/modules/configs/configs.model'
import { CacheService } from '~/processors/cache/cache.service' import { CacheService } from '~/processors/cache/cache.service'
import { getNestExecutionContextRequest } from '~/utils'
import { getIp } from '~/utils/ip.util' import { getIp } from '~/utils/ip.util'
import { getRedisKey } from '~/utils/redis.util' import { getRedisKey } from '~/utils/redis.util'
@@ -47,7 +47,7 @@ export class AnalyzeInterceptor implements NestInterceptor {
next: CallHandler<any>, next: CallHandler<any>,
): Promise<Observable<any>> { ): Promise<Observable<any>> {
const call$ = next.handle() const call$ = next.handle()
const request = this.getRequest(context) const request = getNestExecutionContextRequest(context)
if (!request) { if (!request) {
return call$ return call$
} }
@@ -130,12 +130,4 @@ export class AnalyzeInterceptor implements NestInterceptor {
return call$ return call$
} }
getRequest(context: ExecutionContext) {
const req = context.switchToHttp().getRequest<KV>()
if (req) {
return req as FastifyRequest & { user?: any }
}
return null
}
} }

View File

@@ -0,0 +1,35 @@
import {
CallHandler,
ExecutionContext,
Injectable,
NestInterceptor,
} from '@nestjs/common'
import qs from 'qs'
import { Observable } from 'rxjs'
import { getNestExecutionContextRequest } from '~/utils'
/** 此拦截器用于转换 req.query.query -> js object用于直接数据库查询需要鉴权 */
@Injectable()
export class QueryInterceptor implements NestInterceptor {
intercept(
context: ExecutionContext,
next: CallHandler<any>,
): Observable<any> | Promise<Observable<any>> {
const request = getNestExecutionContextRequest(context)
const query = request.query as any
if (!query) {
return next.handle()
}
const queryObj = query.db_query
if (request.user) {
;(request.query as any).db_query =
typeof queryObj === 'string' ? qs.parse(queryObj) : queryObj
} else {
delete (request.query as any).db_query
}
return next.handle()
}
}

View File

@@ -1,7 +1,8 @@
import { Body, Controller, Post, Query } from '@nestjs/common' import { Body, Controller, Get, Post, Query } from '@nestjs/common'
import { AdminEventsGateway } from '~/processors/gateway/admin/events.gateway' import { AdminEventsGateway } from '~/processors/gateway/admin/events.gateway'
import { EventTypes } from '~/processors/gateway/events.types' import { EventTypes } from '~/processors/gateway/events.types'
import { WebEventsGateway } from '~/processors/gateway/web/events.gateway' import { WebEventsGateway } from '~/processors/gateway/web/events.gateway'
import { PagerDto } from '~/shared/dto/pager.dto'
@Controller('debug') @Controller('debug')
export class DebugController { export class DebugController {
@@ -9,6 +10,10 @@ export class DebugController {
private readonly webEvent: WebEventsGateway, private readonly webEvent: WebEventsGateway,
private readonly adminEvent: AdminEventsGateway, private readonly adminEvent: AdminEventsGateway,
) {} ) {}
@Get('qs')
async qs(@Query() query: PagerDto) {
return query
}
@Post('/events') @Post('/events')
async sendEvent( async sendEvent(

View File

@@ -61,12 +61,12 @@ export class NoteController {
@Paginator @Paginator
@ApiOperation({ summary: '获取记录带分页器' }) @ApiOperation({ summary: '获取记录带分页器' })
async getNotes(@IsMaster() isMaster: boolean, @Query() query: NoteQueryDto) { async getNotes(@IsMaster() isMaster: boolean, @Query() query: NoteQueryDto) {
const { size, select, page, sortBy, sortOrder, year } = query const { size, select, page, sortBy, sortOrder, year, db_query } = query
const condition = { const condition = {
...addHidePasswordAndHideCondition(isMaster), ...addHidePasswordAndHideCondition(isMaster),
...addYearCondition(year), ...addYearCondition(year),
} }
return await this.noteService.model.paginate(condition, { return await this.noteService.model.paginate(db_query ?? condition, {
limit: size, limit: size,
page, page,
select: isMaster select: isMaster

View File

@@ -11,7 +11,11 @@ import {
ValidateIf, ValidateIf,
} from 'class-validator' } from 'class-validator'
export class PagerDto { class DbQueryDto {
@IsOptional()
db_query?: any
}
export class PagerDto extends DbQueryDto {
@Min(1) @Min(1)
@Max(50) @Max(50)
@IsInt() @IsInt()

View File

@@ -1,8 +1,9 @@
import { ExecutionContext } from '@nestjs/common' import { ExecutionContext } from '@nestjs/common'
import type { FastifyRequest } from 'fastify' import type { FastifyRequest } from 'fastify'
import type { UserModel } from '~/modules/user/user.model'
export function getNestExecutionContextRequest( export function getNestExecutionContextRequest(
context: ExecutionContext, context: ExecutionContext,
): FastifyRequest & KV { ): FastifyRequest & { user?: UserModel } & KV {
return context.switchToHttp().getRequest<FastifyRequest>() return context.switchToHttp().getRequest<FastifyRequest>()
// if (req) { // if (req) {
// return req // return req