From 4397369e35683e2cfe55f7c62466d9f312e18b0d Mon Sep 17 00:00:00 2001 From: aaron <> Date: Tue, 12 May 2026 11:19:16 +0800 Subject: [PATCH] 1 --- docker-compose.yml | 2 +- palm_reading.db | 0 web/app/api/[...path]/route.ts | 80 ++++++++++++++++++++++++++++++++++ web/next.config.ts | 10 ----- 4 files changed, 81 insertions(+), 11 deletions(-) create mode 100644 palm_reading.db create mode 100644 web/app/api/[...path]/route.ts diff --git a/docker-compose.yml b/docker-compose.yml index 1c56352..f1c5369 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -7,7 +7,7 @@ services: environment: DATABASE_URL: sqlite+aiosqlite:////app/data/palm_reading.db UPLOAD_DIR: /app/storage/uploads - CORS_ORIGINS: '["http://127.0.0.1:3011","http://localhost:3011"]' + CORS_ORIGINS: '["https://m.xclaw.ren","http://127.0.0.1:3011","http://localhost:3011"]' ports: - "8066:8000" volumes: diff --git a/palm_reading.db b/palm_reading.db new file mode 100644 index 0000000..e69de29 diff --git a/web/app/api/[...path]/route.ts b/web/app/api/[...path]/route.ts new file mode 100644 index 0000000..5c1cc82 --- /dev/null +++ b/web/app/api/[...path]/route.ts @@ -0,0 +1,80 @@ +import { NextRequest } from "next/server"; + +export const runtime = "nodejs"; +export const dynamic = "force-dynamic"; + +const HOP_BY_HOP_HEADERS = new Set([ + "connection", + "keep-alive", + "proxy-authenticate", + "proxy-authorization", + "te", + "trailer", + "transfer-encoding", + "upgrade", + "host", +]); + +type RouteContext = { + params: Promise<{ path: string[] }>; +}; + +function getApiInternalUrl() { + return (process.env.API_INTERNAL_URL || "http://127.0.0.1:8000").replace(/\/$/, ""); +} + +function buildHeaders(request: NextRequest) { + const headers = new Headers(); + request.headers.forEach((value, key) => { + if (!HOP_BY_HOP_HEADERS.has(key.toLowerCase())) { + headers.set(key, value); + } + }); + return headers; +} + +async function proxy(request: NextRequest, context: RouteContext) { + const { path } = await context.params; + const apiPath = path.join("/"); + const targetUrl = new URL(`/api/${apiPath}${request.nextUrl.search}`, getApiInternalUrl()); + const method = request.method.toUpperCase(); + const hasBody = !["GET", "HEAD"].includes(method); + + try { + const upstream = await fetch(targetUrl, { + method, + headers: buildHeaders(request), + body: hasBody ? await request.arrayBuffer() : undefined, + cache: "no-store", + }); + + const responseHeaders = new Headers(upstream.headers); + responseHeaders.delete("content-encoding"); + responseHeaders.delete("content-length"); + + return new Response(upstream.body, { + status: upstream.status, + statusText: upstream.statusText, + headers: responseHeaders, + }); + } catch (error) { + console.error("API proxy failed", { + target: targetUrl.toString(), + error: error instanceof Error ? error.message : String(error), + }); + + return Response.json( + { + detail: "后端服务暂时不可用,请检查 API_INTERNAL_URL 或后端容器状态。", + target: targetUrl.origin, + }, + { status: 502 }, + ); + } +} + +export const GET = proxy; +export const POST = proxy; +export const PUT = proxy; +export const PATCH = proxy; +export const DELETE = proxy; diff --git a/web/next.config.ts b/web/next.config.ts index fdccc03..2f6491c 100644 --- a/web/next.config.ts +++ b/web/next.config.ts @@ -2,16 +2,6 @@ import type { NextConfig } from "next"; const nextConfig: NextConfig = { reactStrictMode: true, - async rewrites() { - const apiInternalUrl = process.env.API_INTERNAL_URL || "http://127.0.0.1:8000"; - - return [ - { - source: "/api/:path*", - destination: `${apiInternalUrl}/api/:path*`, - }, - ]; - }, }; export default nextConfig;