Admin: in-app config screen for site settings #14

Closed
opened 2026-07-10 00:31:52 -06:00 by jhodgkin · 1 comment
Owner

Summary

Add a Config panel to the admin dashboard (/dashboard) that lets the photographer edit site settings directly from the browser — no SSH, no file editing.

Motivation

Currently, changing email, calendar credentials, Venmo username, pricing, session types, or theme requires either:

  • Editing config/site.json on the server (runtime settings)
  • Editing api/.env on CT114 (secrets/env vars)

A config screen removes that friction for day-to-day management.

Scope

Tab 1 — Site Content (writes to config/site.json)

  • Photographer name, bio, tagline
  • Contact email, phone
  • Social links (Instagram, Facebook, etc.)
  • Venmo username
  • Session types (add / edit / remove)
  • Pricing (mini rate, full rate)
  • Theme colors (primary, accent, background) with live preview swatches

Tab 2 — Integrations (writes to api/.env on the server via a new admin API endpoint)

  • PHOTOGRAPHER_EMAIL — notification recipient
  • N8N_WEBHOOK_URL — n8n webhook for email events
  • GOOGLE_CALENDAR_ID — calendar to check for busy dates
  • GOOGLE_SERVICE_ACCOUNT_JSON — paste-in field (textarea, write-only: shown as •••• if already set)
  • SITE_URL — base URL for links in emails

Tab 3 — Security

  • Change ADMIN_SECRET passphrase (requires current passphrase to confirm, invalidates session on change)

API changes needed

  • GET /api/admin/config/site — returns current config/site.json
  • PUT /api/admin/config/site — writes validated JSON back to the volume-mounted file
  • GET /api/admin/config/env — returns env var names + masked values (never exposes raw secrets)
  • PUT /api/admin/config/env — updates specific env vars and restarts the API process
  • POST /api/admin/config/secret — change ADMIN_SECRET (verify current first)

Notes

  • All config endpoints require the existing requireAdmin bearer-token middleware.
  • config/site.json is volume-mounted so writes from the container persist without a rebuild.
  • Env var changes require a process restart; the API can call process.exit(0) and rely on Docker restart: unless-stopped to bring it back up.
  • Changing ADMIN_SECRET must immediately invalidate the current session (redirect to login).
  • Depends on #10 (Authentik OIDC) for a proper session — for now, uses the same bearer token auth.
  • Write a Playwright test for the config GET endpoints and a round-trip PUT → GET.
## Summary Add a **Config** panel to the admin dashboard (`/dashboard`) that lets the photographer edit site settings directly from the browser — no SSH, no file editing. ## Motivation Currently, changing email, calendar credentials, Venmo username, pricing, session types, or theme requires either: - Editing `config/site.json` on the server (runtime settings) - Editing `api/.env` on CT114 (secrets/env vars) A config screen removes that friction for day-to-day management. ## Scope ### Tab 1 — Site Content (writes to `config/site.json`) - Photographer name, bio, tagline - Contact email, phone - Social links (Instagram, Facebook, etc.) - Venmo username - Session types (add / edit / remove) - Pricing (mini rate, full rate) - Theme colors (primary, accent, background) with live preview swatches ### Tab 2 — Integrations (writes to `api/.env` on the server via a new admin API endpoint) - `PHOTOGRAPHER_EMAIL` — notification recipient - `N8N_WEBHOOK_URL` — n8n webhook for email events - `GOOGLE_CALENDAR_ID` — calendar to check for busy dates - `GOOGLE_SERVICE_ACCOUNT_JSON` — paste-in field (textarea, write-only: shown as `••••` if already set) - `SITE_URL` — base URL for links in emails ### Tab 3 — Security - Change `ADMIN_SECRET` passphrase (requires current passphrase to confirm, invalidates session on change) ## API changes needed - `GET /api/admin/config/site` — returns current `config/site.json` - `PUT /api/admin/config/site` — writes validated JSON back to the volume-mounted file - `GET /api/admin/config/env` — returns env var names + masked values (never exposes raw secrets) - `PUT /api/admin/config/env` — updates specific env vars and restarts the API process - `POST /api/admin/config/secret` — change ADMIN_SECRET (verify current first) ## Notes - All config endpoints require the existing `requireAdmin` bearer-token middleware. - `config/site.json` is volume-mounted so writes from the container persist without a rebuild. - Env var changes require a process restart; the API can call `process.exit(0)` and rely on Docker `restart: unless-stopped` to bring it back up. - Changing `ADMIN_SECRET` must immediately invalidate the current session (redirect to login). - Depends on #10 (Authentik OIDC) for a proper session — for now, uses the same bearer token auth. - Write a Playwright test for the config GET endpoints and a round-trip PUT → GET.
Author
Owner

Built and shipped tonight (2026-07-20), verified end-to-end on both dev and prod (commits 4ac34ca, 945ebde).

Backend: GET/PUT /api/admin/config, gated by the existing requireAdmin middleware (OIDC admin session or legacy ADMIN_SECRET). Writes atomically (temp file + rename) and keeps one prior version as site.json.bak. Required a docker-compose.yml fix: the api service had no volume mount for config/ at all before this — only portfolio/nginx did, and read-only — so the API physically could not have written the file the site reads. Added a read-write mount.

Frontend: new Config panel on /dashboard with two tiers — a quick-edit form for the fields actually touched day to day (site title/tagline/hero image, photographer bio, contact/social, Venmo username, session pricing, theme colors), plus a raw JSON textarea for everything else (portfolio categories, locations, session types, Immich settings). Caught and fixed one real bug via testing against the actual dev config: the form used photographer.image, but the real field is photographer.profileImage (945ebde).

Tested: added 2 Playwright tests (panel loads real values; a no-op full-config save round-trips without error, so the real site.json content used by the live site is never mutated by the test run). Ran the full suite against dev — all 69 tests pass. Also manually verified GET/PUT via both ADMIN_SECRET and a real OIDC admin session, on both dev-lisilou.jerodrigged.com and lisilou.jerodrigged.com — confirmed the prod container is byte-identical to dev's (same image digest) via the promotion pipeline built earlier tonight.

Built and shipped tonight (2026-07-20), verified end-to-end on both dev and prod (commits 4ac34ca, 945ebde). **Backend:** `GET`/`PUT /api/admin/config`, gated by the existing `requireAdmin` middleware (OIDC admin session or legacy `ADMIN_SECRET`). Writes atomically (temp file + rename) and keeps one prior version as `site.json.bak`. Required a `docker-compose.yml` fix: the `api` service had **no** volume mount for `config/` at all before this — only `portfolio`/nginx did, and read-only — so the API physically could not have written the file the site reads. Added a read-write mount. **Frontend:** new Config panel on `/dashboard` with two tiers — a quick-edit form for the fields actually touched day to day (site title/tagline/hero image, photographer bio, contact/social, Venmo username, session pricing, theme colors), plus a raw JSON textarea for everything else (portfolio categories, locations, session types, Immich settings). Caught and fixed one real bug via testing against the actual dev config: the form used `photographer.image`, but the real field is `photographer.profileImage` (945ebde). **Tested:** added 2 Playwright tests (panel loads real values; a no-op full-config save round-trips without error, so the real site.json content used by the live site is never mutated by the test run). Ran the full suite against dev — all 69 tests pass. Also manually verified GET/PUT via both `ADMIN_SECRET` and a real OIDC admin session, on both dev-lisilou.jerodrigged.com and lisilou.jerodrigged.com — confirmed the prod container is byte-identical to dev's (same image digest) via the promotion pipeline built earlier tonight.
Sign in to join this conversation.
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: jhodgkin/lisilou-portfolio#14