v1.4 - added admin page and auth
This commit is contained in:
parent
5efdafbb97
commit
1f0aa3078f
29 changed files with 5264 additions and 217 deletions
55
server/src/migrations/003_auth.sql
Normal file
55
server/src/migrations/003_auth.sql
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
-- ═══════════════════════════════════════════════════════════════
|
||||
-- 003 AUTHENTICATION
|
||||
--
|
||||
-- Two tables: who may sign in, and who currently is signed in.
|
||||
--
|
||||
-- There is no self-signup and no registration endpoint. Accounts
|
||||
-- are created from the CLI, on the box, by someone with shell
|
||||
-- access. For a handful of staff that's the right trade: no
|
||||
-- invite flow, no email delivery, no password-reset surface for
|
||||
-- anyone to attack.
|
||||
-- ═══════════════════════════════════════════════════════════════
|
||||
|
||||
CREATE TABLE admin_users (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
|
||||
-- Stored lowercased. The application lowercases on every read
|
||||
-- and write, so the UNIQUE index is genuinely case-insensitive
|
||||
-- without depending on a collation.
|
||||
email TEXT NOT NULL UNIQUE,
|
||||
name TEXT,
|
||||
|
||||
-- Nullable so a Google-only account can exist later with no
|
||||
-- password at all. A row with both can use either route in.
|
||||
password_hash TEXT,
|
||||
|
||||
-- Google's stable subject id. Nullable, unique when present —
|
||||
-- SQLite allows any number of NULLs in a unique index.
|
||||
google_sub TEXT UNIQUE,
|
||||
|
||||
role TEXT NOT NULL DEFAULT 'admin'
|
||||
CHECK (role IN ('admin', 'viewer')),
|
||||
is_active INTEGER NOT NULL DEFAULT 1 CHECK (is_active IN (0, 1)),
|
||||
last_login_at TEXT
|
||||
) STRICT;
|
||||
|
||||
|
||||
-- One row per active login. The cookie holds a random token; this
|
||||
-- table holds only its SHA-256, so a database leak doesn't hand
|
||||
-- anyone a working session.
|
||||
CREATE TABLE sessions (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
token_hash TEXT NOT NULL UNIQUE,
|
||||
user_id INTEGER NOT NULL REFERENCES admin_users(id) ON DELETE CASCADE,
|
||||
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
last_seen_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
expires_at TEXT NOT NULL,
|
||||
|
||||
user_agent TEXT,
|
||||
ip_hash TEXT
|
||||
) STRICT;
|
||||
|
||||
CREATE INDEX sessions_user_idx ON sessions (user_id);
|
||||
CREATE INDEX sessions_expiry_idx ON sessions (expires_at);
|
||||
Loading…
Add table
Add a link
Reference in a new issue