-- A person could be in exactly one project, chosen once at approval, and could -- not use the gateway at all before that. People belong to several teams and -- to none, so membership becomes its own table and a key need not have a -- project: a personal key is owned by a person and bounded by that person's -- own limits. -- Membership at any level of the tree; a member of a team may use every -- project in it. Role is 'member' today; 'manager' is reserved for delegated -- administration of the scope. CREATE TABLE memberships ( user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE, scope_type TEXT NOT NULL, scope_id TEXT NOT NULL, role TEXT NOT NULL DEFAULT 'member', added_at INTEGER NOT NULL, added_by TEXT, PRIMARY KEY (user_id, scope_type, scope_id) ); CREATE INDEX memberships_by_scope ON memberships(scope_type, scope_id); -- Everybody keeps the project they had. INSERT INTO memberships (user_id, scope_type, scope_id, role, added_at, added_by) SELECT id, 'project', project_id, 'member', COALESCE(approved_at, created_at), approved_by FROM users WHERE project_id IS NOT NULL; -- A personal key has an owner and no project. SQLite cannot drop NOT NULL -- from a column, so the table is rebuilt — with foreign keys off while it is, -- as SQLite's own procedure for this requires, and checked before commit. -- migrate: foreign_keys=off CREATE TABLE api_keys_new ( id TEXT PRIMARY KEY, key_hash BLOB NOT NULL UNIQUE, key_prefix TEXT NOT NULL, name TEXT NOT NULL, project_id TEXT REFERENCES projects(id), band TEXT, routing_profile TEXT, created_at INTEGER NOT NULL, expires_at INTEGER, revoked_at INTEGER, last_used_at INTEGER, user_id TEXT REFERENCES users(id) ); INSERT INTO api_keys_new (id, key_hash, key_prefix, name, project_id, band, routing_profile, created_at, expires_at, revoked_at, last_used_at, user_id) SELECT id, key_hash, key_prefix, name, project_id, band, routing_profile, created_at, expires_at, revoked_at, last_used_at, user_id FROM api_keys; DROP TABLE api_keys; ALTER TABLE api_keys_new RENAME TO api_keys; CREATE INDEX api_keys_by_project ON api_keys(project_id); CREATE INDEX api_keys_by_user ON api_keys(user_id); -- Who spent it, recorded with the spend. Deriving it from the key at read time -- counted a key's past against whoever owns it now, and could not attribute -- the chat page's requests, which have no key row at all. ALTER TABLE usage ADD COLUMN user_id TEXT NOT NULL DEFAULT ''; UPDATE usage SET user_id = COALESCE((SELECT k.user_id FROM api_keys k WHERE k.id = usage.key_id), ''); UPDATE usage SET user_id = substr(key_id, 9) WHERE key_id LIKE 'session:%'; CREATE INDEX usage_by_user_ts ON usage(user_id, ts);