Files
yourwillyourwish/lib/redis.ts
2026-02-06 21:44:04 -06:00

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",
};