d50c05b120
Frontend half of the pre-launch audit campaign: - types/index.ts + kol/KolPageClient.tsx: add missing 'reduce' KolAction (backend emits it; frontend lacked the type + color/label maps → undefined styling). Adds ACTION_COLOR/actionLabel/postActionLabel entries. - proxy/[...path]/route.ts: relay real client IP (x-forwarded-for / x-real-ip) so the backend rate limiter buckets per-user instead of per-Next-server (BUG-02). - Settings/BotConfigPanel redesign, paper-mode clarity, copy cleanup. - Assorted page/display fixes, loading states, Pagination component. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
65 lines
2.0 KiB
TypeScript
65 lines
2.0 KiB
TypeScript
import { NextRequest, NextResponse } from 'next/server'
|
|
|
|
const API_BASE = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8000'
|
|
|
|
async function handler(request: NextRequest): Promise<NextResponse> {
|
|
const url = new URL(request.url)
|
|
const targetPath = url.pathname.replace('/api/proxy', '')
|
|
const targetUrl = `${API_BASE}${targetPath}${url.search}`
|
|
|
|
const headers = new Headers(request.headers)
|
|
// Remove Next.js internal headers that would confuse the upstream server.
|
|
// Keep (or relay) x-forwarded-for so the backend's rate limiter can
|
|
// identify individual clients — without it, all users share the Next.js
|
|
// server IP and a single rate-limit bucket.
|
|
headers.delete('host')
|
|
headers.delete('x-forwarded-host')
|
|
headers.delete('x-forwarded-proto')
|
|
|
|
// Relay real client IP. Prefer the header Vercel/CDN already set; fall
|
|
// back to x-real-ip; ultimately unknown if neither is present (e.g. local
|
|
// dev through a raw Node server).
|
|
const clientIp =
|
|
request.headers.get('x-forwarded-for') ??
|
|
request.headers.get('x-real-ip') ??
|
|
'unknown'
|
|
headers.set('x-forwarded-for', clientIp)
|
|
|
|
const body =
|
|
request.method !== 'GET' && request.method !== 'HEAD'
|
|
? await request.arrayBuffer()
|
|
: undefined
|
|
|
|
try {
|
|
const upstream = await fetch(targetUrl, {
|
|
method: request.method,
|
|
headers,
|
|
body,
|
|
})
|
|
|
|
const responseHeaders = new Headers(upstream.headers)
|
|
responseHeaders.delete('transfer-encoding')
|
|
|
|
const responseBody = await upstream.arrayBuffer()
|
|
|
|
return new NextResponse(responseBody, {
|
|
status: upstream.status,
|
|
statusText: upstream.statusText,
|
|
headers: responseHeaders,
|
|
})
|
|
} catch (err) {
|
|
return NextResponse.json(
|
|
{ error: 'Upstream request failed', detail: String(err) },
|
|
{ status: 502 },
|
|
)
|
|
}
|
|
}
|
|
|
|
export const GET = handler
|
|
export const POST = handler
|
|
export const PUT = handler
|
|
export const PATCH = handler
|
|
export const DELETE = handler
|
|
export const HEAD = handler
|
|
export const OPTIONS = handler
|