-- Phase 6: Chat und Echtzeit. -- =========================================================================== -- 1. Erwähnungen an der Nachricht -- -- Beim Senden aufgelöst und gespeichert, nicht beim Lesen aus dem Text -- geparst: der Worker braucht sie für die Benachrichtigungen, und er soll -- den Text gar nicht erst sehen. -- =========================================================================== ALTER TABLE "ChatMessage" ADD COLUMN "mentions" TEXT[] NOT NULL DEFAULT ARRAY[]::TEXT[]; CREATE INDEX "ChatMessage_mentions_idx" ON "ChatMessage" USING GIN ("mentions"); -- =========================================================================== -- 2. Chatnachrichten sind zurückziehbar, nicht löschbar -- -- Dieselbe Zusicherung wie beim Verlaufseintrag, eine Stufe schwächer: der -- Chat ist ausdrücklich kein Aktenbestandteil, aber eine Nachricht, auf die -- jemand geantwortet hat, darf nicht spurlos verschwinden. -- =========================================================================== CREATE RULE chat_no_delete AS ON DELETE TO "ChatMessage" DO INSTEAD NOTHING; -- Autor, Zeitpunkt und Ort einer Nachricht sind unveränderlich. Wer etwas -- anderes sagen will, zieht zurück und schreibt neu. CREATE OR REPLACE FUNCTION chat_immutable_guard() RETURNS trigger AS $$ BEGIN IF NEW."authorId" IS DISTINCT FROM OLD."authorId" OR NEW."createdAt" IS DISTINCT FROM OLD."createdAt" OR NEW."caseId" IS DISTINCT FROM OLD."caseId" OR NEW."channelId" IS DISTINCT FROM OLD."channelId" THEN RAISE EXCEPTION 'authorId, createdAt und der Ort einer ChatMessage sind unveränderlich'; END IF; RETURN NEW; END; $$ LANGUAGE plpgsql; CREATE TRIGGER chat_immutable_guard_trg BEFORE UPDATE ON "ChatMessage" FOR EACH ROW EXECUTE FUNCTION chat_immutable_guard(); -- =========================================================================== -- 3. Benachrichtigungen -- =========================================================================== -- NotifyMode und User.notifyMode gibt es seit dem Grundgeruest, sie werden hier -- nur benutzt. Siehe docs/06-chat-und-realtime.md. CREATE TABLE "NotificationOutbox" ( "id" BIGSERIAL NOT NULL, "kind" TEXT NOT NULL, "caseId" TEXT, "channelId" TEXT, "recipientId" TEXT, "actorId" TEXT NOT NULL, "meta" JSONB, "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, "sentAt" TIMESTAMP(3), "attempts" INTEGER NOT NULL DEFAULT 0, "lastError" TEXT, CONSTRAINT "NotificationOutbox_pkey" PRIMARY KEY ("id") ); CREATE INDEX "NotificationOutbox_sentAt_createdAt_idx" ON "NotificationOutbox"("sentAt", "createdAt"); CREATE INDEX "NotificationOutbox_caseId_idx" ON "NotificationOutbox"("caseId"); -- Der Worker wartet nicht auf seinen Takt, wenn etwas anliegt. CREATE OR REPLACE FUNCTION notify_outbox() RETURNS trigger AS $$ BEGIN PERFORM pg_notify('notification_queued', NEW."id"::text); RETURN NULL; END; $$ LANGUAGE plpgsql; CREATE TRIGGER notification_outbox_notify AFTER INSERT ON "NotificationOutbox" FOR EACH ROW EXECUTE FUNCTION notify_outbox();