schema.prisma 7.6 KB

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