chore: replace redis with ioredis

This commit is contained in:
Innei
2021-08-31 20:12:30 +08:00
parent 44b835d177
commit f3fb7ecb26
16 changed files with 139 additions and 6931 deletions

View File

@@ -1,5 +1,4 @@
import {
CacheInterceptor,
MiddlewareConsumer,
Module,
NestModule,
@@ -60,8 +59,8 @@ export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(AnalyzeMiddleware)
.forRoutes({ path: '*', method: RequestMethod.GET })
.forRoutes({ path: '(.*?)', method: RequestMethod.GET })
.apply(SkipBrowserDefaultRequestMiddleware, SecurityMiddleware)
.forRoutes({ path: '*', method: RequestMethod.ALL })
.forRoutes({ path: '(.*?)', method: RequestMethod.ALL })
}
}

View File

@@ -7,11 +7,11 @@ import {
Logger,
} from '@nestjs/common'
import { FastifyReply, FastifyRequest } from 'fastify'
import { writeFileSync } from 'fs'
import { resolve } from 'path'
import { LOGGER_DIR } from '~/constants/path.constant'
import { isDev } from '~/utils/index.util'
import { getIp } from '../../utils/ip.util'
import { writeFileSync } from 'fs'
import { LOGGER_DIR } from '~/constants/path.constant'
import { resolve } from 'path'
type myError = {
readonly status: number
readonly statusCode?: number

View File

@@ -1,4 +1,4 @@
export enum RedisNames {
export enum RedisKeys {
Access = 'access',
Like = 'like',
Read = 'read',

View File

@@ -1,17 +1,17 @@
import { Logger } from '@nestjs/common'
import { NestFactory } from '@nestjs/core'
import { AppModule } from './app.module'
import { NestFastifyApplication } from '@nestjs/platform-fastify'
import { fastifyApp } from './common/adapt/fastify'
import { isDev } from './utils/index.util'
import { CacheInterceptor, Logger } from '@nestjs/common'
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger'
import { CROSS_DOMAIN } from './app.config'
import { AppModule } from './app.module'
import { fastifyApp } from './common/adapt/fastify'
import { SpiderGuard } from './common/guard/spider.guard'
import { LoggingInterceptor } from './common/interceptors/logging.interceptor'
import {
JSONSerializeInterceptor,
ResponseInterceptor,
} from './common/interceptors/response.interceptors'
import { SpiderGuard } from './common/guard/spider.guard'
import { LoggingInterceptor } from './common/interceptors/logging.interceptor'
import { isDev } from './utils/index.util'
// const PORT = parseInt(process.env.PORT) || 2333
const PORT = 2333
const APIVersion = 1

View File

@@ -9,17 +9,18 @@ import {
SerializeOptions,
UseGuards,
} from '@nestjs/common'
import { AuthGuard } from '@nestjs/passport'
import { ApiBearerAuth, ApiOperation, ApiTags } from '@nestjs/swagger'
import { HttpCache } from '~/common/decorator/cache.decorator'
import { CurrentUser } from '~/common/decorator/current-user.decorator'
import { IpLocation, IpRecord } from '~/common/decorator/ip.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 './dto/user.dto'
import { UserDocument, UserModel } from './user.model'
import { UserService } from './user.service'
import { IsMaster } from '~/common/decorator/role.decorator'
import { AuthGuard } from '@nestjs/passport'
import { getAvatar } from '~/utils/index.util'
import { LoginDto, UserDto, UserPatchDto } from './dto/user.dto'
@ApiTags('User Routes')
@Controller(['master', 'user'])
@@ -50,6 +51,7 @@ export class UserController {
@HttpCode(HttpStatus.OK)
@ApiOperation({ summary: '登录' })
@UseGuards(AuthGuard('local'))
@HttpCache({ disable: true })
async login(
@Body() dto: LoginDto,
@CurrentUser() user: UserDocument,
@@ -76,14 +78,16 @@ export class UserController {
@ApiOperation({ summary: '判断当前 Token 是否有效 ' })
@ApiBearerAuth()
@UseGuards(RolesGuard)
@HttpCache({ disable: true })
checkLogged(@IsMaster() isMaster: boolean) {
return { ok: Number(isMaster), isGuest: !isMaster }
return { ok: +isMaster, isGuest: !isMaster }
}
@Patch()
@ApiOperation({ summary: '修改主人的信息 ' })
@ApiBearerAuth()
@UseGuards(AuthGuard('jwt'))
@HttpCache({ disable: true })
async patchMasterData(
@Body() body: UserPatchDto,
@CurrentUser() user: UserDocument,

View File

@@ -6,9 +6,13 @@ import {
} from '@nestjs/common'
import { ReturnModelType } from '@typegoose/typegoose'
import { compareSync } from 'bcrypt'
import dayjs from 'dayjs'
import { nanoid } from 'nanoid'
import { InjectModel } from 'nestjs-typegoose'
import { RedisKeys } from '~/constants/cache.constant'
import { CacheService } from '~/processors/cache/cache.service'
import { getAvatar } from '~/utils/index.util'
import { getRedisKey } from '~/utils/redis.util'
import { AuthService } from '../auth/auth.service'
import { UserDocument, UserModel } from './user.model'
@@ -19,6 +23,7 @@ export class UserService {
@InjectModel(UserModel)
private readonly userModel: ReturnModelType<typeof UserModel>,
private readonly authService: AuthService,
private readonly redis: CacheService,
) {}
async getMasterInfo(getLoginIp = false) {
@@ -100,30 +105,19 @@ export class UserService {
lastLoginIp: ip,
})
// save to redis
new Promise(async (resolve) => {
// const redisClient = this.redisService.getClient(RedisNames.LoginRecord)
// const dateFormat = dayjs().format('YYYY-MM-DD')
// const value = JSON.parse(
// (await redisClient.get(dateFormat)) || '[]',
// ) as LoginRecord[]
// const stringify = fastJson({
// title: 'login-record schema',
// type: 'array',
// items: {
// type: 'object',
// properties: {
// ip: { type: 'string' },
// date: { type: 'string' },
// },
// },
// })
// await redisClient.set(
// dateFormat,
// stringify(value.concat({ date: new Date().toISOString(), ip })),
// )
// resolve(null)
process.nextTick(async () => {
const redisClient = this.redis.getClient()
const dateFormat = dayjs().format('YYYY-MM-DD')
await redisClient.sadd(
getRedisKey(RedisKeys.LoginRecord, dateFormat),
JSON.stringify({ date: new Date().toISOString(), ip }),
)
})
this.Logger.warn('主人已登录, IP: ' + ip)
return PrevFootstep
}
// TODO 获取最近登陆次数 时间 从 Redis 取
}

View File

@@ -10,7 +10,8 @@ import {
CacheOptionsFactory,
Injectable,
} from '@nestjs/common'
import redisStore from 'cache-manager-redis-store'
// import redisStore from 'cache-manager-redis-store'
import redisStore from 'cache-manager-ioredis'
import { ClientOpts } from 'redis'
import { REDIS } from '~/app.config'

View File

@@ -1,104 +1,39 @@
/**
* Cache service.
* @file Cache 缓存模块服务
* @module processor/cache/service
* @author Surmon <https://github.com/surmon-china>
*/
import { CACHE_MANAGER, Inject, Injectable, Logger } from '@nestjs/common'
import { RedisClient } from 'redis'
import { Cache } from 'cache-manager'
import { Redis } from 'ioredis'
// Cache 客户端管理器
export interface ICacheManager {
store: {
getClient(): RedisClient
}
get(key: TCacheKey): any
set(key: TCacheKey, value: string, options?: { ttl: number }): any
}
// 获取器
export type TCacheKey = string
export type TCacheResult<T> = Promise<T>
// IO 模式通用返回结构
export interface ICacheIoResult<T> {
get(): TCacheResult<T>
update(): TCacheResult<T>
}
// Promise 模式参数
export interface ICachePromiseOption<T> {
key: TCacheKey
promise(): TCacheResult<T>
}
// Promise & IO 模式参数
export interface ICachePromiseIoOption<T> extends ICachePromiseOption<T> {
ioMode?: boolean
}
// Interval & Timeout 超时模式参数(毫秒)
export interface ICacheIntervalTimeoutOption {
error?: number
success?: number
}
// Interval & Timing 定时模式参数(毫秒)
export interface ICacheIntervalTimingOption {
error: number
schedule: any
}
// Interval 模式参数
export interface ICacheIntervalOption<T> {
key: TCacheKey
promise(): TCacheResult<T>
timeout?: ICacheIntervalTimeoutOption
timing?: ICacheIntervalTimingOption
}
// Interval 模式返回类型
export type TCacheIntervalResult<T> = () => TCacheResult<T>
// Interval & IO 模式参数
export interface ICacheIntervalIOOption<T> extends ICacheIntervalOption<T> {
ioMode?: boolean
}
/**
* @class CacheService
* @classdesc 承载缓存服务
* @example CacheService.get(CacheKey).then()
* @example CacheService.set(CacheKey).then()
* @example CacheService.promise({ option })()
* @example CacheService.interval({ option })()
*/
@Injectable()
export class CacheService {
private cache!: ICacheManager
private cache!: Cache
private logger = new Logger(CacheService.name)
constructor(@Inject(CACHE_MANAGER) cache: ICacheManager) {
constructor(@Inject(CACHE_MANAGER) cache: Cache) {
console.log(cache)
this.cache = cache
this.redisClient.on('ready', () => {
this.logger.log('Redis 已准备好!')
})
}
private get redisClient(): RedisClient {
private get redisClient(): Redis {
// @ts-expect-error
return this.cache.store.getClient()
}
// 客户端是否可用
private get checkCacheServiceAvailable(): boolean {
return this.redisClient.connected
}
public get<T>(key: TCacheKey): TCacheResult<T> {
if (!this.checkCacheServiceAvailable) {
return Promise.reject('缓存客户端没准备好!')
}
return this.cache.get(key)
}
@@ -107,51 +42,10 @@ export class CacheService {
value: any,
options?: { ttl: number },
): TCacheResult<T> {
if (!this.checkCacheServiceAvailable) {
return Promise.reject('缓存客户端没准备好!')
}
return this.cache.set(key, value, options)
}
public getClient() {
if (!this.checkCacheServiceAvailable) {
throw '缓存客户端没准备好!'
}
return this.cache
}
/**
* @function promise
* @description 被动更新 | 双向同步 模式Promise -> Redis
* @example CacheService.promise({ key: CacheKey, promise() }) -> promise()
* @example CacheService.promise({ key: CacheKey, promise(), ioMode: true }) -> { get: promise(), update: promise() }
*/
promise<T>(options: ICachePromiseOption<T>): TCacheResult<T>
promise<T>(options: ICachePromiseIoOption<T>): ICacheIoResult<T>
promise(options) {
const { key, promise, ioMode = false } = options
// 包装任务
const doPromiseTask = () => {
return promise().then((data) => {
this.set(key, data)
return data
})
}
// Promise 拦截模式(返回死数据)
const handlePromiseMode = () => {
return this.get(key).then((value) => {
return value !== null && value !== undefined ? value : doPromiseTask()
})
}
// 双向同步模式(返回获取器和更新器)
const handleIoMode = () => ({
get: handlePromiseMode,
update: doPromiseTask,
})
return ioMode ? handleIoMode() : handlePromiseMode()
return this.redisClient
}
}

View File

@@ -7,9 +7,8 @@
* @Coding with Love
*/
import { FastifyRequest } from 'fastify'
import type { FastifyRequest } from 'fastify'
import { IncomingMessage } from 'http'
export const getIp = (request: FastifyRequest | IncomingMessage) => {
const _ = request as any

7
src/utils/redis.util.ts Normal file
View File

@@ -0,0 +1,7 @@
import { RedisKeys } from '~/constants/cache.constant'
export const getRedisKey = (key: RedisKeys, ...concatKeys: string[]) => {
return `mx:${key}${
concatKeys && concatKeys.length ? '_' + concatKeys.join('_') : ''
}`
}