plebdevs/src/middleware.js

93 lines
2.6 KiB
JavaScript

import { NextResponse } from 'next/server';
import { Ratelimit } from '@upstash/ratelimit';
import { kv } from '@vercel/kv';
// In-memory store for development
const inMemoryStore = new Map();
// Simple in-memory rate limiter for development
const localRatelimit = {
limit: async (key) => {
const now = Date.now();
const windowMs = 10 * 1000; // 10 seconds
const maxRequests = 40;
const requestLog = inMemoryStore.get(key) || [];
const windowStart = now - windowMs;
const recentRequests = requestLog.filter(timestamp => timestamp > windowStart);
const isRateLimited = recentRequests.length >= maxRequests;
if (!isRateLimited) {
recentRequests.push(now);
inMemoryStore.set(key, recentRequests);
}
return {
success: !isRateLimited,
limit: maxRequests,
remaining: Math.max(0, maxRequests - recentRequests.length),
reset: windowStart + windowMs,
};
},
};
// Use local rate limiter for development, Upstash for production
const ratelimit = process.env.NODE_ENV === 'production'
? new Ratelimit({
redis: kv,
limiter: Ratelimit.slidingWindow(40, '10 s'),
analytics: true,
timeout: 1000,
})
: localRatelimit;
// Define which routes you want to rate limit
export const config = {
matcher: [
// Exclude .well-known routes from middleware
'/((?!.well-known).*)',
]
};
export default async function middleware(request) {
// Add CORS headers for all responses
const response = NextResponse.next();
// Add CORS headers
response.headers.set('Access-Control-Allow-Origin', '*');
response.headers.set('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
response.headers.set('Access-Control-Allow-Headers', 'Content-Type, Authorization');
// Handle OPTIONS request
if (request.method === 'OPTIONS') {
return new NextResponse(null, {
status: 200,
headers: response.headers
});
}
// Only apply rate limiting to API routes
if (request.nextUrl.pathname.startsWith('/api')) {
const ip = request.ip ?? '127.0.0.1';
const { success, limit, remaining, reset } = await ratelimit.limit(
`ratelimit_middleware_${ip}`
);
if (!success) {
return new NextResponse('Too Many Requests', {
status: 429,
headers: {
'Retry-After': Math.ceil((reset - Date.now()) / 1000).toString(),
'Access-Control-Allow-Origin': '*',
},
});
}
response.headers.set('X-RateLimit-Limit', limit.toString());
response.headers.set('X-RateLimit-Remaining', remaining.toString());
response.headers.set('X-RateLimit-Reset', reset.toString());
}
return response;
}