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:
@@ -71,5 +71,25 @@ function runMigrations(db) {
|
||||
if (!playCols.has('total_play_ms')) {
|
||||
db.exec('ALTER TABLE station_plays ADD COLUMN total_play_ms INTEGER NOT NULL DEFAULT 0');
|
||||
}
|
||||
|
||||
// Multi-user kiosk: per-user metadata + designation of "main" identity.
|
||||
const userCols = new Set(db.prepare("PRAGMA table_info(users)").all().map((c) => c.name));
|
||||
if (!userCols.has('is_main')) {
|
||||
db.exec('ALTER TABLE users ADD COLUMN is_main INTEGER NOT NULL DEFAULT 0');
|
||||
}
|
||||
if (!userCols.has('avatar_color')) {
|
||||
db.exec('ALTER TABLE users ADD COLUMN avatar_color TEXT');
|
||||
}
|
||||
if (!userCols.has('avatar_emoji')) {
|
||||
db.exec('ALTER TABLE users ADD COLUMN avatar_emoji TEXT');
|
||||
}
|
||||
db.exec('CREATE UNIQUE INDEX IF NOT EXISTS idx_users_only_one_main ON users(is_main) WHERE is_main = 1');
|
||||
|
||||
// Cross-client stream sync: when a room enters 'play', record the wall-clock
|
||||
// moment so every client can target the same playhead.
|
||||
const stateCols = new Set(db.prepare("PRAGMA table_info(room_state)").all().map((c) => c.name));
|
||||
if (!stateCols.has('started_at')) {
|
||||
db.exec('ALTER TABLE room_state ADD COLUMN started_at INTEGER');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user