schema.prisma 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. // This is your Prisma schema file,
  2. // learn more about it in the docs: https://pris.ly/d/prisma-schema
  3. generator client {
  4. provider = "prisma-client-js"
  5. }
  6. datasource db {
  7. provider = "postgresql"
  8. url = env("DATABASE_URL")
  9. }
  10. model User {
  11. id String @id @default(cuid())
  12. email String @unique
  13. name String
  14. password String
  15. avatarUrl String?
  16. globalRole GlobalRole @default(MEMBER)
  17. active Boolean @default(true)
  18. storageQuota BigInt @default(524288000) // 500 MB in bytes
  19. storageUsed BigInt @default(0) // bytes consumed
  20. createdAt DateTime @default(now())
  21. updatedAt DateTime @updatedAt
  22. memberships ProjectMember[] @relation("ProjectMembers")
  23. comments Comment[]
  24. projects Project[] @relation("ProjectOwner")
  25. resolvedComments Comment[] @relation("ResolvedBy")
  26. requestedComments Comment[] @relation("RequestedBy")
  27. assets Asset[]
  28. shareLinks AssetShareLink[]
  29. }
  30. model Project {
  31. id String @id @default(cuid())
  32. name String
  33. description String?
  34. ownerId String
  35. createdAt DateTime @default(now())
  36. updatedAt DateTime @updatedAt
  37. assets Asset[]
  38. members ProjectMember[] @relation("ProjectMembers")
  39. invitations Invitation[]
  40. owner User @relation("ProjectOwner", fields: [ownerId], references: [id])
  41. folders Folder[]
  42. }
  43. model SiteSetting {
  44. id String @id @default(cuid())
  45. name String @unique
  46. value String
  47. }
  48. model ProjectMember {
  49. id String @id @default(cuid())
  50. userId String
  51. projectId String
  52. role Role @default(REVIEWER)
  53. joinedAt DateTime @default(now())
  54. invitedBy String? // userId who sent the invite
  55. user User @relation("ProjectMembers", fields: [userId], references: [id], onDelete: Cascade)
  56. project Project @relation("ProjectMembers", fields: [projectId], references: [id], onDelete: Cascade)
  57. @@unique([userId, projectId])
  58. @@index([projectId])
  59. @@index([userId])
  60. }
  61. model Asset {
  62. id String @id @default(cuid())
  63. projectId String
  64. uploaderId String? // null for legacy assets before this feature
  65. title String
  66. filename String
  67. originalFilename String?
  68. filePath String
  69. thumbnail String?
  70. hlsPath String?
  71. originalFilePath String?
  72. maxResolution String?
  73. duration Float?
  74. fps Float @default(30)
  75. codec String?
  76. mimeType String
  77. fileSize BigInt @default(0) // raw video file size in bytes
  78. bitrate BigInt @default(0) // video bitrate in bits/s
  79. status AssetStatus @default(PENDING_REVIEW)
  80. transcodeStatus TranscodeStatus @default(PENDING)
  81. transcodeProgress Int @default(0)
  82. transcodeError String?
  83. transcodePaused Boolean @default(false)
  84. createdAt DateTime @default(now())
  85. updatedAt DateTime @updatedAt
  86. project Project @relation(fields: [projectId], references: [id], onDelete: Cascade)
  87. uploader User? @relation(fields: [uploaderId], references: [id], onDelete: SetNull)
  88. comments Comment[]
  89. shareLinks AssetShareLink[]
  90. folderAssets FolderAsset[]
  91. }
  92. model Comment {
  93. id String @id @default(cuid())
  94. assetId String
  95. userId String?
  96. guestName String?
  97. content String
  98. timestamp Float?
  99. annotations Json?
  100. resolved Boolean @default(false)
  101. resolveStatus ResolveStatus @default(UNRESOLVED)
  102. resolvedById String?
  103. resolvedByAt DateTime?
  104. requestedById String?
  105. requestedByAt DateTime?
  106. parentId String?
  107. deleted Boolean @default(false)
  108. deletedAt DateTime?
  109. deletedById String?
  110. createdAt DateTime @default(now())
  111. updatedAt DateTime @updatedAt
  112. asset Asset @relation(fields: [assetId], references: [id], onDelete: Cascade)
  113. user User? @relation(fields: [userId], references: [id], onDelete: Cascade)
  114. parent Comment? @relation("Replies", fields: [parentId], references: [id], onDelete: Cascade)
  115. replies Comment[] @relation("Replies")
  116. resolvedBy User? @relation("ResolvedBy", fields: [resolvedById], references: [id])
  117. requestedBy User? @relation("RequestedBy", fields: [requestedById], references: [id])
  118. }
  119. enum Role {
  120. ADMIN
  121. EDITOR
  122. REVIEWER
  123. VIEWER
  124. }
  125. enum GlobalRole {
  126. ADMIN // system-wide admin: manage users, all projects, quotas
  127. MEMBER // registered user: create own projects, invite members
  128. PROJECT_USER // invited user: no project creation, workspace visibility only via invite
  129. }
  130. enum InvitationStatus {
  131. PENDING
  132. ACCEPTED
  133. EXPIRED
  134. REVOKED
  135. }
  136. model Invitation {
  137. id String @id @default(cuid())
  138. email String // invitee email
  139. projectId String? // null = workspace invite (creates MEMBER); set = project invite (creates PROJECT_USER)
  140. type InvitationType @default(PROJECT)
  141. role Role @default(REVIEWER)
  142. token String @unique
  143. status InvitationStatus @default(PENDING)
  144. invitedBy String? // userId who sent the invite
  145. expiresAt DateTime
  146. createdAt DateTime @default(now())
  147. project Project? @relation(fields: [projectId], references: [id], onDelete: Cascade)
  148. @@index([projectId])
  149. @@index([email])
  150. @@index([token])
  151. }
  152. enum InvitationType {
  153. WORKSPACE // admin invites MEMBER — no project attached, user registers as MEMBER
  154. PROJECT // admin/project member invites PROJECT_USER — attached to a project
  155. }
  156. enum AssetStatus {
  157. PENDING_REVIEW
  158. CHANGES_REQUESTED
  159. APPROVED
  160. REJECTED
  161. }
  162. enum ResolveStatus {
  163. UNRESOLVED // no request made
  164. PENDING_APPROVAL // someone requested resolve, awaiting approval
  165. RESOLVED // approved and closed
  166. }
  167. enum TranscodeStatus {
  168. PENDING
  169. UPLOADING
  170. PROCESSING
  171. COMPLETED
  172. FAILED
  173. UNSUPPORTED_CODEC
  174. }
  175. // ── Public share links ────────────────────────────────────────────────────────
  176. model AssetShareLink {
  177. id String @id @default(cuid())
  178. assetId String
  179. token String @unique
  180. password String? // bcrypt hash, null = no password required
  181. allowDownload Boolean @default(false)
  182. allowUnregisteredComments Boolean @default(false)
  183. maxViews Int @default(20) // 0 or -1 = unlimited
  184. viewCount Int @default(0)
  185. createdById String
  186. createdAt DateTime @default(now())
  187. updatedAt DateTime @updatedAt
  188. asset Asset @relation(fields: [assetId], references: [id], onDelete: Cascade)
  189. createdBy User @relation(fields: [createdById], references: [id])
  190. @@index([assetId])
  191. @@index([token])
  192. }
  193. // ── Folder system ──────────────────────────────────────────────────────────────
  194. model Folder {
  195. id String @id @default(cuid())
  196. name String
  197. projectId String
  198. parentId String?
  199. order Int @default(0)
  200. createdAt DateTime @default(now())
  201. updatedAt DateTime @updatedAt
  202. project Project @relation(fields: [projectId], references: [id], onDelete: Cascade)
  203. parent Folder? @relation("FolderTree", fields: [parentId], references: [id], onDelete: Cascade)
  204. children Folder[] @relation("FolderTree")
  205. assets FolderAsset[]
  206. @@index([projectId])
  207. @@index([parentId])
  208. }
  209. model FolderAsset {
  210. id String @id @default(cuid())
  211. folderId String
  212. assetId String
  213. addedAt DateTime @default(now())
  214. folder Folder @relation(fields: [folderId], references: [id], onDelete: Cascade)
  215. asset Asset @relation(fields: [assetId], references: [id], onDelete: Cascade)
  216. @@unique([folderId, assetId])
  217. }