154 lines
3.8 KiB
JavaScript
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,
|
|
};
|
|
|