30 lines
1.3 KiB
MySQL
30 lines
1.3 KiB
MySQL
|
|
-- Accounts: every person signs in with their own username and password, and
|
||
|
|
-- the agents they make belong to them alone.
|
||
|
|
--
|
||
|
|
-- Before this, one shared token from `.env` opened the whole install, and one
|
||
|
|
-- hard-coded local user owned every agent. Login is now per person, so a
|
||
|
|
-- session resolves to exactly one user and one workspace, and the shared token
|
||
|
|
-- is gone. `password_hash IS NULL` marks an account that cannot sign in yet.
|
||
|
|
|
||
|
|
ALTER TABLE users
|
||
|
|
ADD COLUMN IF NOT EXISTS username TEXT,
|
||
|
|
ADD COLUMN IF NOT EXISTS password_hash TEXT;
|
||
|
|
|
||
|
|
-- Usernames are addresses: one spelling, matched case-insensitively.
|
||
|
|
CREATE UNIQUE INDEX IF NOT EXISTS users_username_key
|
||
|
|
ON users (lower(username))
|
||
|
|
WHERE username IS NOT NULL;
|
||
|
|
|
||
|
|
-- Server-side sessions. Only the digest of the cookie value is stored, so a
|
||
|
|
-- database leak cannot be replayed as a login, and an API restart no longer
|
||
|
|
-- logs everyone out.
|
||
|
|
CREATE TABLE IF NOT EXISTS app_sessions (
|
||
|
|
token_hash TEXT PRIMARY KEY,
|
||
|
|
user_id TEXT NOT NULL REFERENCES users (id) ON DELETE CASCADE,
|
||
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||
|
|
expires_at TIMESTAMPTZ NOT NULL
|
||
|
|
);
|
||
|
|
|
||
|
|
CREATE INDEX IF NOT EXISTS app_sessions_user_idx ON app_sessions (user_id);
|
||
|
|
CREATE INDEX IF NOT EXISTS app_sessions_expiry_idx ON app_sessions (expires_at);
|