diff --git a/docs/superpowers/specs/2026-05-16-settings-page-design.md b/docs/superpowers/specs/2026-05-16-settings-page-design.md new file mode 100644 index 0000000..81d7ebd --- /dev/null +++ b/docs/superpowers/specs/2026-05-16-settings-page-design.md @@ -0,0 +1,112 @@ +Title: App-wide Settings Page — Design + +Summary + +Add an admin-only Settings page where operators can change app-wide configuration that affects server functions and runtime behavior (feature flags, execution modes, thresholds, API keys for integrations). Settings are persisted in the database and reflected immediately via an in-process cache + event broadcaster so server functions read updated values without restart. + +Goals + +- Provide a single UI for operators to view and change app-wide settings. +- Persist settings reliably and safely (DB-backed) and version/track changes. +- Ensure changes propagate to server functions quickly and predictably. +- Secure the UI and APIs (admin-only). +- Test coverage (unit + E2E). + +Chosen approach (Recommended) + +DB-backed settings with a Settings Service: +- Persist settings in a new Prisma model/table (AppSetting). +- Expose server-side SettingsService with get/set/getAll, an in-memory cache, and an EventEmitter to notify subscribers on change. +- Admin UI: route at /settings (app/routes/settings.tsx) with a guarded admin page to edit values. +- Server functions import SettingsService.get("key") or subscribe to change events when they need dynamic behavior. + +Why DB-backed + +- Atomic persistence, multi-instance safe when coupled with a small invalidation strategy (see below). +- Auditable (store updatedAt, updatedBy, description). +- Fits existing repo (prisma/ exists). + +Data model (Prisma example) + +model AppSetting { + id Int @id @default(autoincrement()) + key String @unique + value Json + description String? + updatedAt DateTime @updatedAt + updatedBy String? // optional admin id/email +} + +Backend: SettingsService + +- File: app/lib/settings.server.ts +- Exports: + - async getSetting(key: string): Promise + - async setSetting(key: string, value: any, opts?: { updatedBy?: string }) + - async getAllSettings(): Promise> + - subscribe(cb: (key,value)=>void) => unsubscribe + +Implementation notes: +- On startup, load all settings into an in-memory Map cache. +- getSetting reads from cache (fast). setSetting updates DB then cache and emits event. +- To support multiple instances, setSetting also writes a lightweight "manifest" or uses DB trigger; for now plan: setSetting updates DB and callers of getSetting should tolerate eventual consistency, or a periodic poll (1s) or Redis pub/sub can be added later. + +API endpoints + +- GET /api/admin/settings -> list all settings (admin-only) +- PUT /api/admin/settings/:key -> update a single setting (admin-only) +- POST /api/admin/settings -> create/replace settings (admin-only) + +Server route files: app/routes/api/admin/settings/index.ts (GET, POST), app/routes/api/admin/settings/[key].ts (PUT) + +Frontend: Settings page + +- File: app/routes/settings.tsx (route available only to admins). +- UI: table/list of settings (key, value editor (text/json/toggle), description, last updated, updatedBy) and a Save button. +- Support types: boolean toggles, numeric inputs, string inputs, JSON editor for advanced values. +- Client calls API endpoints and displays toasts on success/failure. +- When updated, UI will optimistically update local state; eventual confirmation comes from API. + +Security + +- Protect API endpoints and the /settings route with admin guard. If the app has user sessions with roles, require isAdmin. Otherwise gate with an env-based ADMIN_TOKEN for now. +- Validate input server-side: schema for known keys; generic JSON allowed for advanced keys but validated. + +Testing + +- Unit tests for SettingsService (get/set/cache behavior). New tests under app/lib/__tests__. +- Integration test for API endpoints (app/routes/api/admin/settings tests). +- E2E Playwright test: toggling a setting that affects behavior (e.g., feature flag that hides/shows a UI element) should reflect immediately. + +Migration + +- Add Prisma model and generate migration: `npx prisma migrate dev --name add_app_setting` +- Run `npx prisma generate` and deploy migration. + +Rollout + +- Start with critical settings (e.g., ENABLE_TRADING=false, ANALYSIS_BACKGROUND=true, PRICE_STREAM_URL). +- Add a feature flag toggle to hide advanced JSON edits behind "advanced" switch. + +Next steps after approval + +1) Write this spec file into docs (done). (current step) +2) Create PR: add Prisma model, SettingsService, API routes, UI route + components, tests. +3) Run migrations locally and in staging, verify behavior. +4) Add audit logging for changes. + +Notes + +- Multi-instance real-time propagation: simple approach (polling or Redis pub/sub) is deferred to future work if needed. +- Keep settings small and typed where possible to avoid fragile JSON edits. + +Files to create/change (implementation plan will enumerate exact edits) + +- prisma/schema.prisma (add AppSetting model) +- app/lib/settings.server.ts (service) +- app/routes/api/admin/settings/index.ts +- app/routes/api/admin/settings/[key].ts +- app/routes/settings.tsx +- tests: unit + integration + e2e + +Please review and confirm; after approval I'll: 1) commit this spec, 2) produce an implementation plan (writing-plans skill) and then implement if you ask.