Add rate limiting to v5 API routes#16
Conversation
Port the in-memory sliding-window rate limiter (with optional Upstash Redis backend, failing open on Redis errors) from v4 into v5/src/lib/rate-limit.ts, and guard the chat and upload-notion routes. - chat: 20 req/min per IP (key chat:<ip>) - upload-notion: 15 req/min per IP (key upload:<ip>) Both guards run at the top of POST before any Notion fetch or model call and return 429 with a Retry-After header when exceeded. Document the optional UPSTASH_REDIS_REST_URL / UPSTASH_REDIS_REST_TOKEN env vars in v5/.env.example (falls back to in-memory without them). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 7388048bbe
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| @@ -0,0 +1,93 @@ | |||
| import "server-only"; | |||
There was a problem hiding this comment.
Add server-only to the v5 dependency graph
This new import is not resolvable when the v5 app is installed/built from its own manifest: v5/package.json does not list server-only, and v5/package-lock.json has no node_modules/server-only entry. Since both API routes now import this module through rate-limit.ts, a clean v5 build or deployment will fail module resolution before those routes can run; add the dependency to the v5 package/lock or avoid the import here.
Useful? React with 👍 / 👎.
Summary
Adds IP-based rate limiting to the v5 API routes to prevent abuse of the
chat and upload endpoints.
v5/src/lib/rate-limit.tsfrom the v4 root implementation: anin-memory sliding-window limiter with an optional Upstash Redis backend
(auto-detected from
UPSTASH_REDIS_REST_URL+UPSTASH_REDIS_REST_TOKEN),failing open on Redis errors, plus a
getClientIp(req)helper. This isthe canonical rate-limit util for v5.
POSThandler — beforeany Notion fetch or model call — returning
429when the limit is exceeded./api/admin/revalidateis left alone (already secret-gated).Limits
POST /api/chatchat:<ip>POST /api/upload-notionupload:<ip>The 21st chat request (16th upload) from one IP within a minute gets a
429.Env vars
Both optional, documented in
v5/.env.example:UPSTASH_REDIS_REST_URLUPSTASH_REDIS_REST_TOKENWhen both are set, limits are enforced across all serverless instances via
Upstash. Without them, the limiter falls back to an in-memory store (resets on
cold start; fine for basic abuse prevention).
429 response shape:
{ "error": "Too many requests. Please slow down." }with a
Retry-After: 60header.Test plan
cd v5 && npm install && npm run typecheck && npm run lint && npm run build— all pass./api/chatrequest in a minute from one IP returns 429; Upstash-absent path uses the in-memory limiter;getClientIpreadsx-forwarded-forfirst, thenx-real-ip, then"unknown".🤖 Generated with Claude Code