2.1 KiB
2.1 KiB
type, title, description, tags, timestamp
| type | title | description | tags | timestamp | |||
|---|---|---|---|---|---|---|---|
| database | User Table | The `user` table — accounts, roles, and statuses. |
|
2026-07-21T14:43:00Z |
Schema
| Column | Type | Description |
|---|---|---|
id |
TEXT PK | UUID v4 generated on creation. |
username |
TEXT | Unique login name. |
password_hash |
TEXT | Argon2id hash (memory/time/parallelism from env config). |
role |
TEXT | 'admin' or 'player'. Default 'player'. |
status |
TEXT | 'enabled' or 'disabled'. Default 'enabled'. |
created_at |
TEXT | ISO 8601 timestamp (strftime('%Y-%m-%dT%H:%M:%fZ','now')). |
Constraints
- Unique on
username. roleis enforced application-side (seeAdminService.updateUserRole).statusis checked byAuthService.loginandAuthService.refresh— disabled users cannot log in or refresh.
Invariants
- The application enforces the last-admin invariant: at least one
enabled user with
role = 'admin'must always exist. Attempts to demote or delete the last admin are rejected byUsersService.enforceLastAdminInvariant(used byAdminService.updateUserRoleandAdminService.deleteUser).
First-admin bootstrap
When the user table is empty of admins, the dedicated
POST /api/v1/setup/create-admin endpoint (see
Setup Endpoint) creates the very first admin and
auto-issues a session. Once any admin row exists the endpoint becomes
inert and returns 404 NOT_FOUND, so the route is effectively hidden
in production.
The legacy POST /api/v1/auth/register-first-admin route is preserved
for compatibility but the canonical first-admin flow now lives in the
SetupModule.