2 Commits
Author SHA1 Message Date
john-okeefe 4716790564 fix: OPDS base_url placeholder bug + setup gate requires base_url
Three bugs fixed:

1. Schema seeded base_url with fake placeholder 'bookhoard.example.com'.
   Removed seed; startup now seeds from BASE_URL env var only if DB row
   is empty (admin changes persist across restarts). One-time UPDATE
   clears the placeholder in existing installs.

2. config.GetBaseURL() had a broken type assertion (local SystemConfigRow
   vs database.SystemConfig) that always failed, returning . Admin panel
   showed env var fallback instead of actual DB value. Fixed with a
   function-type getter that properly wraps the DB query.

3. OPDS handler read base_url only from DB with no fallback. When DB had
   the placeholder, all feed links pointed to an unreachable domain,
   breaking KOReader search/download. Added deriveBaseURL() helper that
   falls back to the request Host/scheme when DB value is empty.

Setup gate improvements:
- isSetupComplete now requires both admin user AND non-empty base_url
- Setup middleware no longer exempts all /api/ routes; only allows
  /api/auth/register, /api/auth/login, /api/system/config before setup
  is complete. All other API routes get 503.
- Cache invalidated when base_url is saved via admin settings

Dev workflow:
- New bruno/NewDevDBSetup/SetBaseUrl.yml for dev DB setup
- NewDB.sh runs SetBaseUrl between RegisterUser and CreateEbookLibrary
2026-08-06 13:02:35 -04:00
john-okeefe 980aaee0d9 refactor(setup): derive setup-complete status from admin user count
Setup completion was previously tracked by a manually-flipped setup_complete row in system_settings, written via a JWT-protected PUT /api/setup/complete endpoint. This meant any admin user created outside the setup wizard (future CLI, seed scripts, direct DB inserts) would not flip the switch, leaving the app stuck redirecting to /setup.

The trigger is now derived from real data: setup is complete iff at least one admin user exists. This is self-correcting regardless of how users are created, and re-engages setup automatically if all admins are ever removed.

Changes:
- Add internal/setupstatus package with IsSetupComplete() (queries CountAdmins, 10s in-memory cache, fails open on DB error) and Invalidate() to clear the cache. Uses an AdminCounter interface to avoid importing the database package.
- Add CountAdmins sqlc query (SELECT COUNT(*) FROM users WHERE role = 'admin') and regenerate.
- Rewire router/setup.go isSetupComplete() to delegate to setupstatus; drop the old setup_complete setting read, cache vars, and the PUT /api/setup/complete route.
- Call setupstatus.Invalidate() in the auth handler after CreateUser, UpdateUserRole, and DeleteUser so the cache reflects admin-count changes immediately.
- Align first-user promotion in Register to key off !adminExists instead of len(users) == 0, so the two checks cannot diverge.
- Remove the now-dead SetSetupComplete/GetSetupStatus handlers.
- Drop the setup_complete seed row from schema.sql.
- Remove the apiPut('/setup/complete') call from the setup wizard finishSetup(); the admin account created in submitAdmin already marks setup complete server-side.
2026-07-29 11:08:18 -04:00