143 lines
3.7 KiB
TypeScript
143 lines
3.7 KiB
TypeScript
import Redis from "ioredis";
|
|
|
|
let redisInstance: Redis | null = null;
|
|
|
|
export async function getRedis(): Promise<Redis> {
|
|
if (redisInstance) {
|
|
return redisInstance;
|
|
}
|
|
|
|
const redisUrl = process.env.REDIS_URL || "redis://localhost:6379";
|
|
|
|
try {
|
|
redisInstance = new Redis(redisUrl, {
|
|
lazyConnect: false,
|
|
maxRetriesPerRequest: null,
|
|
enableReadyCheck: false,
|
|
enableOfflineQueue: true,
|
|
retryStrategy: (times) => {
|
|
const delay = Math.min(times * 50, 2000);
|
|
return delay;
|
|
},
|
|
});
|
|
|
|
redisInstance.on("error", (err) => {
|
|
console.error("[Redis] Error:", err.message);
|
|
});
|
|
|
|
redisInstance.on("connect", () => {
|
|
console.log("[Redis] Connected");
|
|
});
|
|
|
|
redisInstance.on("ready", () => {
|
|
console.log("[Redis] Ready");
|
|
});
|
|
|
|
redisInstance.on("reconnecting", () => {
|
|
console.log("[Redis] Reconnecting...");
|
|
});
|
|
|
|
// Test connection
|
|
await redisInstance.ping();
|
|
console.log("[Redis] Ping successful");
|
|
|
|
return redisInstance;
|
|
} catch (error) {
|
|
console.error("[Redis] Connection failed:", error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
export async function closeRedis(): Promise<void> {
|
|
if (redisInstance) {
|
|
await redisInstance.quit();
|
|
redisInstance = null;
|
|
}
|
|
}
|
|
|
|
// Cache helper functions
|
|
export async function getCached<T>(key: string): Promise<T | null> {
|
|
try {
|
|
const redis = await getRedis();
|
|
const data = await redis.get(key);
|
|
return data ? JSON.parse(data) : null;
|
|
} catch (error) {
|
|
console.error(`[Redis] Error getting cache for ${key}:`, error);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
export async function setCached<T>(
|
|
key: string,
|
|
value: T,
|
|
expirationSeconds?: number
|
|
): Promise<boolean> {
|
|
try {
|
|
const redis = await getRedis();
|
|
const data = JSON.stringify(value);
|
|
|
|
if (expirationSeconds) {
|
|
await redis.setex(key, expirationSeconds, data);
|
|
} else {
|
|
await redis.set(key, data);
|
|
}
|
|
return true;
|
|
} catch (error) {
|
|
console.error(`[Redis] Error setting cache for ${key}:`, error);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
export async function deleteCached(key: string): Promise<boolean> {
|
|
try {
|
|
const redis = await getRedis();
|
|
const result = await redis.del(key);
|
|
return result > 0;
|
|
} catch (error) {
|
|
console.error(`[Redis] Error deleting cache for ${key}:`, error);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
export async function invalidateCachePattern(pattern: string): Promise<number> {
|
|
try {
|
|
const redis = await getRedis();
|
|
const keys = await redis.keys(pattern);
|
|
if (keys.length === 0) return 0;
|
|
return await redis.del(...keys);
|
|
} catch (error) {
|
|
console.error(`[Redis] Error invalidating cache pattern ${pattern}:`, error);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
// Session helpers for BetterAuth
|
|
export async function getSession(sessionId: string): Promise<any> {
|
|
return getCached(`session:${sessionId}`);
|
|
}
|
|
|
|
export async function setSession(
|
|
sessionId: string,
|
|
sessionData: any,
|
|
expirationSeconds: number = 86400 // 24 hours
|
|
): Promise<boolean> {
|
|
return setCached(`session:${sessionId}`, sessionData, expirationSeconds);
|
|
}
|
|
|
|
export async function deleteSession(sessionId: string): Promise<boolean> {
|
|
return deleteCached(`session:${sessionId}`);
|
|
}
|
|
|
|
// Cache key generators
|
|
export const cacheKeys = {
|
|
user: (userId: string) => `user:${userId}`,
|
|
userByEmail: (email: string) => `user:email:${email}`,
|
|
session: (sessionId: string) => `session:${sessionId}`,
|
|
webinar: (webinarId: string) => `webinar:${webinarId}`,
|
|
webinars: (page: number = 1) => `webinars:list:${page}`,
|
|
registrations: (userId: string) => `registrations:${userId}`,
|
|
contact: (contactId: string) => `contact:${contactId}`,
|
|
config: "system:config",
|
|
adminSetup: "admin:setup",
|
|
};
|