This commit is contained in:
2026-04-21 14:31:59 +00:00
parent c6edc5c0bf
commit 74d7f49c8d
12 changed files with 125 additions and 28 deletions

View File

@@ -42,6 +42,9 @@ browser ──► settings server.js (:SETTINGS_PORT, default 12752)
| `GET /api/discord/guild` | `GET /internal/discord/guild` |
| `POST /api/restart` | `POST /internal/restart` |
| `GET /api/restart/status` | `GET /internal/restart/status` |
| `GET /api/notifications/alerts` | `GET /internal/notifications/alerts` |
| `GET /api/notifications/state` | `GET /internal/notifications/state` |
| `POST /api/notifications/toggle` | `POST /internal/notifications/toggle` |
Every response-shape change in the bot's `/internal/*` handlers (`routes/internalApi.js`) is a breaking change here. The bot also gates `POST /internal/config` on an `ALLOWED_CONFIG_KEYS` allowlist — **adding a new field to the UI requires adding the key to that Set in the bot first**, otherwise the save returns 400 for that key.
@@ -49,11 +52,11 @@ Every response-shape change in the bot's `/internal/*` handlers (`routes/interna
`server.js:20-26` sets `cookie.secure: true`. Browsers will refuse to persist the session cookie over plain HTTP, so login silently fails when not behind an HTTPS reverse proxy (`SETTINGS_DOMAIN` is the deployed domain). If you're reproducing a login bug, check this first before debugging auth logic. The `session secret` falls back to `'fallback-secret-change-me'` when `INTERNAL_API_SECRET` is unset — don't rely on the fallback in any environment that matters.
### Client-side routing
`public/index.html` is a single page with all sections rendered; `public/js/app.js` toggles `.hidden` on sections based on `location.pathname`. Routes live in the `ROUTES` map (`app.js:425`). The server has `app.get('*', requireAuth, …)` as a catch-all back to `index.html` (`server.js:97`), so any new client route works without server changes as long as it's added to `ROUTES`.
`public/js/` is split into focused modules (phase 4 refactor): `app.js` (bootstrap), `router.js`, `fields.js`, `notifications.js`, `discord.js`, `login.js`, `util.js` — no bundler, loaded via `<script>` tags. Routes live in the `ROUTES` map (`router.js:4`); the server has a catch-all back to `index.html` (`server.js:202`, Express 5 `'/*splat'` syntax), so adding a client route only requires editing `ROUTES`.
### Config field binding (frontend)
Any form element with `data-key="SOME_CONFIG_KEY"` participates in the editor:
- `populateFields()` (`app.js:102`) fills it from `GET /api/config` and wires change listeners.
- `populateFields()` (`fields.js:11`) fills it from `GET /api/config` and wires change listeners.
- Checkboxes serialize to the strings `'true'` / `'false'`, and `<input type="color">` serializes to `0xRRGGBB` — this matches how the bot stores these values.
- `pendingChanges` accumulates diffs; `saveConfig()` POSTs the whole diff at once.
- `data-smart="channel|category|role|member|multi-member"` swaps the bare `<input>` for a searchable Discord picker backed by `GET /api/discord/guild` (see `public/js/discord.js`).
@@ -61,7 +64,7 @@ Any form element with `data-key="SOME_CONFIG_KEY"` participates in the editor:
**To add a new editable config field:** (1) add the key to the bot's `ALLOWED_CONFIG_KEYS`, (2) add a `<input data-key="NEW_KEY">` (optionally `data-smart=…`) inside the appropriate `.section` in `public/index.html`. No JS changes needed.
### Notification thresholds editor
The Notifications section is **not** a simple `data-key` field — it's a custom editor (`app.js:239-423`) that serializes into a single hidden `NOTIFICATION_THRESHOLDS_JSON` field. Alert keys are hard-coded in `NOTIFICATION_TAB_KEYS` (surge / patterns / unclaimed / chat) and described in `NOTIFICATION_ALERT_DESCRIPTIONS`. **Adding a new alert key requires editing both of those objects**otherwise it won't show up in any tab. Threshold values accept whole numbers or duration strings matching `^(\d+[mhd])+$` (e.g. `15m`, `1h`, `1d6h`).
The Notifications section is **not** a simple `data-key` field — it's a custom editor in `notifications.js` that serializes into a single hidden `NOTIFICATION_THRESHOLDS_JSON` field. Alert metadata is now a **dynamic registry** (phase 5): the bot is canonical and serves it via `GET /api/notifications/alerts`; `notifications.js` uses `FALLBACK_TAB_KEYS` only if the fetch fails. **To add a new alert key, register it in the bot** (not in this codebase) — the UI picks it up automatically on next load. Threshold values accept whole numbers or duration strings matching `^(\d+[mhd])+$` (e.g. `15m`, `1h`, `1d6h`).
## Gotchas