untrack CLAUDE.md
This commit is contained in:
@@ -1,76 +0,0 @@
|
||||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
## Scope
|
||||
|
||||
This is the **settings-site** subdirectory of the broccolini-bot repo. It is a **separate Express process** that provides an admin web UI for editing the bot's runtime config. It is **not** part of the bot's Node process.
|
||||
|
||||
The parent repo's rules in `/opt/broccolini-bot/CLAUDE.md` still apply here — especially **CommonJS only**, **read before write**, and **no unsolicited refactors**. Read that file alongside this one.
|
||||
|
||||
## Commands
|
||||
|
||||
- `npm start` — run the settings site (`node server.js`).
|
||||
- `npm run dev` — run with `node --watch` for auto-reload.
|
||||
- No lint, no test framework, no build step. Frontend is vanilla JS served from `public/` — no bundler.
|
||||
- Deploy via its own compose file: `docker compose up --build -d` from this directory. Container name `broccolini-settings`, joins external `broccoli-net`.
|
||||
|
||||
## Architecture
|
||||
|
||||
### Two processes, one `.env`
|
||||
The settings site is a thin HTTPS-oriented proxy in front of the bot's internal API:
|
||||
|
||||
```
|
||||
browser ──► settings server.js (:SETTINGS_PORT, default 12752)
|
||||
│ session auth (SETTINGS_ADMIN_PASSWORD)
|
||||
▼
|
||||
bot internalApp (broccoli-net only, INTERNAL_API_PORT, default 12753)
|
||||
│ header auth (x-internal-secret = INTERNAL_API_SECRET)
|
||||
▼
|
||||
routes/internalApi.js in /opt/broccolini-bot
|
||||
```
|
||||
|
||||
`server.js` loads `../.env` (the **bot's** env file) — both processes share it. `docker-compose.yml` also mounts `env_file: ../.env`, not a local one. There is no settings-site-specific env beyond what's in `.env.example`.
|
||||
|
||||
### Proxied endpoints
|
||||
`server.js` exposes five authenticated endpoints that forward to the bot's `/internal/*` API via `callBot()`:
|
||||
|
||||
| Settings route | Bot route |
|
||||
|---|---|
|
||||
| `GET /api/config` | `GET /internal/config` |
|
||||
| `POST /api/config` | `POST /internal/config` |
|
||||
| `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.
|
||||
|
||||
### Second admin password
|
||||
`server.js` accepts an optional `SETTINGS_ADMIN_PASSWORD_2`. If set, the `/login` handler grants the same session for either password; no audit distinction between them. The primary `SETTINGS_ADMIN_PASSWORD` is still required at startup — only the second is optional. Both are redacted by the bot's `GET /internal/config` response.
|
||||
|
||||
### Session cookie requires HTTPS
|
||||
`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/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()` (`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`).
|
||||
|
||||
**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 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
|
||||
|
||||
- The frontend has no framework and no build — edit `public/js/*.js` directly; changes are live on reload.
|
||||
- `getaddrinfo` failures from `callBot()` surface to the UI as "Bot unreachable" (502). This is almost always the bot process being down or the internal port being wrong, not a bug in this codebase.
|
||||
- `docker-compose.yml` binds the port to the Tailscale IP `100.114.205.53:12752` — not `0.0.0.0`. Changing that binding has security implications.
|
||||
Reference in New Issue
Block a user