mirror of
https://github.com/freedbygrace/ActiveDirectoryManager.git
synced 2026-08-10 10:48:21 +00:00
Checkpoint
Replit-Commit-Author: Agent Replit-Commit-Session-Id: 705f2157-ef97-4fbd-89e4-8c7f2ecaea90 Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/7ed01c5f-a82d-405a-b728-b2e3d127c60c/30fe5a86-4e58-47d8-aa32-104aaf3a85ce.jpg
This commit is contained in:
+174
@@ -0,0 +1,174 @@
|
||||
import Redis from 'ioredis';
|
||||
import debugLib from 'debug';
|
||||
|
||||
const debug = debugLib('api:cache');
|
||||
|
||||
// Create Redis client
|
||||
let redisClient: Redis | null = null;
|
||||
|
||||
export const CACHE_TTL = {
|
||||
SHORT: 60, // 1 minute
|
||||
MEDIUM: 300, // 5 minutes
|
||||
LONG: 1800, // 30 minutes
|
||||
VERY_LONG: 3600 * 24, // 24 hours
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize Redis client for caching
|
||||
*/
|
||||
export function initCache(): Redis | null {
|
||||
try {
|
||||
if (process.env.REDIS_URL) {
|
||||
redisClient = new Redis(process.env.REDIS_URL);
|
||||
debug('Redis cache initialized');
|
||||
|
||||
redisClient.on('error', (err) => {
|
||||
console.error('Redis error:', err);
|
||||
redisClient = null;
|
||||
});
|
||||
|
||||
return redisClient;
|
||||
} else {
|
||||
debug('No REDIS_URL found, caching disabled');
|
||||
return null;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to initialize Redis cache:', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get cached data
|
||||
* @param key Cache key
|
||||
*/
|
||||
export async function getCached<T>(key: string): Promise<T | null> {
|
||||
if (!redisClient) return null;
|
||||
|
||||
try {
|
||||
const data = await redisClient.get(key);
|
||||
if (data) {
|
||||
debug(`Cache hit for: ${key}`);
|
||||
return JSON.parse(data) as T;
|
||||
}
|
||||
debug(`Cache miss for: ${key}`);
|
||||
return null;
|
||||
} catch (error) {
|
||||
console.error(`Error getting from cache: ${key}`, error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set data in cache
|
||||
* @param key Cache key
|
||||
* @param data Data to cache
|
||||
* @param ttl Time to live in seconds
|
||||
*/
|
||||
export async function setCached(key: string, data: any, ttl: number = CACHE_TTL.MEDIUM): Promise<void> {
|
||||
if (!redisClient) return;
|
||||
|
||||
try {
|
||||
await redisClient.set(key, JSON.stringify(data), 'EX', ttl);
|
||||
debug(`Cached: ${key} (TTL: ${ttl}s)`);
|
||||
} catch (error) {
|
||||
console.error(`Error setting cache: ${key}`, error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove data from cache
|
||||
* @param key Cache key
|
||||
*/
|
||||
export async function invalidateCache(key: string): Promise<void> {
|
||||
if (!redisClient) return;
|
||||
|
||||
try {
|
||||
await redisClient.del(key);
|
||||
debug(`Invalidated cache: ${key}`);
|
||||
} catch (error) {
|
||||
console.error(`Error invalidating cache: ${key}`, error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove data from cache by pattern
|
||||
* @param pattern Key pattern to match (e.g. "users:*")
|
||||
*/
|
||||
export async function invalidateCachePattern(pattern: string): Promise<void> {
|
||||
if (!redisClient) return;
|
||||
|
||||
try {
|
||||
const keys = await redisClient.keys(pattern);
|
||||
if (keys.length > 0) {
|
||||
await redisClient.del(...keys);
|
||||
debug(`Invalidated ${keys.length} keys matching: ${pattern}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Error invalidating cache pattern: ${pattern}`, error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get cache stats
|
||||
*/
|
||||
export async function getCacheStats(): Promise<Record<string, any>> {
|
||||
if (!redisClient) return { enabled: false };
|
||||
|
||||
try {
|
||||
const info = await redisClient.info();
|
||||
const dbSize = await redisClient.dbsize();
|
||||
|
||||
return {
|
||||
enabled: true,
|
||||
size: dbSize,
|
||||
info: info,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Error getting cache stats:', error);
|
||||
return { enabled: true, error: 'Failed to get cache stats' };
|
||||
}
|
||||
}
|
||||
|
||||
// Middleware to cache API responses
|
||||
export function cacheMiddleware(ttl: number = CACHE_TTL.MEDIUM) {
|
||||
return async (req: any, res: any, next: any) => {
|
||||
if (!redisClient || req.method !== 'GET') {
|
||||
return next();
|
||||
}
|
||||
|
||||
const cacheKey = `api:${req.originalUrl}`;
|
||||
|
||||
try {
|
||||
const cachedData = await getCached(cacheKey);
|
||||
|
||||
if (cachedData) {
|
||||
res.setHeader('X-Cache', 'HIT');
|
||||
return res.json(cachedData);
|
||||
}
|
||||
|
||||
// Store the original json method
|
||||
const originalJson = res.json;
|
||||
|
||||
// Override the json method
|
||||
res.json = function(data: any) {
|
||||
// Set the data back to original json method
|
||||
res.setHeader('X-Cache', 'MISS');
|
||||
originalJson.call(this, data);
|
||||
|
||||
// Cache the data
|
||||
setCached(cacheKey, data, ttl).catch(err =>
|
||||
console.error(`Error caching response for ${cacheKey}:`, err)
|
||||
);
|
||||
};
|
||||
|
||||
next();
|
||||
} catch (error) {
|
||||
console.error(`Cache middleware error for: ${cacheKey}`, error);
|
||||
next();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Initialize cache on startup
|
||||
export const redis = initCache();
|
||||
Reference in New Issue
Block a user