diff --git a/AGENTS.md b/AGENTS.md index b9a973c..1bce70e 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -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`. -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 -& 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: - reads `klalb-config.json` from CWD - 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/`: - **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). -- **Component installation**: **Must** use CLI via `pnpm dlx shadcn@latest add ` (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 ` (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**: - `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 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 @@ -70,6 +78,12 @@ Key controller config fields: - `enableTUN`: boolean flag for TUN interface creation (`"TUNName"` configures device name). - `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`, `List`) 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 - Sources are UTF-8; comments, log/UI strings, and commit messages are largely Chinese. diff --git a/dashboard b/dashboard index 4531e53..3f0b20e 160000 --- a/dashboard +++ b/dashboard @@ -1 +1 @@ -Subproject commit 4531e5366bb652e6db116a3848b1d25da1c796e4 +Subproject commit 3f0b20e75736551027e65eabd2e33061940ff8f7 diff --git a/src/org/kne/cloud/network/klalb/web/KLALBWebServer.java b/src/org/kne/cloud/network/klalb/web/KLALBWebServer.java index c6cba48..b64ea12 100644 --- a/src/org/kne/cloud/network/klalb/web/KLALBWebServer.java +++ b/src/org/kne/cloud/network/klalb/web/KLALBWebServer.java @@ -119,7 +119,15 @@ public class KLALBWebServer { private void sendJsonResponse(HttpExchange exchange, int statusCode, Object data) throws IOException { 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.sendResponseHeaders(statusCode, bytes.length); try (OutputStream os = exchange.getResponseBody()) {