diff --git a/src/migrations/001_init.sql b/src/migrations/001_init.sql index 33a2c73..3c35d51 100644 --- a/src/migrations/001_init.sql +++ b/src/migrations/001_init.sql @@ -6,6 +6,8 @@ CREATE TABLE IF NOT EXISTS users ( email TEXT, photo TEXT, url TEXT, + status TEXT NOT NULL DEFAULT 'active' CHECK(status IN ('active', 'suspended', 'inactive')), + role TEXT NOT NULL DEFAULT 'user', is_admin INTEGER NOT NULL DEFAULT 0, created_at INTEGER NOT NULL DEFAULT (strftime('%s','now')) ); @@ -45,14 +47,91 @@ CREATE TABLE IF NOT EXISTS invites ( used INTEGER NOT NULL DEFAULT 0, used_by INTEGER, used_at INTEGER, + max_uses INTEGER DEFAULT 1, + current_uses INTEGER NOT NULL DEFAULT 0, + expires_at INTEGER, + note TEXT, + message TEXT, created_at INTEGER NOT NULL DEFAULT (strftime('%s','now')), FOREIGN KEY (created_by) REFERENCES users(id) ON DELETE CASCADE, FOREIGN KEY (used_by) REFERENCES users(id) ON DELETE SET NULL ); +CREATE TABLE IF NOT EXISTS apps ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + client_id TEXT NOT NULL UNIQUE, + redirect_uris TEXT NOT NULL, + name TEXT, + logo_url TEXT, + description TEXT, + is_preregistered INTEGER NOT NULL DEFAULT 0, + client_secret_hash TEXT, + available_roles TEXT, + default_role TEXT, + first_seen INTEGER NOT NULL DEFAULT (strftime('%s','now')), + last_used INTEGER NOT NULL DEFAULT (strftime('%s','now')) +); + +CREATE TABLE IF NOT EXISTS permissions ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + user_id INTEGER NOT NULL, + client_id TEXT NOT NULL, + scopes TEXT NOT NULL, + role TEXT, + granted_at INTEGER NOT NULL DEFAULT (strftime('%s','now')), + last_used INTEGER NOT NULL DEFAULT (strftime('%s','now')), + FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE, + FOREIGN KEY (client_id) REFERENCES apps(client_id) ON DELETE CASCADE, + UNIQUE(user_id, client_id) +); + +CREATE TABLE IF NOT EXISTS authcodes ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + code TEXT NOT NULL UNIQUE, + user_id INTEGER NOT NULL, + client_id TEXT NOT NULL, + redirect_uri TEXT NOT NULL, + scopes TEXT NOT NULL, + code_challenge TEXT NOT NULL, + code_challenge_method TEXT NOT NULL DEFAULT 'S256', + expires_at INTEGER NOT NULL, + used INTEGER NOT NULL DEFAULT 0, + created_at INTEGER NOT NULL DEFAULT (strftime('%s','now')), + FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE +); + +CREATE TABLE IF NOT EXISTS invite_roles ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + invite_id INTEGER NOT NULL, + app_id INTEGER NOT NULL, + role TEXT NOT NULL, + created_at INTEGER NOT NULL DEFAULT (strftime('%s','now')), + FOREIGN KEY (invite_id) REFERENCES invites(id) ON DELETE CASCADE, + FOREIGN KEY (app_id) REFERENCES apps(id) ON DELETE CASCADE, + UNIQUE(invite_id, app_id) +); + +CREATE TABLE IF NOT EXISTS invite_uses ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + invite_id INTEGER NOT NULL, + user_id INTEGER NOT NULL, + used_at INTEGER NOT NULL DEFAULT (strftime('%s','now')), + FOREIGN KEY (invite_id) REFERENCES invites(id) ON DELETE CASCADE, + FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE +); + CREATE INDEX IF NOT EXISTS idx_sessions_token ON sessions(token); CREATE INDEX IF NOT EXISTS idx_sessions_expires_at ON sessions(expires_at); CREATE INDEX IF NOT EXISTS idx_challenges_challenge ON challenges(challenge); CREATE INDEX IF NOT EXISTS idx_challenges_expires_at ON challenges(expires_at); CREATE INDEX IF NOT EXISTS idx_credentials_user_id ON credentials(user_id); CREATE INDEX IF NOT EXISTS idx_invites_code ON invites(code); +CREATE INDEX IF NOT EXISTS idx_apps_client_id ON apps(client_id); +CREATE INDEX IF NOT EXISTS idx_permissions_user_id ON permissions(user_id); +CREATE INDEX IF NOT EXISTS idx_permissions_client_id ON permissions(client_id); +CREATE INDEX IF NOT EXISTS idx_authcodes_code ON authcodes(code); +CREATE INDEX IF NOT EXISTS idx_authcodes_expires_at ON authcodes(expires_at); +CREATE INDEX IF NOT EXISTS idx_invite_roles_invite_id ON invite_roles(invite_id); +CREATE INDEX IF NOT EXISTS idx_invite_roles_app_id ON invite_roles(app_id); +CREATE INDEX IF NOT EXISTS idx_invite_uses_invite_id ON invite_uses(invite_id); +CREATE INDEX IF NOT EXISTS idx_invite_uses_user_id ON invite_uses(user_id); diff --git a/src/migrations/002_add_user_status_role.sql b/src/migrations/002_add_user_status_role.sql deleted file mode 100644 index 4c25e6a..0000000 --- a/src/migrations/002_add_user_status_role.sql +++ /dev/null @@ -1,5 +0,0 @@ --- Add status and role columns to users table -ALTER TABLE users ADD COLUMN status TEXT NOT NULL DEFAULT 'active' CHECK(status IN ('active', 'suspended', 'inactive')); -ALTER TABLE users ADD COLUMN role TEXT NOT NULL DEFAULT 'user'; --- Update existing admin users to have 'admin' role -UPDATE users SET role = 'admin' WHERE is_admin = 1; diff --git a/src/migrations/003_add_indieauth_tables.sql b/src/migrations/003_add_indieauth_tables.sql deleted file mode 100644 index 26d5280..0000000 --- a/src/migrations/003_add_indieauth_tables.sql +++ /dev/null @@ -1,43 +0,0 @@ --- Add tables for IndieAuth/OAuth 2.0 support --- Apps (auto-registered on first authorization request) -CREATE TABLE IF NOT EXISTS apps ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - client_id TEXT NOT NULL UNIQUE, - redirect_uris TEXT NOT NULL, -- JSON array - name TEXT, - first_seen INTEGER NOT NULL DEFAULT (strftime('%s','now')), - last_used INTEGER NOT NULL DEFAULT (strftime('%s','now')) -); --- User permissions per app -CREATE TABLE IF NOT EXISTS permissions ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - user_id INTEGER NOT NULL, - client_id TEXT NOT NULL, - scopes TEXT NOT NULL, -- JSON array - granted_at INTEGER NOT NULL DEFAULT (strftime('%s','now')), - last_used INTEGER NOT NULL DEFAULT (strftime('%s','now')), - FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE, - FOREIGN KEY (client_id) REFERENCES apps(client_id) ON DELETE CASCADE, - UNIQUE(user_id, client_id) -); --- Authorization codes (short-lived, single-use) -CREATE TABLE IF NOT EXISTS authcodes ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - code TEXT NOT NULL UNIQUE, - user_id INTEGER NOT NULL, - client_id TEXT NOT NULL, - redirect_uri TEXT NOT NULL, - scopes TEXT NOT NULL, -- JSON array - code_challenge TEXT NOT NULL, - code_challenge_method TEXT NOT NULL DEFAULT 'S256', - expires_at INTEGER NOT NULL, - used INTEGER NOT NULL DEFAULT 0, - created_at INTEGER NOT NULL DEFAULT (strftime('%s','now')), - FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE -); --- Indexes -CREATE INDEX IF NOT EXISTS idx_apps_client_id ON apps(client_id); -CREATE INDEX IF NOT EXISTS idx_permissions_user_id ON permissions(user_id); -CREATE INDEX IF NOT EXISTS idx_permissions_client_id ON permissions(client_id); -CREATE INDEX IF NOT EXISTS idx_authcodes_code ON authcodes(code); -CREATE INDEX IF NOT EXISTS idx_authcodes_expires_at ON authcodes(expires_at); diff --git a/src/migrations/004_add_client_preregistration_and_roles.sql b/src/migrations/004_add_client_preregistration_and_roles.sql deleted file mode 100644 index d2158e0..0000000 --- a/src/migrations/004_add_client_preregistration_and_roles.sql +++ /dev/null @@ -1,7 +0,0 @@ --- Add columns to apps table for pre-registration metadata -ALTER TABLE apps ADD COLUMN logo_url TEXT; -ALTER TABLE apps ADD COLUMN description TEXT; -ALTER TABLE apps ADD COLUMN is_preregistered INTEGER NOT NULL DEFAULT 0; -ALTER TABLE apps ADD COLUMN client_secret_hash TEXT; --- Add role column to permissions table for per-user, per-app roles -ALTER TABLE permissions ADD COLUMN role TEXT; diff --git a/src/migrations/004_enhance_invites.sql b/src/migrations/004_enhance_invites.sql deleted file mode 100644 index cc3c5d5..0000000 --- a/src/migrations/004_enhance_invites.sql +++ /dev/null @@ -1,48 +0,0 @@ --- Enhance invites table with usage limits, expiry, and app role assignments --- Note: SQLite doesn't support DROP COLUMN, so we keep old columns for backward compatibility --- But we'll use the new columns going forward - --- Add new columns to invites table -ALTER TABLE invites ADD COLUMN max_uses INTEGER DEFAULT 1; -ALTER TABLE invites ADD COLUMN current_uses INTEGER NOT NULL DEFAULT 0; -ALTER TABLE invites ADD COLUMN expires_at INTEGER; -ALTER TABLE invites ADD COLUMN note TEXT; - --- Create invite_roles table for app-specific role assignments -CREATE TABLE IF NOT EXISTS invite_roles ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - invite_id INTEGER NOT NULL, - app_id INTEGER NOT NULL, - role TEXT NOT NULL, - created_at INTEGER NOT NULL DEFAULT (strftime('%s','now')), - FOREIGN KEY (invite_id) REFERENCES invites(id) ON DELETE CASCADE, - FOREIGN KEY (app_id) REFERENCES apps(id) ON DELETE CASCADE, - UNIQUE(invite_id, app_id) -); - -CREATE INDEX IF NOT EXISTS idx_invite_roles_invite_id ON invite_roles(invite_id); -CREATE INDEX IF NOT EXISTS idx_invite_roles_app_id ON invite_roles(app_id); - --- Create invite_uses table to track each use (supports multi-use invites) -CREATE TABLE IF NOT EXISTS invite_uses ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - invite_id INTEGER NOT NULL, - user_id INTEGER NOT NULL, - used_at INTEGER NOT NULL DEFAULT (strftime('%s','now')), - FOREIGN KEY (invite_id) REFERENCES invites(id) ON DELETE CASCADE, - FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE -); - -CREATE INDEX IF NOT EXISTS idx_invite_uses_invite_id ON invite_uses(invite_id); -CREATE INDEX IF NOT EXISTS idx_invite_uses_user_id ON invite_uses(user_id); - --- Migrate existing single-use invites to new structure --- For invites that have been used, set current_uses = 1 and max_uses = 1 -UPDATE invites SET current_uses = 1, max_uses = 1 WHERE used = 1; - --- For unused invites, set max_uses = 1 and current_uses = 0 -UPDATE invites SET max_uses = 1, current_uses = 0 WHERE used = 0; - --- Migrate old invite uses to new invite_uses table -INSERT INTO invite_uses (invite_id, user_id, used_at) -SELECT id, used_by, used_at FROM invites WHERE used = 1 AND used_by IS NOT NULL AND used_at IS NOT NULL; diff --git a/src/migrations/005_add_app_roles.sql b/src/migrations/005_add_app_roles.sql deleted file mode 100644 index a38c6c5..0000000 --- a/src/migrations/005_add_app_roles.sql +++ /dev/null @@ -1,5 +0,0 @@ --- Add available_roles column to apps table (JSON array of role names) -ALTER TABLE apps ADD COLUMN available_roles TEXT; - --- Add default_role column to apps table -ALTER TABLE apps ADD COLUMN default_role TEXT; diff --git a/src/migrations/006_add_invite_message.sql b/src/migrations/006_add_invite_message.sql deleted file mode 100644 index 7420fdb..0000000 --- a/src/migrations/006_add_invite_message.sql +++ /dev/null @@ -1,2 +0,0 @@ --- Add public message field to invites -ALTER TABLE invites ADD COLUMN message TEXT;