Files
portfolio/lib/cache.ts
2025-09-10 10:59:14 +02:00

118 lines
3.5 KiB
TypeScript

import { cache } from './redis';
// API Response caching
export const apiCache = {
// Generate cache key based on query parameters
generateProjectsKey(params: Record<string, string | null> = {}) {
const { page = '1', limit = '50', category, featured, published, difficulty, search } = params;
const keyParts = ['api:projects'];
if (page !== '1') keyParts.push(`page:${page}`);
if (limit !== '50') keyParts.push(`limit:${limit}`);
if (category) keyParts.push(`cat:${category}`);
if (featured !== null) keyParts.push(`feat:${featured}`);
if (published !== null) keyParts.push(`pub:${published}`);
if (difficulty) keyParts.push(`diff:${difficulty}`);
if (search) keyParts.push(`search:${search}`);
return keyParts.join(':');
},
async getProjects(params: Record<string, string | null> = {}) {
const key = this.generateProjectsKey(params);
return await cache.get(key);
},
async setProjects(params: Record<string, string | null> = {}, projects: unknown, ttlSeconds = 300) {
const key = this.generateProjectsKey(params);
return await cache.set(key, projects, ttlSeconds);
},
async getProject(id: number) {
return await cache.get(`api:project:${id}`);
},
async setProject(id: number, project: unknown, ttlSeconds = 300) {
return await cache.set(`api:project:${id}`, project, ttlSeconds);
},
async invalidateProject(id: number) {
await cache.del(`api:project:${id}`);
// Invalidate all project list caches
await this.invalidateAllProjectLists();
},
async invalidateAllProjectLists() {
// Clear all project list caches by pattern
// This is a simplified approach - in production you'd use Redis SCAN
const commonKeys = [
'api:projects',
'api:projects:pub:true',
'api:projects:feat:true:pub:true:limit:6',
'api:projects:page:1:limit:50',
'api:projects:pub:true:page:1:limit:50'
];
for (const key of commonKeys) {
await cache.del(key);
}
},
async invalidateAll() {
await this.invalidateAllProjectLists();
// Clear all project caches
const keys = await this.getAllProjectKeys();
for (const key of keys) {
await cache.del(key);
}
},
async getAllProjectKeys() {
// This would need to be implemented with Redis SCAN
// For now, we'll use a simple approach
return [];
}
};
// Performance metrics caching
export const performanceCache = {
async getMetrics(url: string) {
return await cache.get(`perf:${url}`);
},
async setMetrics(url: string, metrics: unknown, ttlSeconds = 600) {
return await cache.set(`perf:${url}`, metrics, ttlSeconds);
},
async getWebVitals() {
return await cache.get('perf:webvitals');
},
async setWebVitals(vitals: unknown, ttlSeconds = 300) {
return await cache.set('perf:webvitals', vitals, ttlSeconds);
}
};
// User session caching
export const userCache = {
async getSession(sessionId: string) {
return await cache.get(`user:session:${sessionId}`);
},
async setSession(sessionId: string, data: unknown, ttlSeconds = 86400) {
return await cache.set(`user:session:${sessionId}`, data, ttlSeconds);
},
async deleteSession(sessionId: string) {
return await cache.del(`user:session:${sessionId}`);
},
async getUserPreferences(userId: string) {
return await cache.get(`user:prefs:${userId}`);
},
async setUserPreferences(userId: string, prefs: unknown, ttlSeconds = 86400) {
return await cache.set(`user:prefs:${userId}`, prefs, ttlSeconds);
}
};