UNPKG

webpods

Version:

Append-only log service with OAuth authentication

57 lines 2.17 kB
/** * Rate limiting middleware */ import { checkRateLimit, getRateLimitStatus } from "../domain/ratelimit.js"; import { getDb } from "../db.js"; import { createLogger } from "../logger.js"; import { getIpAddress } from "../utils.js"; const logger = createLogger("webpods:ratelimit"); /** * Rate limiting middleware factory */ export function rateLimit(action) { return async (req, res, next) => { try { const db = getDb(); // Use user ID if authenticated, otherwise IP address with prefix const key = req.auth ? req.auth.user_id : `ip:${getIpAddress(req)}`; const result = await checkRateLimit(db, key, action); if (!result.success) { logger.error("Rate limit check failed", { error: result.error }); res.status(500).json({ error: { code: "INTERNAL_ERROR", message: "Rate limit check failed", }, }); return; } // Get the limit for headers const statusResult = await getRateLimitStatus(db, key, action); const limit = statusResult.success ? statusResult.data.limit : 1000; // Set rate limit headers res.setHeader("X-RateLimit-Limit", limit.toString()); res.setHeader("X-RateLimit-Remaining", result.data.remaining.toString()); res.setHeader("X-RateLimit-Reset", result.data.resetAt.toISOString()); if (!result.data.allowed) { logger.warn("Rate limit exceeded", { key, action }); res.status(429).json({ error: { code: "RATE_LIMIT_EXCEEDED", message: "Too many requests", }, }); return; } next(); } catch (error) { logger.error("Rate limit middleware error", { error }); // On error, allow the request to proceed next(); } }; } //# sourceMappingURL=ratelimit.js.map