76 lines
2.3 KiB
TypeScript
76 lines
2.3 KiB
TypeScript
import { runtimeEnv } from "@/utils/envUtils.js";
|
|
import type { Context, Env, Next } from "hono";
|
|
import type { HonoEnv } from "@/honoUtils/HonoEnv.js";
|
|
import {
|
|
getLimiterForType,
|
|
getRateLimitKey,
|
|
setRateLimitKeyInContext,
|
|
} from "@/internal/misc/rateLimiter/rateLimitFactory";
|
|
import {
|
|
getOrgAggregateType,
|
|
getRateLimitType,
|
|
RateLimitType,
|
|
} from "../internal/misc/rateLimiter/rateLimitConfigs";
|
|
|
|
/**
|
|
* In-memory rate limiting middleware for Hono
|
|
* Uses different rate limits based on endpoint type (General, Track, Check)
|
|
*/
|
|
export const rateLimitMiddleware = async (c: Context<HonoEnv>, next: Next) => {
|
|
const ctx = c.get("ctx");
|
|
|
|
try {
|
|
// 1. Determine rate limit type based on endpoint
|
|
const rateLimitType = getRateLimitType(c);
|
|
|
|
if (
|
|
rateLimitType === RateLimitType.Attach &&
|
|
(runtimeEnv.NODE_ENV === "development" ||
|
|
runtimeEnv.NODE_ENV === "test") &&
|
|
ctx.org?.id === runtimeEnv.TESTS_ORG_ID
|
|
) {
|
|
return await next();
|
|
}
|
|
|
|
// 2. Get rate limit key based on type
|
|
const rateLimitKey = getRateLimitKey({ c, rateLimitType });
|
|
|
|
// 3. Store key in context for keyGenerator to access
|
|
setRateLimitKeyInContext(c as Context, rateLimitKey);
|
|
|
|
// 4. Get the appropriate limiter for this type
|
|
const limiter = getLimiterForType(rateLimitType);
|
|
|
|
const aggregateType = getOrgAggregateType(rateLimitType);
|
|
if (!aggregateType) {
|
|
// 5. Apply rate limiting
|
|
return await limiter(c as Context<Env>, next);
|
|
}
|
|
|
|
// 5. Org-aggregate limiter wraps the per-customer one; the key slot is
|
|
// swapped between them since keyGenerator reads it at execution time.
|
|
setRateLimitKeyInContext(
|
|
c as Context,
|
|
getRateLimitKey({ c, rateLimitType: aggregateType }),
|
|
);
|
|
const aggregateLimiter = getLimiterForType(aggregateType);
|
|
|
|
let innerResponse: Response | undefined;
|
|
const aggregateResponse = await aggregateLimiter(
|
|
c as Context<Env>,
|
|
async () => {
|
|
setRateLimitKeyInContext(c as Context, rateLimitKey);
|
|
innerResponse = (await limiter(c as Context<Env>, next)) ?? undefined;
|
|
},
|
|
);
|
|
|
|
// hono-rate-limiter discards next()'s return, so re-surface an inner 429.
|
|
return aggregateResponse ?? innerResponse;
|
|
} catch (error) {
|
|
ctx.logger.error(
|
|
`Error checking rate limit, error: ${error}. Bypassing for now`,
|
|
);
|
|
return await next();
|
|
}
|
|
};
|