forked from
rocksky.app/rocksky
A decentralized music tracking and discovery platform built on AT Protocol 馃幍
1import { ctx } from "context";
2import type { Context, MiddlewareHandler } from "hono";
3
4type RateLimitOptions = {
5 limit: number; // max requests
6 window: number; // window in seconds
7 keyPrefix?: string;
8};
9
10export const rateLimiter = (options: RateLimitOptions): MiddlewareHandler => {
11 const { limit, window, keyPrefix = "ratelimit" } = options;
12
13 return async (c: Context, next) => {
14 const ip =
15 c.req.header("x-forwarded-for") ||
16 c.req.raw.headers.get("x-real-ip") ||
17 c.req.raw.headers.get("host");
18 const key = `${keyPrefix}:${ip}`;
19
20 const current = await ctx.redis.incr(key);
21
22 if (current === 1) {
23 await ctx.redis.expire(key, window);
24 }
25
26 const remaining = limit - current;
27 c.header("X-RateLimit-Limit", limit.toString());
28 c.header("X-RateLimit-Remaining", Math.max(remaining, 0).toString());
29
30 if (current > limit) {
31 c.status(429);
32 const reset = await ctx.redis.ttl(key);
33 c.header("X-RateLimit-Reset", reset.toString());
34 return c.text("Too Many Requests");
35 }
36
37 await next();
38 };
39};