(
request: Request,
env: Env,
options: { name: string; limit: number; windowSeconds: number },
)
| 222 | } |
| 223 | |
| 224 | async function assertRateLimit( |
| 225 | request: Request, |
| 226 | env: Env, |
| 227 | options: { name: string; limit: number; windowSeconds: number }, |
| 228 | ): Promise<void> { |
| 229 | if (!env.FEEDBACK_RATE_LIMIT) { |
| 230 | throw new HttpError(500, "Rate limit storage is not configured"); |
| 231 | } |
| 232 | |
| 233 | const now = Math.floor(Date.now() / 1000); |
| 234 | const windowStart = now - (now % options.windowSeconds); |
| 235 | const clientKey = await getClientKey(request); |
| 236 | const key = `feedback:${options.name}:${windowStart}:${clientKey}`; |
| 237 | const current = Number.parseInt((await env.FEEDBACK_RATE_LIMIT.get(key)) ?? "0", 10); |
| 238 | |
| 239 | if (current >= options.limit) { |
| 240 | throw new HttpError(429, "Too many feedback requests. Please try again later."); |
| 241 | } |
| 242 | |
| 243 | await env.FEEDBACK_RATE_LIMIT.put(key, String(current + 1), { |
| 244 | expirationTtl: options.windowSeconds + 300, |
| 245 | }); |
| 246 | } |
| 247 | |
| 248 | async function getClientKey(request: Request): Promise<string> { |
| 249 | const ip = |
no test coverage detected