phase: 168_admin_settings_panel
**Phase 168 — Admin Settings panel — final verification: PASS** 1. **Implemented/verified:** `/settings.html` (admin-only) with the 75-field catalogue (`app/core/settings_catalog.py`), DB-over-env overrides in Postgres (`app_settings`, migration 0028 at head, `settings_overlay.py` resolver, `app/api/admin_settings.py` — 4 admin-only routes), live-apply on save with no restart + restart-required badges, Reset/Clear all, write-only secrets, `.env`-never-written, `BOR_SETTINGS_DB=0` kill switch. 2. **Phase E2E: 12/12** — run with `E2E_APP_PORT=8200` (ports 8123/8124 are held by an unrelated "undercooked" project; no process killed, no code change — the documented env override). 3. **Regression set: all 7 suites green in isolation** (theme tab, API tokens, configurable brand, global tuning, admin auth, cache busting, wide desktop column — 38 tests). 4. **Full suite: unit+integration exit 0 (4066 passed), app/ coverage 99%** (gate >90%); **ruff clean, pyright 0 errors / 0 warnings.** 5. **Log contract (rule 10) verified live:** per-turn + per-response byte-budget lines present; settings write lines carry key + action, **never the value**. 6. **Byte-budget audit: all endpoints under the 262,144 B budget**; settings catalogue 23,513 B (gzip 3,326). 7. **Defect fixed (one):** criterion 5 had no integration pin — added `test_bad_row_inserted_directly_never_bricks_boot` to `tests/integration/test_admin_settings_api.py` (malformed row inserted directly → fresh `create_app()` boots, row skipped with logged reason, rest of table applies, `/api/health` 200) — 12/12 green. 8. **Docs pass:** `README.md` and `AGENTS.md` reviewed in full — both consistent with the as-built feature (Settings page table, Settings section, Configuration-reference note, `.env.example` block); **no changes required**. 9. **Cleanup:** audit app killed (by PID); `app_settings` left empty (0 rows); all changes left in the working tree (no commit, per protocol). 10. **Completion criteria:** all 7 checkboxes verified green (criterion 7's commit is the harness's). 11. **Next pending phase:** `170_edit_bubble_sizing`.
This commit is contained in:
@@ -67,6 +67,7 @@ client-side router. Direct URLs deep-link to the matching view.
|
||||
| **Saved chats** | `/history.html` | Admin only |
|
||||
| **Access tokens** | `/tokens.html` | Admin only |
|
||||
| **Theme** | `/theme.html` | Admin only |
|
||||
| **Settings** | `/settings.html` | Admin only |
|
||||
| **Document viewer** | `/document.html?source=X&path=Y` | Token users & admins |
|
||||
| **Login** | `/login.html` | Everyone |
|
||||
| **Shared chat** | `/shared/<token>` | Everyone (anonymous) |
|
||||
@@ -207,6 +208,22 @@ chat. Admin-only view.
|
||||
Generate tokens and hand them out — a token opens chat, the answers, and
|
||||
the documents they cite. Shared chats stay open to everyone.
|
||||
|
||||
### Settings (`/settings.html`)
|
||||
|
||||
Edit any setting that today lives in `.env` — every `BOR_*` setting, grouped
|
||||
and type-correct — from the browser. A value you **Save** is stored in the
|
||||
database and **wins over the environment variable from that instant on, with
|
||||
no restart** (boot-wired settings — the pool, the session middleware, the log
|
||||
level — carry a `restart required` badge and apply on the next start); **Reset**
|
||||
hands control back to the environment, and **Clear all overrides** empties the
|
||||
override table. `.env` is never written: the environment stays the base, the
|
||||
database row wins. Secrets are write-only (you set a value, never read one
|
||||
back); a value that fails validation is rejected before anything is stored.
|
||||
Admin-only — anonymous visitors see a sign-in gate.
|
||||
|
||||
The overlay can be switched off for the whole process with
|
||||
`BOR_SETTINGS_DB=0` (env-only; the panel stays visible and says so).
|
||||
|
||||
### Shared chat (`/shared/<token>`)
|
||||
|
||||
A read-only snapshot of any conversation, shareable via a link. Open to
|
||||
@@ -1075,6 +1092,29 @@ Everything is an environment variable (the `BOR_` prefix, plus
|
||||
[`.env.example`](.env.example) is documented below** — copy that file
|
||||
to `.env` and adjust.
|
||||
|
||||
**DB-over-env overrides (phase 168):** the precedence for every setting
|
||||
is **`app_settings` row → `BOR_*` environment → code default** — an
|
||||
override the admin saves in the Settings panel is stored in Postgres
|
||||
(`app_settings`, one row per key) and **wins over the env var for
|
||||
that key — with no restart**. The panel never writes `.env` (the env
|
||||
value stays the base; per-key Reset deletes the row and the env value
|
||||
wins again). `BOR_SETTINGS_DB=0` (set in the **container env**, not
|
||||
`.env` — a committed `0` is a silent "my change did nothing")
|
||||
disables all DB overrides — the pre-phase behavior. Two keys are NOT
|
||||
overridable: `BOR_DATABASE_URL` (it builds the very engine that would
|
||||
read the override) and `BOR_EMBEDDING_DIM` (fixed at `chunks` table
|
||||
creation). A stored value that fails the setting's validators is
|
||||
skipped at read with a logged reason — a DB row never bricks boot.
|
||||
"No restart" is not universal: the settings the panel marks
|
||||
**restart required** (DB pool sizes, session cookie wiring, log
|
||||
level, compression, the static dir) apply on the next
|
||||
`podman compose restart app`; everything else applies immediately.
|
||||
Admins read/write the overrides through the admin-only
|
||||
`/api/admin/settings` routes (GET the catalogue, PUT `/{key}` set one
|
||||
value, DELETE `/{key}` reset one, DELETE all clear everything);
|
||||
secrets are write-only there (the response reports `has_value`,
|
||||
never the plaintext).
|
||||
|
||||
### App & branding
|
||||
|
||||
| Env | Default | Meaning |
|
||||
|
||||
Reference in New Issue
Block a user