From b2db6310fe9c4e73be253cabb9ebcbc81e1ea8d4 Mon Sep 17 00:00:00 2001 From: neodk2004 Date: Sun, 7 Jun 2026 12:20:26 +0200 Subject: [PATCH] feat(02-01): add 8 Prisma models, 3 enums, Pet back-relations - Append PostType, MilestoneType (16 values), ReportReason enums - Add Post, PostImage, Milestone, Story, StoryView, Follow, Block, Report models - Add Pet back-relations for all Phase 2 social graph relations - Explicit named relations on all self-referential models (Follower/Followee, Blocker/Blocked, StoryViewer, Reporter) - StoryView.storyId has NO onDelete cascade per D-08 (views survive story expiry) - npx prisma validate + generate + db push all succeeded --- prisma/schema.prisma | 142 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 141 insertions(+), 1 deletion(-) diff --git a/prisma/schema.prisma b/prisma/schema.prisma index f9238a9..7e6468a 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -40,9 +40,149 @@ model Pet { name String bio String? adoptionStory String? - avatarKey String? // R2 object key, NOT a full URL + avatarKey String? // Supabase Storage object key, NOT a full URL createdAt DateTime @default(now()) updatedAt DateTime @updatedAt + // Phase 2 back-relations + posts Post[] + stories Story[] + storyViews StoryView[] @relation("StoryViewer") + following Follow[] @relation("Follower") + followers Follow[] @relation("Followee") + blocking Block[] @relation("Blocker") + blockedBy Block[] @relation("Blocked") + reports Report[] @relation("Reporter") + @@index([ownerId]) } + +// ─── Phase 2 Enums ─────────────────────────────────────────────────────────── + +enum PostType { + PHOTO + MILESTONE +} + +enum MilestoneType { + BIRTHDAY + ADOPTION_DAY + FIRST_OUTING + VET_VISIT + LITTER + FOLLOWERS_500 + FOLLOWERS_1K + FOLLOWERS_2_5K + FOLLOWERS_5K + FOLLOWERS_10K + FOLLOWERS_25K + FOLLOWERS_50K + FOLLOWERS_100K + FOLLOWERS_250K + FOLLOWERS_500K + FOLLOWERS_1M +} + +enum ReportReason { + SPAM + HARASSMENT + INAPPROPRIATE + MISINFORMATION + OTHER +} + +// ─── Phase 2 Models ────────────────────────────────────────────────────────── + +model Post { + id String @id @default(cuid()) + pet Pet @relation(fields: [petId], references: [id], onDelete: Cascade) + petId String + type PostType @default(PHOTO) + caption String? @db.VarChar(500) + images PostImage[] + milestone Milestone? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + @@index([petId, createdAt(sort: Desc)]) +} + +model PostImage { + id String @id @default(cuid()) + post Post @relation(fields: [postId], references: [id], onDelete: Cascade) + postId String + storageKey String // Supabase Storage object key + position Int // 0-based order in carousel + width Int? + height Int? + + @@unique([postId, position]) + @@index([postId]) +} + +model Milestone { + id String @id @default(cuid()) + post Post @relation(fields: [postId], references: [id], onDelete: Cascade) + postId String @unique + milestoneType MilestoneType + notes String? @db.VarChar(500) // free text for 'litter' type (D-09) +} + +model Story { + id String @id @default(cuid()) + pet Pet @relation(fields: [petId], references: [id], onDelete: Cascade) + petId String + storageKey String // Supabase Storage object key + expiresAt DateTime // set to NOW() + 24 hours on create (D-07) + views StoryView[] + createdAt DateTime @default(now()) + + @@index([petId, expiresAt]) // supports lazy-delete filter query +} + +model StoryView { + story Story @relation(fields: [storyId], references: [id]) // NO onDelete:Cascade (D-08) + storyId String + viewerPet Pet @relation("StoryViewer", fields: [viewerPetId], references: [id]) + viewerPetId String + viewedAt DateTime @default(now()) + + @@id([storyId, viewerPetId]) // deduplication: one view per viewer per story + @@index([viewedAt]) // supports 30-day cleanup job (D-08) +} + +model Follow { + follower Pet @relation("Follower", fields: [followerPetId], references: [id], onDelete: Cascade) + followerPetId String + followee Pet @relation("Followee", fields: [followeePetId], references: [id], onDelete: Cascade) + followeePetId String + createdAt DateTime @default(now()) + + @@id([followerPetId, followeePetId]) + @@index([followerPetId]) + @@index([followeePetId]) +} + +model Block { + blocker Pet @relation("Blocker", fields: [blockerPetId], references: [id], onDelete: Cascade) + blockerPetId String + blocked Pet @relation("Blocked", fields: [blockedPetId], references: [id], onDelete: Cascade) + blockedPetId String + createdAt DateTime @default(now()) + + @@id([blockerPetId, blockedPetId]) + @@index([blockerPetId]) +} + +model Report { + id String @id @default(cuid()) + reporter Pet @relation("Reporter", fields: [reporterPetId], references: [id]) + reporterPetId String + targetPostId String? // null if reporting a profile + targetPetId String? // null if reporting a post + reason ReportReason + notes String? @db.VarChar(500) + createdAt DateTime @default(now()) + + @@index([reporterPetId]) +}