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.
Add a Server URL field to the admin-account step of the setup wizard so
the public base URL (used for device sync, OPDS feed, and API endpoints)
is configured up front instead of requiring a later visit to admin
settings.
- setup.templ: base URL input on the admin step plus a summary entry
- setup.ts: default baseUrl to window.location.origin and persist it via
PUT /system/config ({ base_url }) right after the admin account is
created, with a non-fatal warning if the save fails
Add a single-page multi-step setup wizard that guides new users
through initial configuration:
Step 1 - Admin Registration: Creates the first user (auto-admin)
using the existing POST /api/auth/register endpoint, with
real-time password validation and confirmation matching.
Step 2 - Library Creation: Create one or more libraries (Ebooks,
Audiobooks, Comics, Manga) using POST /api/libraries. Libraries
list updates inline as they're added.
Step 3 - Folder Configuration: Add filesystem folders to each
library using the existing GET /api/libraries/browse endpoint for
a visual directory browser. Folders are attached via POST
/api/libraries/:id/folders.
Step 4 - Initial Scan: Triggers a manual scan of all libraries via
POST /api/libraries/scan with real-time progress polling using the
existing scan status endpoint.
On completion, the wizard calls PUT /api/setup/complete and sets the
selectedLibrary cookie to the first library's UUID, ensuring the
dashboard loads with populated content instead of an empty 'All
Libraries' view. Handles the edge case where a stale JWT from a
previous database instance triggers an 'already exists' error by
auto-advancing to step 2.
The wizard reuses all existing API calls, Alpine.js utilities, and
form validation functions — no backend logic was duplicated.