Files
kgva/api/src/plugins/dialog/migrations/001_dialog.sql
T
karim bba1df32cb Squashed 'cms/core/' content from commit 59b9e20
git-subtree-dir: cms/core
git-subtree-split: 59b9e2075ac48cf017db38009d54dbe56a98fffc
2026-06-30 01:46:20 +02:00

100 lines
4.7 KiB
SQL

-- dialog plugin schema — forums / threads / comments (+ comment_stats view).
--
-- Captured 2026-06-29 from the live openbureau dev DB (CT 134 → openbureau-db,
-- supabase/postgres 15.8) via `pg_dump --schema-only --no-owner --no-privileges`
-- of public.{forums,threads,comments,comment_stats}. Made idempotent (IF NOT
-- EXISTS / OR REPLACE / constraint guards) so it is a no-op against the existing
-- openbureau DB and safe to apply to a fresh instance.
--
-- Notes:
-- * gen_random_uuid() is built into Postgres 13+ — no pgcrypto extension needed.
-- * RLS is enabled with NO policies: every read/write is mediated by the CMS
-- using the Supabase service_role key (which bypasses RLS). The dialog plugin
-- therefore only makes sense with `auth: supabase`; kgva (local auth) omits it.
-- * `posts` exists in the live DB but the dialog code never touches it — NOT a
-- dialog table, intentionally excluded.
-- ── tables ──────────────────────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS public.forums (
id uuid DEFAULT gen_random_uuid() NOT NULL,
slug text NOT NULL,
name text NOT NULL,
description text DEFAULT ''::text,
color text,
sort integer DEFAULT 0 NOT NULL,
kind text DEFAULT 'forum'::text NOT NULL,
created_at timestamp with time zone DEFAULT now() NOT NULL
);
CREATE TABLE IF NOT EXISTS public.threads (
id uuid DEFAULT gen_random_uuid() NOT NULL,
forum_id uuid,
key text NOT NULL,
title text NOT NULL,
url text,
kind text DEFAULT 'forum'::text NOT NULL,
author_name text,
user_id uuid,
locked boolean DEFAULT false NOT NULL,
deleted boolean DEFAULT false NOT NULL,
created_at timestamp with time zone DEFAULT now() NOT NULL
);
CREATE TABLE IF NOT EXISTS public.comments (
id uuid DEFAULT gen_random_uuid() NOT NULL,
thread text NOT NULL,
parent_id uuid,
user_id uuid,
author_name text,
author_avatar text,
body text NOT NULL,
created_at timestamp with time zone DEFAULT now() NOT NULL,
deleted boolean DEFAULT false NOT NULL,
author_role text
);
-- ── constraints (guarded — Postgres has no ADD CONSTRAINT IF NOT EXISTS) ──────
DO $$ BEGIN
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'forums_pkey') THEN
ALTER TABLE ONLY public.forums ADD CONSTRAINT forums_pkey PRIMARY KEY (id);
END IF;
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'forums_slug_key') THEN
ALTER TABLE ONLY public.forums ADD CONSTRAINT forums_slug_key UNIQUE (slug);
END IF;
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'threads_pkey') THEN
ALTER TABLE ONLY public.threads ADD CONSTRAINT threads_pkey PRIMARY KEY (id);
END IF;
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'threads_key_key') THEN
ALTER TABLE ONLY public.threads ADD CONSTRAINT threads_key_key UNIQUE (key);
END IF;
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'comments_pkey') THEN
ALTER TABLE ONLY public.comments ADD CONSTRAINT comments_pkey PRIMARY KEY (id);
END IF;
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'threads_forum_id_fkey') THEN
ALTER TABLE ONLY public.threads ADD CONSTRAINT threads_forum_id_fkey
FOREIGN KEY (forum_id) REFERENCES public.forums(id) ON DELETE CASCADE;
END IF;
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'comments_parent_id_fkey') THEN
ALTER TABLE ONLY public.comments ADD CONSTRAINT comments_parent_id_fkey
FOREIGN KEY (parent_id) REFERENCES public.comments(id) ON DELETE CASCADE;
END IF;
END $$;
-- ── indexes ───────────────────────────────────────────────────────────────────
CREATE INDEX IF NOT EXISTS threads_forum_idx ON public.threads USING btree (forum_id);
CREATE INDEX IF NOT EXISTS comments_thread_idx ON public.comments USING btree (thread, created_at);
-- ── view: per-thread comment counts (used by dialog-store) ────────────────────
CREATE OR REPLACE VIEW public.comment_stats AS
SELECT comments.thread,
(count(*))::integer AS count,
max(comments.created_at) AS last
FROM public.comments
WHERE (NOT comments.deleted)
GROUP BY comments.thread;
-- ── row-level security (enabled, no policies — service_role-mediated) ─────────
ALTER TABLE public.forums ENABLE ROW LEVEL SECURITY;
ALTER TABLE public.threads ENABLE ROW LEVEL SECURITY;
ALTER TABLE public.comments ENABLE ROW LEVEL SECURITY;