Add rate limiting / origin middleware using vercel kv and upstash, need to deploy to fully test

This commit is contained in:
austinkelsay 2024-09-29 16:40:23 -05:00
parent f48559bb29
commit 3ce3a2f037
3 changed files with 92 additions and 0 deletions

44
package-lock.json generated
View File

@ -25,6 +25,8 @@
"@tanstack/react-query": "^5.51.21",
"@uiw/react-markdown-preview": "^5.1.2",
"@uiw/react-md-editor": "^3.11.0",
"@upstash/ratelimit": "^2.0.3",
"@vercel/kv": "^3.0.0",
"axios": "^1.7.2",
"bech32": "^2.0.0",
"chart.js": "^4.4.4",
@ -5065,6 +5067,48 @@
"integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==",
"license": "ISC"
},
"node_modules/@upstash/core-analytics": {
"version": "0.0.10",
"resolved": "https://registry.npmjs.org/@upstash/core-analytics/-/core-analytics-0.0.10.tgz",
"integrity": "sha512-7qJHGxpQgQr9/vmeS1PktEwvNAF7TI4iJDi8Pu2CFZ9YUGHZH4fOP5TfYlZ4aVxfopnELiE4BS4FBjyK7V1/xQ==",
"license": "MIT",
"dependencies": {
"@upstash/redis": "^1.28.3"
},
"engines": {
"node": ">=16.0.0"
}
},
"node_modules/@upstash/ratelimit": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/@upstash/ratelimit/-/ratelimit-2.0.3.tgz",
"integrity": "sha512-BMUpZPZ9IMwrUwohw0HoVAwjBRo5SDb0riAxfCGrLbutuZTPiVagh017Cm3GfhMqwUWLOp0xJQxTCXp812UJVQ==",
"license": "MIT",
"dependencies": {
"@upstash/core-analytics": "^0.0.10"
}
},
"node_modules/@upstash/redis": {
"version": "1.34.0",
"resolved": "https://registry.npmjs.org/@upstash/redis/-/redis-1.34.0.tgz",
"integrity": "sha512-TrXNoJLkysIl8SBc4u9bNnyoFYoILpCcFJcLyWCccb/QSUmaVKdvY0m5diZqc3btExsapcMbaw/s/wh9Sf1pJw==",
"license": "MIT",
"dependencies": {
"crypto-js": "^4.2.0"
}
},
"node_modules/@vercel/kv": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@vercel/kv/-/kv-3.0.0.tgz",
"integrity": "sha512-pKT8fRnfyYk2MgvyB6fn6ipJPCdfZwiKDdw7vB+HL50rjboEBHDVBEcnwfkEpVSp2AjNtoaOUH7zG+bVC/rvSg==",
"license": "Apache-2.0",
"dependencies": {
"@upstash/redis": "^1.34.0"
},
"engines": {
"node": ">=14.6"
}
},
"node_modules/@vladfrangu/async_event_emitter": {
"version": "2.4.6",
"resolved": "https://registry.npmjs.org/@vladfrangu/async_event_emitter/-/async_event_emitter-2.4.6.tgz",

View File

@ -26,6 +26,8 @@
"@tanstack/react-query": "^5.51.21",
"@uiw/react-markdown-preview": "^5.1.2",
"@uiw/react-md-editor": "^3.11.0",
"@upstash/ratelimit": "^2.0.3",
"@vercel/kv": "^3.0.0",
"axios": "^1.7.2",
"bech32": "^2.0.0",
"chart.js": "^4.4.4",

46
src/middleware.js Normal file
View File

@ -0,0 +1,46 @@
import { NextResponse } from 'next/server';
import { Ratelimit } from '@upstash/ratelimit';
import { kv } from '@vercel/kv';
const FRONTEND_HOSTNAME = process.env.FRONTEND_HOSTNAME
const FRONTEND_STAGING_HOSTNAME = process.env.FRONTEND_STAGING_HOSTNAME
const ratelimit = new Ratelimit({
redis: kv,
// 5 requests from the same IP in 10 seconds
limiter: Ratelimit.slidingWindow(5, '10 s'),
});
export const config = {
matcher: ['/api/:path*'],
};
export default async function combinedMiddleware(request) {
const ip = request.ip ?? '127.0.0.1';
const hostname = request.nextUrl.hostname;
const referer = request.headers.get('referer') || '';
// Bypass rate limiting and referer check for the deployment IP
if (hostname === FRONTEND_HOSTNAME || hostname === FRONTEND_STAGING_HOSTNAME) {
return NextResponse.next();
}
// Bypass referer check for paths following /link
if (request.nextUrl.pathname.startsWith('/.well-known')) {
const { success } = await ratelimit.limit(ip);
return success
? NextResponse.next()
: NextResponse.redirect(new URL('/blocked', request.url));
}
// Apply referer check for all other routes
if (!referer.startsWith(FRONTEND_HOSTNAME) && !referer.startsWith(FRONTEND_STAGING_HOSTNAME)) {
return new NextResponse(JSON.stringify({ error: 'Forbidden' }), { status: 403 });
}
// Apply rate limiting for all other routes
const { success } = await ratelimit.limit(ip);
return success
? NextResponse.next()
: NextResponse.redirect(new URL('/blocked', request.url));
}