-- ===================================================================== -- TeeCup — Rundespesifikk kommentartråd for org-turneringer, migrasjon -- 090 (ADR-102). Speiler `058_round_messages.sql` + -- `059_post_reactions_and_comments.sql` sin round_message/ -- round_message_reaction/round_message_comment, MEN med organization_id -- + full RLS -- i motsetning til den frittstående sidens bevisst -- RLS-frie plain_connection()-mønster (ADR-033 Beslutning A), er -- turneringer ER org-scopet (CLAUDE.md-invarianten). -- -- Én tråd PER TURNERINGSRUNDE (hele feltet på tvers av flighter deler -- tråd, IKKE per flight) -- bevisst valgt av bruker, se ADR-102. -- -- Skriverett = leserett = org-medlemskap (get_authorized_org), IKKE en -- ny synlighetsmodell -- samme presedens som individual_tournaments.py -- sin egen modul-docstring allerede fastslår for runde-/deltaker- -- OPPSETT ("vanlig org-medlemsnivå", ikke det strengere self-only-kravet -- update_hole har for selve SCORING-en). Moderering: forfatter ELLER -- org-admin (is_org_admin), samme regel som Banter Board (ADR-025). -- -- Bevisst UTENFOR omfang i v1 (se ADR-102): ingen @-tagging (round_ -- message_tag-motparten er utelatt), ingen WebSocket-kringkasting -- (individual-tournament-detail.tsx har ingen sanntids-tilkobling i -- det hele tatt ennå -- eget, uprioritert gap). -- ===================================================================== \set ON_ERROR_STOP on CREATE TABLE tournament_round_message ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), organization_id uuid NOT NULL, tournament_round_id uuid NOT NULL, author_user_id uuid NOT NULL REFERENCES app_user(id), -- Frosset ved skrivetidspunkt, samme prinsipp som round_message.author_display_name -- -- fullt navn (roster-/forfatter-kontekst, ikke direkte adressering, -- CLAUDE.md navneformat-regel). author_display_name text NOT NULL, body text, image_key text, -- Offline-kø-idempotens (samme mønster som round_message.client_message_id, -- migrasjon 073) -- et gjentatt POST med samme id (avbrutt synk prøvd på -- nytt) returnerer den allerede opprettede meldingen fremfor en duplikat. client_message_id uuid, created_at timestamptz NOT NULL DEFAULT now(), FOREIGN KEY (organization_id, tournament_round_id) REFERENCES tournament_round(organization_id, id) ON DELETE CASCADE, CHECK (body IS NOT NULL OR image_key IS NOT NULL) ); CREATE INDEX ON tournament_round_message (organization_id, tournament_round_id, created_at); CREATE INDEX ON tournament_round_message (organization_id, author_user_id); CREATE UNIQUE INDEX ON tournament_round_message (tournament_round_id, client_message_id) WHERE client_message_id IS NOT NULL; CREATE TABLE tournament_round_message_reaction ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), organization_id uuid NOT NULL, tournament_round_message_id uuid NOT NULL REFERENCES tournament_round_message(id) ON DELETE CASCADE, user_id uuid NOT NULL REFERENCES app_user(id), emoji text NOT NULL, created_at timestamptz NOT NULL DEFAULT now(), UNIQUE (tournament_round_message_id, user_id) ); CREATE INDEX ON tournament_round_message_reaction (organization_id, tournament_round_message_id); CREATE TABLE tournament_round_message_comment ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), organization_id uuid NOT NULL, tournament_round_message_id uuid NOT NULL REFERENCES tournament_round_message(id) ON DELETE CASCADE, parent_comment_id uuid REFERENCES tournament_round_message_comment(id) ON DELETE CASCADE, author_user_id uuid NOT NULL REFERENCES app_user(id), author_display_name text NOT NULL, body text NOT NULL, created_at timestamptz NOT NULL DEFAULT now(), CHECK (length(btrim(body)) > 0) ); CREATE INDEX ON tournament_round_message_comment (organization_id, tournament_round_message_id, created_at); CREATE INDEX ON tournament_round_message_comment (organization_id, parent_comment_id); -- --------------------------------------------------------------------- -- Row-Level Security (samme org_isolation-policy-mønster som 040) -- --------------------------------------------------------------------- DO $$ DECLARE t text; BEGIN FOREACH t IN ARRAY ARRAY[ 'tournament_round_message', 'tournament_round_message_reaction', 'tournament_round_message_comment' ] LOOP EXECUTE format('ALTER TABLE %I ENABLE ROW LEVEL SECURITY;', t); EXECUTE format('ALTER TABLE %I FORCE ROW LEVEL SECURITY;', t); EXECUTE format($p$ CREATE POLICY org_isolation ON %I USING (organization_id = current_setting('app.current_org', true)::uuid) WITH CHECK (organization_id = current_setting('app.current_org', true)::uuid); $p$, t); END LOOP; END $$; -- Samme "slett og opprett på nytt, aldri rediger"-prinsipp som -- round_message/message -- ingen UPDATE-grant på selve meldingen/ -- kommentaren. Reaksjoner får UPDATE (emoji-bytte er en UPSERT). GRANT SELECT, INSERT, DELETE ON tournament_round_message TO teecup_app; GRANT SELECT, INSERT, UPDATE, DELETE ON tournament_round_message_reaction TO teecup_app; GRANT SELECT, INSERT, DELETE ON tournament_round_message_comment TO teecup_app;