Files
parenthese/backend/prisma/schema.prisma
T
IliesAllaliandClaude Opus 5.5 8bfc78b2f5 Contributions fiables : prénom, mot d'accompagnement, liens gardés
- Prénom demandé à l'envoi d'une contribution et d'un souvenir, retenu
  sur l'appareil pour la fois suivante (submittedByLabel). Le propriétaire
  lit « par Camille » au lieu de « par Contributeur ».
- Mot d'accompagnement enregistré (ContributionSession.comment, migration
  additive) et affiché à la relecture.
- Une personne ajoutée par la famille garde ses liens : identifiant
  provisoire « tmp:… » remplacé par le vrai id à l'application ; un lien
  vers une personne refusée est écarté au lieu de faire échouer la
  relecture. Frère ou sœur : parents calculés aussi pour la famille.
- Soumission et relecture en transaction, appliquées dans l'ordre
  personnes, unions, liens : tout ou rien.
- Bouton « Ajouter une personne » pour la famille sur ordinateur ; après
  l'ajout, le panneau se ferme et un message confirme que la personne est
  dans les modifications ; il ne réapparaît plus au retour en édition.
- Relecture : les liens se lisent « Léon, enfant de Marc ».

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-25 13:52:23 +02:00

349 lines
9.6 KiB
Plaintext

generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
enum MembershipRole {
owner
admin
member
}
enum ContributionPolicy {
direct
pending
}
enum VisitorPolicy {
read_only
}
enum ContributionSessionStatus {
pending
approved
rejected
}
enum ContributionActorMode {
user
tree_access
}
enum ContributionEntityType {
person
union
parent_child_link
annotation
media
}
// Souvenir envoyé par la famille : en attente jusqu'à la relecture du propriétaire
enum MediaStatus {
approved
pending
rejected
}
enum AnnotationType {
drawing
sticker
text
photo
}
enum ContributionAction {
create
update
delete
reorder
}
enum ConflictState {
none
needs_review
}
enum ContributionDecision {
approved
rejected
}
enum SharedAccessRole {
visitor
contributor
}
model User {
id String @id @default(cuid())
email String @unique
passwordHash String
lastOpenedTreeId String?
lastOpenedAccessMode String?
lastOpenedRole String?
lastOpenedAt DateTime?
createdAt DateTime @default(now())
ownedTrees Tree[] @relation("TreeOwner")
memberships TreeMembership[]
sharedTreeAccesses UserTreeAccess[]
submittedContributionSessions ContributionSession[] @relation("ContributionSubmittedBy")
reviewedContributionSessions ContributionSession[] @relation("ContributionReviewedBy")
lastOpenedTree Tree? @relation("UserLastOpenedTree", fields: [lastOpenedTreeId], references: [id], onDelete: SetNull)
@@index([lastOpenedAt])
@@map("users")
}
model Tree {
id String @id @default(cuid())
ownerUserId String
slug String @unique
name String
description String?
rootPersonId String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime?
owner User @relation("TreeOwner", fields: [ownerUserId], references: [id], onDelete: Restrict)
accessPasswords TreeAccessPasswords?
memberships TreeMembership[]
sharedUserAccesses UserTreeAccess[]
settings TreeSettings?
persons Person[]
unions Union[]
parentChildLinks ParentChildLink[]
mediaItems MediaItem[]
contributionSessions ContributionSession[]
auditLogs AuditLog[]
annotations Annotation[]
lastOpenedByUsers User[] @relation("UserLastOpenedTree")
@@index([ownerUserId])
@@map("trees")
}
model TreeAccessPasswords {
treeId String @id
visitorHash String
contributorHash String
updatedAt DateTime @updatedAt
tree Tree @relation(fields: [treeId], references: [id], onDelete: Cascade)
@@map("tree_access_passwords")
}
model TreeMembership {
id String @id @default(cuid())
treeId String
userId String
role MembershipRole
contributionModeOverride ContributionPolicy?
createdAt DateTime @default(now())
tree Tree @relation(fields: [treeId], references: [id], onDelete: Cascade)
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@unique([treeId, userId])
@@index([userId])
@@map("tree_memberships")
}
model UserTreeAccess {
id String @id @default(cuid())
treeId String
userId String
role SharedAccessRole
createdAt DateTime @default(now())
updatedAt DateTime @default(now()) @updatedAt
tree Tree @relation(fields: [treeId], references: [id], onDelete: Cascade)
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@unique([treeId, userId])
@@index([userId, updatedAt])
@@map("user_tree_accesses")
}
model TreeSettings {
treeId String @id
memberContributionPolicy ContributionPolicy @default(pending)
visitorPolicy VisitorPolicy @default(read_only)
contributorPolicy ContributionPolicy @default(pending)
updatedAt DateTime @updatedAt
tree Tree @relation(fields: [treeId], references: [id], onDelete: Cascade)
@@map("tree_settings")
}
model Person {
id String @id @default(cuid())
treeId String
firstName String
lastName String
birthName String?
birthDate DateTime?
deathDate DateTime?
birthPlace String?
profession String?
region String?
nationality String?
avatarPath String?
avatarMimeType String?
sex String?
notes String?
createdBy String?
updatedBy String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime?
tree Tree @relation(fields: [treeId], references: [id], onDelete: Cascade)
@@index([treeId])
@@map("persons")
}
model Union {
id String @id @default(cuid())
treeId String
partner1PersonId String
partner2PersonId String?
unionType String?
startDate DateTime?
endDate DateTime?
displayOrder Int @default(1)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime?
tree Tree @relation(fields: [treeId], references: [id], onDelete: Cascade)
@@index([treeId])
@@map("unions")
}
model ParentChildLink {
id String @id @default(cuid())
treeId String
parentPersonId String
childPersonId String
viaUnionId String?
parentageType String?
displayOrder Int @default(1)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime?
tree Tree @relation(fields: [treeId], references: [id], onDelete: Cascade)
@@index([treeId])
@@map("parent_child_links")
}
model MediaItem {
id String @id @default(cuid())
treeId String
personId String
type String
mimeType String
filePath String
thumbPath String?
caption String?
source String?
sizeBytes Int
displayOrder Int @default(1)
isFeatured Boolean @default(false)
uploadedBy String?
status MediaStatus @default(approved)
contributionSessionId String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime?
tree Tree @relation(fields: [treeId], references: [id], onDelete: Cascade)
@@index([treeId, personId, displayOrder])
@@map("media_items")
}
model ContributionSession {
id String @id @default(cuid())
treeId String
submittedByUserId String?
submittedByLabel String
comment String?
mode ContributionActorMode
status ContributionSessionStatus @default(pending)
title String
createdAt DateTime @default(now())
reviewedAt DateTime?
reviewedBy String?
tree Tree @relation(fields: [treeId], references: [id], onDelete: Cascade)
submittedByUser User? @relation("ContributionSubmittedBy", fields: [submittedByUserId], references: [id], onDelete: SetNull)
reviewer User? @relation("ContributionReviewedBy", fields: [reviewedBy], references: [id], onDelete: SetNull)
changes ContributionChange[]
@@index([treeId, status, createdAt])
@@index([submittedByUserId])
@@map("contribution_sessions")
}
model ContributionChange {
id String @id @default(cuid())
sessionId String
entityType ContributionEntityType
entityId String?
action ContributionAction
beforeJson Json?
afterJson Json?
conflictState ConflictState @default(none)
decision ContributionDecision?
createdAt DateTime @default(now())
session ContributionSession @relation(fields: [sessionId], references: [id], onDelete: Cascade)
@@index([sessionId])
@@map("contribution_changes")
}
model Annotation {
id String @id @default(cuid())
treeId String
type AnnotationType
x Float
y Float
content String
style Json @default("{}")
zIndex Int @default(0)
createdBy String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime?
tree Tree @relation(fields: [treeId], references: [id], onDelete: Cascade)
@@index([treeId])
@@map("annotations")
}
model AuditLog {
id String @id @default(cuid())
treeId String
actorType String
actorId String?
action String
entityType String
entityId String
payloadJson Json?
createdAt DateTime @default(now())
tree Tree @relation(fields: [treeId], references: [id], onDelete: Cascade)
@@index([treeId, createdAt])
@@map("audit_log")
}
model WaitlistEntry {
id String @id @default(cuid())
email String @unique
prenom String?
source String @default("direct")
createdAt DateTime @default(now())
@@map("waitlist_entries")
}