Skip to main content
Glama
redis.ts7.41 kB
import Redis from 'ioredis'; // ============================================ // Configuration // ============================================ const redis = new Redis({ host: process.env.REDIS_HOST || 'localhost', port: parseInt(process.env.REDIS_PORT || '6379'), password: process.env.REDIS_PASSWORD, db: parseInt(process.env.REDIS_DB || '0'), retryStrategy: (times) => Math.min(times * 50, 2000), }); redis.on('connect', () => console.log('Redis connected')); redis.on('error', (err) => console.error('Redis error:', err)); // ============================================ // Basic Operations // ============================================ export async function set(key: string, value: unknown, ttlSeconds?: number): Promise<void> { const serialized = JSON.stringify(value); if (ttlSeconds) { await redis.setex(key, ttlSeconds, serialized); } else { await redis.set(key, serialized); } } export async function get<T>(key: string): Promise<T | null> { const value = await redis.get(key); return value ? JSON.parse(value) : null; } export async function del(key: string): Promise<boolean> { const result = await redis.del(key); return result > 0; } export async function exists(key: string): Promise<boolean> { const result = await redis.exists(key); return result === 1; } // ============================================ // Cache Helper // ============================================ export async function cached<T>( key: string, ttlSeconds: number, fetchFn: () => Promise<T> ): Promise<T> { const cached = await get<T>(key); if (cached !== null) { return cached; } const fresh = await fetchFn(); await set(key, fresh, ttlSeconds); return fresh; } // ============================================ // Rate Limiting // ============================================ export interface RateLimitResult { allowed: boolean; remaining: number; resetIn: number; } export async function rateLimit( key: string, maxRequests: number, windowSeconds: number ): Promise<RateLimitResult> { const current = await redis.incr(key); if (current === 1) { await redis.expire(key, windowSeconds); } const ttl = await redis.ttl(key); return { allowed: current <= maxRequests, remaining: Math.max(0, maxRequests - current), resetIn: ttl > 0 ? ttl : windowSeconds, }; } // ============================================ // Session Store // ============================================ export interface Session { userId: string; data: Record<string, unknown>; createdAt: number; } const SESSION_PREFIX = 'session:'; const SESSION_TTL = 86400; // 24 hours export const sessionStore = { async create(sessionId: string, userId: string, data: Record<string, unknown> = {}): Promise<void> { const session: Session = { userId, data, createdAt: Date.now(), }; await set(`${SESSION_PREFIX}${sessionId}`, session, SESSION_TTL); }, async get(sessionId: string): Promise<Session | null> { return get<Session>(`${SESSION_PREFIX}${sessionId}`); }, async update(sessionId: string, data: Record<string, unknown>): Promise<void> { const session = await this.get(sessionId); if (session) { session.data = { ...session.data, ...data }; await set(`${SESSION_PREFIX}${sessionId}`, session, SESSION_TTL); } }, async destroy(sessionId: string): Promise<boolean> { return del(`${SESSION_PREFIX}${sessionId}`); }, async refresh(sessionId: string): Promise<void> { await redis.expire(`${SESSION_PREFIX}${sessionId}`, SESSION_TTL); }, }; // ============================================ // Pub/Sub // ============================================ const subscriber = redis.duplicate(); type MessageHandler = (message: string, channel: string) => void; const handlers = new Map<string, MessageHandler[]>(); subscriber.on('message', (channel, message) => { const channelHandlers = handlers.get(channel) || []; channelHandlers.forEach(handler => handler(message, channel)); }); export async function subscribe(channel: string, handler: MessageHandler): Promise<void> { const existing = handlers.get(channel) || []; handlers.set(channel, [...existing, handler]); if (existing.length === 0) { await subscriber.subscribe(channel); } } export async function publish(channel: string, message: unknown): Promise<number> { const serialized = typeof message === 'string' ? message : JSON.stringify(message); return redis.publish(channel, serialized); } // ============================================ // Queue (Simple Job Queue) // ============================================ export interface Job<T = unknown> { id: string; type: string; data: T; createdAt: number; attempts: number; } const QUEUE_PREFIX = 'queue:'; export const queue = { async push<T>(name: string, type: string, data: T): Promise<string> { const job: Job<T> = { id: `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`, type, data, createdAt: Date.now(), attempts: 0, }; await redis.lpush(`${QUEUE_PREFIX}${name}`, JSON.stringify(job)); return job.id; }, async pop<T>(name: string, timeout = 0): Promise<Job<T> | null> { const result = await redis.brpop(`${QUEUE_PREFIX}${name}`, timeout); if (!result) return null; const job = JSON.parse(result[1]) as Job<T>; job.attempts++; return job; }, async size(name: string): Promise<number> { return redis.llen(`${QUEUE_PREFIX}${name}`); }, async clear(name: string): Promise<void> { await redis.del(`${QUEUE_PREFIX}${name}`); }, }; // ============================================ // Leaderboard // ============================================ export const leaderboard = { async addScore(name: string, memberId: string, score: number): Promise<void> { await redis.zadd(name, score, memberId); }, async incrementScore(name: string, memberId: string, increment: number): Promise<number> { return redis.zincrby(name, increment, memberId); }, async getTop(name: string, count = 10): Promise<Array<{ memberId: string; score: number }>> { const results = await redis.zrevrange(name, 0, count - 1, 'WITHSCORES'); const entries: Array<{ memberId: string; score: number }> = []; for (let i = 0; i < results.length; i += 2) { entries.push({ memberId: results[i], score: parseFloat(results[i + 1]), }); } return entries; }, async getRank(name: string, memberId: string): Promise<number | null> { const rank = await redis.zrevrank(name, memberId); return rank !== null ? rank + 1 : null; }, async getScore(name: string, memberId: string): Promise<number | null> { const score = await redis.zscore(name, memberId); return score !== null ? parseFloat(score) : null; }, }; // ============================================ // Exports // ============================================ export { redis }; export default redis;

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/millsydotdev/Code-MCP'

If you have feedback or need assistance with the MCP directory API, please join our Discord server