import { NextRequest, NextResponse } from "next/server" import { getAuthUser, type AuthUser } from "./auth" import { checkRateLimit, getClientIdentifier, getRateLimitHeaders } from "./rate-limit" /** * Reusable API route wrapper that enforces authentication. * Usage: * export const POST = withAuth(async (req, user) => { ... }) */ export function withAuth( handler: (request: NextRequest, user: AuthUser) => Promise ) { return async (request: NextRequest) => { const user = await getAuthUser() if (!user) { return NextResponse.json( { error: "Authentication required" }, { status: 401 } ) } return handler(request, user) } } /** * Rate limit configuration presets for different route types. */ export const RATE_LIMIT_PRESETS = { /** Standard write ops: 30 req/hour guest, 100 req/hour user */ write: { guest: { max: 30, window: 3600 }, user: { max: 100, window: 3600 } }, /** Expensive ops (AI search, run): 10 req/hour guest, 50 req/hour user */ expensive: { guest: { max: 10, window: 3600 }, user: { max: 50, window: 3600 } }, /** Read-heavy: 120 req/hour guest, 300 req/hour user */ read: { guest: { max: 120, window: 3600 }, user: { max: 300, window: 3600 } }, } as const type RateLimitPreset = keyof typeof RATE_LIMIT_PRESETS /** * Reusable API route wrapper that enforces rate limiting. * Usage: * export const POST = withRateLimit(async (req) => { ... }, "write") */ export function withRateLimit( handler: (request: NextRequest) => Promise, preset: RateLimitPreset = "write" ) { return async (request: NextRequest) => { const user = await getAuthUser() const identifier = getClientIdentifier(request, user?.id ?? undefined) const isAuth = !!user const rateLimit = await checkRateLimit( `${preset}:${identifier}`, isAuth ) if (!rateLimit.success) { const headers = getRateLimitHeaders(rateLimit) return NextResponse.json( { error: rateLimit.error }, { status: 429, headers: Object.fromEntries(headers.entries()), } ) } const response = await handler(request) // Append rate limit headers to successful responses const rlHeaders = getRateLimitHeaders(rateLimit) rlHeaders.forEach((value, key) => { response.headers.set(key, value) }) return response } } /** * Combined: auth + rate limit wrapper. * Usage: * export const POST = withAuthAndRateLimit(async (req, user) => { ... }, "write") */ export function withAuthAndRateLimit( handler: (request: NextRequest, user: AuthUser) => Promise, preset: RateLimitPreset = "write" ) { return async (request: NextRequest) => { // 1. Auth check const user = await getAuthUser() if (!user) { return NextResponse.json( { error: "Authentication required" }, { status: 401 } ) } // 2. Rate limit check const identifier = getClientIdentifier(request, user.id) const rateLimit = await checkRateLimit(`${preset}:${identifier}`, true) if (!rateLimit.success) { const headers = getRateLimitHeaders(rateLimit) return NextResponse.json( { error: rateLimit.error }, { status: 429, headers: Object.fromEntries(headers.entries()), } ) } const response = await handler(request, user) // Append rate limit headers const rlHeaders = getRateLimitHeaders(rateLimit) rlHeaders.forEach((value, key) => { response.headers.set(key, value) }) return response } } /** * Sanitize user-provided text: strip HTML tags and limit length. */ export function sanitizeText(text: string, maxLength: number = 10000): string { return text .replace(/<[^>]*>/g, "") .trim() .slice(0, maxLength) } /** * Clamp a number to a range. */ export function clamp(value: number, min: number, max: number): number { return Math.min(Math.max(value, min), max) }