feat: add multi-user support for favorites management and room clock synchronization

- Implemented a new API endpoint for retrieving and managing user favorites in /api/users.
- Added functionality for admins to edit the shared "main" user's favorites.
- Created a one-shot DB smoke test script for verifying multi-user kiosk migrations.
- Introduced a RoomClock class for synchronizing server time across clients using WebSocket.
This commit is contained in:
Marco Mooren
2026-05-13 13:53:12 +02:00
parent f6cdfd975c
commit 29423288ca
41 changed files with 4229 additions and 275 deletions

View File

@@ -3,8 +3,30 @@ CREATE TABLE IF NOT EXISTS users (
username TEXT NOT NULL UNIQUE,
password_hash TEXT NOT NULL,
role TEXT NOT NULL DEFAULT 'user' CHECK (role IN ('admin','user')),
is_main INTEGER NOT NULL DEFAULT 0, -- exactly one user is the shared "main" identity
avatar_color TEXT, -- cosmetic, used by avatar picker
avatar_emoji TEXT, -- cosmetic, used by avatar picker
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
);
-- Note: the partial unique index on users(is_main) is created by runMigrations()
-- after legacy DBs have the is_main column added via ALTER TABLE.
-- Trusted kiosk/browser devices. A device cookie (random token) is set after
-- an admin marks the device trusted. Users on the device's whitelist can
-- fast-switch without re-entering a password.
CREATE TABLE IF NOT EXISTS kiosk_devices (
id INTEGER PRIMARY KEY AUTOINCREMENT,
token TEXT NOT NULL UNIQUE,
label TEXT,
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
last_seen_at TEXT
);
CREATE TABLE IF NOT EXISTS kiosk_device_users (
device_id INTEGER NOT NULL REFERENCES kiosk_devices(id) ON DELETE CASCADE,
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
PRIMARY KEY (device_id, user_id)
);
CREATE INDEX IF NOT EXISTS idx_kiosk_device_users_user ON kiosk_device_users(user_id);
CREATE TABLE IF NOT EXISTS sessions (
token TEXT PRIMARY KEY,
@@ -132,6 +154,7 @@ CREATE TABLE IF NOT EXISTS room_state (
station_id INTEGER REFERENCES stations(id) ON DELETE SET NULL,
playing INTEGER NOT NULL DEFAULT 0,
volume REAL NOT NULL DEFAULT 0.7,
started_at INTEGER, -- epoch ms when playback (or current station) began; anchors cross-client sync
updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
);