initial commit
This commit is contained in:
141
lib/cache.ts
Normal file
141
lib/cache.ts
Normal file
@@ -0,0 +1,141 @@
|
||||
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<void> {
|
||||
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<void> {
|
||||
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<any | null> {
|
||||
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<void> {
|
||||
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<void> {
|
||||
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<void> {
|
||||
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}`,
|
||||
}
|
||||
Reference in New Issue
Block a user