auth/src/services/redisClient.js

154 lines
3.8 KiB
JavaScript

// src/services/redisClient.js
// === ADDED FOR RATE LIMITING ===
// Redis client setup for rate limiting and OTP tracking
const redis = require('redis');
// Redis connection configuration
// Supports REDIS_URL or REDIS_HOST + REDIS_PORT
const redisUrl = process.env.REDIS_URL;
const redisHost = process.env.REDIS_HOST || 'localhost';
const redisPort = process.env.REDIS_PORT || 6379;
const redisPassword = process.env.REDIS_PASSWORD || undefined;
let redisClient = null;
let redisClientReady = false;
let redisInitAttempted = false;
let redisInitFailed = false;
let errorLogged = false;
/**
* Initialize Redis client
* Falls back gracefully if Redis is not available
*/
async function initRedis() {
// If already initialized and ready, return it
if (redisClient && redisClientReady) {
return redisClient;
}
// If we've already attempted and failed, don't try again
if (redisInitFailed) {
return null;
}
// If we've already attempted and client exists but not ready, return it (don't retry)
if (redisInitAttempted && redisClient) {
return redisClient;
}
redisInitAttempted = true;
try {
const config = redisUrl
? { url: redisUrl }
: {
socket: {
host: redisHost,
port: parseInt(redisPort, 10),
reconnectStrategy: false, // Disable automatic reconnection
},
password: redisPassword,
};
redisClient = redis.createClient(config);
// Track if we've logged the error to avoid spam
redisClient.on('error', (err) => {
if (!errorLogged) {
console.warn('⚠️ Redis not available. Rate limiting will use in-memory fallback.');
console.warn(' To enable Redis, set REDIS_URL or REDIS_HOST/REDIS_PORT');
console.warn(' Error:', err.message);
errorLogged = true;
}
redisClientReady = false;
redisInitFailed = true;
});
redisClient.on('connect', () => {
if (!errorLogged) {
console.log('Redis Client: Connecting...');
}
});
redisClient.on('ready', () => {
console.log('✅ Redis Client: Ready');
redisClientReady = true;
redisInitFailed = false;
errorLogged = false; // Reset error flag on successful connection
});
redisClient.on('end', () => {
if (redisClientReady) {
console.log('Redis Client: Connection ended');
}
redisClientReady = false;
});
await redisClient.connect();
return redisClient;
} catch (err) {
if (!errorLogged) {
console.warn('⚠️ Redis not available. Rate limiting will use in-memory fallback.');
console.warn(' To enable Redis, set REDIS_URL or REDIS_HOST/REDIS_PORT');
errorLogged = true;
}
redisClientReady = false;
redisInitFailed = true;
// Clean up the client if connection failed
if (redisClient) {
try {
await redisClient.quit().catch(() => {});
} catch (e) {
// Ignore cleanup errors
}
redisClient = null;
}
return null;
}
}
/**
* Get Redis client (initialize if needed)
*/
async function getRedisClient() {
// If we've already failed to connect, don't try again
if (redisInitFailed) {
return null;
}
if (!redisClient && !redisInitAttempted) {
await initRedis();
}
return redisClient;
}
/**
* Check if Redis is available and ready
*/
function isRedisReady() {
return redisClientReady && redisClient;
}
/**
* Gracefully close Redis connection
*/
async function closeRedis() {
if (redisClient) {
try {
await redisClient.quit();
redisClient = null;
redisClientReady = false;
} catch (err) {
console.error('Error closing Redis connection:', err);
}
}
}
module.exports = {
initRedis,
getRedisClient,
isRedisReady,
closeRedis,
};