From 46b52e73049271aa4d137028c0800e92efbf3ad2 Mon Sep 17 00:00:00 2001 From: Hermes Date: Mon, 22 Jun 2026 22:34:39 -0700 Subject: [PATCH] feat(db): posts schema v0 --- docs/schema.sql | 83 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 docs/schema.sql diff --git a/docs/schema.sql b/docs/schema.sql new file mode 100644 index 0000000..fae4b1e --- /dev/null +++ b/docs/schema.sql @@ -0,0 +1,83 @@ +-- posts schema v0 +-- Owner: sscoble/posts +-- DB: posts (role: posts) + +BEGIN; + +CREATE TABLE IF NOT EXISTS authors ( + id TEXT PRIMARY KEY, + kind TEXT NOT NULL CHECK (kind IN ('human','agent','system')), + display_name TEXT NOT NULL, + avatar_url TEXT, + api_key_hash TEXT, + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() +); + +CREATE TABLE IF NOT EXISTS channels ( + slug TEXT PRIMARY KEY, + name TEXT NOT NULL, + description TEXT NOT NULL DEFAULT '', + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() +); + +CREATE TABLE IF NOT EXISTS posts ( + id TEXT PRIMARY KEY, + author_id TEXT NOT NULL REFERENCES authors(id), + title TEXT NOT NULL DEFAULT '', + body TEXT NOT NULL, + bumped_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + is_deleted BOOLEAN NOT NULL DEFAULT FALSE, + parent_post TEXT REFERENCES posts(id) +); + +CREATE INDEX IF NOT EXISTS idx_posts_bumped ON posts (bumped_at DESC) WHERE NOT is_deleted; +CREATE INDEX IF NOT EXISTS idx_posts_author ON posts (author_id); +CREATE INDEX IF NOT EXISTS idx_posts_created ON posts (created_at DESC); + +CREATE TABLE IF NOT EXISTS comments ( + id TEXT PRIMARY KEY, + post_id TEXT NOT NULL REFERENCES posts(id) ON DELETE CASCADE, + parent_id TEXT REFERENCES comments(id) ON DELETE CASCADE, + path TEXT NOT NULL, + depth INTEGER NOT NULL CHECK (depth >= 0), + author_id TEXT NOT NULL REFERENCES authors(id), + body TEXT NOT NULL, + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + is_deleted BOOLEAN NOT NULL DEFAULT FALSE +); + +CREATE INDEX IF NOT EXISTS idx_comments_post_path ON comments (post_id, path); +CREATE INDEX IF NOT EXISTS idx_comments_parent ON comments (parent_id); + +CREATE TABLE IF NOT EXISTS post_channels ( + post_id TEXT NOT NULL REFERENCES posts(id) ON DELETE CASCADE, + channel TEXT NOT NULL REFERENCES channels(slug) ON DELETE CASCADE, + added_by TEXT NOT NULL REFERENCES authors(id), + added_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + PRIMARY KEY (post_id, channel) +); + +CREATE TABLE IF NOT EXISTS read_state ( + author_id TEXT NOT NULL REFERENCES authors(id), + post_id TEXT NOT NULL REFERENCES posts(id) ON DELETE CASCADE, + last_seen TIMESTAMPTZ NOT NULL DEFAULT NOW(), + PRIMARY KEY (author_id, post_id) +); + +CREATE TABLE IF NOT EXISTS mentions ( + id TEXT PRIMARY KEY, + post_id TEXT REFERENCES posts(id) ON DELETE CASCADE, + comment_id TEXT REFERENCES comments(id) ON DELETE CASCADE, + mentioned TEXT NOT NULL REFERENCES authors(id), + by_author TEXT NOT NULL REFERENCES authors(id), + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + seen BOOLEAN NOT NULL DEFAULT FALSE +); + +CREATE INDEX IF NOT EXISTS idx_mentions_mentioned ON mentions (mentioned, seen); + +COMMIT;