From 2dc50852ac54d58aef107f51094e15b6b317f156 Mon Sep 17 00:00:00 2001 From: Josue Date: Mon, 17 Feb 2025 21:41:17 -0500 Subject: [PATCH] fix(api): comments not working and modifying cache tool --- api/package-lock.json | 29 +++++++------- api/package.json | 3 +- api/src/comment/comment.module.ts | 8 +++- api/src/comment/comment.service.ts | 42 +++++++++++++++++---- api/src/comment/dao/comment.dao.ts | 14 ++++++- api/src/comment/dto/comment.dto.ts | 2 +- api/src/comment/guard/comment-auth.guard.ts | 2 +- api/src/config/redisCache.ts | 20 ++++++---- api/src/list/list.controller.ts | 12 ++++-- api/src/list/list.service.ts | 35 ++--------------- 10 files changed, 96 insertions(+), 71 deletions(-) diff --git a/api/package-lock.json b/api/package-lock.json index 7e6c702..1faa1a3 100644 --- a/api/package-lock.json +++ b/api/package-lock.json @@ -9,6 +9,7 @@ "version": "0.0.1", "license": "MIT", "dependencies": { + "@keyv/redis": "^4.2.0", "@nestjs/axios": "^4.0.0", "@nestjs/cache-manager": "^3.0.0", "@nestjs/common": "^11.0.1", @@ -23,9 +24,9 @@ "argon2": "^0.40.1", "axios": "^1.6.7", "cache-manager": "^6.4.0", - "cache-manager-redis-store": "^3.0.1", "class-transformer": "^0.5.1", "class-validator": "^0.14.0", + "keyv": "^5.2.3", "mailgun.js": "^9.3.0", "passport": "^0.6.0", "passport-jwt": "^4.0.1", @@ -1856,6 +1857,20 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@keyv/redis": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@keyv/redis/-/redis-4.2.0.tgz", + "integrity": "sha512-QszmBfZZ3wOKJ5z1hn0CTLf04WN/552ITrSDYC3Yg4jT6yVdlz2fJxi5CNrnZ8NIu/Qaj7OAkbSL+pyFUXp6oA==", + "license": "MIT", + "dependencies": { + "cluster-key-slot": "^1.1.2", + "keyv": "^5.2.2", + "redis": "^4.7.0" + }, + "engines": { + "node": ">= 18" + } + }, "node_modules/@keyv/serialize": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@keyv/serialize/-/serialize-1.0.2.tgz", @@ -4835,18 +4850,6 @@ "keyv": "^5.2.3" } }, - "node_modules/cache-manager-redis-store": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/cache-manager-redis-store/-/cache-manager-redis-store-3.0.1.tgz", - "integrity": "sha512-o560kw+dFqusC9lQJhcm6L2F2fMKobJ5af+FoR2PdnMVdpQ3f3Bz6qzvObTGyvoazQJxjQNWgMQeChP4vRTuXQ==", - "license": "MIT", - "dependencies": { - "redis": "^4.3.1" - }, - "engines": { - "node": ">= 16.18.0" - } - }, "node_modules/cacheable-lookup": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz", diff --git a/api/package.json b/api/package.json index 9a32682..33366ed 100644 --- a/api/package.json +++ b/api/package.json @@ -22,6 +22,7 @@ "db:format": "npx --yes prisma format" }, "dependencies": { + "@keyv/redis": "^4.2.0", "@nestjs/axios": "^4.0.0", "@nestjs/cache-manager": "^3.0.0", "@nestjs/common": "^11.0.1", @@ -36,9 +37,9 @@ "argon2": "^0.40.1", "axios": "^1.6.7", "cache-manager": "^6.4.0", - "cache-manager-redis-store": "^3.0.1", "class-transformer": "^0.5.1", "class-validator": "^0.14.0", + "keyv": "^5.2.3", "mailgun.js": "^9.3.0", "passport": "^0.6.0", "passport-jwt": "^4.0.1", diff --git a/api/src/comment/comment.module.ts b/api/src/comment/comment.module.ts index ad63b96..5fc1ab0 100644 --- a/api/src/comment/comment.module.ts +++ b/api/src/comment/comment.module.ts @@ -3,9 +3,15 @@ import { PrismaModule } from '../prisma/prisma.module'; import { NotificationModule } from '../notification/notification.module'; import { CommentDao } from './dao/comment.dao'; import { CommentsService } from './comment.service'; +import { CacheModule } from '@nestjs/cache-manager'; +import { RedisOptions } from 'src/config/redisCache'; @Module({ - imports: [PrismaModule, NotificationModule], + imports: [ + PrismaModule, + NotificationModule, + CacheModule.registerAsync(RedisOptions), + ], providers: [CommentsService, CommentDao], exports: [CommentsService], }) diff --git a/api/src/comment/comment.service.ts b/api/src/comment/comment.service.ts index 03b60c2..a58c567 100644 --- a/api/src/comment/comment.service.ts +++ b/api/src/comment/comment.service.ts @@ -1,24 +1,50 @@ -import { Injectable } from '@nestjs/common'; +import { Inject, Injectable } from '@nestjs/common'; import { CommentDao } from './dao/comment.dao'; -import { CommentQueryDto } from './dto'; +import { CommentDto, CommentQueryDto } from './dto'; +import { CACHE_MANAGER } from '@nestjs/cache-manager'; +import { Cache } from 'cache-manager'; @Injectable() export class CommentsService { - constructor(private commentDao: CommentDao) {} + constructor( + @Inject(CACHE_MANAGER) private cacheManager: Cache, + private commentDao: CommentDao, + ) {} - getComments(query: CommentQueryDto) { - return this.commentDao.getComments(query); + async get(query: CommentQueryDto): Promise { + if (query.listId && !query.userId && !query.commentId) { + let comments = await this.cacheManager.get( + `${query.listId}-comments`, + ); + + if (comments) { + return comments; + } + + comments = await this.commentDao.getComments(query); + await this.cacheManager.set(`${query.listId}-comments`, comments); + + return comments; + } + + return await this.commentDao.getComments(query); } - createComment(comment: string, listId: string, userId: string) { + async create(comment: string, listId: string, userId: string) { + await this.cacheManager.del(`${listId}-comments`); + return this.commentDao.createComment(listId, userId, comment); } - updateComment(comment: string, commentId: string) { + async update(comment: string, commentId: string, listId: string) { + await this.cacheManager.del(`${listId}-comments`); + return this.commentDao.updateComment(commentId, comment); } - deleteComment(commentId: string) { + async delete(commentId: string, listId: string) { + await this.cacheManager.del(`${listId}-comments`); + return this.commentDao.deleteComment(commentId); } } diff --git a/api/src/comment/dao/comment.dao.ts b/api/src/comment/dao/comment.dao.ts index f4f01d6..9a69278 100644 --- a/api/src/comment/dao/comment.dao.ts +++ b/api/src/comment/dao/comment.dao.ts @@ -1,13 +1,13 @@ import { Injectable } from '@nestjs/common'; import { PrismaService } from '../../prisma/prisma.service'; -import { CommentQueryDto } from '../dto'; +import { CommentDto, CommentQueryDto } from '../dto'; @Injectable() export class CommentDao { constructor(private readonly prisma: PrismaService) {} async getComments(query: CommentQueryDto) { - return await this.prisma.comment.findMany({ + const res = await this.prisma.comment.findMany({ where: { AND: [ { @@ -25,6 +25,16 @@ export class CommentDao { createdAt: 'desc', }, }); + + const r: CommentDto[] = res.map((r) => ({ + id: r.id, + userId: r.userId, + text: r.text, + createdAt: r.createdAt, + updatedAt: r.updatedAt, + })); + + return r; } async createComment(listId: string, userId: string, text: string) { diff --git a/api/src/comment/dto/comment.dto.ts b/api/src/comment/dto/comment.dto.ts index c0c0e8f..ff1fece 100644 --- a/api/src/comment/dto/comment.dto.ts +++ b/api/src/comment/dto/comment.dto.ts @@ -8,7 +8,7 @@ export class CommentDto { @Expose() @IsString() - username: string; + userId: string; @Expose() @IsString() diff --git a/api/src/comment/guard/comment-auth.guard.ts b/api/src/comment/guard/comment-auth.guard.ts index 16a58e2..a4501c9 100644 --- a/api/src/comment/guard/comment-auth.guard.ts +++ b/api/src/comment/guard/comment-auth.guard.ts @@ -11,7 +11,7 @@ export class CommentAuthorizationGuard implements CanActivate { const { commentId } = request.params; const [comments] = await Promise.all([ - this.commentService.getComments({ + this.commentService.get({ commentId, }), ]); diff --git a/api/src/config/redisCache.ts b/api/src/config/redisCache.ts index 8f7e2d6..a55e990 100644 --- a/api/src/config/redisCache.ts +++ b/api/src/config/redisCache.ts @@ -1,24 +1,28 @@ +import KeyvRedis from '@keyv/redis'; +import Keyv from 'keyv'; import { CacheModuleAsyncOptions } from '@nestjs/cache-manager'; import { ConfigModule, ConfigService } from '@nestjs/config'; -import { redisStore } from 'cache-manager-redis-store'; export const RedisOptions: CacheModuleAsyncOptions = { imports: [ConfigModule], - useFactory: async (configService: ConfigService) => { - const store = await redisStore({ + useFactory: (configService: ConfigService) => { + const redisHost = configService.get('REDIS_HOST') ?? 'locahost'; + const port = parseInt(configService.get('REDIS_PORT') ?? '6379'); + + const store = new KeyvRedis({ + url: `redis://${redisHost}:${port.toString()}`, + password: configService.get('REDIS_AUTH') ?? 'docker', socket: { - host: configService.get('REDIS_HOST'), - port: parseInt(configService.get('REDIS_PORT') ?? '6379'), reconnectStrategy: parseInt( configService.get('REDIS_RETRY_INTERVAL') ?? '10000', ), + tls: false, + keepAlive: 30000, }, - password: configService.get('REDIS_AUTH') ?? 'docker', - ttl: 0, }); return { - store: () => store, + stores: [new Keyv(store)], }; }, diff --git a/api/src/list/list.controller.ts b/api/src/list/list.controller.ts index e0752c2..2fba046 100644 --- a/api/src/list/list.controller.ts +++ b/api/src/list/list.controller.ts @@ -115,7 +115,7 @@ export class ListController { if (!req.user) throw new UnauthorizedException('user not found'); const { id: userId } = req.user; - const createdComment = await this.commentService.createComment( + const createdComment = await this.commentService.create( text, listId, userId, @@ -180,16 +180,20 @@ export class ListController { @HttpCode(HttpStatus.NO_CONTENT) updateComment( @Param('commentId') commentId: string, + @Param('id') listId: string, @Body() updateCommentDto: string, ) { - return this.commentService.updateComment(updateCommentDto, commentId); + return this.commentService.update(updateCommentDto, commentId, listId); } @UseGuards(ListAuthGuard, CommentAuthorizationGuard) @Delete('/:id/comments/:commentId') @HttpCode(HttpStatus.NO_CONTENT) - deleteComment(@Param('commentId') commentId: string) { - return this.commentService.deleteComment(commentId); + deleteComment( + @Param('commentId') commentId: string, + @Param('id') listId: string, + ) { + return this.commentService.delete(commentId, listId); } @UseInterceptors(RemoveListFieldsInterceptor) diff --git a/api/src/list/list.service.ts b/api/src/list/list.service.ts index 888248d..82381e6 100644 --- a/api/src/list/list.service.ts +++ b/api/src/list/list.service.ts @@ -4,7 +4,6 @@ import { Cache } from 'cache-manager'; import { ListDao } from './dao/list.dao'; import { UserService } from '../user/user.service'; import { ListItem, UpdateListDto } from './dto'; -import { CommentDto } from '../comment/dto'; import { NotificationService } from '../notification/notification.service'; import { NotificationTypes } from '../notification/templates'; import { CommentsService } from '../comment/comment.service'; @@ -27,43 +26,15 @@ export class ListService { } async getList(listId: string) { - let list: ListItem | undefined | null = await this.cacheManager.get(listId); - - let comments: CommentDto[] | undefined | null = await this.cacheManager.get( - `${listId}-comments`, - ); + let list: ListItem | undefined | null = + await this.cacheManager.get(listId); + const comments = await this.commentService.get({ listId }); if (!list) { list = await this.listDao.getList(listId); await this.cacheManager.set(listId, list); } - if (!comments) { - const c = await this.commentService.getComments({ listId }); - - const commentsWithUsername: CommentDto[] = []; - - if (c.length) { - const users = await Promise.all( - c.map((comment) => - this.userService.getUsers({ - id: comment.userId, - } as QueryDto), - ), - ); - - c.forEach((comment, i) => - commentsWithUsername.push({ - ...comment, - username: users[i].users[0].username, - }), - ); - } - comments = commentsWithUsername; - - await this.cacheManager.set(`${listId}-comments`, comments); - } - return { id: list.id, name: list.name,