From a42aa89aec567845d26748d43fae5554e8a29e6c Mon Sep 17 00:00:00 2001 From: vchikalkin Date: Tue, 31 Oct 2023 21:58:39 +0300 Subject: [PATCH] apps/api: move redis caching feature to ldap module --- apps/api/src/ldap/ldap.controller.ts | 9 ++++++++ apps/api/src/ldap/ldap.module.ts | 14 ++++++++++-- apps/api/src/ldap/ldap.service.ts | 31 +++++++++++++++++++++----- apps/api/src/users/users.cache.ts | 21 ----------------- apps/api/src/users/users.controller.ts | 13 +---------- apps/api/src/users/users.module.ts | 19 +++------------- apps/api/src/users/users.service.ts | 23 +------------------ 7 files changed, 52 insertions(+), 78 deletions(-) delete mode 100644 apps/api/src/users/users.cache.ts diff --git a/apps/api/src/ldap/ldap.controller.ts b/apps/api/src/ldap/ldap.controller.ts index 0380abd..c1fa6b8 100644 --- a/apps/api/src/ldap/ldap.controller.ts +++ b/apps/api/src/ldap/ldap.controller.ts @@ -71,4 +71,13 @@ export class LdapController { return reply.status(HttpStatus.UNAUTHORIZED).send(); } } + + @Get('/get-user') + async getUser(@Req() req: FastifyRequest, @Res() reply: FastifyReply) { + const token = req.cookies[env.COOKIE_TOKEN_NAME]; + + const user = await this.ldapService.getUser(token); + + return reply.send(user); + } } diff --git a/apps/api/src/ldap/ldap.module.ts b/apps/api/src/ldap/ldap.module.ts index 765ef9e..58b5265 100644 --- a/apps/api/src/ldap/ldap.module.ts +++ b/apps/api/src/ldap/ldap.module.ts @@ -1,11 +1,21 @@ -import { UsersModule } from '../users/users.module'; import { LdapController } from './ldap.controller'; import { LdapService } from './ldap.service'; +import { CacheModule } from '@nestjs/cache-manager'; import { Module } from '@nestjs/common'; +import * as redisStore from 'cache-manager-ioredis'; +import type { RedisOptions } from 'ioredis'; +import { env } from 'src/config/env'; @Module({ controllers: [LdapController], - imports: [UsersModule], + imports: [ + CacheModule.register({ + host: env.REDIS_HOST, + port: env.REDIS_PORT, + store: redisStore, + ttl: env.API_CACHE_TTL, + }), + ], providers: [LdapService], }) // eslint-disable-next-line @typescript-eslint/no-extraneous-class diff --git a/apps/api/src/ldap/ldap.service.ts b/apps/api/src/ldap/ldap.service.ts index e1d520c..dc28f36 100644 --- a/apps/api/src/ldap/ldap.service.ts +++ b/apps/api/src/ldap/ldap.service.ts @@ -1,14 +1,15 @@ -import { UsersCache } from '../users/users.cache'; import type { DecodedToken, TokenPayload } from './types/jwt'; -import { Injectable } from '@nestjs/common'; +import { CACHE_MANAGER } from '@nestjs/cache-manager'; +import { Inject, Injectable } from '@nestjs/common'; import { JwtService } from '@nestjs/jwt'; +import { Cache } from 'cache-manager'; import { env } from 'src/config/env'; import * as ldap from 'src/utils/ldap'; @Injectable() export class LdapService { constructor( - private readonly usersCache: UsersCache, + @Inject(CACHE_MANAGER) private readonly cacheManager: Cache, private readonly jwtService: JwtService ) {} @@ -16,7 +17,7 @@ export class LdapService { const user = await ldap.authenticate(login, password); const { username } = user; - await this.usersCache.addUser(username, user); + await this.cacheManager.set(username, user); const payload: TokenPayload = { domain: env.LDAP_DOMAIN, @@ -28,7 +29,10 @@ export class LdapService { public async logout(token: string) { const { username } = this.jwtService.decode(token) as DecodedToken; - await this.usersCache.deleteUser(username); + + if (this.cacheManager.get(username)) { + await this.cacheManager.del(username); + } } public checkToken(token: string) { @@ -36,8 +40,25 @@ export class LdapService { } public refreshToken(token: string) { + // eslint-disable-next-line @typescript-eslint/no-unused-vars const { exp, iat, ...payload } = this.jwtService.decode(token) as DecodedToken; return this.jwtService.sign(payload); } + + public async getUser(token: string) { + const { username } = this.jwtService.decode(token) as DecodedToken; + + const cachedUser = await this.cacheManager.get(username); + + if (!cachedUser) { + const user = await ldap.authenticate(username); + + await this.cacheManager.set(username, user); + + return user; + } + + return cachedUser; + } } diff --git a/apps/api/src/users/users.cache.ts b/apps/api/src/users/users.cache.ts deleted file mode 100644 index 56daa7f..0000000 --- a/apps/api/src/users/users.cache.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { CACHE_MANAGER } from '@nestjs/cache-manager'; -import { Inject, Injectable } from '@nestjs/common'; -import { Cache } from 'cache-manager'; - -@Injectable() -export class UsersCache { - constructor(@Inject(CACHE_MANAGER) private readonly cacheManager: Cache) {} - public async getUser(username: string) { - return (await this.cacheManager.get(username)) as T; - } - - public async addUser(username: string, user: T) { - await this.cacheManager.set(username, user); - } - - public async deleteUser(username: string) { - if (this.cacheManager.get(username)) { - await this.cacheManager.del(username); - } - } -} diff --git a/apps/api/src/users/users.controller.ts b/apps/api/src/users/users.controller.ts index aba7c34..ed86b12 100644 --- a/apps/api/src/users/users.controller.ts +++ b/apps/api/src/users/users.controller.ts @@ -3,24 +3,13 @@ /* eslint-disable import/no-extraneous-dependencies */ import { CreateUserDto } from './dto/create-user.dto'; import { UsersService } from './users.service'; -import { Body, Controller, Delete, Get, Param, Post, Req, Res } from '@nestjs/common'; -import { FastifyReply, FastifyRequest } from 'fastify'; -import { env } from 'src/config/env'; +import { Body, Controller, Delete, Get, Param, Post } from '@nestjs/common'; import { User } from 'src/schemas/user.schema'; @Controller() export class UsersController { constructor(private readonly usersService: UsersService) {} - @Get('/get-user') - async getUser(@Req() req: FastifyRequest, @Res() reply: FastifyReply) { - const token = req.cookies[env.COOKIE_TOKEN_NAME]; - - const user = await this.usersService.getUser(token); - - return reply.send(user); - } - @Post('/users/create-user') async create(@Body() createUserDto: CreateUserDto) { return this.usersService.create(createUserDto); diff --git a/apps/api/src/users/users.module.ts b/apps/api/src/users/users.module.ts index 4de94c7..5e27ca4 100644 --- a/apps/api/src/users/users.module.ts +++ b/apps/api/src/users/users.module.ts @@ -1,27 +1,14 @@ -import { UsersCache } from './users.cache'; import { UsersController } from './users.controller'; import { UsersService } from './users.service'; -import { CacheModule } from '@nestjs/cache-manager'; import { Module } from '@nestjs/common'; import { MongooseModule } from '@nestjs/mongoose'; -import * as redisStore from 'cache-manager-ioredis'; -import type { RedisOptions } from 'ioredis'; -import { env } from 'src/config/env'; import { User, UserSchema } from 'src/schemas/user.schema'; @Module({ controllers: [UsersController], - exports: [UsersCache], - imports: [ - CacheModule.register({ - host: env.REDIS_HOST, - port: env.REDIS_PORT, - store: redisStore, - ttl: env.API_CACHE_TTL, - }), - MongooseModule.forFeature([{ name: User.name, schema: UserSchema }]), - ], - providers: [UsersService, UsersCache], + exports: [], + imports: [MongooseModule.forFeature([{ name: User.name, schema: UserSchema }])], + providers: [UsersService], }) // eslint-disable-next-line @typescript-eslint/no-extraneous-class export class UsersModule {} diff --git a/apps/api/src/users/users.service.ts b/apps/api/src/users/users.service.ts index d7a6912..63ce0b7 100644 --- a/apps/api/src/users/users.service.ts +++ b/apps/api/src/users/users.service.ts @@ -1,40 +1,19 @@ -import type { DecodedToken } from '../ldap/types/jwt'; import type { CreateUserDto } from './dto/create-user.dto'; -import { UsersCache } from './users.cache'; import { Injectable, NotFoundException, UnauthorizedException } from '@nestjs/common'; import { JwtService } from '@nestjs/jwt'; import { InjectModel } from '@nestjs/mongoose'; import * as bcrypt from 'bcrypt'; import { Model } from 'mongoose'; import { User } from 'src/schemas/user.schema'; -import * as ldap from 'src/utils/ldap'; import { generatePassword } from 'src/utils/password'; @Injectable() export class UsersService { constructor( - private readonly usersCache: UsersCache, private readonly jwtService: JwtService, - @InjectModel(User.name) - private userModel: Model + @InjectModel(User.name) private userModel: Model ) {} - public async getUser(token: string) { - const { username } = this.jwtService.decode(token) as DecodedToken; - - const cachedUser = await this.usersCache.getUser(username); - - if (!cachedUser) { - const user = await ldap.authenticate(username); - - await this.usersCache.addUser(username, user); - - return user; - } - - return cachedUser; - } - public async create(createUserDto: CreateUserDto): Promise { const password = createUserDto.password || generatePassword();