From 3f7aec7c476fd41f8244fffd015e5c8da30ea4e4 Mon Sep 17 00:00:00 2001 From: clawbot Date: Mon, 9 Feb 2026 17:49:27 -0800 Subject: [PATCH] Split schema: 001 = migrations table only, 002 = all schema All schema changes go into 002_schema.sql until 1.0.0 is tagged. No migrations during early development phase. --- internal/db/schema/001_initial.sql | 87 ----------------------------- internal/db/schema/002_schema.sql | 89 ++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+), 87 deletions(-) create mode 100644 internal/db/schema/002_schema.sql diff --git a/internal/db/schema/001_initial.sql b/internal/db/schema/001_initial.sql index 7adb90b..3741469 100644 --- a/internal/db/schema/001_initial.sql +++ b/internal/db/schema/001_initial.sql @@ -1,91 +1,4 @@ --- Schema migrations tracking CREATE TABLE IF NOT EXISTS schema_migrations ( version INTEGER PRIMARY KEY, applied_at DATETIME DEFAULT CURRENT_TIMESTAMP ); - --- Users: accounts and authentication -CREATE TABLE IF NOT EXISTS users ( - id TEXT PRIMARY KEY, -- UUID - nick TEXT NOT NULL UNIQUE, - password_hash TEXT NOT NULL, - created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - last_seen_at DATETIME -); - --- Auth tokens: one user can have multiple active tokens (multiple devices) -CREATE TABLE IF NOT EXISTS auth_tokens ( - token TEXT PRIMARY KEY, -- random token string - user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE, - created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - expires_at DATETIME, -- NULL = no expiry - last_used_at DATETIME -); -CREATE INDEX IF NOT EXISTS idx_auth_tokens_user_id ON auth_tokens(user_id); - --- Channels: chat rooms -CREATE TABLE IF NOT EXISTS channels ( - id TEXT PRIMARY KEY, -- UUID - name TEXT NOT NULL UNIQUE, -- #general, etc. - topic TEXT NOT NULL DEFAULT '', - modes TEXT NOT NULL DEFAULT '', -- +i, +m, +s, +t, +n - created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP -); - --- Channel members: who is in which channel, with per-user modes -CREATE TABLE IF NOT EXISTS channel_members ( - channel_id TEXT NOT NULL REFERENCES channels(id) ON DELETE CASCADE, - user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE, - modes TEXT NOT NULL DEFAULT '', -- +o (operator), +v (voice) - joined_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - PRIMARY KEY (channel_id, user_id) -); -CREATE INDEX IF NOT EXISTS idx_channel_members_user_id ON channel_members(user_id); - --- Messages: channel and DM history (rotated per MAX_HISTORY) -CREATE TABLE IF NOT EXISTS messages ( - id TEXT PRIMARY KEY, -- UUID - ts DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - from_user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE, - from_nick TEXT NOT NULL, -- denormalized for history - target TEXT NOT NULL, -- #channel name or user UUID for DMs - type TEXT NOT NULL DEFAULT 'message', -- message, action, notice, join, part, quit, topic, mode, nick, system - body TEXT NOT NULL DEFAULT '', - meta TEXT NOT NULL DEFAULT '{}', -- JSON extensible metadata - created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP -); -CREATE INDEX IF NOT EXISTS idx_messages_target_ts ON messages(target, ts); -CREATE INDEX IF NOT EXISTS idx_messages_from_user ON messages(from_user_id); - --- Message queue: per-user pending delivery (unread messages) -CREATE TABLE IF NOT EXISTS message_queue ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE, - message_id TEXT NOT NULL REFERENCES messages(id) ON DELETE CASCADE, - queued_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - UNIQUE(user_id, message_id) -); -CREATE INDEX IF NOT EXISTS idx_message_queue_user_id ON message_queue(user_id, queued_at); - --- Sessions: server-held session state -CREATE TABLE IF NOT EXISTS sessions ( - id TEXT PRIMARY KEY, -- UUID - user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE, - created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - last_active_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - expires_at DATETIME -- idle timeout -); -CREATE INDEX IF NOT EXISTS idx_sessions_user_id ON sessions(user_id); - --- Server links: federation peer configuration -CREATE TABLE IF NOT EXISTS server_links ( - id TEXT PRIMARY KEY, -- UUID - name TEXT NOT NULL UNIQUE, -- human-readable peer name - url TEXT NOT NULL, -- base URL of peer server - shared_key_hash TEXT NOT NULL, -- hashed shared secret - is_active INTEGER NOT NULL DEFAULT 1, - created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - last_seen_at DATETIME -); diff --git a/internal/db/schema/002_schema.sql b/internal/db/schema/002_schema.sql new file mode 100644 index 0000000..58dcb70 --- /dev/null +++ b/internal/db/schema/002_schema.sql @@ -0,0 +1,89 @@ +-- All schema changes go into this file until 1.0.0 is tagged. +-- There will not be migrations during the early development phase. +-- After 1.0.0, new changes get their own numbered migration files. + +-- Users: accounts and authentication +CREATE TABLE IF NOT EXISTS users ( + id TEXT PRIMARY KEY, -- UUID + nick TEXT NOT NULL UNIQUE, + password_hash TEXT NOT NULL, + created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + last_seen_at DATETIME +); + +-- Auth tokens: one user can have multiple active tokens (multiple devices) +CREATE TABLE IF NOT EXISTS auth_tokens ( + token TEXT PRIMARY KEY, -- random token string + user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE, + created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + expires_at DATETIME, -- NULL = no expiry + last_used_at DATETIME +); +CREATE INDEX IF NOT EXISTS idx_auth_tokens_user_id ON auth_tokens(user_id); + +-- Channels: chat rooms +CREATE TABLE IF NOT EXISTS channels ( + id TEXT PRIMARY KEY, -- UUID + name TEXT NOT NULL UNIQUE, -- #general, etc. + topic TEXT NOT NULL DEFAULT '', + modes TEXT NOT NULL DEFAULT '', -- +i, +m, +s, +t, +n + created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +-- Channel members: who is in which channel, with per-user modes +CREATE TABLE IF NOT EXISTS channel_members ( + channel_id TEXT NOT NULL REFERENCES channels(id) ON DELETE CASCADE, + user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE, + modes TEXT NOT NULL DEFAULT '', -- +o (operator), +v (voice) + joined_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (channel_id, user_id) +); +CREATE INDEX IF NOT EXISTS idx_channel_members_user_id ON channel_members(user_id); + +-- Messages: channel and DM history (rotated per MAX_HISTORY) +CREATE TABLE IF NOT EXISTS messages ( + id TEXT PRIMARY KEY, -- UUID + ts DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + from_user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE, + from_nick TEXT NOT NULL, -- denormalized for history + target TEXT NOT NULL, -- #channel name or user UUID for DMs + type TEXT NOT NULL DEFAULT 'message', -- message, action, notice, join, part, quit, topic, mode, nick, system + body TEXT NOT NULL DEFAULT '', + meta TEXT NOT NULL DEFAULT '{}', -- JSON extensible metadata + created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP +); +CREATE INDEX IF NOT EXISTS idx_messages_target_ts ON messages(target, ts); +CREATE INDEX IF NOT EXISTS idx_messages_from_user ON messages(from_user_id); + +-- Message queue: per-user pending delivery (unread messages) +CREATE TABLE IF NOT EXISTS message_queue ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE, + message_id TEXT NOT NULL REFERENCES messages(id) ON DELETE CASCADE, + queued_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + UNIQUE(user_id, message_id) +); +CREATE INDEX IF NOT EXISTS idx_message_queue_user_id ON message_queue(user_id, queued_at); + +-- Sessions: server-held session state +CREATE TABLE IF NOT EXISTS sessions ( + id TEXT PRIMARY KEY, -- UUID + user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE, + created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + last_active_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + expires_at DATETIME -- idle timeout +); +CREATE INDEX IF NOT EXISTS idx_sessions_user_id ON sessions(user_id); + +-- Server links: federation peer configuration +CREATE TABLE IF NOT EXISTS server_links ( + id TEXT PRIMARY KEY, -- UUID + name TEXT NOT NULL UNIQUE, -- human-readable peer name + url TEXT NOT NULL, -- base URL of peer server + shared_key_hash TEXT NOT NULL, -- hashed shared secret + is_active INTEGER NOT NULL DEFAULT 1, + created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + last_seen_at DATETIME +);