From ff99b1248af7f5c650ba8f7f643921b0f79c829c Mon Sep 17 00:00:00 2001
From: yiekheng
Date: Sat, 2 May 2026 20:34:31 +0800
Subject: [PATCH] =?UTF-8?q?feat(web):=20hide=20/api=20entirely=20=E2=80=94?=
=?UTF-8?q?=20RSC=20+=20Server=20Actions=20instead?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
The Route Handler proxy and hash mapping are gone. Browser never
hits a JSON endpoint: data reads happen in React Server Components
fetching api-server:3000 server-side; mutations (B2) will use
Next.js Server Actions. Zero public API surface to scrape or
enumerate.
Co-Authored-By: Claude Opus 4.7 (1M context)
---
web/app/api/[...path]/route.ts | 55 ----------------------------------
web/app/page.tsx | 23 +++++++-------
web/lib/api-paths.ts | 23 --------------
3 files changed, 12 insertions(+), 89 deletions(-)
delete mode 100644 web/app/api/[...path]/route.ts
delete mode 100644 web/lib/api-paths.ts
diff --git a/web/app/api/[...path]/route.ts b/web/app/api/[...path]/route.ts
deleted file mode 100644
index 176eb2d..0000000
--- a/web/app/api/[...path]/route.ts
+++ /dev/null
@@ -1,55 +0,0 @@
-import { NextRequest, NextResponse } from "next/server";
-import { PUBLIC_TO_UPSTREAM } from "@/lib/api-paths";
-
-const API_BASE_URL = process.env.API_BASE_URL ?? "http://api-server:3000";
-
-async function forward(
- request: NextRequest,
- path: string[],
-): Promise {
- const hash = path[0];
- const route = PUBLIC_TO_UPSTREAM[hash];
- if (!route) {
- return NextResponse.json({ error: "Not Found" }, { status: 404 });
- }
- const target = `${API_BASE_URL}/${route.upstream}${route.trailingSlash ? "/" : ""}`;
-
- const init: RequestInit = {
- method: request.method,
- headers: {
- "content-type":
- request.headers.get("content-type") ?? "application/json",
- },
- };
- if (request.method !== "GET" && request.method !== "HEAD") {
- init.body = await request.text();
- }
-
- try {
- const upstream = await fetch(target, init);
- const body = await upstream.text();
- return new NextResponse(body, {
- status: upstream.status,
- headers: {
- "content-type":
- upstream.headers.get("content-type") ?? "application/json",
- },
- });
- } catch (err) {
- return NextResponse.json({ error: String(err) }, { status: 500 });
- }
-}
-
-export async function GET(
- request: NextRequest,
- ctx: { params: Promise<{ path: string[] }> },
-): Promise {
- return forward(request, (await ctx.params).path);
-}
-
-export async function POST(
- request: NextRequest,
- ctx: { params: Promise<{ path: string[] }> },
-): Promise {
- return forward(request, (await ctx.params).path);
-}
diff --git a/web/app/page.tsx b/web/app/page.tsx
index 381d3c4..24631a2 100644
--- a/web/app/page.tsx
+++ b/web/app/page.tsx
@@ -42,24 +42,25 @@ export default function Home() {
- {/* Smoke test */}
+ {/* Architecture note */}
- Smoke Test
+ Architecture
- Verify the API proxy reaches{" "}
+ No public{" "}
+
+ /api
+ {" "}
+ surface. Reads come from{" "}
+ React Server Components fetching{" "}
api-server:3000
-
- :
+ {" "}
+ inside the docker network; mutations go through{" "}
+ Server Actions. The browser
+ never calls a JSON endpoint.
-
- GET /api/414322309db5c06d/ →
-
{/* Footer marker */}
diff --git a/web/lib/api-paths.ts b/web/lib/api-paths.ts
deleted file mode 100644
index b346433..0000000
--- a/web/lib/api-paths.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-// SHA-256(endpoint).hex[:16]. Deterministic; no salt. Public-boundary only:
-// the upstream api-server still uses the readable names. See B1 spec.
-//
-// Verify with:
-// python3 -c "import hashlib; print(hashlib.sha256(b'acc').hexdigest()[:16])"
-export const API_PATHS = {
- acc: "414322309db5c06d", // upstream: /acc/
- user: "04f8996da763b7a9", // upstream: /user/
- updateAcc: "982830e2982d95de", // upstream: /update-acc-data
- updateUser: "f1a25b37d8db494c", // upstream: /update-user-data
-} as const;
-
-// Reverse map for the Route Handler. Keys are the public path segment,
-// values are { upstream: string; trailingSlash: boolean }.
-export const PUBLIC_TO_UPSTREAM: Record<
- string,
- { upstream: string; trailingSlash: boolean }
-> = {
- [API_PATHS.acc]: { upstream: "acc", trailingSlash: true },
- [API_PATHS.user]: { upstream: "user", trailingSlash: true },
- [API_PATHS.updateAcc]: { upstream: "update-acc-data", trailingSlash: false },
- [API_PATHS.updateUser]: { upstream: "update-user-data", trailingSlash: false },
-};