schema.prisma 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  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. duration Float?
  72. fps Float @default(30)
  73. codec String?
  74. mimeType String
  75. fileSize BigInt @default(0) // raw video file size in bytes
  76. bitrate BigInt @default(0) // video bitrate in bits/s
  77. status AssetStatus @default(PENDING_REVIEW)
  78. transcodeStatus TranscodeStatus @default(PENDING)
  79. transcodeProgress Int @default(0)
  80. transcodeError String?
  81. transcodePaused Boolean @default(false)
  82. createdAt DateTime @default(now())
  83. updatedAt DateTime @updatedAt
  84. project Project @relation(fields: [projectId], references: [id], onDelete: Cascade)
  85. uploader User? @relation(fields: [uploaderId], references: [id], onDelete: SetNull)
  86. comments Comment[]
  87. shareLinks AssetShareLink[]
  88. folderAssets FolderAsset[]
  89. }
  90. model Comment {
  91. id String @id @default(cuid())
  92. assetId String
  93. userId String
  94. content String
  95. timestamp Float?
  96. annotations Json?
  97. resolved Boolean @default(false)
  98. resolveStatus ResolveStatus @default(UNRESOLVED)
  99. resolvedById String?
  100. resolvedByAt DateTime?
  101. requestedById String?
  102. requestedByAt DateTime?
  103. parentId String?
  104. deleted Boolean @default(false)
  105. deletedAt DateTime?
  106. deletedById String?
  107. createdAt DateTime @default(now())
  108. updatedAt DateTime @updatedAt
  109. asset Asset @relation(fields: [assetId], references: [id], onDelete: Cascade)
  110. user User @relation(fields: [userId], references: [id], onDelete: Cascade)
  111. parent Comment? @relation("Replies", fields: [parentId], references: [id], onDelete: Cascade)
  112. replies Comment[] @relation("Replies")
  113. resolvedBy User? @relation("ResolvedBy", fields: [resolvedById], references: [id])
  114. requestedBy User? @relation("RequestedBy", fields: [requestedById], references: [id])
  115. }
  116. enum Role {
  117. ADMIN
  118. EDITOR
  119. REVIEWER
  120. VIEWER
  121. }
  122. enum GlobalRole {
  123. ADMIN // system-wide admin: manage users, all projects, quotas
  124. MEMBER // registered user: create own projects, invite members
  125. PROJECT_USER // invited user: no project creation, workspace visibility only via invite
  126. }
  127. enum InvitationStatus {
  128. PENDING
  129. ACCEPTED
  130. EXPIRED
  131. REVOKED
  132. }
  133. model Invitation {
  134. id String @id @default(cuid())
  135. email String // invitee email
  136. projectId String? // null = workspace invite (creates MEMBER); set = project invite (creates PROJECT_USER)
  137. type InvitationType @default(PROJECT)
  138. role Role @default(REVIEWER)
  139. token String @unique
  140. status InvitationStatus @default(PENDING)
  141. invitedBy String? // userId who sent the invite
  142. expiresAt DateTime
  143. createdAt DateTime @default(now())
  144. project Project? @relation(fields: [projectId], references: [id], onDelete: Cascade)
  145. @@index([projectId])
  146. @@index([email])
  147. @@index([token])
  148. }
  149. enum InvitationType {
  150. WORKSPACE // admin invites MEMBER — no project attached, user registers as MEMBER
  151. PROJECT // admin/project member invites PROJECT_USER — attached to a project
  152. }
  153. enum AssetStatus {
  154. PENDING_REVIEW
  155. CHANGES_REQUESTED
  156. APPROVED
  157. REJECTED
  158. }
  159. enum ResolveStatus {
  160. UNRESOLVED // no request made
  161. PENDING_APPROVAL // someone requested resolve, awaiting approval
  162. RESOLVED // approved and closed
  163. }
  164. enum TranscodeStatus {
  165. PENDING
  166. UPLOADING
  167. PROCESSING
  168. COMPLETED
  169. FAILED
  170. UNSUPPORTED_CODEC
  171. }
  172. // ── Public share links ────────────────────────────────────────────────────────
  173. model AssetShareLink {
  174. id String @id @default(cuid())
  175. assetId String
  176. token String @unique
  177. password String? // bcrypt hash, null = no password required
  178. allowDownload Boolean @default(false)
  179. maxViews Int @default(20) // 0 or -1 = unlimited
  180. viewCount Int @default(0)
  181. createdById String
  182. createdAt DateTime @default(now())
  183. updatedAt DateTime @updatedAt
  184. asset Asset @relation(fields: [assetId], references: [id], onDelete: Cascade)
  185. createdBy User @relation(fields: [createdById], references: [id])
  186. @@index([assetId])
  187. @@index([token])
  188. }
  189. // ── Folder system ──────────────────────────────────────────────────────────────
  190. model Folder {
  191. id String @id @default(cuid())
  192. name String
  193. projectId String
  194. parentId String?
  195. order Int @default(0)
  196. createdAt DateTime @default(now())
  197. updatedAt DateTime @updatedAt
  198. project Project @relation(fields: [projectId], references: [id], onDelete: Cascade)
  199. parent Folder? @relation("FolderTree", fields: [parentId], references: [id], onDelete: Cascade)
  200. children Folder[] @relation("FolderTree")
  201. assets FolderAsset[]
  202. @@index([projectId])
  203. @@index([parentId])
  204. }
  205. model FolderAsset {
  206. id String @id @default(cuid())
  207. folderId String
  208. assetId String
  209. addedAt DateTime @default(now())
  210. folder Folder @relation(fields: [folderId], references: [id], onDelete: Cascade)
  211. asset Asset @relation(fields: [assetId], references: [id], onDelete: Cascade)
  212. @@unique([folderId, assetId])
  213. }