Every settings update answered with a bare success, so the console could not tell a change that took effect at once from one written to the file that the running program would keep ignoring until it was restarted — the listener address, the storage paths, the Komari dashboard URL. Classify the patch instead. config.RestartRequiredKeys expands a patch into the dot-separated paths of its leaves and intersects them with the settings main reads before it starts serving, and the settings endpoint returns that list as data.restartRequired. It is a pure function of the patch, so UpdateSettings keeps its signature and nothing else has to change; the write succeeds either way, and the field only says which edits are not live. Matching is by overlap rather than equality, so a patch that names a section — replacing it, or deleting it with a null — reports the startup-only keys inside it, while a sibling subtree like webhook.endpoints is not mistaken for webhook.enabled. The result is never nil, so an update with nothing to report serializes as [] rather than null and a client can iterate it without a guard. The console reads the field in ConfigSection and warns instead of confirming, naming the keys that are pending; Settings.vue's hints for the Komari URL, the listen address and the storage paths now say which of their fields is affected rather than labelling the whole card.
Nukumizu Console
Web console for the Nukumizu backend — a remote-server monitor and alert bot. Built with Vue 3 + Vite, no UI framework; styling is hand-rolled on CSS custom properties with a light/dark theme.
Features
- Sign in / first-run admin — login, or create the very first admin while the database is still empty (the backend rejects further registrations once any user exists).
- Nodes (overview) — live server cards from the backend status/info APIs: online state, CPU / RAM / disk load, plus the per-node status notify switch persisted to
bot_node_config.json. Searchable, auto-refreshes. - Bot trust — manage QQ (NapCat) and Telegram admins & trusted groups in
bot_user_config.json: add / remove members and toggle each member'sstatus notify,startup notify, andcommand replies. - Settings — structured forms for the global
config.json(system, debug, Komari dashboard, controller methods, message templates, storage paths). Each card saves only its own section. - System logs — live WebSocket log stream (
/api/system/getLogs) with severity filter chips, search, pause/resume and export.
Development
npm install
npm run dev # http://localhost:5173
The dev server proxies /api (and the log websocket) to the backend. By default it targets http://127.0.0.1:8080; override with:
# Windows PowerShell
$env:NUKUMIZU_API = "http://192.168.20.4:8080"; npm run dev
Production build:
npm run build # outputs ../web/dist
npm run preview
Vite writes to ../web/dist rather than frontend/dist (see build.outDir in vite.config.js) because the Go backend embeds that directory into the binary — go:embed cannot reach outside the package it sits in, so the output has to live under web/. The repo's build-* scripts run this build for you and compile the backend afterwards; npm run build alone does not change what an already-built binary serves.
API contract notes
- Auth uses two headers on every request:
X-Token(from login) andX-Timestamp— a Unix timestamp in seconds (not milliseconds) with a ±30 min tolerance (skipped whensystem.debugModeis on). - Token & user are kept in
localStorage. On a401the session is cleared and you are returned to the login page. - The panel talks to
/api/server/getStatus,/api/server/getInfo,/api/settings/get,/api/settings/setand/api/user/login|register. All management endpoints require anadmintoken. /api/settings/setis a deep merge: sending a JSONnullfor a key removes it (used when deleting a trusted member).
Project structure
frontend/
├── index.html
├── vite.config.js
└── src/
├── main.js / App.vue
├── router/ # routes + auth guard
├── api/index.js # authApi, serverApi, settingsApi
├── utils/ # auth, http client, theme, toasts, formatting
├── styles/ # theme.css (tokens) + ui.css (primitives)
├── components/ # TopBar, SideBar, Modal, Toggle, TagsEditor, HeadersEditor
└── views/ # Login, Layout, Overview, Trusted, Settings, Logs
Caveats
- The log websocket needs an
admintoken, and a browser cannot set headers on a WebSocket handshake, so the token rides in the query string (/api/system/getLogs?token=…×tamp=…). That URL is a credential: it can end up in proxy and access logs, so don't paste it into third-party tools. The view reconnects with a fresh token fromlocalStorageon every attempt. - Registering more than one user is intentionally impossible; the backend only accepts the very first registration.