impl Web UI (dashboard) #2
@@ -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 <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**:
|
||||
- `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<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
|
||||
|
||||
- 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 {
|
||||
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()) {
|
||||
|
||||
Reference in New Issue
Block a user