UNPKG

svelte-guardian

Version:

Batteries included authentication for SvelteKit applications.

73 lines (72 loc) 2.69 kB
import { Redis } from '@upstash/redis'; export class UpstashRedisRateLimiter { redis; constructor(redisUrl, redisToken) { this.redis = new Redis({ url: redisUrl, token: redisToken }); } async limit(event, config) { const now = Date.now(); // Default key generation const key = this.defaultKeyGenerator(event); try { // Check for existing block const blockKey = `block:${key}`; const isBlocked = await this.redis.exists(blockKey); if (isBlocked) { const blockExpiration = await this.redis.ttl(blockKey); return { allowed: false, remaining: 0, resetTime: now + blockExpiration * 1000, blockedUntil: now + blockExpiration * 1000 }; } // Increment request count const current = await this.redis.incr(key); // Set expiration if this is the first request if (current === 1) { await this.redis.expire(key, Math.ceil((config.windowMs || 60000) / 1000)); } // Default block duration to 5 minutes const blockDuration = config.blockDuration || 5 * 60 * 1000; // Check if limit is exceeded if (current > (config.maxRequests || 10)) { // Create block key await this.redis.set(blockKey, 'blocked', 'PX', blockDuration); return { allowed: false, remaining: 0, resetTime: now + config.windowMs, blockedUntil: now + blockDuration }; } return { allowed: true, remaining: (config.maxRequests || 10) - current, resetTime: now + (config.windowMs || 60000) }; } catch (error) { console.error('Upstash Redis rate limiting error:', error); // Fail open if Redis is unavailable return { allowed: true, remaining: config.maxRequests || 10, resetTime: now + (config.windowMs || 60000) }; } } defaultKeyGenerator(event) { // Prioritize user ID if available const session = event.locals.auth?.() || event.locals.session; if (session?.user?.id) { return `rate_limit:user:${session.user.id}`; } // Fallback to IP address const ip = event.getClientAddress(); return `rate_limit:ip:${ip}`; } }