apps/api: move redis caching feature to ldap module

This commit is contained in:
vchikalkin 2023-10-31 21:58:39 +03:00
parent 01422661e8
commit a42aa89aec
7 changed files with 52 additions and 78 deletions

View File

@ -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);
}
}

View File

@ -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<RedisOptions>({
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

View File

@ -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;
}
}

View File

@ -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<T extends object>(username: string) {
return (await this.cacheManager.get(username)) as T;
}
public async addUser<T extends object>(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);
}
}
}

View File

@ -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);

View File

@ -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<RedisOptions>({
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 {}

View File

@ -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<User>
@InjectModel(User.name) private userModel: Model<User>
) {}
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<User> {
const password = createUserDto.password || generatePassword();