diff --git a/packages/wallet/backend/src/config/redis.ts b/packages/wallet/backend/src/config/redis.ts index b8b44510b..b5e0f0b63 100644 --- a/packages/wallet/backend/src/config/redis.ts +++ b/packages/wallet/backend/src/config/redis.ts @@ -1,6 +1,24 @@ import { Env } from '@/config/env' import { Redis } from 'ioredis' import { RedisClient } from '@shared/backend' +let redisClient: Redis | null = null + +export const createRedisClient = (env: Env): Redis => { + redisClient = new Redis(env.REDIS_URL) + + redisClient.on('error', (err) => { + console.error('Redis error:', err) + }) + + return redisClient +} + +export const getRedisClient = (env: Env): Redis | null => { + if (!redisClient) { + return createRedisClient(env) + } + return redisClient +} export function createRedis(env: Env) { const redis = new Redis(env.REDIS_URL) diff --git a/packages/wallet/backend/src/middleware/rateLimit.ts b/packages/wallet/backend/src/middleware/rateLimit.ts index 455e78939..ddfeea4ae 100644 --- a/packages/wallet/backend/src/middleware/rateLimit.ts +++ b/packages/wallet/backend/src/middleware/rateLimit.ts @@ -1,38 +1,7 @@ import { env } from '@/config/env' -import Redis from 'ioredis' import { RateLimiterRedisHelper } from '@/rateLimit/service' import { NextFunction, Request, Response } from 'express' - -const redisClient = new Redis(env.REDIS_URL) - -const sendEmailLimiter = new RateLimiterRedisHelper({ - storeClient: redisClient, - keyPrefix: 'send_email', - points: env.SEND_EMAIL_RATE_LIMIT, - duration: env.SEND_EMAIL_RATE_LIMIT_RESET_INTERVAL_IN_SECONDS, - blockDuration: env.SEND_EMAIL_RATE_LIMIT_PAUSE_IN_SECONDS -}) -const loginAttemptLimiter = new RateLimiterRedisHelper({ - storeClient: redisClient, - keyPrefix: 'login_email', - points: env.LOGIN_RATE_LIMIT, - duration: env.LOGIN_RATE_LIMIT_RESET_INTERVAL_IN_SECONDS, - blockDuration: env.LOGIN_RATE_LIMIT_PAUSE_IN_SECONDS -}) -const loginIPLimiter = new RateLimiterRedisHelper({ - storeClient: redisClient, - keyPrefix: 'login_ip', - points: env.LOGIN_IP_RATE_LIMIT, - duration: env.LOGIN_IP_RATE_LIMIT_RESET_INTERVAL_IN_SECONDS, - blockDuration: env.LOGIN_IP_RATE_LIMIT_PAUSE_IN_SECONDS -}) -const loginBlockIPLimiter = new RateLimiterRedisHelper({ - storeClient: redisClient, - keyPrefix: 'login_block_ip', - points: env.LOGIN_IP_BLOCK_RATE_LIMIT, - duration: env.LOGIN_IP_BLOCK_RATE_LIMIT_RESET_INTERVAL_IN_SECONDS, - blockDuration: env.LOGIN_IP_BLOCK_RATE_LIMIT_PAUSE_IN_SECONDS -}) +import { getRedisClient } from '@/config/redis' export const rateLimiterEmail = async ( req: Request, @@ -40,6 +9,13 @@ export const rateLimiterEmail = async ( next: NextFunction ): Promise => { try { + const sendEmailLimiter = new RateLimiterRedisHelper({ + storeClient: getRedisClient(env), + keyPrefix: 'send_email', + points: env.SEND_EMAIL_RATE_LIMIT, + duration: env.SEND_EMAIL_RATE_LIMIT_RESET_INTERVAL_IN_SECONDS, + blockDuration: env.SEND_EMAIL_RATE_LIMIT_PAUSE_IN_SECONDS + }) await sendEmailLimiter.checkAttempts(req.body.email) await sendEmailLimiter.useAttempt(req.body.email) } catch (e) { @@ -55,6 +31,28 @@ export const rateLimiterLogin = async ( ): Promise => { try { const userIp = `${req.ip}` + const loginAttemptLimiter = new RateLimiterRedisHelper({ + storeClient: getRedisClient(env), + keyPrefix: 'login_email', + points: env.LOGIN_RATE_LIMIT, + duration: env.LOGIN_RATE_LIMIT_RESET_INTERVAL_IN_SECONDS, + blockDuration: env.LOGIN_RATE_LIMIT_PAUSE_IN_SECONDS + }) + const loginIPLimiter = new RateLimiterRedisHelper({ + storeClient: getRedisClient(env), + keyPrefix: 'login_ip', + points: env.LOGIN_IP_RATE_LIMIT, + duration: env.LOGIN_IP_RATE_LIMIT_RESET_INTERVAL_IN_SECONDS, + blockDuration: env.LOGIN_IP_RATE_LIMIT_PAUSE_IN_SECONDS + }) + const loginBlockIPLimiter = new RateLimiterRedisHelper({ + storeClient: getRedisClient(env), + keyPrefix: 'login_block_ip', + points: env.LOGIN_IP_BLOCK_RATE_LIMIT, + duration: env.LOGIN_IP_BLOCK_RATE_LIMIT_RESET_INTERVAL_IN_SECONDS, + blockDuration: env.LOGIN_IP_BLOCK_RATE_LIMIT_PAUSE_IN_SECONDS + }) + await loginBlockIPLimiter.checkAttempts(userIp) await loginBlockIPLimiter.useAttempt(userIp) diff --git a/packages/wallet/backend/tests/auth/controller.test.ts b/packages/wallet/backend/tests/auth/controller.test.ts index 397112a99..80b1a2222 100644 --- a/packages/wallet/backend/tests/auth/controller.test.ts +++ b/packages/wallet/backend/tests/auth/controller.test.ts @@ -14,6 +14,7 @@ import type { AuthController } from '@/auth/controller' import type { AuthService } from '@/auth/service' import { applyMiddleware } from '@/tests/utils' import { withSession } from '@/middleware/withSession' +import { getRedisClient } from '@/config/redis' import { rateLimiterLogin, rateLimiterEmail } from '@/middleware/rateLimit' import type { UserService } from '@/user/service' import { @@ -63,6 +64,8 @@ describe('Authentication Controller', (): void => { afterAll(async (): Promise => { await appContainer.stop() await knex.destroy() + const redisClient = getRedisClient(env) + redisClient?.disconnect() }) afterEach(async (): Promise => {