// middleware.tsimport { Ratelimit } from "@unkey/ratelimit";import { NextResponse } from "next/server";import type { NextRequest } from "next/server";const limiter = new Ratelimit({ rootKey: process.env.UNKEY_ROOT_KEY!, namespace: "api", limit: 100, duration: "1m",});// Define endpoint-specific limits (most specific first)const ENDPOINT_LIMITS: Array<{ pattern: RegExp; limit: number; duration: string; namespace: string;}> = [ // Expensive AI endpoints - very tight limits { pattern: /^\/api\/ai\/.*/, limit: 10, duration: "1m", namespace: "ai", }, // Data export - limit per hour { pattern: /^\/api\/export\/.*/, limit: 5, duration: "1h", namespace: "export", }, // Write operations - moderate limits { pattern: /^\/api\/.*\/(create|update|delete)/, limit: 30, duration: "1m", namespace: "writes", }, // Default API routes { pattern: /^\/api\/.*/, limit: 100, duration: "1m", namespace: "api", },];function getEndpointConfig(pathname: string) { for (const config of ENDPOINT_LIMITS) { if (config.pattern.test(pathname)) { return config; } } return ENDPOINT_LIMITS[ENDPOINT_LIMITS.length - 1]; // default}export async function middleware(request: NextRequest) { // Skip non-API routes if (!request.nextUrl.pathname.startsWith("/api")) { return NextResponse.next(); } const userId = request.headers.get("x-user-id") ?? request.ip ?? "anonymous"; const config = getEndpointConfig(request.nextUrl.pathname); // Use endpoint-specific namespace for separate counters const { success, remaining, reset } = await limiter.limit( `${config.namespace}:${userId}`, { limit: config.limit, duration: config.duration as any, }, ); if (!success) { return NextResponse.json( { error: "Rate limit exceeded", endpoint: config.namespace, retryAfter: Math.ceil((reset - Date.now()) / 1000), }, { status: 429, headers: { "X-RateLimit-Limit": config.limit.toString(), "X-RateLimit-Remaining": "0", "X-RateLimit-Reset": reset.toString(), "Retry-After": Math.ceil((reset - Date.now()) / 1000).toString(), }, }, ); } const response = NextResponse.next(); response.headers.set("X-RateLimit-Limit", config.limit.toString()); response.headers.set("X-RateLimit-Remaining", remaining.toString()); response.headers.set("X-RateLimit-Reset", reset.toString()); return response;}export const config = { matcher: "/api/:path*",};