Admin: in-app config screen for site settings #14
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
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:
config/site.jsonon the server (runtime settings)api/.envon 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)Tab 2 — Integrations (writes to
api/.envon the server via a new admin API endpoint)PHOTOGRAPHER_EMAIL— notification recipientN8N_WEBHOOK_URL— n8n webhook for email eventsGOOGLE_CALENDAR_ID— calendar to check for busy datesGOOGLE_SERVICE_ACCOUNT_JSON— paste-in field (textarea, write-only: shown as••••if already set)SITE_URL— base URL for links in emailsTab 3 — Security
ADMIN_SECRETpassphrase (requires current passphrase to confirm, invalidates session on change)API changes needed
GET /api/admin/config/site— returns currentconfig/site.jsonPUT /api/admin/config/site— writes validated JSON back to the volume-mounted fileGET /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 processPOST /api/admin/config/secret— change ADMIN_SECRET (verify current first)Notes
requireAdminbearer-token middleware.config/site.jsonis volume-mounted so writes from the container persist without a rebuild.process.exit(0)and rely on Dockerrestart: unless-stoppedto bring it back up.ADMIN_SECRETmust immediately invalidate the current session (redirect to login).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 existingrequireAdminmiddleware (OIDC admin session or legacyADMIN_SECRET). Writes atomically (temp file + rename) and keeps one prior version assite.json.bak. Required adocker-compose.ymlfix: theapiservice had no volume mount forconfig/at all before this — onlyportfolio/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
/dashboardwith 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 usedphotographer.image, but the real field isphotographer.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_SECRETand 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.