check / check (push) Waiting to run
History was selected by msg_to, so whoever next held a released nick, or created a deleted channel again under its old name, could read the earlier holder's messages. Each message row now records the session a DM or INVITE was sent to (to_session_id) and the channel a channel message was sent to (to_channel_id), and GET /api/v1/history selects by those ids. A session's DM history now also follows it across nick changes. Model: opus-5-5
126 lines
5.1 KiB
SQL
126 lines
5.1 KiB
SQL
-- Chat server schema (pre-1.0 consolidated)
|
|
PRAGMA foreign_keys = ON;
|
|
|
|
-- Sessions: each session is a user identity (nick + optional password + signing key)
|
|
CREATE TABLE IF NOT EXISTS sessions (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
uuid TEXT NOT NULL UNIQUE,
|
|
nick TEXT NOT NULL UNIQUE,
|
|
username TEXT NOT NULL DEFAULT '',
|
|
hostname TEXT NOT NULL DEFAULT '',
|
|
ip TEXT NOT NULL DEFAULT '',
|
|
is_oper INTEGER NOT NULL DEFAULT 0,
|
|
is_wallops INTEGER NOT NULL DEFAULT 0,
|
|
password_hash TEXT NOT NULL DEFAULT '',
|
|
signing_key TEXT NOT NULL DEFAULT '',
|
|
away_message TEXT NOT NULL DEFAULT '',
|
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
last_seen DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_sessions_uuid ON sessions(uuid);
|
|
|
|
-- Clients: each session can have multiple connected clients
|
|
CREATE TABLE IF NOT EXISTS clients (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
uuid TEXT NOT NULL UNIQUE,
|
|
session_id INTEGER NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
|
|
token TEXT NOT NULL UNIQUE,
|
|
ip TEXT NOT NULL DEFAULT '',
|
|
hostname TEXT NOT NULL DEFAULT '',
|
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
last_seen DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_clients_token ON clients(token);
|
|
CREATE INDEX IF NOT EXISTS idx_clients_session ON clients(session_id);
|
|
|
|
-- Channels
|
|
CREATE TABLE IF NOT EXISTS channels (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
name TEXT NOT NULL UNIQUE,
|
|
topic TEXT NOT NULL DEFAULT '',
|
|
topic_set_by TEXT NOT NULL DEFAULT '',
|
|
topic_set_at DATETIME,
|
|
hashcash_bits INTEGER NOT NULL DEFAULT 0,
|
|
is_moderated INTEGER NOT NULL DEFAULT 0,
|
|
is_topic_locked INTEGER NOT NULL DEFAULT 1,
|
|
is_invite_only INTEGER NOT NULL DEFAULT 0,
|
|
is_secret INTEGER NOT NULL DEFAULT 0,
|
|
is_no_external INTEGER NOT NULL DEFAULT 1,
|
|
channel_key TEXT NOT NULL DEFAULT '',
|
|
user_limit INTEGER NOT NULL DEFAULT 0,
|
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
|
|
-- Channel bans
|
|
CREATE TABLE IF NOT EXISTS channel_bans (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
channel_id INTEGER NOT NULL REFERENCES channels(id) ON DELETE CASCADE,
|
|
mask TEXT NOT NULL,
|
|
set_by TEXT NOT NULL,
|
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
UNIQUE(channel_id, mask)
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_channel_bans_channel ON channel_bans(channel_id);
|
|
|
|
-- Channel invites (in-memory would be simpler but DB survives restarts)
|
|
CREATE TABLE IF NOT EXISTS channel_invites (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
channel_id INTEGER NOT NULL REFERENCES channels(id) ON DELETE CASCADE,
|
|
session_id INTEGER NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
|
|
invited_by TEXT NOT NULL DEFAULT '',
|
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
UNIQUE(channel_id, session_id)
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_channel_invites_channel ON channel_invites(channel_id);
|
|
|
|
-- Channel members
|
|
CREATE TABLE IF NOT EXISTS channel_members (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
channel_id INTEGER NOT NULL REFERENCES channels(id) ON DELETE CASCADE,
|
|
session_id INTEGER NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
|
|
is_operator INTEGER NOT NULL DEFAULT 0,
|
|
is_voiced INTEGER NOT NULL DEFAULT 0,
|
|
joined_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
UNIQUE(channel_id, session_id)
|
|
);
|
|
|
|
-- Messages: IRC envelope format. to_session_id is the session a DM
|
|
-- or INVITE was sent to, to_channel_id the channel a channel message
|
|
-- was sent to. History is selected by these ids because nicks and
|
|
-- channel names are reused once released, and ids are not.
|
|
CREATE TABLE IF NOT EXISTS messages (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
uuid TEXT NOT NULL UNIQUE,
|
|
command TEXT NOT NULL DEFAULT 'PRIVMSG',
|
|
msg_from TEXT NOT NULL DEFAULT '',
|
|
msg_to TEXT NOT NULL DEFAULT '',
|
|
to_session_id INTEGER REFERENCES sessions(id) ON DELETE SET NULL,
|
|
to_channel_id INTEGER REFERENCES channels(id) ON DELETE SET NULL,
|
|
params TEXT NOT NULL DEFAULT '[]',
|
|
body TEXT NOT NULL DEFAULT '[]',
|
|
meta TEXT NOT NULL DEFAULT '{}',
|
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_messages_to_session ON messages(to_session_id, id);
|
|
CREATE INDEX IF NOT EXISTS idx_messages_to_channel ON messages(to_channel_id, id);
|
|
CREATE INDEX IF NOT EXISTS idx_messages_created ON messages(created_at);
|
|
|
|
-- Spent hashcash tokens for replay prevention (1-year TTL)
|
|
CREATE TABLE IF NOT EXISTS spent_hashcash (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
stamp_hash TEXT NOT NULL UNIQUE,
|
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_spent_hashcash_created ON spent_hashcash(created_at);
|
|
|
|
-- Per-client message queues for fan-out delivery
|
|
CREATE TABLE IF NOT EXISTS client_queues (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
client_id INTEGER NOT NULL REFERENCES clients(id) ON DELETE CASCADE,
|
|
message_id INTEGER NOT NULL REFERENCES messages(id) ON DELETE CASCADE,
|
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
UNIQUE(client_id, message_id)
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_client_queues_client ON client_queues(client_id, id);
|