v1.5 - history and timeline as well as many datastructure updates added, polished, fixes
This commit is contained in:
parent
1f0aa3078f
commit
1d84400aef
63 changed files with 7927 additions and 208 deletions
86
server/src/migrations/009_superadmin.sql
Normal file
86
server/src/migrations/009_superadmin.sql
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
-- ═══════════════════════════════════════════════════════════════
|
||||
-- 009_superadmin.sql
|
||||
--
|
||||
-- Adds a third role above 'admin'. A CHECK constraint can't be
|
||||
-- altered in place, so the table is rebuilt — the recipe from the
|
||||
-- SQLite docs, in the order it has to happen.
|
||||
--
|
||||
-- Foreign keys are OFF for the duration on purpose. `sessions`
|
||||
-- references admin_users(id), and:
|
||||
--
|
||||
-- * with FKs ON, DROP TABLE admin_users fires the ON DELETE
|
||||
-- CASCADE and empties `sessions` — everyone signed out;
|
||||
-- * with FKs ON, the RENAME afterwards tries to rewrite the
|
||||
-- REFERENCES clause in `sessions` and fails, because the table
|
||||
-- it points at no longer exists.
|
||||
--
|
||||
-- With them OFF neither happens: `sessions` keeps pointing at the
|
||||
-- name "admin_users", which the rename puts back underneath it.
|
||||
--
|
||||
-- ⚠ PRAGMA foreign_keys is a no-op inside a transaction. If the
|
||||
-- migration runner wraps each file in BEGIN/COMMIT, this file will
|
||||
-- appear to work and then fail at the rename. Check the runner
|
||||
-- before applying, or run this one by hand:
|
||||
--
|
||||
-- sudo systemctl stop ngu-api
|
||||
-- sudo sqlite3 /var/lib/ngu/ngu.db < 009_superadmin.sql
|
||||
-- sudo systemctl start ngu-api
|
||||
--
|
||||
-- Verify after:
|
||||
--
|
||||
-- PRAGMA user_version; -- 9
|
||||
-- PRAGMA foreign_key_check; -- no rows
|
||||
-- SELECT email, role FROM admin_users;
|
||||
-- ═══════════════════════════════════════════════════════════════
|
||||
|
||||
PRAGMA foreign_keys = OFF;
|
||||
|
||||
CREATE TABLE admin_users_new (
|
||||
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,
|
||||
|
||||
-- Listed low to high. The application treats these as a ladder,
|
||||
-- not a set: 'superadmin' passes every check 'admin' passes.
|
||||
-- The default stays 'admin' — a new account should never arrive
|
||||
-- at the top of the ladder by accident.
|
||||
role TEXT NOT NULL DEFAULT 'admin'
|
||||
CHECK (role IN ('viewer', 'editor', 'admin', 'superadmin')),
|
||||
is_active INTEGER NOT NULL DEFAULT 1 CHECK (is_active IN (0, 1)),
|
||||
last_login_at TEXT
|
||||
) STRICT;
|
||||
|
||||
-- Columns listed explicitly rather than SELECT *, so this breaks
|
||||
-- loudly if the old shape isn't what this file assumes.
|
||||
INSERT INTO admin_users_new
|
||||
(id, created_at, email, name, password_hash, google_sub,
|
||||
role, is_active, last_login_at)
|
||||
SELECT
|
||||
id, created_at, email, name, password_hash, google_sub,
|
||||
role, is_active, last_login_at
|
||||
FROM admin_users;
|
||||
|
||||
DROP TABLE admin_users;
|
||||
|
||||
ALTER TABLE admin_users_new RENAME TO admin_users;
|
||||
|
||||
-- Informational: prints offending rows and returns nothing if the
|
||||
-- rebuild left the graph intact.
|
||||
PRAGMA foreign_key_check;
|
||||
|
||||
PRAGMA foreign_keys = ON;
|
||||
|
||||
PRAGMA user_version = 9; -- ← set to this migration's number
|
||||
Loading…
Add table
Add a link
Reference in a new issue