mirror of
https://github.com/freedbygrace/ActiveDirectoryManager.git
synced 2026-07-26 11:59:14 +00:00
d77ce2adbe
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
174 lines
4.2 KiB
TypeScript
174 lines
4.2 KiB
TypeScript
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(); |