From 4ad500087ad0db3c444acc3c8ff8cac2c231c8cf Mon Sep 17 00:00:00 2001 From: Ahmed Ibrahim Date: Wed, 4 Dec 2024 19:57:51 +0200 Subject: [PATCH] feat(models): impl. call cache --- packages/models/src/cache/base.ts | 2 +- packages/models/src/cache/call.ts | 67 ++++++++++++++++++++++++++++ packages/models/src/cache/index.ts | 3 ++ services/server/src/handlers/call.ts | 1 - 4 files changed, 71 insertions(+), 2 deletions(-) create mode 100644 packages/models/src/cache/call.ts diff --git a/packages/models/src/cache/base.ts b/packages/models/src/cache/base.ts index 0001bd8b..3970e0e6 100644 --- a/packages/models/src/cache/base.ts +++ b/packages/models/src/cache/base.ts @@ -16,7 +16,7 @@ export class CacheBase { public client: RedisClientType ) {} - encode(values: T): string { + encode(values: T): string { return JSON.stringify(values); } diff --git a/packages/models/src/cache/call.ts b/packages/models/src/cache/call.ts new file mode 100644 index 00000000..cbe69129 --- /dev/null +++ b/packages/models/src/cache/call.ts @@ -0,0 +1,67 @@ +import { CacheBase } from "@/cache/base"; + +export class Call extends CacheBase { + private readonly prefixes = { + call: "call", + user: "user:call", + }; + private ttl = 60 * 60; // 1 hour + + withTtl(ttl: number): Call { + this.ttl = ttl; + return this; + } + + async addMember({ callId, userId }: { callId: number; userId: number }) { + const callKey = this.asCallKey(callId); + const userKey = this.asUserKey(userId); + await this.client + .multi() + .sAdd(callKey, this.encode(userId)) + .set(userKey, this.encode(callId)) + .expire(callKey, this.ttl) + .expire(userKey, this.ttl) + .exec(); + } + + async removeMember({ callId, userId }: { callId: number; userId: number }) { + await this.client + .multi() + .sRem(this.asCallKey(callId), userId.toString()) + .del(this.asUserKey(userId)) + .exec(); + } + + async removeMemberByUserId(userId: number) { + const result = await this.client.get(this.asUserKey(userId)); + if (!result) return; + const callId = this.decode(result) as number; + await this.removeMember({ callId, userId }); + } + + async getMembers(callId: number): Promise { + const result = await this.client.sMembers(this.asCallKey(callId)); + return result.map((value) => this.decode(value)); + } + + async isMember({ + callId, + userId, + }: { + callId: number; + userId: number; + }): Promise { + return await this.client.sIsMember( + this.asCallKey(callId), + this.encode(userId) + ); + } + + private asCallKey(callId: number): string { + return `${this.prefixes.call}:${callId}`; + } + + private asUserKey(userId: number): string { + return `${this.prefixes.user}:${userId}`; + } +} diff --git a/packages/models/src/cache/index.ts b/packages/models/src/cache/index.ts index b26e8aca..7c3cc01d 100644 --- a/packages/models/src/cache/index.ts +++ b/packages/models/src/cache/index.ts @@ -3,11 +3,13 @@ import { Tutors } from "@/cache/tutors"; import { Rules } from "@/cache/rules"; import { RedisClient } from "@/cache/base"; import { Peer } from "@/cache/peer"; +import { Call } from "@/cache/call"; export class Cache { public tutors: Tutors; public rules: Rules; public peer: Peer; + public call: Call; private readonly client: RedisClient; constructor(url: string) { @@ -16,6 +18,7 @@ export class Cache { this.tutors = new Tutors(client); this.rules = new Rules(client); this.peer = new Peer(client); + this.call = new Call(client); } async flush() { diff --git a/services/server/src/handlers/call.ts b/services/server/src/handlers/call.ts index 541a16b4..3043f831 100644 --- a/services/server/src/handlers/call.ts +++ b/services/server/src/handlers/call.ts @@ -2,7 +2,6 @@ import { calls } from "@litespace/models"; import { NextFunction, Request, Response } from "express"; import asyncHandler from "express-async-handler"; import { withNamedId } from "@/validation/utils"; -import { groupBy } from "lodash"; import { forbidden, notfound } from "@/lib/error"; import { isAdmin, isGhost, isUser } from "@litespace/auth";