import { connectRedis } from './redis' /** * Cache management utilities for dashboard and topic data */ export class DashboardCache { /** * Invalidate dashboard cache for a specific user */ static async invalidateUserDashboard(userId: string): Promise { try { const redisClient = await connectRedis() const cacheKey = `dashboard:user:${userId}` await redisClient.del(cacheKey) console.log(`Dashboard cache invalidated for user ${userId}`) } catch (error) { console.warn('Failed to invalidate dashboard cache:', error) } } /** * Invalidate dashboard caches for multiple users */ static async invalidateMultipleUserDashboards(userIds: string[]): Promise { try { const redisClient = await connectRedis() const cacheKeys = userIds.map((userId) => `dashboard:user:${userId}`) if (cacheKeys.length > 0) { await redisClient.del(cacheKeys) console.log(`Dashboard caches invalidated for ${userIds.length} users`) } } catch (error) { console.warn('Failed to invalidate multiple dashboard caches:', error) } } /** * Get cached dashboard data for a user */ static async getUserDashboard(userId: string): Promise { try { const redisClient = await connectRedis() const cacheKey = `dashboard:user:${userId}` const cachedData = await redisClient.get(cacheKey) if (!cachedData) return null // Handle both string and Buffer responses const dataString = Buffer.isBuffer(cachedData) ? cachedData.toString('utf-8') : String(cachedData) return JSON.parse(dataString) } catch (error) { console.warn('Failed to get cached dashboard data:', error) return null } } /** * Set dashboard cache for a user */ static async setUserDashboard(userId: string, data: any, ttlSeconds = 300): Promise { try { const redisClient = await connectRedis() const cacheKey = `dashboard:user:${userId}` await redisClient.setEx(cacheKey, ttlSeconds, JSON.stringify(data)) console.log(`Dashboard data cached for user ${userId} (TTL: ${ttlSeconds}s)`) } catch (error) { console.warn('Failed to cache dashboard data:', error) } } } export class TopicCache { /** * Invalidate topic-specific caches when a topic is modified */ static async invalidateTopicCaches(topicSlug: string, authorId: string): Promise { try { const redisClient = await connectRedis() // Invalidate patterns that might be affected const cachePatterns = [ `topic:${topicSlug}`, // Individual topic cache `topics:public:*`, // Public topic listings `topics:user:${authorId}:*`, // User's topic listings `dashboard:user:${authorId}`, // User's dashboard ] for (const pattern of cachePatterns) { if (pattern.includes('*')) { // For patterns with wildcards, we need to scan and delete const keys = await redisClient.keys(pattern) if (keys.length > 0) { await redisClient.del(keys) console.log(`Invalidated ${keys.length} cache keys matching pattern: ${pattern}`) } } else { // Direct key deletion await redisClient.del(pattern) console.log(`Cache key deleted: ${pattern}`) } } } catch (error) { console.warn('Failed to invalidate topic caches:', error) } } /** * Invalidate public topic listing caches */ static async invalidatePublicTopicCaches(): Promise { try { const redisClient = await connectRedis() const keys = await redisClient.keys('topics:public:*') if (keys.length > 0) { await redisClient.del(keys) console.log(`Invalidated ${keys.length} public topic cache keys`) } } catch (error) { console.warn('Failed to invalidate public topic caches:', error) } } } /** * Utility function to generate consistent cache keys */ export const CacheKeys = { userDashboard: (userId: string) => `dashboard:user:${userId}`, userTopics: (userId: string, page = 1, limit = 10) => `topics:user:${userId}:page:${page}:limit:${limit}`, publicTopics: (page = 1, limit = 10, filters?: string) => { const filterKey = filters ? `:filters:${Buffer.from(filters).toString('base64')}` : '' return `topics:public:page:${page}:limit:${limit}${filterKey}` }, individualTopic: (slug: string) => `topic:${slug}`, relatedTopics: (topicId: string) => `topics:related:${topicId}`, }