46 lines
1.8 KiB
Markdown
46 lines
1.8 KiB
Markdown
---
|
|
type: database
|
|
title: User Table
|
|
description: The `user` table — accounts, roles, and statuses.
|
|
tags: [database, user, auth]
|
|
timestamp: 2026-07-21T14:18: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`.
|
|
- `role` is enforced application-side (see `AdminService.updateUserRole`).
|
|
- `status` is checked by `AuthService.login` and `AuthService.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 by
|
|
`UsersService.enforceLastAdminInvariant` (used by
|
|
`AdminService.updateUserRole` and `AdminService.deleteUser`).
|
|
|
|
# First-admin bootstrap
|
|
|
|
When the `user` table is empty of admins, `POST /api/v1/auth/register-first-admin`
|
|
is the only endpoint that can create one. After that, the endpoint
|
|
returns `409 SYSTEM_INITIALIZED`.
|
|
|
|
# See also
|
|
|
|
- [Auth and Settings Tables](/database/auth-settings.md)
|
|
- [REST API Overview](/api/rest-overview.md)
|
|
- [Admin Endpoints](/api/admin.md)
|