feat(chat): schema + migration — Multi-Emoji-Reaktionen + Soft-Delete

- ChatMessageReaction + DirectMessageReaction (PK user+message, emoji-Spalte,
  1 Reaktion/User/Message = WhatsApp-Verhalten)
- deleted_at (Tombstone) auf chat_messages + direct_messages
- Realtime-Publication für beide Reaction-Tabellen
NICHT gepusht: Push triggert Auto-Migration auf Staging-DB.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
chahinebrini 2026-05-30 10:56:39 +02:00
parent 4135e388ff
commit 28887cfc49
2 changed files with 82 additions and 7 deletions

View File

@ -0,0 +1,42 @@
-- Chat-Feature: Multi-Emoji-Reaktionen + Soft-Delete (Tombstone) für Room-Chat + DMs
-- Spiegelt die chat_message_likes-Struktur (20250712_chat_rooms_and_features),
-- erweitert um eine emoji-Spalte. PK (user_id, message_id) = eine Reaktion pro
-- User pro Message (neues Emoji ersetzt das alte — WhatsApp-Verhalten).
-- ─── Soft-Delete: deleted_at auf beiden Message-Typen ───────────────────────
ALTER TABLE "rebreak"."chat_messages" ADD COLUMN "deleted_at" TIMESTAMP(3);
ALTER TABLE "rebreak"."direct_messages" ADD COLUMN "deleted_at" TIMESTAMP(3);
-- ─── Reaction-Tabelle: Room-Chat ────────────────────────────────────────────
CREATE TABLE "rebreak"."chat_message_reactions" (
"user_id" UUID NOT NULL,
"message_id" UUID NOT NULL,
"emoji" TEXT NOT NULL,
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "chat_message_reactions_pkey" PRIMARY KEY ("user_id","message_id")
);
CREATE INDEX "chat_message_reactions_message_id_idx" ON "rebreak"."chat_message_reactions"("message_id");
ALTER TABLE "rebreak"."chat_message_reactions"
ADD CONSTRAINT "chat_message_reactions_message_id_fkey"
FOREIGN KEY ("message_id") REFERENCES "rebreak"."chat_messages"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- ─── Reaction-Tabelle: Direct-Messages ──────────────────────────────────────
CREATE TABLE "rebreak"."direct_message_reactions" (
"user_id" UUID NOT NULL,
"message_id" UUID NOT NULL,
"emoji" TEXT NOT NULL,
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "direct_message_reactions_pkey" PRIMARY KEY ("user_id","message_id")
);
CREATE INDEX "direct_message_reactions_message_id_idx" ON "rebreak"."direct_message_reactions"("message_id");
ALTER TABLE "rebreak"."direct_message_reactions"
ADD CONSTRAINT "direct_message_reactions_message_id_fkey"
FOREIGN KEY ("message_id") REFERENCES "rebreak"."direct_messages"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- ─── Supabase Realtime ──────────────────────────────────────────────────────
-- Reaction-Tabellen zur Publication (chat_messages/direct_messages sind bereits
-- via 20260516_enable_chat_realtime drin; deren UPDATE-Events tragen das
-- deleted_at-Tombstone). Chat-Tabellen nutzen kein RLS (Prisma via Service-Role,
-- Scoping client-seitig via postgres_changes-Filter) — Reaction-Tabellen gleich.
ALTER PUBLICATION supabase_realtime ADD TABLE "rebreak"."chat_message_reactions";
ALTER PUBLICATION supabase_realtime ADD TABLE "rebreak"."direct_message_reactions";

View File

@ -381,11 +381,13 @@ model ChatMessage {
attachmentName String? @map("attachment_name") attachmentName String? @map("attachment_name")
likesCount Int @default(0) @map("likes_count") likesCount Int @default(0) @map("likes_count")
createdAt DateTime @default(now()) @map("created_at") createdAt DateTime @default(now()) @map("created_at")
deletedAt DateTime? @map("deleted_at")
room ChatRoom? @relation(fields: [roomId], references: [id], onDelete: Cascade) room ChatRoom? @relation(fields: [roomId], references: [id], onDelete: Cascade)
replyTo ChatMessage? @relation("ChatReplies", fields: [replyToId], references: [id], onDelete: SetNull) replyTo ChatMessage? @relation("ChatReplies", fields: [replyToId], references: [id], onDelete: SetNull)
replies ChatMessage[] @relation("ChatReplies") replies ChatMessage[] @relation("ChatReplies")
likes ChatMessageLike[] likes ChatMessageLike[]
reactions ChatMessageReaction[]
@@map("chat_messages") @@map("chat_messages")
@@schema("rebreak") @@schema("rebreak")
@ -402,6 +404,21 @@ model ChatMessageLike {
@@schema("rebreak") @@schema("rebreak")
} }
model ChatMessageReaction {
userId String @map("user_id") @db.Uuid
messageId String @map("message_id") @db.Uuid
emoji String
createdAt DateTime @default(now()) @map("created_at")
message ChatMessage @relation(fields: [messageId], references: [id], onDelete: Cascade)
// Eine Reaktion pro User pro Message (neues Emoji ersetzt das alte — WhatsApp-Verhalten)
@@id([userId, messageId])
@@index([messageId])
@@map("chat_message_reactions")
@@schema("rebreak")
}
model DirectMessage { model DirectMessage {
id String @id @default(uuid()) @db.Uuid id String @id @default(uuid()) @db.Uuid
senderId String @map("sender_id") @db.Uuid senderId String @map("sender_id") @db.Uuid
@ -414,10 +431,12 @@ model DirectMessage {
likesCount Int @default(0) @map("likes_count") likesCount Int @default(0) @map("likes_count")
createdAt DateTime @default(now()) @map("created_at") createdAt DateTime @default(now()) @map("created_at")
readAt DateTime? @map("read_at") readAt DateTime? @map("read_at")
deletedAt DateTime? @map("deleted_at")
replyTo DirectMessage? @relation("DmReplies", fields: [replyToId], references: [id], onDelete: SetNull) replyTo DirectMessage? @relation("DmReplies", fields: [replyToId], references: [id], onDelete: SetNull)
replies DirectMessage[] @relation("DmReplies") replies DirectMessage[] @relation("DmReplies")
likes DirectMessageLike[] likes DirectMessageLike[]
reactions DirectMessageReaction[]
@@map("direct_messages") @@map("direct_messages")
@@schema("rebreak") @@schema("rebreak")
@ -434,6 +453,20 @@ model DirectMessageLike {
@@schema("rebreak") @@schema("rebreak")
} }
model DirectMessageReaction {
userId String @map("user_id") @db.Uuid
messageId String @map("message_id") @db.Uuid
emoji String
createdAt DateTime @default(now()) @map("created_at")
message DirectMessage @relation(fields: [messageId], references: [id], onDelete: Cascade)
@@id([userId, messageId])
@@index([messageId])
@@map("direct_message_reactions")
@@schema("rebreak")
}
model UserFollow { model UserFollow {
followerId String @map("follower_id") @db.Uuid followerId String @map("follower_id") @db.Uuid
followingId String @map("following_id") @db.Uuid followingId String @map("following_id") @db.Uuid