This commit is contained in:
55
proxy.ts
Normal file
55
proxy.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import { NextResponse } from 'next/server';
|
||||
import type { NextRequest } from 'next/server';
|
||||
import { getClientAddress } from '@/lib/request';
|
||||
|
||||
const RATE_LIMIT_WINDOW = 60 * 1000;
|
||||
const MAX_REQUESTS = 100;
|
||||
const CLEANUP_THRESHOLD = 500;
|
||||
|
||||
const requestMap = new Map<string, { count: number; expires: number }>();
|
||||
let nextCleanup = Date.now() + RATE_LIMIT_WINDOW;
|
||||
|
||||
function cleanupExpired(now: number) {
|
||||
if (requestMap.size <= CLEANUP_THRESHOLD && now < nextCleanup) return;
|
||||
|
||||
for (const [key, value] of requestMap.entries()) {
|
||||
if (now > value.expires) {
|
||||
requestMap.delete(key);
|
||||
}
|
||||
}
|
||||
|
||||
nextCleanup = now + RATE_LIMIT_WINDOW;
|
||||
}
|
||||
|
||||
export function proxy(request: NextRequest) {
|
||||
const now = Date.now();
|
||||
cleanupExpired(now);
|
||||
|
||||
const ip = getClientAddress(request.headers);
|
||||
const record = requestMap.get(ip);
|
||||
|
||||
if (!record || now > record.expires) {
|
||||
requestMap.set(ip, { count: 1, expires: now + RATE_LIMIT_WINDOW });
|
||||
} else {
|
||||
record.count += 1;
|
||||
|
||||
if (record.count > MAX_REQUESTS) {
|
||||
return new NextResponse('Too Many Requests', {
|
||||
status: 429,
|
||||
headers: {
|
||||
'Retry-After': String(Math.ceil((record.expires - now) / 1000)),
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (process.env.REQUEST_LOGS === 'true') {
|
||||
console.log(`[${new Date(now).toISOString()}] ${request.nextUrl.pathname} ${ip}`);
|
||||
}
|
||||
|
||||
return NextResponse.next();
|
||||
}
|
||||
|
||||
export const config = {
|
||||
matcher: ['/((?!_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp|ico)$).*)'],
|
||||
};
|
||||
Reference in New Issue
Block a user