fix(web): avoid gson-2.1 members-wrapped JSON responses; update AGENTS.md
- sendJsonResponse now serializes JsonElement via toString(), since gson-2.1's JSON_ELEMENT factory misses JsonObject/JsonArray subclasses and falls back to reflection (leaking the internal "members" field into every REST response) - Document javac full-path requirement, pre-existing compile warnings, frontend page data flow, and gson quirks in AGENTS.md
This commit is contained in:
@@ -6,12 +6,14 @@ KLALB ("KLALB Decentralized SRv6 Network") — Java load-balancing/tunnel system
|
|||||||
|
|
||||||
No Maven/Gradle. Plain Eclipse/IntelliJ project: dependencies are vendored jars in `lib/`, output goes to `bin/` (gitignored). When adding a jar, update **both** `.classpath` and `KLALB.iml`.
|
No Maven/Gradle. Plain Eclipse/IntelliJ project: dependencies are vendored jars in `lib/`, output goes to `bin/` (gitignored). When adding a jar, update **both** `.classpath` and `KLALB.iml`.
|
||||||
|
|
||||||
Compile (verified; `-encoding UTF-8` is mandatory — sources contain Chinese text):
|
Compile (`javac` is NOT on PATH — use the full JDK path; `-encoding UTF-8` is mandatory because sources contain Chinese text):
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
& javac -encoding UTF-8 -cp "lib/*" -d bin (Get-ChildItem -Recurse src -Filter *.java | ForEach-Object FullName)
|
& "C:\Program Files\Zulu\zulu-25\bin\javac.exe" -encoding UTF-8 -cp "lib/*" -d bin (Get-ChildItem -Recurse src -Filter *.java | ForEach-Object FullName)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Warnings about `ThreadTool` varargs / deprecated `finalize` are pre-existing and expected — success = exit code 0. After recompiling, restart the running app (IDE-debugged JVMs keep old classes).
|
||||||
|
|
||||||
Run from the repo root — CWD matters:
|
Run from the repo root — CWD matters:
|
||||||
- reads `klalb-config.json` from CWD
|
- reads `klalb-config.json` from CWD
|
||||||
- loads native libs from CWD: `tuntap4j.dll/.so/.dylib`, `wintun.dll`, `fastcopy.dll` (TUN device support)
|
- loads native libs from CWD: `tuntap4j.dll/.so/.dylib`, `wintun.dll`, `fastcopy.dll` (TUN device support)
|
||||||
@@ -51,13 +53,19 @@ No test suite, no CI. Classes named `*Test*` (`nathole/`, `ntp/`) are manual `ma
|
|||||||
|
|
||||||
Located in `dashboard/`:
|
Located in `dashboard/`:
|
||||||
- **Stack**: Vite + React 19 + TypeScript + Tailwind CSS v4 + `@base-ui/react` (style: `base-nova`, icons: `lucide-react`, toasts: `@base-ui/react/toast`).
|
- **Stack**: Vite + React 19 + TypeScript + Tailwind CSS v4 + `@base-ui/react` (style: `base-nova`, icons: `lucide-react`, toasts: `@base-ui/react/toast`).
|
||||||
- **Routing**: Hash-based routing (`#/overview`, `#/connections`, `#/settings`, etc.) for seamless SPA hosting under Java `KLALBWebServer`.
|
- **Routing**: Hash-based routing (`#/overview`, `#/connections`, `#/topology`, `#/settings`, etc.) for seamless SPA hosting under Java `KLALBWebServer`.
|
||||||
- **Package Manager**: `pnpm` (run all commands from `dashboard/` directory).
|
- **Package Manager**: `pnpm` (run all commands from `dashboard/` directory).
|
||||||
- **Component installation**: **Must** use CLI via `pnpm dlx shadcn@latest add <component>` (e.g. `pnpm dlx shadcn@latest add alert card badge toast`). Never create or fake shadcn components manually.
|
- **Component installation**: **Must** use CLI via `pnpm dlx shadcn@latest add <component>` (e.g. `pnpm dlx shadcn@latest add alert card badge toast`). Never create or fake shadcn components manually. Non-shadcn libs (`@xyflow/react`, `d3-force`) are installed via plain `pnpm add`.
|
||||||
- **Commands**:
|
- **Commands**:
|
||||||
- `pnpm dev` — Start Vite dev server (proxies `/api` to backend `http://127.0.0.1:4665`).
|
- `pnpm dev` — Start Vite dev server (proxies `/api` to backend `http://127.0.0.1:4665`).
|
||||||
- `pnpm build` — Typecheck and build SPA to `dashboard/dist` (which Java `KLALBWebServer` serves directly).
|
- `pnpm build` — Typecheck and build SPA to `dashboard/dist` (which Java `KLALBWebServer` serves directly).
|
||||||
- `pnpm lint` / `pnpm typecheck` — Verification.
|
- `pnpm lint` / `pnpm typecheck` — Verification.
|
||||||
|
- **Pages & data flow**:
|
||||||
|
- Overview / Connections read the SSE stream (`use-klalb-sse.ts`, 200ms pushes of status + links).
|
||||||
|
- Settings loads/saves `/api/config` (`use-klalb-config.ts`); save payload must keep legacy field aliases alongside new names for compatibility.
|
||||||
|
- Topology polls `/api/nodes` every 1s (`use-topology.ts`) — SSE does NOT carry topology.
|
||||||
|
- Topology layout: `d3-force` headless simulation (recomputed only when node/edge structure changes) rendered by `@xyflow/react` with custom `device-node` / `link-edge` components in `src/components/topology/`.
|
||||||
|
- React hooks lint rule forbids `setState` synchronously inside effects — initialize form state via component `key` remount + lazy `useState(() => ...)` initializers (see `SettingsForm` pattern).
|
||||||
|
|
||||||
## Config
|
## Config
|
||||||
|
|
||||||
@@ -70,6 +78,12 @@ Key controller config fields:
|
|||||||
- `enableTUN`: boolean flag for TUN interface creation (`"TUNName"` configures device name).
|
- `enableTUN`: boolean flag for TUN interface creation (`"TUNName"` configures device name).
|
||||||
- `webPort`: default `4665`.
|
- `webPort`: default `4665`.
|
||||||
|
|
||||||
|
Gson quirks:
|
||||||
|
- **gson-2.1 (vendored) is ancient**: its `JSON_ELEMENT` adapter factory only matches exact `JsonElement.class`, NOT subclasses. Calling `gson.toJson(Object)` with a runtime `JsonObject`/`JsonArray` reflectively serializes the internal field as `{"members": {...}}`. `KLALBWebServer.sendJsonResponse` guards against this by using `JsonElement.toString()` for JsonElement instances — keep that guard when adding new response paths. SSE avoids the issue entirely via `JsonObject.toString()`.
|
||||||
|
- `/api/config` GET/POST is parsed field-by-field in `KLALBWebServer.handleConfig` (NOT whole-object Gson reflection) because polymorphic fields (`List<InetAddress>`, `List<MultiProtocolSocketAddress>`) break reflective mapping. Keep new config fields in sync there, accepting both legacy and new JSON key names.
|
||||||
|
- `InetAddress`, `MultiProtocolSocketAddress`, and `KLALBConfigItem` custom adapters are registered on the shared Gson in `KLALBProxySystem`; the web server reuses that instance via `proxySystem.getGson()`.
|
||||||
|
- Saving via web API persists through `KLALBProxySystem.saveConfigToFile()` (GUI save consumer takes precedence when present).
|
||||||
|
|
||||||
## Conventions
|
## Conventions
|
||||||
|
|
||||||
- Sources are UTF-8; comments, log/UI strings, and commit messages are largely Chinese.
|
- Sources are UTF-8; comments, log/UI strings, and commit messages are largely Chinese.
|
||||||
|
|||||||
+1
-1
Submodule dashboard updated: 4531e5366b...3f0b20e757
@@ -119,7 +119,15 @@ public class KLALBWebServer {
|
|||||||
|
|
||||||
private void sendJsonResponse(HttpExchange exchange, int statusCode, Object data) throws IOException {
|
private void sendJsonResponse(HttpExchange exchange, int statusCode, Object data) throws IOException {
|
||||||
setCorsHeaders(exchange);
|
setCorsHeaders(exchange);
|
||||||
byte[] bytes = gson.toJson(data).getBytes(StandardCharsets.UTF_8);
|
String json;
|
||||||
|
if (data instanceof JsonElement) {
|
||||||
|
// gson-2.1 的 toJson(Object) 会按运行时类型 JsonObject 反射序列化出内部的 members 字段,
|
||||||
|
// 必须走 JsonElement 重载(或 toString)直接输出 JSON 树
|
||||||
|
json = ((JsonElement) data).toString();
|
||||||
|
} else {
|
||||||
|
json = gson.toJson(data);
|
||||||
|
}
|
||||||
|
byte[] bytes = json.getBytes(StandardCharsets.UTF_8);
|
||||||
exchange.getResponseHeaders().set("Content-Type", "application/json; charset=utf-8");
|
exchange.getResponseHeaders().set("Content-Type", "application/json; charset=utf-8");
|
||||||
exchange.sendResponseHeaders(statusCode, bytes.length);
|
exchange.sendResponseHeaders(statusCode, bytes.length);
|
||||||
try (OutputStream os = exchange.getResponseBody()) {
|
try (OutputStream os = exchange.getResponseBody()) {
|
||||||
|
|||||||
Reference in New Issue
Block a user