CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; CREATE TYPE user_role AS ENUM ('user', 'admin'); CREATE TABLE IF NOT EXISTS users ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), github_id BIGINT NOT NULL UNIQUE, email TEXT, name TEXT NOT NULL, avatar_url TEXT, role user_role NOT NULL DEFAULT 'user', created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE TABLE IF NOT EXISTS posts ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), slug TEXT NOT NULL UNIQUE, title TEXT NOT NULL, summary TEXT, content_md TEXT NOT NULL DEFAULT '', content_html TEXT NOT NULL DEFAULT '', tags TEXT[] NOT NULL DEFAULT '{}', status TEXT NOT NULL DEFAULT 'draft', author_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), published_at TIMESTAMPTZ, CONSTRAINT valid_status CHECK (status IN ('draft', 'private', 'public')) ); CREATE TABLE IF NOT EXISTS drafts ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), post_id UUID NOT NULL UNIQUE REFERENCES posts(id) ON DELETE CASCADE, content_md TEXT NOT NULL DEFAULT '', updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE TABLE IF NOT EXISTS comments ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), post_id UUID NOT NULL REFERENCES posts(id) ON DELETE CASCADE, user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, content TEXT NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), deleted_at TIMESTAMPTZ ); CREATE INDEX IF NOT EXISTS idx_posts_status_published_at ON posts(status, published_at DESC NULLS LAST); CREATE INDEX IF NOT EXISTS idx_posts_slug ON posts(slug); CREATE INDEX IF NOT EXISTS idx_posts_tags ON posts USING GIN(tags); CREATE INDEX IF NOT EXISTS idx_comments_post_id ON comments(post_id, created_at DESC); CREATE INDEX IF NOT EXISTS idx_drafts_post_id ON drafts(post_id); CREATE INDEX IF NOT EXISTS idx_users_github_id ON users(github_id);