@ken2008/zapit-schema 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,3735 @@
1
+ generator client {
2
+ provider = "prisma-client-js"
3
+ output = "../generated/prisma"
4
+ }
5
+
6
+ datasource db {
7
+ provider = "postgresql"
8
+ }
9
+
10
+ enum UserStatus {
11
+ ACTIVE
12
+ SUSPENDED
13
+ BANNED
14
+ PENDING_DELETION
15
+ }
16
+
17
+ enum WebsiteStatusCode {
18
+ OPERATIONAL
19
+ MAINTENANCE
20
+ DEGRADED
21
+ OUTAGE
22
+ }
23
+
24
+ enum LobbyStatus {
25
+ WAITING
26
+ STARTED
27
+ GAME
28
+ QUESTION
29
+ SCOREBOARD
30
+ RESULTS
31
+ HALTED
32
+ ENDED
33
+ }
34
+
35
+ enum ParticipantStatus {
36
+ ACTIVE
37
+ KICKED
38
+ LEFT
39
+ FINISHED
40
+ LOADING
41
+ }
42
+
43
+ enum GameStatus {
44
+ WAITING
45
+ ACTIVE
46
+ FINISHED
47
+ CANCELLED
48
+ }
49
+
50
+ enum PublicationStatus {
51
+ DRAFT
52
+ PUBLISHED
53
+ ARCHIVED
54
+ }
55
+
56
+ enum Visibility {
57
+ PRIVATE
58
+ UNLISTED
59
+ PUBLIC
60
+ }
61
+
62
+ enum CourseLevel {
63
+ GCSE
64
+ AS_LEVEL
65
+ A_LEVEL
66
+ IB
67
+ UNIVERSITY
68
+ OTHER
69
+ }
70
+
71
+ enum LessonType {
72
+ VIDEO
73
+ INTERACTIVE
74
+ MIXED
75
+ READING
76
+ QUIZ
77
+ EXAM_PRACTICE
78
+ }
79
+
80
+ enum LessonStepType {
81
+ READING
82
+ INTERACTIVE
83
+ VIDEO
84
+ QUIZ
85
+ EXAM_PRACTICE
86
+ EXAM_MODE
87
+ FLASHCARDS
88
+ ACTIVITY
89
+ DOCUMENT
90
+ NOTES
91
+ }
92
+
93
+ enum VideoDifficulty {
94
+ BEGINNER
95
+ INTERMEDIATE
96
+ ADVANCED
97
+ }
98
+
99
+ enum QuestionType {
100
+ MULTIPLE_CHOICE
101
+ TRUE_FALSE
102
+ SHORT_ANSWER
103
+ LIST_ORDER
104
+ SELECT
105
+ FILL_BLANK
106
+ MATCHING
107
+ IMAGE_MCQ
108
+ }
109
+
110
+ enum ExamStatus {
111
+ DRAFT
112
+ PUBLISHED
113
+ ARCHIVED
114
+ }
115
+
116
+ enum ExamSessionStatus {
117
+ ACTIVE
118
+ SUBMITTED
119
+ GRADED
120
+ FLAGGED
121
+ EXPIRED
122
+ }
123
+
124
+ enum TutoringSessionStatus {
125
+ REQUESTED
126
+ SCHEDULED
127
+ CONFIRMED
128
+ IN_PROGRESS
129
+ COMPLETED
130
+ CANCELLED
131
+ NO_SHOW
132
+ }
133
+
134
+ enum TutoringSessionType {
135
+ INDIVIDUAL
136
+ GROUP
137
+ LIVE_DROP_IN
138
+ }
139
+
140
+ enum SessionFormat {
141
+ VIRTUAL
142
+ PHYSICAL
143
+ HYBRID
144
+ }
145
+
146
+ enum TutorVerificationStatus {
147
+ PENDING
148
+ APPROVED
149
+ REJECTED
150
+ }
151
+
152
+ enum EnrollmentStatus {
153
+ ACTIVE
154
+ INVITED
155
+ PAUSED
156
+ COMPLETED
157
+ DROPPED
158
+ }
159
+
160
+ enum ClassroomRole {
161
+ STUDENT
162
+ TEACHER
163
+ ASSISTANT
164
+ PARENT
165
+ }
166
+
167
+ enum AssignmentStatus {
168
+ DRAFT
169
+ PUBLISHED
170
+ CLOSED
171
+ ARCHIVED
172
+ }
173
+
174
+ enum SubmissionStatus {
175
+ NOT_STARTED
176
+ SUBMITTED
177
+ LATE
178
+ GRADED
179
+ RETURNED
180
+ }
181
+
182
+ enum ConversationType {
183
+ DIRECT
184
+ GROUP
185
+ SUPPORT
186
+ }
187
+
188
+ enum ConversationParticipantRole {
189
+ OWNER
190
+ MEMBER
191
+ AGENT
192
+ }
193
+
194
+ enum MessageSenderRole {
195
+ USER
196
+ AGENT
197
+ SYSTEM
198
+ }
199
+
200
+ enum AnnouncementStatus {
201
+ DRAFT
202
+ SCHEDULED
203
+ SENT
204
+ ARCHIVED
205
+ }
206
+
207
+ enum NotificationType {
208
+ MESSAGE
209
+ BOOKING
210
+ ASSIGNMENT
211
+ REVIEW
212
+ PAYMENT
213
+ SYSTEM
214
+ SECURITY
215
+ SUPPORT
216
+ GENERAL
217
+ }
218
+
219
+ enum NotificationChannel {
220
+ IN_APP
221
+ EMAIL
222
+ PUSH
223
+ SMS
224
+ }
225
+
226
+ enum SupportStatus {
227
+ OPEN
228
+ PENDING
229
+ IN_PROGRESS
230
+ RESOLVED
231
+ CLOSED
232
+ }
233
+
234
+ enum SupportPriority {
235
+ LOW
236
+ MEDIUM
237
+ HIGH
238
+ URGENT
239
+ }
240
+
241
+ enum FeedbackType {
242
+ BUG
243
+ FEATURE
244
+ CONTENT
245
+ CONFUSING
246
+ GENERAL
247
+ }
248
+
249
+ enum FeedbackStatus {
250
+ NEW
251
+ PENDING
252
+ IN_REVIEW
253
+ RESOLVED
254
+ ARCHIVED
255
+ }
256
+
257
+ enum CommunityVisibility {
258
+ PUBLIC
259
+ PRIVATE
260
+ RESTRICTED
261
+ }
262
+
263
+ enum CommunityResourceType {
264
+ LINK
265
+ FILE
266
+ NOTE
267
+ TEMPLATE
268
+ }
269
+
270
+ enum UserEventType {
271
+ TRIVIT
272
+ ZAPIT_QUIZ
273
+ LIVE_SESSION
274
+ STUDY_RACE
275
+ FLASHCARD_BATTLE
276
+ COMMUNITY_EVENT
277
+ }
278
+
279
+ enum UserEventStatus {
280
+ COMPLETED
281
+ ABANDONED
282
+ IN_PROGRESS
283
+ }
284
+
285
+ enum PlacementTier {
286
+ FIRST
287
+ SECOND
288
+ THIRD
289
+ TOP_TEN
290
+ PARTICIPATED
291
+ }
292
+
293
+ enum FeatureFlagStatus {
294
+ ENABLED
295
+ PARTIAL
296
+ DISABLED
297
+ }
298
+
299
+ enum DeploymentEnvironment {
300
+ DEVELOPMENT
301
+ STAGING
302
+ PRODUCTION
303
+ }
304
+
305
+ enum ExperimentStatus {
306
+ DRAFT
307
+ RUNNING
308
+ COMPLETED
309
+ PAUSED
310
+ }
311
+
312
+ enum SubscriptionStatus {
313
+ TRIALING
314
+ ACTIVE
315
+ PAST_DUE
316
+ CANCELLED
317
+ EXPIRED
318
+ }
319
+
320
+ enum BillingInterval {
321
+ MONTHLY
322
+ YEARLY
323
+ LIFETIME
324
+ }
325
+
326
+ enum PaymentStatus {
327
+ PENDING
328
+ SUCCEEDED
329
+ FAILED
330
+ REFUNDED
331
+ DISPUTED
332
+ }
333
+
334
+ enum RefundStatus {
335
+ REQUESTED
336
+ APPROVED
337
+ REJECTED
338
+ COMPLETED
339
+ }
340
+
341
+ enum AuditSeverity {
342
+ INFO
343
+ WARNING
344
+ ERROR
345
+ CRITICAL
346
+ }
347
+
348
+ enum AssetType {
349
+ VIDEO
350
+ AUDIO
351
+ IMAGE
352
+ DOCUMENT
353
+ RIVE
354
+ ANIMATION
355
+ OTHER
356
+ }
357
+
358
+ enum TrivitStatus {
359
+ DRAFT
360
+ SCHEDULED
361
+ LIVE
362
+ COMPLETED
363
+ CANCELLED
364
+ }
365
+
366
+ enum AudienceScope {
367
+ EVERYONE
368
+ FOLLOWERS
369
+ NOBODY
370
+ }
371
+
372
+ enum ThemeMode {
373
+ LIGHT
374
+ DARK
375
+ AUTO
376
+ }
377
+
378
+ enum NotificationDigestMode {
379
+ NONE
380
+ DAILY
381
+ WEEKLY
382
+ }
383
+
384
+ enum CollectionType {
385
+ CURATED
386
+ LEARNING_PATH
387
+ ONBOARDING_BUNDLE
388
+ REVISION_PLAYLIST
389
+ }
390
+
391
+ enum CollectionItemType {
392
+ QUIZ
393
+ FLASHCARD_SET
394
+ VIDEO
395
+ LESSON
396
+ EXAM
397
+ COURSE
398
+ RESOURCE
399
+ TRIVIT
400
+ LIVE_SESSION
401
+ COMMUNITY_RESOURCE
402
+ }
403
+
404
+ enum PollStatus {
405
+ DRAFT
406
+ ACTIVE
407
+ CLOSED
408
+ ARCHIVED
409
+ }
410
+
411
+ enum PetElement {
412
+ FIRE
413
+ WATER
414
+ EARTH
415
+ AIR
416
+ MYTH
417
+ LIGHTNING
418
+ }
419
+
420
+ enum PetActionType {
421
+ FEED
422
+ PLAY
423
+ REST
424
+ TRAIN
425
+ BATTLE
426
+ EXPLORE
427
+ CHEER
428
+ HATCH
429
+ }
430
+
431
+ enum PromotionType {
432
+ PERCENT
433
+ FIXED_AMOUNT
434
+ TRIAL_EXTENSION
435
+ }
436
+
437
+ enum SandboxStatus {
438
+ RUNNING
439
+ STOPPED
440
+ ARCHIVED
441
+ FAILED
442
+ }
443
+
444
+ enum SandboxPromotionStatus {
445
+ PENDING
446
+ COMPLETED
447
+ ROLLED_BACK
448
+ FAILED
449
+ }
450
+
451
+ enum AchievementRarity {
452
+ COMMON
453
+ RARE
454
+ EPIC
455
+ LEGENDARY
456
+ }
457
+
458
+ enum AchievementScope {
459
+ PLATFORM
460
+ GAME
461
+ FLASHCARD
462
+ EXAM
463
+ LESSON
464
+ PROFILE
465
+ COMMUNITY
466
+ }
467
+
468
+ enum RewardScope {
469
+ GLOBAL_GAME
470
+ EXAM
471
+ LESSON
472
+ FLASHCARD
473
+ SEASONAL_EVENT
474
+ }
475
+
476
+ enum RewardStatus {
477
+ DRAFT
478
+ ACTIVE
479
+ SCHEDULED
480
+ ARCHIVED
481
+ }
482
+
483
+ enum AntiCheatSeverity {
484
+ LOW
485
+ MEDIUM
486
+ HIGH
487
+ CRITICAL
488
+ }
489
+
490
+ enum AntiCheatStatus {
491
+ OPEN
492
+ REVIEWED
493
+ VALID
494
+ INVALID
495
+ DISMISSED
496
+ }
497
+
498
+ enum CalendarEntryType {
499
+ LESSON
500
+ TASK
501
+ FLASHCARD
502
+ TUTORING
503
+ TEST
504
+ ACTIVITY
505
+ EVENT
506
+ FREE
507
+ REVISION
508
+ }
509
+
510
+ enum CalendarEntryStatus {
511
+ PLANNED
512
+ COMPLETED
513
+ CANCELLED
514
+ SKIPPED
515
+ }
516
+
517
+ enum BackupType {
518
+ FULL
519
+ DATABASE
520
+ MEDIA
521
+ CONFIG
522
+ ARCHIVE
523
+ INCREMENTAL
524
+ }
525
+
526
+ enum LobbyMode {
527
+ COMPETITIVE
528
+ CASUAL
529
+ MINIGAME
530
+ PRIVATE_PRACTICE
531
+ }
532
+
533
+ enum GameServerStatus {
534
+ HEALTHY
535
+ WARNING
536
+ OFFLINE
537
+ MAINTENANCE
538
+ }
539
+
540
+ model Account {
541
+ id String @id @default(cuid())
542
+ userId Int
543
+ provider String
544
+ refresh_token String?
545
+ access_token String?
546
+ expires_at Int?
547
+ token_type String?
548
+ scope String?
549
+ id_token String?
550
+ session_state String?
551
+
552
+ accountId String
553
+ providerId String?
554
+ accessToken String?
555
+ refreshToken String?
556
+ idToken String?
557
+ accessTokenExpiresAt DateTime?
558
+ refreshTokenExpiresAt DateTime?
559
+ password String?
560
+ createdAt DateTime @default(now())
561
+ updatedAt DateTime @updatedAt
562
+
563
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
564
+
565
+ @@unique([provider, providerId])
566
+ @@index([userId])
567
+ @@map("account")
568
+ }
569
+
570
+ model Session {
571
+ id String @id @default(cuid())
572
+ userId Int
573
+ expiresAt DateTime
574
+ token String
575
+ createdAt DateTime @default(now())
576
+ updatedAt DateTime @updatedAt
577
+ ipAddress String?
578
+ userAgent String?
579
+ impersonatedBy String?
580
+ deviceName String?
581
+ location String?
582
+ isCurrent Boolean @default(false)
583
+
584
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
585
+
586
+ @@unique([token])
587
+ @@index([userId])
588
+ @@map("session")
589
+ }
590
+
591
+ model VerificationToken {
592
+ identifier String
593
+ token String @unique
594
+ expires DateTime
595
+
596
+ @@unique([identifier, token])
597
+ }
598
+
599
+ model Verification {
600
+ id String @id
601
+ identifier String
602
+ value String
603
+ expiresAt DateTime
604
+ createdAt DateTime @default(now())
605
+ updatedAt DateTime @updatedAt
606
+
607
+ @@index([identifier])
608
+ @@map("verification")
609
+ }
610
+
611
+ model Jwks {
612
+ id Int @id @default(autoincrement())
613
+ publicKey String
614
+ privateKey String
615
+ createdAt DateTime
616
+ expiresAt DateTime?
617
+
618
+ @@map("jwks")
619
+ }
620
+
621
+ model WebsiteStatus {
622
+ id String @id @default("singleton")
623
+ maintenance Boolean @default(false)
624
+ temporaryDown Boolean @default(false)
625
+ bannerMessage String?
626
+ statusCode WebsiteStatusCode @default(OPERATIONAL)
627
+ lastChecked DateTime?
628
+ createdAt DateTime @default(now())
629
+ updatedAt DateTime @updatedAt
630
+ }
631
+
632
+ model News {
633
+ id String @id @default(cuid())
634
+ title String
635
+ slug String? @unique
636
+ content String
637
+ excerpt String?
638
+ coverImage String?
639
+ category String?
640
+ visibility Visibility @default(PUBLIC)
641
+ status PublicationStatus @default(DRAFT)
642
+ published Boolean @default(false)
643
+ publishedAt DateTime?
644
+ createdAt DateTime @default(now())
645
+ updatedAt DateTime @updatedAt
646
+ }
647
+
648
+ model User {
649
+ id Int @id @default(autoincrement())
650
+ name String?
651
+ email String? @unique
652
+ password String?
653
+ emailVerified Boolean?
654
+ image String?
655
+ role String @default("USER")
656
+ status UserStatus @default(ACTIVE)
657
+ username String? @unique
658
+ displayUsername String?
659
+ bio String?
660
+ headline String?
661
+ pronunciation String?
662
+ academicLevel String?
663
+ timezone String?
664
+ location String?
665
+ phone String?
666
+ website String?
667
+ banned Boolean? @default(false)
668
+ banReason String?
669
+ banExpires DateTime?
670
+ lastSeenAt DateTime?
671
+ createdAt DateTime @default(now())
672
+ updatedAt DateTime @updatedAt
673
+
674
+ accounts Account[]
675
+ sessions Session[]
676
+ quizzes Quiz[]
677
+ quizAttempts QuizAttempt[]
678
+ gameResults GameResult[]
679
+ hostedLobbies Lobby[] @relation("HostedLobbies")
680
+ participants Participant[]
681
+ statistics Statistics?
682
+ statisticsWeekly StatisticsWeeklyReport[]
683
+ flashcardSets FlashcardSet[]
684
+ flashcardStudySessions FlashcardStudySession[]
685
+ flashcardReviews FlashcardReview[]
686
+ coursesAuthored Course[] @relation("CourseAuthor")
687
+ courseEnrollments CourseEnrollment[]
688
+ lessonsAuthored Lesson[] @relation("LessonAuthor")
689
+ lessonProgress LessonProgress[]
690
+ videosAuthored Video[] @relation("VideoAuthor")
691
+ videoProgress VideoProgress[]
692
+ examSessions ExamSession[]
693
+ tutorProfile TutorProfile?
694
+ studentProfile StudentProfile?
695
+ tutorBookingsAsTutor TutorBooking[] @relation("TutorBookings")
696
+ tutorBookingsAsStudent TutorBooking[] @relation("StudentBookings")
697
+ tutoringSessionsAsTutor TutoringSession[] @relation("TutorSessions")
698
+ tutoringSessionAttendees TutoringSessionAttendee[]
699
+ tutorReviewsWritten TutorReview[] @relation("TutorReviewAuthor")
700
+ tutorReviewsReceived TutorReview[] @relation("TutorReviewTarget")
701
+ classroomsOwned Classroom[] @relation("ClassroomOwner")
702
+ classroomEnrollments ClassroomEnrollment[]
703
+ assignmentsAuthored Assignment[] @relation("AssignmentAuthor")
704
+ assignmentSubmissions AssignmentSubmission[]
705
+ conversationsCreated Conversation[] @relation("ConversationCreator")
706
+ conversationParticipants ConversationParticipant[]
707
+ messagesSent Message[] @relation("UserMessages")
708
+ announcements Announcement[] @relation("AnnouncementAuthor")
709
+ announcementRecipients AnnouncementRecipient[]
710
+ notifications Notification[]
711
+ notificationPreference NotificationPreference?
712
+ supportTickets SupportTicket[] @relation("SupportTicketRequester")
713
+ assignedSupportTickets SupportTicket[] @relation("SupportTicketAssignee")
714
+ supportConversations SupportConversation[] @relation("SupportConversationRequester")
715
+ supportAgentConversations SupportConversation[] @relation("SupportConversationAgent")
716
+ supportMessages SupportMessage[] @relation("SupportMessageAuthor")
717
+ communitiesOwned Community[] @relation("CommunityOwner")
718
+ communityMemberships CommunityMembership[]
719
+ communityPosts CommunityPost[]
720
+ communityReplies CommunityReply[]
721
+ communityEventsHosted CommunityEvent[] @relation("CommunityEventHost")
722
+ feedback Feedback[]
723
+ subscriptions UserSubscription[]
724
+ paymentTransactions PaymentTransaction[]
725
+ refundRequests RefundRequest[] @relation("RefundRequester")
726
+ userEvents UserActivityEvent[]
727
+ hostedGames Game[] @relation("HostedGames")
728
+ hostedTrivits TrivitEvent[] @relation("HostedTrivits")
729
+ trivitParticipants TrivitParticipant[]
730
+ roleAssignments UserRoleAssignment[]
731
+ auditLogs AuditLog[]
732
+ resourceFolders ResourceFolder[]
733
+ resources ResourceItem[]
734
+ tutorPaymentMethods TutorPaymentMethod[]
735
+ payoutRequests PayoutRequest[]
736
+ pricingPackages TutorPricingPackage[]
737
+ issuedInvoices TutorInvoice[] @relation("InvoiceIssuer")
738
+ tutorVerificationRequests TutorVerificationRequest[]
739
+ following UserFollow[] @relation("UserFollowFollower")
740
+ followers UserFollow[] @relation("UserFollowFollowing")
741
+ profileReportsFiled ProfileReport[] @relation("ProfileReportReporter")
742
+ profileReportsReceived ProfileReport[] @relation("ProfileReportTarget")
743
+ userAchievements UserAchievement[]
744
+ pinnedContent UserPinnedContent[]
745
+ preferences UserPreference?
746
+ cookieConsent CookieConsentPreference?
747
+ studyCollections StudyCollection[] @relation("StudyCollectionOwner")
748
+ studyCollectionEnrollments StudyCollectionEnrollment[]
749
+ classroomPollsCreated ClassroomPoll[] @relation("ClassroomPollCreator")
750
+ classroomPollResponses ClassroomPollResponse[]
751
+ zaplitProfile ZaplitProfile?
752
+ createdPromotionCodes PromotionCode[] @relation("PromotionCodeCreator")
753
+ createdSandboxes SandboxEnvironment[] @relation("SandboxCreator")
754
+ sandboxPromotions SandboxPromotion[] @relation("SandboxPromotionActor")
755
+ dataSeedRunsTriggered DataSeedRun[] @relation("DataSeedRunActor")
756
+ createdAchievementDefinitions AchievementDefinition[] @relation("AchievementDefinitionCreator")
757
+ examQuestionVersions ExamQuestionVersion[] @relation("ExamQuestionVersionAuthor")
758
+ reviewedAntiCheatFlags ExamAntiCheatFlag[] @relation("ExamAntiCheatReviewer")
759
+ createdCertificationRules CertificationRule[] @relation("CertificationRuleCreator")
760
+ createdRewardRules RewardRule[] @relation("RewardRuleCreator")
761
+ securityTwoFactorMethods SecurityTwoFactorMethod[]
762
+ calendarEntries CalendarEntry[]
763
+ backupRestoreRunsInitiated BackupRestoreRun[] @relation("BackupRestoreInitiator")
764
+
765
+ @@index([role])
766
+ @@index([status])
767
+ @@map("user")
768
+ }
769
+
770
+ model Statistics {
771
+ id String @id @default(cuid())
772
+ userId Int @unique
773
+ totalQuizsCreated Int @default(0)
774
+ totalGamesHosted Int @default(0)
775
+ xpCount Int @default(0)
776
+ timeSpentLearning Int @default(0)
777
+ credits Int @default(0)
778
+ totalWins Int @default(0)
779
+ totalCorrectAnswers Int @default(0)
780
+ totalCrates Int @default(0)
781
+ flashcardSessions Int @default(0)
782
+ examsCompleted Int @default(0)
783
+ tutoringSessions Int @default(0)
784
+ currentStreak Int @default(0)
785
+ longestStreak Int @default(0)
786
+ updatedAt DateTime @updatedAt
787
+
788
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
789
+
790
+ @@map("statistics")
791
+ }
792
+
793
+ model StatisticsWeeklyReport {
794
+ id String @id @default(cuid())
795
+ userId Int
796
+ weekStart DateTime
797
+ weekEnd DateTime?
798
+ totalQuizsCreated Int @default(0)
799
+ totalGamesHosted Int @default(0)
800
+ xpCount Int @default(0)
801
+ timeSpentLearning Int @default(0)
802
+ credits Int @default(0)
803
+ totalWins Int @default(0)
804
+ totalCorrectAnswers Int @default(0)
805
+ totalCrates Int @default(0)
806
+ flashcardSessions Int @default(0)
807
+ examsCompleted Int @default(0)
808
+ createdAt DateTime @default(now())
809
+
810
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
811
+
812
+ @@unique([userId, weekStart])
813
+ @@index([userId, createdAt])
814
+ @@map("statisticsWeeklyReport")
815
+ }
816
+
817
+ // Data used in public profiles, follows, badges, settings, and collections.
818
+ model UserFollow {
819
+ id String @id @default(cuid())
820
+ followerId Int
821
+ followingId Int
822
+ createdAt DateTime @default(now())
823
+
824
+ follower User @relation("UserFollowFollower", fields: [followerId], references: [id], onDelete: Cascade)
825
+ following User @relation("UserFollowFollowing", fields: [followingId], references: [id], onDelete: Cascade)
826
+
827
+ @@unique([followerId, followingId])
828
+ @@index([followingId, createdAt])
829
+ }
830
+
831
+ model ProfileReport {
832
+ id String @id @default(cuid())
833
+ reporterId Int?
834
+ targetUserId Int
835
+ reason String
836
+ details String?
837
+ status String @default("OPEN")
838
+ metadata Json?
839
+ createdAt DateTime @default(now())
840
+ reviewedAt DateTime?
841
+
842
+ reporter User? @relation("ProfileReportReporter", fields: [reporterId], references: [id], onDelete: SetNull)
843
+ target User @relation("ProfileReportTarget", fields: [targetUserId], references: [id], onDelete: Cascade)
844
+
845
+ @@index([reporterId])
846
+ @@index([targetUserId, status])
847
+ }
848
+
849
+ model AchievementDefinition {
850
+ id String @id @default(cuid())
851
+ key String @unique
852
+ title String
853
+ description String?
854
+ scope AchievementScope @default(PLATFORM)
855
+ rarity AchievementRarity @default(COMMON)
856
+ rewardText String?
857
+ icon String?
858
+ points Int @default(0)
859
+ criteria Json?
860
+ createdById Int?
861
+ active Boolean @default(true)
862
+ createdAt DateTime @default(now())
863
+ updatedAt DateTime @updatedAt
864
+
865
+ createdBy User? @relation("AchievementDefinitionCreator", fields: [createdById], references: [id], onDelete: SetNull)
866
+ awards UserAchievement[]
867
+ certificationRules CertificationRule[]
868
+
869
+ @@index([scope, active])
870
+ @@index([createdById])
871
+ }
872
+
873
+ model UserAchievement {
874
+ id String @id @default(cuid())
875
+ userId Int
876
+ achievementId String
877
+ progress Int @default(0)
878
+ completed Boolean @default(false)
879
+ completedAt DateTime?
880
+ metadata Json?
881
+ createdAt DateTime @default(now())
882
+
883
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
884
+ achievement AchievementDefinition @relation(fields: [achievementId], references: [id], onDelete: Cascade)
885
+
886
+ @@unique([userId, achievementId])
887
+ @@index([achievementId, completed])
888
+ }
889
+
890
+ model UserPinnedContent {
891
+ id String @id @default(cuid())
892
+ userId Int
893
+ contentType CollectionItemType
894
+ contentId String
895
+ title String?
896
+ rankingMetric String?
897
+ sortOrder Int @default(0)
898
+ metadata Json?
899
+ createdAt DateTime @default(now())
900
+
901
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
902
+
903
+ @@unique([userId, contentType, contentId])
904
+ @@unique([userId, sortOrder])
905
+ @@index([userId, createdAt])
906
+ }
907
+
908
+ model UserPreference {
909
+ id String @id @default(cuid())
910
+ userId Int @unique
911
+ whoCanMessage AudienceScope @default(EVERYONE)
912
+ whoCanInvite AudienceScope @default(EVERYONE)
913
+ whoCanSeeProfile AudienceScope @default(EVERYONE)
914
+ profileVisible Boolean @default(true)
915
+ appearOnline Boolean @default(true)
916
+ showInLeaderboards Boolean @default(true)
917
+ showActivity Boolean @default(true)
918
+ theme ThemeMode @default(DARK)
919
+ accentColor String? @default("yellow")
920
+ reduceMotion Boolean @default(false)
921
+ fontSize Int @default(16)
922
+ showTutorRating Boolean @default(true)
923
+ showStudentCount Boolean @default(true)
924
+ allowDirectMessages Boolean @default(true)
925
+ showLocation Boolean @default(true)
926
+ updatedAt DateTime @updatedAt
927
+
928
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
929
+ }
930
+
931
+ model CookieConsentPreference {
932
+ id String @id @default(cuid())
933
+ userId Int @unique
934
+ essential Boolean @default(true)
935
+ analytics Boolean @default(false)
936
+ functional Boolean @default(false)
937
+ marketing Boolean @default(false)
938
+ consentedAt DateTime?
939
+ updatedAt DateTime @updatedAt
940
+
941
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
942
+ }
943
+
944
+ model StudyCollection {
945
+ id String @id @default(cuid())
946
+ ownerId Int?
947
+ title String
948
+ slug String? @unique
949
+ description String?
950
+ type CollectionType @default(CURATED)
951
+ visibility Visibility @default(PRIVATE)
952
+ status PublicationStatus @default(DRAFT)
953
+ tag String?
954
+ coverImage String?
955
+ totalItems Int @default(0)
956
+ totalDurationMinutes Int @default(0)
957
+ enrolledCount Int @default(0)
958
+ metadata Json?
959
+ createdAt DateTime @default(now())
960
+ updatedAt DateTime @updatedAt
961
+
962
+ owner User? @relation("StudyCollectionOwner", fields: [ownerId], references: [id], onDelete: SetNull)
963
+ items StudyCollectionItem[]
964
+ enrollments StudyCollectionEnrollment[]
965
+
966
+ @@index([ownerId])
967
+ @@index([status, visibility])
968
+ }
969
+
970
+ model StudyCollectionItem {
971
+ id String @id @default(cuid())
972
+ collectionId String
973
+ type CollectionItemType
974
+ contentId String
975
+ title String?
976
+ sortOrder Int @default(0)
977
+ metadata Json?
978
+
979
+ collection StudyCollection @relation(fields: [collectionId], references: [id], onDelete: Cascade)
980
+
981
+ @@unique([collectionId, type, contentId])
982
+ @@unique([collectionId, sortOrder])
983
+ @@index([collectionId])
984
+ }
985
+
986
+ model StudyCollectionEnrollment {
987
+ id String @id @default(cuid())
988
+ collectionId String
989
+ userId Int
990
+ progress Int @default(0)
991
+ currentItemOrder Int?
992
+ startedAt DateTime @default(now())
993
+ completedAt DateTime?
994
+
995
+ collection StudyCollection @relation(fields: [collectionId], references: [id], onDelete: Cascade)
996
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
997
+
998
+ @@unique([collectionId, userId])
999
+ @@index([userId, startedAt])
1000
+ }
1001
+
1002
+ // Data used in the Zaplits dashboard.
1003
+ model ZaplitProfile {
1004
+ id String @id @default(cuid())
1005
+ userId Int @unique
1006
+ activePetId String?
1007
+ eggCount Int @default(0)
1008
+ coins Int @default(0)
1009
+ totalXp Int @default(0)
1010
+ createdAt DateTime @default(now())
1011
+ updatedAt DateTime @updatedAt
1012
+
1013
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
1014
+ pets ZaplitPet[]
1015
+ }
1016
+
1017
+ model ZaplitPet {
1018
+ id String @id @default(cuid())
1019
+ profileId String
1020
+ name String
1021
+ species String?
1022
+ element PetElement?
1023
+ icon String?
1024
+ level Int @default(1)
1025
+ xp Int @default(0)
1026
+ maxXp Int @default(100)
1027
+ hp Int @default(100)
1028
+ energy Int @default(100)
1029
+ mood Int @default(100)
1030
+ nextLevel Int?
1031
+ quizzesUntilNext Int?
1032
+ isActive Boolean @default(false)
1033
+ metadata Json?
1034
+ unlockedAt DateTime @default(now())
1035
+ lastInteractedAt DateTime?
1036
+
1037
+ profile ZaplitProfile @relation(fields: [profileId], references: [id], onDelete: Cascade)
1038
+ actions ZaplitPetActionLog[]
1039
+
1040
+ @@index([profileId, isActive])
1041
+ }
1042
+
1043
+ model ZaplitPetActionLog {
1044
+ id String @id @default(cuid())
1045
+ petId String
1046
+ action PetActionType
1047
+ deltaXp Int @default(0)
1048
+ deltaHp Int @default(0)
1049
+ deltaEnergy Int @default(0)
1050
+ deltaMood Int @default(0)
1051
+ metadata Json?
1052
+ createdAt DateTime @default(now())
1053
+
1054
+ pet ZaplitPet @relation(fields: [petId], references: [id], onDelete: Cascade)
1055
+
1056
+ @@index([petId, createdAt])
1057
+ }
1058
+
1059
+ model CalendarEntry {
1060
+ id String @id @default(cuid())
1061
+ userId Int
1062
+ title String
1063
+ description String?
1064
+ type CalendarEntryType
1065
+ status CalendarEntryStatus @default(PLANNED)
1066
+ startsAt DateTime
1067
+ endsAt DateTime?
1068
+ subjectName String?
1069
+ location String?
1070
+ room String?
1071
+ notes String?
1072
+ sourceType String?
1073
+ sourceId String?
1074
+ recurringRule String?
1075
+ isAutoGenerated Boolean @default(false)
1076
+ metadata Json?
1077
+ createdAt DateTime @default(now())
1078
+ updatedAt DateTime @updatedAt
1079
+
1080
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
1081
+
1082
+ @@index([userId, startsAt])
1083
+ @@index([type, status])
1084
+ }
1085
+
1086
+ model AcademicBoard {
1087
+ id String @id @default(cuid())
1088
+ name String
1089
+ code String @unique
1090
+ countryCode String?
1091
+ description String?
1092
+ createdAt DateTime @default(now())
1093
+ updatedAt DateTime @updatedAt
1094
+
1095
+ courses Course[]
1096
+ exams Exam[]
1097
+ tutorSubjects TutorSubject[]
1098
+ classrooms Classroom[]
1099
+ }
1100
+
1101
+ model Subject {
1102
+ id String @id @default(cuid())
1103
+ name String
1104
+ slug String @unique
1105
+ description String?
1106
+ color String?
1107
+ icon String?
1108
+ createdAt DateTime @default(now())
1109
+ updatedAt DateTime @updatedAt
1110
+
1111
+ courses Course[]
1112
+ lessons Lesson[]
1113
+ videos Video[]
1114
+ quizzes Quiz[]
1115
+ exams Exam[]
1116
+ tutorSubjects TutorSubject[]
1117
+ tutorBookings TutorBooking[]
1118
+ classrooms Classroom[]
1119
+ communities Community[]
1120
+ userEvents UserActivityEvent[]
1121
+ tutoringSessions TutoringSession[]
1122
+ trivitEvents TrivitEvent[]
1123
+ certificationRules CertificationRule[]
1124
+
1125
+ @@unique([name])
1126
+ }
1127
+
1128
+ model Course {
1129
+ id String @id @default(cuid())
1130
+ authorId Int
1131
+ subjectId String?
1132
+ boardId String?
1133
+ title String
1134
+ slug String @unique
1135
+ description String?
1136
+ level CourseLevel?
1137
+ yearGroup String?
1138
+ totalHours Int?
1139
+ totalLessons Int @default(0)
1140
+ totalQuizzes Int @default(0)
1141
+ totalFlashcards Int @default(0)
1142
+ status PublicationStatus @default(DRAFT)
1143
+ visibility Visibility @default(PRIVATE)
1144
+ coverImage String?
1145
+ colorFrom String?
1146
+ colorTo String?
1147
+ metadata Json?
1148
+ publishedAt DateTime?
1149
+ createdAt DateTime @default(now())
1150
+ updatedAt DateTime @updatedAt
1151
+
1152
+ author User @relation("CourseAuthor", fields: [authorId], references: [id], onDelete: Cascade)
1153
+ subject Subject? @relation(fields: [subjectId], references: [id], onDelete: SetNull)
1154
+ board AcademicBoard? @relation(fields: [boardId], references: [id], onDelete: SetNull)
1155
+ topics CourseTopic[]
1156
+ lessons Lesson[]
1157
+ videos Video[]
1158
+ videoPlaylists VideoPlaylist[]
1159
+ quizzes Quiz[]
1160
+ flashcardSets FlashcardSet[]
1161
+ resources ResourceItem[]
1162
+ enrollments CourseEnrollment[]
1163
+ assignments Assignment[]
1164
+ exams Exam[]
1165
+ classrooms Classroom[]
1166
+
1167
+ @@index([authorId])
1168
+ @@index([subjectId])
1169
+ @@index([boardId])
1170
+ @@index([status, visibility])
1171
+ }
1172
+
1173
+ model CourseEnrollment {
1174
+ id String @id @default(cuid())
1175
+ courseId String
1176
+ userId Int
1177
+ status EnrollmentStatus @default(ACTIVE)
1178
+ progress Int @default(0)
1179
+ lessonsLeft Int @default(0)
1180
+ streak Int @default(0)
1181
+ isPinned Boolean @default(false)
1182
+ specMatch Int?
1183
+ specInfo String?
1184
+ lastStudiedAt DateTime?
1185
+ completedAt DateTime?
1186
+ createdAt DateTime @default(now())
1187
+ updatedAt DateTime @updatedAt
1188
+
1189
+ course Course @relation(fields: [courseId], references: [id], onDelete: Cascade)
1190
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
1191
+
1192
+ @@unique([courseId, userId])
1193
+ @@index([userId, status])
1194
+ }
1195
+
1196
+ model CourseTopic {
1197
+ id String @id @default(cuid())
1198
+ courseId String
1199
+ number Int?
1200
+ name String
1201
+ slug String?
1202
+ description String?
1203
+ durationMinutes Int?
1204
+ order Int @default(0)
1205
+ videosCount Int @default(0)
1206
+ quizzesCount Int @default(0)
1207
+ flashcardsCount Int @default(0)
1208
+ lessonsCount Int @default(0)
1209
+ createdAt DateTime @default(now())
1210
+ updatedAt DateTime @updatedAt
1211
+
1212
+ course Course @relation(fields: [courseId], references: [id], onDelete: Cascade)
1213
+ lessons Lesson[]
1214
+ videos Video[]
1215
+ quizzes Quiz[]
1216
+ exams Exam[]
1217
+ questions Question[]
1218
+
1219
+ @@index([courseId, order])
1220
+ }
1221
+
1222
+ model Lesson {
1223
+ id String @id @default(cuid())
1224
+ authorId Int
1225
+ courseId String?
1226
+ topicId String?
1227
+ subjectId String?
1228
+ title String
1229
+ slug String? @unique
1230
+ description String?
1231
+ type LessonType @default(MIXED)
1232
+ status PublicationStatus @default(DRAFT)
1233
+ visibility Visibility @default(PRIVATE)
1234
+ durationMinutes Int?
1235
+ progressSummary Int @default(0)
1236
+ learningOutcomes Json?
1237
+ downloadableNotes Json?
1238
+ relatedFlashcards Json?
1239
+ explainedByName String?
1240
+ explainedByAvatar String?
1241
+ metadata Json?
1242
+ createdAt DateTime @default(now())
1243
+ updatedAt DateTime @updatedAt
1244
+ publishedAt DateTime?
1245
+
1246
+ author User? @relation("LessonAuthor", fields: [authorId], references: [id], onDelete: Cascade)
1247
+ course Course? @relation(fields: [courseId], references: [id], onDelete: SetNull)
1248
+ topic CourseTopic? @relation(fields: [topicId], references: [id], onDelete: SetNull)
1249
+ subject Subject? @relation(fields: [subjectId], references: [id], onDelete: SetNull)
1250
+ steps LessonStep[]
1251
+ versions LessonVersion[]
1252
+ assets LessonAsset[]
1253
+ progress LessonProgress[]
1254
+ videos Video[]
1255
+ resources ResourceItem[]
1256
+ quizzes Quiz[]
1257
+ exams Exam[]
1258
+ assignments Assignment[]
1259
+
1260
+ @@index([authorId])
1261
+ @@index([courseId])
1262
+ @@index([topicId])
1263
+ @@index([subjectId])
1264
+ @@index([status, visibility])
1265
+ }
1266
+
1267
+ model LessonStep {
1268
+ id String @id @default(cuid())
1269
+ lessonId String
1270
+ order Int @default(0)
1271
+ type LessonStepType
1272
+ title String
1273
+ durationMinutes Int?
1274
+ isRequired Boolean @default(true)
1275
+ content Json
1276
+ metadata Json?
1277
+ createdAt DateTime @default(now())
1278
+ updatedAt DateTime @updatedAt
1279
+
1280
+ lesson Lesson @relation(fields: [lessonId], references: [id], onDelete: Cascade)
1281
+
1282
+ @@unique([lessonId, order])
1283
+ @@index([lessonId, type])
1284
+ }
1285
+
1286
+ model LessonVersion {
1287
+ id String @id @default(cuid())
1288
+ lessonId String
1289
+ versionLabel String
1290
+ notes String?
1291
+ snapshot Json
1292
+ createdById Int?
1293
+ createdAt DateTime @default(now())
1294
+
1295
+ lesson Lesson @relation(fields: [lessonId], references: [id], onDelete: Cascade)
1296
+
1297
+ @@index([lessonId, createdAt])
1298
+ }
1299
+
1300
+ model LessonAsset {
1301
+ id String @id @default(cuid())
1302
+ lessonId String
1303
+ type AssetType
1304
+ name String
1305
+ url String
1306
+ mimeType String?
1307
+ sizeBytes Int?
1308
+ metadata Json?
1309
+ uploadedById Int?
1310
+ createdAt DateTime @default(now())
1311
+
1312
+ lesson Lesson @relation(fields: [lessonId], references: [id], onDelete: Cascade)
1313
+
1314
+ @@index([lessonId, type])
1315
+ }
1316
+
1317
+ model LessonProgress {
1318
+ id String @id @default(cuid())
1319
+ lessonId String
1320
+ userId Int
1321
+ currentStepIndex Int @default(0)
1322
+ xp Int @default(0)
1323
+ streak Int @default(0)
1324
+ maxStreak Int @default(0)
1325
+ correctAnswers Int @default(0)
1326
+ incorrectAnswers Int @default(0)
1327
+ hintsUsed Int @default(0)
1328
+ progress Int @default(0)
1329
+ startedAt DateTime @default(now())
1330
+ completedAt DateTime?
1331
+ updatedAt DateTime @updatedAt
1332
+
1333
+ lesson Lesson @relation(fields: [lessonId], references: [id], onDelete: Cascade)
1334
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
1335
+
1336
+ @@unique([lessonId, userId])
1337
+ @@index([userId, updatedAt])
1338
+ }
1339
+
1340
+ model Video {
1341
+ id String @id @default(cuid())
1342
+ authorId Int
1343
+ courseId String?
1344
+ topicId String?
1345
+ subjectId String?
1346
+ lessonId String?
1347
+ title String
1348
+ description String?
1349
+ status PublicationStatus @default(DRAFT)
1350
+ visibility Visibility @default(PRIVATE)
1351
+ difficulty VideoDifficulty?
1352
+ url String?
1353
+ thumbnail String?
1354
+ durationSeconds Int @default(0)
1355
+ transcript String?
1356
+ views Int @default(0)
1357
+ likes Int @default(0)
1358
+ isFeatured Boolean @default(false)
1359
+ hasNotes Boolean @default(false)
1360
+ hasQuiz Boolean @default(false)
1361
+ tags Json?
1362
+ metadata Json?
1363
+ publishedAt DateTime?
1364
+ createdAt DateTime @default(now())
1365
+ updatedAt DateTime @updatedAt
1366
+
1367
+ author User @relation("VideoAuthor", fields: [authorId], references: [id], onDelete: Cascade)
1368
+ course Course? @relation(fields: [courseId], references: [id], onDelete: SetNull)
1369
+ topic CourseTopic? @relation(fields: [topicId], references: [id], onDelete: SetNull)
1370
+ subject Subject? @relation(fields: [subjectId], references: [id], onDelete: SetNull)
1371
+ lesson Lesson? @relation(fields: [lessonId], references: [id], onDelete: SetNull)
1372
+ chapters VideoChapter[]
1373
+ playlists VideoPlaylistItem[]
1374
+ progress VideoProgress[]
1375
+
1376
+ @@index([authorId])
1377
+ @@index([courseId])
1378
+ @@index([subjectId])
1379
+ @@index([status, visibility])
1380
+ }
1381
+
1382
+ model VideoChapter {
1383
+ id String @id @default(cuid())
1384
+ videoId String
1385
+ title String
1386
+ timeSecond Int
1387
+ order Int @default(0)
1388
+
1389
+ video Video @relation(fields: [videoId], references: [id], onDelete: Cascade)
1390
+
1391
+ @@unique([videoId, order])
1392
+ }
1393
+
1394
+ model VideoPlaylist {
1395
+ id String @id @default(cuid())
1396
+ title String
1397
+ description String?
1398
+ courseId String?
1399
+ thumbnail String?
1400
+ totalDuration Int @default(0)
1401
+ createdAt DateTime @default(now())
1402
+ updatedAt DateTime @updatedAt
1403
+
1404
+ course Course? @relation(fields: [courseId], references: [id], onDelete: SetNull)
1405
+ items VideoPlaylistItem[]
1406
+
1407
+ @@index([courseId])
1408
+ }
1409
+
1410
+ model VideoPlaylistItem {
1411
+ id String @id @default(cuid())
1412
+ playlistId String
1413
+ videoId String
1414
+ order Int @default(0)
1415
+
1416
+ playlist VideoPlaylist @relation(fields: [playlistId], references: [id], onDelete: Cascade)
1417
+ video Video @relation(fields: [videoId], references: [id], onDelete: Cascade)
1418
+
1419
+ @@unique([playlistId, videoId])
1420
+ @@unique([playlistId, order])
1421
+ }
1422
+
1423
+ model VideoProgress {
1424
+ id String @id @default(cuid())
1425
+ videoId String
1426
+ userId Int
1427
+ watchedSeconds Int @default(0)
1428
+ progress Int @default(0)
1429
+ isBookmarked Boolean @default(false)
1430
+ isLiked Boolean @default(false)
1431
+ completedAt DateTime?
1432
+ updatedAt DateTime @updatedAt
1433
+
1434
+ video Video @relation(fields: [videoId], references: [id], onDelete: Cascade)
1435
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
1436
+
1437
+ @@unique([videoId, userId])
1438
+ @@index([userId, updatedAt])
1439
+ }
1440
+
1441
+ model Quiz {
1442
+ id String @id @default(cuid())
1443
+ title String
1444
+ slug String? @unique
1445
+ description String?
1446
+ isPublic Boolean @default(false)
1447
+ visibility Visibility @default(PRIVATE)
1448
+ status PublicationStatus @default(DRAFT)
1449
+ coverImage String?
1450
+ subjectId String?
1451
+ courseId String?
1452
+ topicId String?
1453
+ lessonId String?
1454
+ boardCode String?
1455
+ level CourseLevel?
1456
+ category String?
1457
+ estimatedDurationMinutes Int?
1458
+ tags Json?
1459
+ createdAt DateTime @default(now())
1460
+ updatedAt DateTime @updatedAt
1461
+ authorId Int
1462
+
1463
+ author User @relation(fields: [authorId], references: [id], onDelete: Cascade)
1464
+ subject Subject? @relation(fields: [subjectId], references: [id], onDelete: SetNull)
1465
+ course Course? @relation(fields: [courseId], references: [id], onDelete: SetNull)
1466
+ topic CourseTopic? @relation(fields: [topicId], references: [id], onDelete: SetNull)
1467
+ lesson Lesson? @relation(fields: [lessonId], references: [id], onDelete: SetNull)
1468
+ questions Question[]
1469
+ games Game[]
1470
+ lobbies Lobby[]
1471
+ attempts QuizAttempt[]
1472
+
1473
+ @@index([authorId])
1474
+ @@index([subjectId])
1475
+ @@index([courseId])
1476
+ @@index([topicId])
1477
+ @@index([lessonId])
1478
+ @@index([status, visibility])
1479
+ }
1480
+
1481
+ model Question {
1482
+ id String @id @default(cuid())
1483
+ quizId String
1484
+ topicId String?
1485
+ text String
1486
+ imageUrl String?
1487
+ explanation String?
1488
+ hint String?
1489
+ timeLimit Int @default(30)
1490
+ points Int @default(100)
1491
+ sortOrder Int @default(0)
1492
+ type QuestionType @default(MULTIPLE_CHOICE)
1493
+ metadata Json?
1494
+
1495
+ quiz Quiz @relation(fields: [quizId], references: [id], onDelete: Cascade)
1496
+ topic CourseTopic? @relation(fields: [topicId], references: [id], onDelete: SetNull)
1497
+ options QuestionOption[]
1498
+ answers QuizAttemptAnswer[]
1499
+
1500
+ @@index([quizId, sortOrder])
1501
+ @@index([topicId])
1502
+ }
1503
+
1504
+ model QuestionOption {
1505
+ id String @id @default(cuid())
1506
+ questionId String
1507
+ text String
1508
+ isCorrect Boolean
1509
+ explanation String?
1510
+ mediaUrl String?
1511
+ sortOrder Int @default(0)
1512
+
1513
+ question Question @relation(fields: [questionId], references: [id], onDelete: Cascade)
1514
+
1515
+ @@index([questionId, sortOrder])
1516
+ }
1517
+
1518
+ model QuizAttempt {
1519
+ id String @id @default(cuid())
1520
+ quizId String
1521
+ userId Int
1522
+ score Int @default(0)
1523
+ correctAnswers Int @default(0)
1524
+ totalQuestions Int @default(0)
1525
+ accuracy Float @default(0)
1526
+ durationSeconds Int?
1527
+ startedAt DateTime @default(now())
1528
+ completedAt DateTime?
1529
+
1530
+ quiz Quiz @relation(fields: [quizId], references: [id], onDelete: Cascade)
1531
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
1532
+ answers QuizAttemptAnswer[]
1533
+
1534
+ @@index([quizId, completedAt])
1535
+ @@index([userId, completedAt])
1536
+ }
1537
+
1538
+ model QuizAttemptAnswer {
1539
+ id String @id @default(cuid())
1540
+ attemptId String
1541
+ questionId String
1542
+ answerText String?
1543
+ selectedIndexes Json?
1544
+ isCorrect Boolean @default(false)
1545
+ pointsEarned Int @default(0)
1546
+ timeTakenSecond Int?
1547
+
1548
+ attempt QuizAttempt @relation(fields: [attemptId], references: [id], onDelete: Cascade)
1549
+ question Question @relation(fields: [questionId], references: [id], onDelete: Cascade)
1550
+
1551
+ @@index([attemptId])
1552
+ @@index([questionId])
1553
+ }
1554
+
1555
+ model FlashcardSet {
1556
+ id String @id @default(cuid())
1557
+ title String
1558
+ description String?
1559
+ subjectId String?
1560
+ courseId String?
1561
+ visibility Visibility @default(PRIVATE)
1562
+ status PublicationStatus @default(DRAFT)
1563
+ authorId Int
1564
+ createdAt DateTime @default(now())
1565
+ updatedAt DateTime @updatedAt
1566
+
1567
+ author User @relation(fields: [authorId], references: [id], onDelete: Cascade)
1568
+ course Course? @relation(fields: [courseId], references: [id], onDelete: SetNull)
1569
+ subdecks FlashcardSubdeck[]
1570
+ studySessions FlashcardStudySession[]
1571
+
1572
+ @@index([authorId])
1573
+ @@index([courseId])
1574
+ }
1575
+
1576
+ model FlashcardSubdeck {
1577
+ id String @id @default(cuid())
1578
+ title String
1579
+ setId String
1580
+ parentId String?
1581
+ sortOrder Int @default(0)
1582
+ createdAt DateTime @default(now())
1583
+ updatedAt DateTime @updatedAt
1584
+
1585
+ flashcardSet FlashcardSet @relation(fields: [setId], references: [id], onDelete: Cascade)
1586
+ parent FlashcardSubdeck? @relation("SubdeckToSubdeck", fields: [parentId], references: [id], onDelete: SetNull)
1587
+ children FlashcardSubdeck[] @relation("SubdeckToSubdeck")
1588
+ flashcards Flashcard[]
1589
+
1590
+ @@index([setId, sortOrder])
1591
+ @@index([parentId])
1592
+ }
1593
+
1594
+ model Flashcard {
1595
+ id String @id @default(cuid())
1596
+ subdeckId String
1597
+ question String
1598
+ answer String
1599
+ explanation String?
1600
+ hint String?
1601
+ imageUrl String?
1602
+ tags Json?
1603
+ easeFactor Float @default(2.5)
1604
+ intervalDays Int @default(0)
1605
+ repetitions Int @default(0)
1606
+ nextReviewAt DateTime?
1607
+ createdAt DateTime @default(now())
1608
+ updatedAt DateTime @updatedAt
1609
+
1610
+ subdeck FlashcardSubdeck @relation(fields: [subdeckId], references: [id], onDelete: Cascade)
1611
+ reviews FlashcardReview[]
1612
+
1613
+ @@index([subdeckId])
1614
+ @@index([nextReviewAt])
1615
+ }
1616
+
1617
+ model FlashcardStudySession {
1618
+ id String @id @default(cuid())
1619
+ setId String
1620
+ userId Int
1621
+ cardsStudied Int @default(0)
1622
+ correctCount Int @default(0)
1623
+ durationSecond Int @default(0)
1624
+ startedAt DateTime @default(now())
1625
+ endedAt DateTime?
1626
+
1627
+ set FlashcardSet @relation(fields: [setId], references: [id], onDelete: Cascade)
1628
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
1629
+
1630
+ @@index([setId])
1631
+ @@index([userId, startedAt])
1632
+ }
1633
+
1634
+ model FlashcardReview {
1635
+ id String @id @default(cuid())
1636
+ flashcardId String
1637
+ userId Int
1638
+ rating Int
1639
+ reviewedAt DateTime @default(now())
1640
+
1641
+ flashcard Flashcard @relation(fields: [flashcardId], references: [id], onDelete: Cascade)
1642
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
1643
+
1644
+ @@index([flashcardId, reviewedAt])
1645
+ @@index([userId, reviewedAt])
1646
+ }
1647
+
1648
+ model Exam {
1649
+ id String @id @default(cuid())
1650
+ title String
1651
+ slug String? @unique
1652
+ description String?
1653
+ subjectId String?
1654
+ boardId String?
1655
+ courseId String?
1656
+ lessonId String?
1657
+ topicId String?
1658
+ level CourseLevel?
1659
+ paper String?
1660
+ status ExamStatus @default(DRAFT)
1661
+ timeLimitMinutes Int?
1662
+ totalMarks Int @default(0)
1663
+ instructions String?
1664
+ metadata Json?
1665
+ createdById Int?
1666
+ createdAt DateTime @default(now())
1667
+ updatedAt DateTime @updatedAt
1668
+ publishedAt DateTime?
1669
+
1670
+ subject Subject? @relation(fields: [subjectId], references: [id], onDelete: SetNull)
1671
+ board AcademicBoard? @relation(fields: [boardId], references: [id], onDelete: SetNull)
1672
+ course Course? @relation(fields: [courseId], references: [id], onDelete: SetNull)
1673
+ lesson Lesson? @relation(fields: [lessonId], references: [id], onDelete: SetNull)
1674
+ topic CourseTopic? @relation(fields: [topicId], references: [id], onDelete: SetNull)
1675
+ sections ExamSection[]
1676
+ questions ExamQuestion[]
1677
+ sessions ExamSession[]
1678
+ certificationRules CertificationRule[]
1679
+
1680
+ @@index([subjectId])
1681
+ @@index([boardId])
1682
+ @@index([courseId])
1683
+ @@index([status])
1684
+ }
1685
+
1686
+ model ExamSection {
1687
+ id String @id @default(cuid())
1688
+ examId String
1689
+ title String
1690
+ instructions String?
1691
+ sortOrder Int @default(0)
1692
+ marks Int @default(0)
1693
+
1694
+ exam Exam @relation(fields: [examId], references: [id], onDelete: Cascade)
1695
+ questions ExamQuestion[]
1696
+
1697
+ @@unique([examId, sortOrder])
1698
+ }
1699
+
1700
+ model ExamQuestion {
1701
+ id String @id @default(cuid())
1702
+ examId String
1703
+ sectionId String?
1704
+ prompt String
1705
+ questionNumber Int?
1706
+ marks Int @default(1)
1707
+ expectedAnswer String?
1708
+ workedSolution String?
1709
+ markScheme Json?
1710
+ allowWhiteboard Boolean @default(false)
1711
+ metadata Json?
1712
+
1713
+ exam Exam @relation(fields: [examId], references: [id], onDelete: Cascade)
1714
+ section ExamSection? @relation(fields: [sectionId], references: [id], onDelete: SetNull)
1715
+ responses ExamResponse[]
1716
+ tags ExamQuestionTag[]
1717
+ versions ExamQuestionVersion[]
1718
+
1719
+ @@index([examId])
1720
+ @@index([sectionId])
1721
+ }
1722
+
1723
+ model ExamSession {
1724
+ id String @id @default(cuid())
1725
+ examId String
1726
+ userId Int
1727
+ status ExamSessionStatus @default(ACTIVE)
1728
+ progress Int @default(0)
1729
+ score Int @default(0)
1730
+ maxScore Int @default(0)
1731
+ grade String?
1732
+ timeRemainingSeconds Int?
1733
+ flags Json?
1734
+ startedAt DateTime @default(now())
1735
+ submittedAt DateTime?
1736
+ gradedAt DateTime?
1737
+
1738
+ exam Exam @relation(fields: [examId], references: [id], onDelete: Cascade)
1739
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
1740
+ responses ExamResponse[]
1741
+ antiCheatFlags ExamAntiCheatFlag[]
1742
+
1743
+ @@index([examId, status])
1744
+ @@index([userId, startedAt])
1745
+ }
1746
+
1747
+ model ExamResponse {
1748
+ id String @id @default(cuid())
1749
+ sessionId String
1750
+ questionId String
1751
+ answerText String?
1752
+ attachments Json?
1753
+ marksAwarded Int?
1754
+ isCorrect Boolean?
1755
+ flagged Boolean @default(false)
1756
+ timeSpentSeconds Int?
1757
+
1758
+ session ExamSession @relation(fields: [sessionId], references: [id], onDelete: Cascade)
1759
+ question ExamQuestion @relation(fields: [questionId], references: [id], onDelete: Cascade)
1760
+ antiCheatFlags ExamAntiCheatFlag[]
1761
+
1762
+ @@index([sessionId])
1763
+ @@index([questionId])
1764
+ }
1765
+
1766
+ model QuestionTag {
1767
+ id String @id @default(cuid())
1768
+ name String
1769
+ category String?
1770
+ description String?
1771
+ createdAt DateTime @default(now())
1772
+
1773
+ questions ExamQuestionTag[]
1774
+
1775
+ @@unique([name, category])
1776
+ }
1777
+
1778
+ model ExamQuestionTag {
1779
+ id String @id @default(cuid())
1780
+ questionId String
1781
+ tagId String
1782
+
1783
+ question ExamQuestion @relation(fields: [questionId], references: [id], onDelete: Cascade)
1784
+ tag QuestionTag @relation(fields: [tagId], references: [id], onDelete: Cascade)
1785
+
1786
+ @@unique([questionId, tagId])
1787
+ }
1788
+
1789
+ model ExamQuestionVersion {
1790
+ id String @id @default(cuid())
1791
+ questionId String
1792
+ versionLabel String
1793
+ changeSummary String?
1794
+ snapshot Json
1795
+ isCurrent Boolean @default(false)
1796
+ createdById Int?
1797
+ createdAt DateTime @default(now())
1798
+
1799
+ question ExamQuestion @relation(fields: [questionId], references: [id], onDelete: Cascade)
1800
+ createdBy User? @relation("ExamQuestionVersionAuthor", fields: [createdById], references: [id], onDelete: SetNull)
1801
+
1802
+ @@index([questionId, createdAt])
1803
+ @@index([createdById])
1804
+ }
1805
+
1806
+ model ExamAntiCheatFlag {
1807
+ id String @id @default(cuid())
1808
+ sessionId String
1809
+ responseId String?
1810
+ reason String
1811
+ severity AntiCheatSeverity
1812
+ status AntiCheatStatus @default(OPEN)
1813
+ details String?
1814
+ timeline Json?
1815
+ metadata Json?
1816
+ reviewedById Int?
1817
+ detectedAt DateTime @default(now())
1818
+ reviewedAt DateTime?
1819
+
1820
+ session ExamSession @relation(fields: [sessionId], references: [id], onDelete: Cascade)
1821
+ response ExamResponse? @relation(fields: [responseId], references: [id], onDelete: SetNull)
1822
+ reviewedBy User? @relation("ExamAntiCheatReviewer", fields: [reviewedById], references: [id], onDelete: SetNull)
1823
+
1824
+ @@index([sessionId, status])
1825
+ @@index([responseId])
1826
+ @@index([reviewedById])
1827
+ }
1828
+
1829
+ model CertificationRule {
1830
+ id String @id @default(cuid())
1831
+ title String
1832
+ description String?
1833
+ criteria String
1834
+ rewardText String?
1835
+ rewardXp Int @default(0)
1836
+ active Boolean @default(true)
1837
+ examId String?
1838
+ subjectId String?
1839
+ achievementId String?
1840
+ createdById Int?
1841
+ metadata Json?
1842
+ createdAt DateTime @default(now())
1843
+ updatedAt DateTime @updatedAt
1844
+
1845
+ exam Exam? @relation(fields: [examId], references: [id], onDelete: SetNull)
1846
+ subject Subject? @relation(fields: [subjectId], references: [id], onDelete: SetNull)
1847
+ achievement AchievementDefinition? @relation(fields: [achievementId], references: [id], onDelete: SetNull)
1848
+ createdBy User? @relation("CertificationRuleCreator", fields: [createdById], references: [id], onDelete: SetNull)
1849
+
1850
+ @@index([examId])
1851
+ @@index([subjectId])
1852
+ @@index([achievementId])
1853
+ @@index([createdById, active])
1854
+ }
1855
+
1856
+ // Data used in Hire Tutor page and tutoring dashboards.
1857
+ model TutorProfile {
1858
+ id String @id @default(cuid())
1859
+ userId Int @unique
1860
+ qualification String?
1861
+ bio String?
1862
+ yearsExperience Int?
1863
+ hourlyRate Float?
1864
+ originalHourlyRate Float?
1865
+ ratingAverage Float @default(0)
1866
+ reviewCount Int @default(0)
1867
+ verificationStatus TutorVerificationStatus @default(PENDING)
1868
+ availabilityText String?
1869
+ isVerified Boolean @default(false)
1870
+ createdAt DateTime @default(now())
1871
+ updatedAt DateTime @updatedAt
1872
+
1873
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
1874
+ subjects TutorSubject[]
1875
+ availability TutorAvailability[]
1876
+ }
1877
+
1878
+ model TutorSubject {
1879
+ id String @id @default(cuid())
1880
+ tutorProfileId String
1881
+ subjectId String
1882
+ boardId String?
1883
+ level CourseLevel?
1884
+ price Float?
1885
+
1886
+ tutorProfile TutorProfile @relation(fields: [tutorProfileId], references: [id], onDelete: Cascade)
1887
+ subject Subject @relation(fields: [subjectId], references: [id], onDelete: Cascade)
1888
+ board AcademicBoard? @relation(fields: [boardId], references: [id], onDelete: SetNull)
1889
+
1890
+ @@unique([tutorProfileId, subjectId, boardId])
1891
+ }
1892
+
1893
+ model TutorAvailability {
1894
+ id String @id @default(cuid())
1895
+ tutorProfileId String
1896
+ dayOfWeek Int
1897
+ startTime String
1898
+ endTime String
1899
+ timezone String?
1900
+ available Boolean @default(true)
1901
+
1902
+ tutorProfile TutorProfile @relation(fields: [tutorProfileId], references: [id], onDelete: Cascade)
1903
+
1904
+ @@index([tutorProfileId, dayOfWeek])
1905
+ }
1906
+
1907
+ model StudentProfile {
1908
+ id String @id @default(cuid())
1909
+ userId Int @unique
1910
+ grade String?
1911
+ targetGrade String?
1912
+ strengths Json?
1913
+ weaknesses Json?
1914
+ parentName String?
1915
+ parentEmail String?
1916
+ parentPhone String?
1917
+ attendance Int?
1918
+ progress Int?
1919
+ sessionsCompleted Int @default(0)
1920
+ totalSessions Int @default(0)
1921
+ createdAt DateTime @default(now())
1922
+ updatedAt DateTime @updatedAt
1923
+
1924
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
1925
+ }
1926
+
1927
+ // Data used in the professional teacher/tutor resource dashboard.
1928
+ model ResourceFolder {
1929
+ id String @id @default(cuid())
1930
+ ownerId Int
1931
+ name String
1932
+ color String?
1933
+ description String?
1934
+ createdAt DateTime @default(now())
1935
+ updatedAt DateTime @updatedAt
1936
+
1937
+ owner User @relation(fields: [ownerId], references: [id], onDelete: Cascade)
1938
+ resources ResourceItem[]
1939
+
1940
+ @@index([ownerId, name])
1941
+ }
1942
+
1943
+ model ResourceItem {
1944
+ id String @id @default(cuid())
1945
+ ownerId Int
1946
+ folderId String?
1947
+ courseId String?
1948
+ lessonId String?
1949
+ title String
1950
+ type String
1951
+ format String?
1952
+ sizeBytes Int?
1953
+ url String?
1954
+ externalUrl String?
1955
+ shared Boolean @default(false)
1956
+ starred Boolean @default(false)
1957
+ downloads Int @default(0)
1958
+ views Int @default(0)
1959
+ archivedAt DateTime?
1960
+ createdAt DateTime @default(now())
1961
+ lastUsedAt DateTime?
1962
+ updatedAt DateTime @updatedAt
1963
+
1964
+ owner User @relation(fields: [ownerId], references: [id], onDelete: Cascade)
1965
+ folder ResourceFolder? @relation(fields: [folderId], references: [id], onDelete: SetNull)
1966
+ course Course? @relation(fields: [courseId], references: [id], onDelete: SetNull)
1967
+ lesson Lesson? @relation(fields: [lessonId], references: [id], onDelete: SetNull)
1968
+
1969
+ @@index([ownerId, createdAt])
1970
+ @@index([folderId])
1971
+ @@index([courseId])
1972
+ @@index([lessonId])
1973
+ }
1974
+
1975
+ // Data used in professional earnings, invoices, withdrawals, and package pricing.
1976
+ model TutorPaymentMethod {
1977
+ id String @id @default(cuid())
1978
+ userId Int
1979
+ type String
1980
+ name String
1981
+ provider String?
1982
+ externalRef String?
1983
+ isDefault Boolean @default(false)
1984
+ status String?
1985
+ createdAt DateTime @default(now())
1986
+ updatedAt DateTime @updatedAt
1987
+
1988
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
1989
+ payoutRequests PayoutRequest[]
1990
+
1991
+ @@index([userId, isDefault])
1992
+ }
1993
+
1994
+ model PayoutRequest {
1995
+ id String @id @default(cuid())
1996
+ userId Int
1997
+ paymentMethodId String?
1998
+ amount Float
1999
+ currency String @default("USD")
2000
+ status String
2001
+ requestedAt DateTime @default(now())
2002
+ processedAt DateTime?
2003
+ notes String?
2004
+
2005
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
2006
+ paymentMethod TutorPaymentMethod? @relation(fields: [paymentMethodId], references: [id], onDelete: SetNull)
2007
+
2008
+ @@index([userId, requestedAt])
2009
+ @@index([paymentMethodId])
2010
+ }
2011
+
2012
+ model TutorPricingPackage {
2013
+ id String @id @default(cuid())
2014
+ userId Int
2015
+ name String
2016
+ price Float
2017
+ durationLabel String
2018
+ savingsLabel String?
2019
+ popularity Int @default(0)
2020
+ active Boolean @default(true)
2021
+ createdAt DateTime @default(now())
2022
+ updatedAt DateTime @updatedAt
2023
+
2024
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
2025
+
2026
+ @@index([userId, active])
2027
+ }
2028
+
2029
+ model TutorInvoice {
2030
+ id String @id @default(cuid())
2031
+ invoiceNumber String @unique
2032
+ issuerId Int
2033
+ studentName String
2034
+ studentEmail String?
2035
+ amount Float
2036
+ dueAt DateTime?
2037
+ paidAt DateTime?
2038
+ status String
2039
+ description String?
2040
+ createdAt DateTime @default(now())
2041
+ updatedAt DateTime @updatedAt
2042
+
2043
+ issuer User @relation("InvoiceIssuer", fields: [issuerId], references: [id], onDelete: Cascade)
2044
+
2045
+ @@index([issuerId, status])
2046
+ @@index([dueAt])
2047
+ }
2048
+
2049
+ model TutorVerificationRequest {
2050
+ id String @id @default(cuid())
2051
+ userId Int
2052
+ status String
2053
+ documents Json?
2054
+ submittedAt DateTime @default(now())
2055
+ reviewedAt DateTime?
2056
+ reviewerId Int?
2057
+ notes String?
2058
+
2059
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
2060
+
2061
+ @@index([userId, status])
2062
+ @@index([submittedAt])
2063
+ }
2064
+
2065
+ model TutorBooking {
2066
+ id String @id @default(cuid())
2067
+ tutorId Int
2068
+ studentId Int
2069
+ subjectId String?
2070
+ requestedTopic String?
2071
+ message String?
2072
+ proposedStartAt DateTime?
2073
+ proposedEndAt DateTime?
2074
+ sessionType TutoringSessionType @default(INDIVIDUAL)
2075
+ format SessionFormat @default(VIRTUAL)
2076
+ status TutoringSessionStatus @default(REQUESTED)
2077
+ quotedPrice Float?
2078
+ createdAt DateTime @default(now())
2079
+ updatedAt DateTime @updatedAt
2080
+ tutoringSessionId String?
2081
+
2082
+ tutor User @relation("TutorBookings", fields: [tutorId], references: [id], onDelete: Cascade)
2083
+ student User @relation("StudentBookings", fields: [studentId], references: [id], onDelete: Cascade)
2084
+ subject Subject? @relation(fields: [subjectId], references: [id], onDelete: SetNull)
2085
+ tutoringSession TutoringSession? @relation(fields: [tutoringSessionId], references: [id], onDelete: SetNull)
2086
+
2087
+ @@index([tutorId, status])
2088
+ @@index([studentId, status])
2089
+ }
2090
+
2091
+ model TutoringSession {
2092
+ id String @id @default(cuid())
2093
+ tutorId Int
2094
+ subjectId String?
2095
+ boardCode String?
2096
+ title String
2097
+ topic String?
2098
+ sessionType TutoringSessionType @default(INDIVIDUAL)
2099
+ format SessionFormat @default(VIRTUAL)
2100
+ status TutoringSessionStatus @default(SCHEDULED)
2101
+ scheduledStartAt DateTime
2102
+ scheduledEndAt DateTime
2103
+ timezone String?
2104
+ location String?
2105
+ meetingUrl String?
2106
+ recurring Boolean @default(false)
2107
+ recurrenceRule String?
2108
+ notes String?
2109
+ materials Json?
2110
+ recordingUrl String?
2111
+ whiteboardState Json?
2112
+ maxParticipants Int?
2113
+ priceAmount Float?
2114
+ currency String? @default("USD")
2115
+ createdAt DateTime @default(now())
2116
+ updatedAt DateTime @updatedAt
2117
+
2118
+ tutor User @relation("TutorSessions", fields: [tutorId], references: [id], onDelete: Cascade)
2119
+ subject Subject? @relation(fields: [subjectId], references: [id], onDelete: SetNull)
2120
+ attendees TutoringSessionAttendee[]
2121
+ bookings TutorBooking[]
2122
+
2123
+ @@index([tutorId, scheduledStartAt])
2124
+ @@index([subjectId])
2125
+ @@index([status])
2126
+ }
2127
+
2128
+ model TutoringSessionAttendee {
2129
+ id String @id @default(cuid())
2130
+ tutoringSessionId String
2131
+ userId Int
2132
+ role ClassroomRole @default(STUDENT)
2133
+ status EnrollmentStatus @default(ACTIVE)
2134
+ joinedAt DateTime?
2135
+ leftAt DateTime?
2136
+ attendance Int?
2137
+ rating Int?
2138
+ feedback String?
2139
+
2140
+ tutoringSession TutoringSession @relation(fields: [tutoringSessionId], references: [id], onDelete: Cascade)
2141
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
2142
+
2143
+ @@unique([tutoringSessionId, userId])
2144
+ @@index([userId])
2145
+ }
2146
+
2147
+ model TutorReview {
2148
+ id String @id @default(cuid())
2149
+ tutorId Int
2150
+ userId Int
2151
+ rating Int
2152
+ content String?
2153
+ createdAt DateTime @default(now())
2154
+
2155
+ tutor User @relation("TutorReviewTarget", fields: [tutorId], references: [id], onDelete: Cascade)
2156
+ user User @relation("TutorReviewAuthor", fields: [userId], references: [id], onDelete: Cascade)
2157
+
2158
+ @@index([tutorId, createdAt])
2159
+ @@index([userId])
2160
+ }
2161
+
2162
+ model Classroom {
2163
+ id String @id @default(cuid())
2164
+ ownerId Int
2165
+ courseId String?
2166
+ subjectId String?
2167
+ boardId String?
2168
+ name String
2169
+ description String?
2170
+ joinCode String? @unique
2171
+ status PublicationStatus @default(DRAFT)
2172
+ color String?
2173
+ metadata Json?
2174
+ createdAt DateTime @default(now())
2175
+ updatedAt DateTime @updatedAt
2176
+
2177
+ owner User @relation("ClassroomOwner", fields: [ownerId], references: [id], onDelete: Cascade)
2178
+ course Course? @relation(fields: [courseId], references: [id], onDelete: SetNull)
2179
+ subject Subject? @relation(fields: [subjectId], references: [id], onDelete: SetNull)
2180
+ board AcademicBoard? @relation(fields: [boardId], references: [id], onDelete: SetNull)
2181
+ enrollments ClassroomEnrollment[]
2182
+ activities ClassroomActivity[]
2183
+ assignments Assignment[]
2184
+ polls ClassroomPoll[]
2185
+
2186
+ @@index([ownerId])
2187
+ @@index([courseId])
2188
+ @@index([subjectId])
2189
+ }
2190
+
2191
+ model ClassroomEnrollment {
2192
+ id String @id @default(cuid())
2193
+ classroomId String
2194
+ userId Int
2195
+ role ClassroomRole @default(STUDENT)
2196
+ status EnrollmentStatus @default(ACTIVE)
2197
+ progress Int @default(0)
2198
+ attendance Int @default(0)
2199
+ joinedAt DateTime @default(now())
2200
+
2201
+ classroom Classroom @relation(fields: [classroomId], references: [id], onDelete: Cascade)
2202
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
2203
+
2204
+ @@unique([classroomId, userId])
2205
+ @@index([userId, status])
2206
+ }
2207
+
2208
+ model ClassroomActivity {
2209
+ id String @id @default(cuid())
2210
+ classroomId String
2211
+ type String
2212
+ title String
2213
+ description String?
2214
+ metadata Json?
2215
+ createdAt DateTime @default(now())
2216
+
2217
+ classroom Classroom @relation(fields: [classroomId], references: [id], onDelete: Cascade)
2218
+
2219
+ @@index([classroomId, createdAt])
2220
+ }
2221
+
2222
+ model ClassroomPoll {
2223
+ id String @id @default(cuid())
2224
+ classroomId String
2225
+ createdById Int?
2226
+ question String
2227
+ isAnonymous Boolean @default(false)
2228
+ status PollStatus @default(DRAFT)
2229
+ startsAt DateTime?
2230
+ endsAt DateTime?
2231
+ createdAt DateTime @default(now())
2232
+ updatedAt DateTime @updatedAt
2233
+
2234
+ classroom Classroom @relation(fields: [classroomId], references: [id], onDelete: Cascade)
2235
+ createdBy User? @relation("ClassroomPollCreator", fields: [createdById], references: [id], onDelete: SetNull)
2236
+ options ClassroomPollOption[]
2237
+ responses ClassroomPollResponse[]
2238
+
2239
+ @@index([classroomId, status])
2240
+ @@index([createdById])
2241
+ }
2242
+
2243
+ model ClassroomPollOption {
2244
+ id String @id @default(cuid())
2245
+ pollId String
2246
+ text String
2247
+ sortOrder Int @default(0)
2248
+ votes Int @default(0)
2249
+
2250
+ poll ClassroomPoll @relation(fields: [pollId], references: [id], onDelete: Cascade)
2251
+ responses ClassroomPollResponse[]
2252
+
2253
+ @@unique([pollId, sortOrder])
2254
+ @@index([pollId])
2255
+ }
2256
+
2257
+ model ClassroomPollResponse {
2258
+ id String @id @default(cuid())
2259
+ pollId String
2260
+ optionId String
2261
+ userId Int
2262
+ respondedAt DateTime @default(now())
2263
+
2264
+ poll ClassroomPoll @relation(fields: [pollId], references: [id], onDelete: Cascade)
2265
+ option ClassroomPollOption @relation(fields: [optionId], references: [id], onDelete: Cascade)
2266
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
2267
+
2268
+ @@unique([pollId, userId])
2269
+ @@index([optionId])
2270
+ @@index([userId])
2271
+ }
2272
+
2273
+ model Assignment {
2274
+ id String @id @default(cuid())
2275
+ authorId Int
2276
+ classroomId String?
2277
+ courseId String?
2278
+ lessonId String?
2279
+ title String
2280
+ description String?
2281
+ status AssignmentStatus @default(DRAFT)
2282
+ dueAt DateTime?
2283
+ maxPoints Int?
2284
+ attachments Json?
2285
+ rubric Json?
2286
+ metadata Json?
2287
+ createdAt DateTime @default(now())
2288
+ updatedAt DateTime @updatedAt
2289
+
2290
+ author User @relation("AssignmentAuthor", fields: [authorId], references: [id], onDelete: Cascade)
2291
+ classroom Classroom? @relation(fields: [classroomId], references: [id], onDelete: SetNull)
2292
+ course Course? @relation(fields: [courseId], references: [id], onDelete: SetNull)
2293
+ lesson Lesson? @relation(fields: [lessonId], references: [id], onDelete: SetNull)
2294
+ submissions AssignmentSubmission[]
2295
+
2296
+ @@index([authorId])
2297
+ @@index([classroomId])
2298
+ @@index([courseId])
2299
+ @@index([lessonId])
2300
+ @@index([status, dueAt])
2301
+ }
2302
+
2303
+ model AssignmentSubmission {
2304
+ id String @id @default(cuid())
2305
+ assignmentId String
2306
+ userId Int
2307
+ status SubmissionStatus @default(NOT_STARTED)
2308
+ content String?
2309
+ attachments Json?
2310
+ score Float?
2311
+ feedback String?
2312
+ submittedAt DateTime?
2313
+ gradedAt DateTime?
2314
+ createdAt DateTime @default(now())
2315
+ updatedAt DateTime @updatedAt
2316
+
2317
+ assignment Assignment @relation(fields: [assignmentId], references: [id], onDelete: Cascade)
2318
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
2319
+
2320
+ @@unique([assignmentId, userId])
2321
+ @@index([userId, status])
2322
+ }
2323
+
2324
+ model Conversation {
2325
+ id String @id @default(cuid())
2326
+ createdById Int?
2327
+ type ConversationType @default(DIRECT)
2328
+ title String?
2329
+ lastMessageAt DateTime?
2330
+ createdAt DateTime @default(now())
2331
+ updatedAt DateTime @updatedAt
2332
+
2333
+ createdBy User? @relation("ConversationCreator", fields: [createdById], references: [id], onDelete: SetNull)
2334
+ participants ConversationParticipant[]
2335
+ messages Message[]
2336
+
2337
+ @@index([createdById])
2338
+ @@index([lastMessageAt])
2339
+ }
2340
+
2341
+ model ConversationParticipant {
2342
+ id String @id @default(cuid())
2343
+ conversationId String
2344
+ userId Int
2345
+ role ConversationParticipantRole @default(MEMBER)
2346
+ unreadCount Int @default(0)
2347
+ archivedAt DateTime?
2348
+ pinnedAt DateTime?
2349
+ joinedAt DateTime @default(now())
2350
+
2351
+ conversation Conversation @relation(fields: [conversationId], references: [id], onDelete: Cascade)
2352
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
2353
+
2354
+ @@unique([conversationId, userId])
2355
+ @@index([userId, unreadCount])
2356
+ }
2357
+
2358
+ model Message {
2359
+ id String @id @default(cuid())
2360
+ conversationId String
2361
+ senderUserId Int?
2362
+ senderRole MessageSenderRole @default(USER)
2363
+ content String
2364
+ metadata Json?
2365
+ createdAt DateTime @default(now())
2366
+ editedAt DateTime?
2367
+ readAt DateTime?
2368
+
2369
+ conversation Conversation @relation(fields: [conversationId], references: [id], onDelete: Cascade)
2370
+ sender User? @relation("UserMessages", fields: [senderUserId], references: [id], onDelete: SetNull)
2371
+ attachments MessageAttachment[]
2372
+
2373
+ @@index([conversationId, createdAt])
2374
+ @@index([senderUserId])
2375
+ }
2376
+
2377
+ model MessageAttachment {
2378
+ id String @id @default(cuid())
2379
+ messageId String
2380
+ name String
2381
+ url String
2382
+ mimeType String?
2383
+ sizeBytes Int?
2384
+
2385
+ message Message @relation(fields: [messageId], references: [id], onDelete: Cascade)
2386
+
2387
+ @@index([messageId])
2388
+ }
2389
+
2390
+ model Announcement {
2391
+ id String @id @default(cuid())
2392
+ authorId Int
2393
+ title String
2394
+ content String
2395
+ recipients String?
2396
+ status AnnouncementStatus @default(DRAFT)
2397
+ scheduledFor DateTime?
2398
+ sentAt DateTime?
2399
+ createdAt DateTime @default(now())
2400
+ updatedAt DateTime @updatedAt
2401
+
2402
+ author User @relation("AnnouncementAuthor", fields: [authorId], references: [id], onDelete: Cascade)
2403
+ audience AnnouncementRecipient[]
2404
+
2405
+ @@index([authorId])
2406
+ @@index([status, scheduledFor])
2407
+ }
2408
+
2409
+ model AnnouncementRecipient {
2410
+ id String @id @default(cuid())
2411
+ announcementId String
2412
+ userId Int
2413
+ deliveredAt DateTime?
2414
+ readAt DateTime?
2415
+
2416
+ announcement Announcement @relation(fields: [announcementId], references: [id], onDelete: Cascade)
2417
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
2418
+
2419
+ @@unique([announcementId, userId])
2420
+ }
2421
+
2422
+ model NotificationPreference {
2423
+ id String @id @default(cuid())
2424
+ userId Int @unique
2425
+ inAppEnabled Boolean @default(true)
2426
+ emailEnabled Boolean @default(true)
2427
+ pushEnabled Boolean @default(false)
2428
+ quietHoursEnabled Boolean @default(false)
2429
+ quietHoursStart String?
2430
+ quietHoursEnd String?
2431
+ digestMode NotificationDigestMode @default(NONE)
2432
+ notifyGameInvites Boolean @default(true)
2433
+ notifyQuizUpdates Boolean @default(true)
2434
+ notifyAchievements Boolean @default(true)
2435
+ notifyFollowers Boolean @default(true)
2436
+ notifyComments Boolean @default(true)
2437
+ notifyMarketing Boolean @default(false)
2438
+ emailNewBooking Boolean @default(true)
2439
+ emailCancellation Boolean @default(true)
2440
+ emailMessages Boolean @default(true)
2441
+ emailWeeklyReport Boolean @default(true)
2442
+ pushNewBooking Boolean @default(true)
2443
+ pushMessages Boolean @default(true)
2444
+ pushReminders Boolean @default(true)
2445
+ smsReminders Boolean @default(false)
2446
+ allowDirectMessages Boolean @default(true)
2447
+ updatedAt DateTime @updatedAt
2448
+
2449
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
2450
+ }
2451
+
2452
+ model Notification {
2453
+ id String @id @default(cuid())
2454
+ userId Int
2455
+ type NotificationType @default(GENERAL)
2456
+ channel NotificationChannel @default(IN_APP)
2457
+ title String?
2458
+ content String
2459
+ actionUrl String?
2460
+ isRead Boolean @default(false)
2461
+ readAt DateTime?
2462
+ metadata Json?
2463
+ createdAt DateTime @default(now())
2464
+
2465
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
2466
+
2467
+ @@index([userId, isRead, createdAt])
2468
+ }
2469
+
2470
+ model SupportTicket {
2471
+ id String @id @default(cuid())
2472
+ requesterId Int
2473
+ assigneeId Int?
2474
+ conversationId String? @unique
2475
+ subject String
2476
+ description String
2477
+ category String
2478
+ status SupportStatus @default(OPEN)
2479
+ priority SupportPriority @default(MEDIUM)
2480
+ metadata Json?
2481
+ createdAt DateTime @default(now())
2482
+ updatedAt DateTime @updatedAt
2483
+ resolvedAt DateTime?
2484
+
2485
+ requester User @relation("SupportTicketRequester", fields: [requesterId], references: [id], onDelete: Cascade)
2486
+ assignee User? @relation("SupportTicketAssignee", fields: [assigneeId], references: [id], onDelete: SetNull)
2487
+ conversation SupportConversation? @relation(fields: [conversationId], references: [id], onDelete: SetNull)
2488
+
2489
+ @@index([requesterId])
2490
+ @@index([assigneeId])
2491
+ @@index([status, priority])
2492
+ }
2493
+
2494
+ model SupportConversation {
2495
+ id String @id @default(cuid())
2496
+ userId Int
2497
+ agentId Int?
2498
+ subject String?
2499
+ lastMessage String?
2500
+ lastMessageAt DateTime?
2501
+ unreadCount Int @default(0)
2502
+ status SupportStatus @default(OPEN)
2503
+ createdAt DateTime @default(now())
2504
+ updatedAt DateTime @updatedAt
2505
+
2506
+ user User @relation("SupportConversationRequester", fields: [userId], references: [id], onDelete: Cascade)
2507
+ agent User? @relation("SupportConversationAgent", fields: [agentId], references: [id], onDelete: SetNull)
2508
+ ticket SupportTicket?
2509
+ messages SupportMessage[]
2510
+
2511
+ @@index([userId, status])
2512
+ @@index([agentId])
2513
+ }
2514
+
2515
+ model SupportMessage {
2516
+ id String @id @default(cuid())
2517
+ conversationId String
2518
+ authorId Int?
2519
+ senderName String
2520
+ senderRole MessageSenderRole @default(USER)
2521
+ senderAvatar String?
2522
+ text String
2523
+ isRead Boolean @default(false)
2524
+ createdAt DateTime @default(now())
2525
+
2526
+ conversation SupportConversation @relation(fields: [conversationId], references: [id], onDelete: Cascade)
2527
+ author User? @relation("SupportMessageAuthor", fields: [authorId], references: [id], onDelete: SetNull)
2528
+ attachments SupportAttachment[]
2529
+
2530
+ @@index([conversationId, createdAt])
2531
+ @@index([authorId])
2532
+ }
2533
+
2534
+ model SupportAttachment {
2535
+ id String @id @default(cuid())
2536
+ messageId String
2537
+ name String
2538
+ url String
2539
+ type String
2540
+ size Int
2541
+
2542
+ message SupportMessage @relation(fields: [messageId], references: [id], onDelete: Cascade)
2543
+
2544
+ @@index([messageId])
2545
+ }
2546
+
2547
+ model Community {
2548
+ id String @id @default(cuid())
2549
+ ownerId Int
2550
+ subjectId String?
2551
+ name String
2552
+ slug String? @unique
2553
+ description String?
2554
+ visibility CommunityVisibility @default(PUBLIC)
2555
+ createdAt DateTime @default(now())
2556
+ updatedAt DateTime @updatedAt
2557
+
2558
+ owner User @relation("CommunityOwner", fields: [ownerId], references: [id], onDelete: Cascade)
2559
+ subject Subject? @relation(fields: [subjectId], references: [id], onDelete: SetNull)
2560
+ memberships CommunityMembership[]
2561
+ posts CommunityPost[]
2562
+ events CommunityEvent[]
2563
+ resources CommunityResource[]
2564
+
2565
+ @@index([ownerId])
2566
+ @@index([subjectId])
2567
+ }
2568
+
2569
+ model CommunityMembership {
2570
+ id String @id @default(cuid())
2571
+ communityId String
2572
+ userId Int
2573
+ joinedAt DateTime @default(now())
2574
+
2575
+ community Community @relation(fields: [communityId], references: [id], onDelete: Cascade)
2576
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
2577
+
2578
+ @@unique([communityId, userId])
2579
+ }
2580
+
2581
+ model CommunityPost {
2582
+ id String @id @default(cuid())
2583
+ communityId String
2584
+ authorId Int
2585
+ content String
2586
+ images Json?
2587
+ codeSnippet String?
2588
+ tags Json?
2589
+ likes Int @default(0)
2590
+ isPinned Boolean @default(false)
2591
+ createdAt DateTime @default(now())
2592
+ updatedAt DateTime @updatedAt
2593
+
2594
+ community Community @relation(fields: [communityId], references: [id], onDelete: Cascade)
2595
+ author User @relation(fields: [authorId], references: [id], onDelete: Cascade)
2596
+ replies CommunityReply[]
2597
+
2598
+ @@index([communityId, createdAt])
2599
+ @@index([authorId])
2600
+ }
2601
+
2602
+ model CommunityReply {
2603
+ id String @id @default(cuid())
2604
+ postId String
2605
+ authorId Int
2606
+ content String
2607
+ likes Int @default(0)
2608
+ createdAt DateTime @default(now())
2609
+
2610
+ post CommunityPost @relation(fields: [postId], references: [id], onDelete: Cascade)
2611
+ author User @relation(fields: [authorId], references: [id], onDelete: Cascade)
2612
+
2613
+ @@index([postId, createdAt])
2614
+ }
2615
+
2616
+ model CommunityEvent {
2617
+ id String @id @default(cuid())
2618
+ communityId String
2619
+ hostId Int?
2620
+ title String
2621
+ description String?
2622
+ startsAt DateTime
2623
+ endsAt DateTime?
2624
+ location String?
2625
+ metadata Json?
2626
+ createdAt DateTime @default(now())
2627
+
2628
+ community Community @relation(fields: [communityId], references: [id], onDelete: Cascade)
2629
+ host User? @relation("CommunityEventHost", fields: [hostId], references: [id], onDelete: SetNull)
2630
+
2631
+ @@index([communityId, startsAt])
2632
+ }
2633
+
2634
+ model CommunityResource {
2635
+ id String @id @default(cuid())
2636
+ communityId String
2637
+ title String
2638
+ description String?
2639
+ type CommunityResourceType
2640
+ url String?
2641
+ metadata Json?
2642
+ createdAt DateTime @default(now())
2643
+
2644
+ community Community @relation(fields: [communityId], references: [id], onDelete: Cascade)
2645
+
2646
+ @@index([communityId, type])
2647
+ }
2648
+
2649
+ model UserActivityEvent {
2650
+ id String @id @default(cuid())
2651
+ userId Int
2652
+ subjectId String?
2653
+ type UserEventType
2654
+ title String
2655
+ subjectName String?
2656
+ boardCode String?
2657
+ topic String?
2658
+ occurredAt DateTime
2659
+ durationMinutes Int @default(0)
2660
+ status UserEventStatus @default(COMPLETED)
2661
+ placement PlacementTier @default(PARTICIPATED)
2662
+ rank Int?
2663
+ totalParticipants Int?
2664
+ score Int?
2665
+ maxScore Int?
2666
+ accuracy Float?
2667
+ correctAnswers Int?
2668
+ totalQuestions Int?
2669
+ xpEarned Int @default(0)
2670
+ streakBonus Int?
2671
+ achievements Json?
2672
+ hostName String?
2673
+ hostAvatar String?
2674
+ gameCode String?
2675
+ avgResponseTime Float?
2676
+ fastestAnswer Float?
2677
+ longestStreak Int?
2678
+ improvement Float?
2679
+ difficulty String?
2680
+ badges Json?
2681
+ metadata Json?
2682
+
2683
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
2684
+ subject Subject? @relation(fields: [subjectId], references: [id], onDelete: SetNull)
2685
+
2686
+ @@index([userId, occurredAt])
2687
+ @@index([subjectId])
2688
+ }
2689
+
2690
+ model Feedback {
2691
+ id String @id @default(cuid())
2692
+ userId Int?
2693
+ type FeedbackType
2694
+ email String?
2695
+ page String?
2696
+ message String
2697
+ rating Int?
2698
+ status FeedbackStatus @default(NEW)
2699
+ priority SupportPriority @default(MEDIUM)
2700
+ createdAt DateTime @default(now())
2701
+ updatedAt DateTime @updatedAt
2702
+
2703
+ user User? @relation(fields: [userId], references: [id], onDelete: SetNull)
2704
+
2705
+ @@index([userId])
2706
+ @@index([type, status])
2707
+ }
2708
+
2709
+ // Data used in admin user-management, moderation, security, monitoring, and data tools.
2710
+ model FraudAlert {
2711
+ id String @id @default(cuid())
2712
+ userId Int?
2713
+ type String
2714
+ riskScore Int
2715
+ details String
2716
+ status String
2717
+ detectedAt DateTime @default(now())
2718
+ resolvedAt DateTime?
2719
+ resolutionNotes String?
2720
+
2721
+ @@index([userId, status])
2722
+ @@index([riskScore, detectedAt])
2723
+ }
2724
+
2725
+ model BehavioralRule {
2726
+ id String @id @default(cuid())
2727
+ name String
2728
+ description String
2729
+ severity String
2730
+ autoAction String
2731
+ enabled Boolean @default(true)
2732
+ createdAt DateTime @default(now())
2733
+ updatedAt DateTime @updatedAt
2734
+ }
2735
+
2736
+ model ModerationCase {
2737
+ id String @id @default(cuid())
2738
+ contentType String
2739
+ contentId String?
2740
+ title String
2741
+ authorName String?
2742
+ authorUserId Int?
2743
+ reason String
2744
+ reportCount Int @default(0)
2745
+ status String
2746
+ priority String
2747
+ preview String?
2748
+ aiAction String?
2749
+ aiConfidence Int?
2750
+ aiReason String?
2751
+ openedAt DateTime @default(now())
2752
+ reviewedAt DateTime?
2753
+
2754
+ actions ModerationAction[]
2755
+
2756
+ @@index([status, priority])
2757
+ @@index([authorUserId])
2758
+ }
2759
+
2760
+ model ModerationAction {
2761
+ id String @id @default(cuid())
2762
+ moderationCaseId String
2763
+ action String
2764
+ moderatorName String?
2765
+ moderatorId Int?
2766
+ reason String?
2767
+ aiAssisted Boolean @default(false)
2768
+ createdAt DateTime @default(now())
2769
+
2770
+ moderationCase ModerationCase @relation(fields: [moderationCaseId], references: [id], onDelete: Cascade)
2771
+
2772
+ @@index([moderationCaseId, createdAt])
2773
+ }
2774
+
2775
+ model ModerationRule {
2776
+ id String @id @default(cuid())
2777
+ name String
2778
+ category String?
2779
+ status String
2780
+ sensitivity Int?
2781
+ falsePositiveRate Float?
2782
+ matches Int @default(0)
2783
+ lastTriggeredAt DateTime?
2784
+ createdAt DateTime @default(now())
2785
+ updatedAt DateTime @updatedAt
2786
+ }
2787
+
2788
+ model IpBan {
2789
+ id String @id @default(cuid())
2790
+ ipAddress String @unique
2791
+ reason String
2792
+ bannedBy String?
2793
+ attempts Int @default(0)
2794
+ bannedAt DateTime @default(now())
2795
+ expiresAt DateTime?
2796
+ isPermanent Boolean @default(false)
2797
+ }
2798
+
2799
+ model SecurityScan {
2800
+ id String @id @default(cuid())
2801
+ scanType String
2802
+ status String
2803
+ issuesFound Int @default(0)
2804
+ startedAt DateTime?
2805
+ completedAt DateTime?
2806
+ durationSeconds Int?
2807
+ metadata Json?
2808
+ createdAt DateTime @default(now())
2809
+
2810
+ @@index([status, createdAt])
2811
+ }
2812
+
2813
+ model SecurityTwoFactorMethod {
2814
+ id String @id @default(cuid())
2815
+ userId Int
2816
+ method String
2817
+ enabledAt DateTime @default(now())
2818
+ lastUsedAt DateTime?
2819
+ active Boolean @default(true)
2820
+
2821
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
2822
+
2823
+ @@index([userId, active])
2824
+ }
2825
+
2826
+ model ApiEndpointStat {
2827
+ id String @id @default(cuid())
2828
+ endpoint String
2829
+ method String
2830
+ status String
2831
+ latencyMs Int?
2832
+ requests Int @default(0)
2833
+ errors Int @default(0)
2834
+ capturedAt DateTime @default(now())
2835
+
2836
+ @@index([endpoint, method, capturedAt])
2837
+ }
2838
+
2839
+ model ApiBillingTier {
2840
+ id String @id @default(cuid())
2841
+ name String @unique
2842
+ monthlyCallLimit Int?
2843
+ requestsPerMinute Int?
2844
+ dailyRequestLimit Int?
2845
+ burstLimit Int?
2846
+ price Float @default(0)
2847
+ active Boolean @default(true)
2848
+ isDefault Boolean @default(false)
2849
+ createdAt DateTime @default(now())
2850
+ updatedAt DateTime @updatedAt
2851
+ }
2852
+
2853
+ model ApplicationErrorLog {
2854
+ id String @id @default(cuid())
2855
+ errorCode String?
2856
+ errorType String
2857
+ message String
2858
+ filePath String?
2859
+ stack String?
2860
+ status String
2861
+ count Int @default(1)
2862
+ firstSeenAt DateTime @default(now())
2863
+ lastSeenAt DateTime?
2864
+
2865
+ @@index([status, lastSeenAt])
2866
+ }
2867
+
2868
+ model SystemLogEntry {
2869
+ id String @id @default(cuid())
2870
+ level String
2871
+ message String
2872
+ source String?
2873
+ endpoint String?
2874
+ count Int?
2875
+ createdAt DateTime @default(now())
2876
+
2877
+ @@index([level, createdAt])
2878
+ }
2879
+
2880
+ model SystemIncident {
2881
+ id String @id @default(cuid())
2882
+ title String
2883
+ status String
2884
+ severity String
2885
+ startTime DateTime
2886
+ resolvedTime DateTime?
2887
+ affectedServices Json?
2888
+ assignee String?
2889
+ createdAt DateTime @default(now())
2890
+ updatedAt DateTime @updatedAt
2891
+ }
2892
+
2893
+ model SystemAlertRule {
2894
+ id String @id @default(cuid())
2895
+ name String
2896
+ channel String
2897
+ threshold String
2898
+ recipients String?
2899
+ escalation String?
2900
+ enabled Boolean @default(true)
2901
+ createdAt DateTime @default(now())
2902
+ updatedAt DateTime @updatedAt
2903
+ }
2904
+
2905
+ model IncidentPlaybook {
2906
+ id String @id @default(cuid())
2907
+ name String
2908
+ description String?
2909
+ stepsCount Int @default(0)
2910
+ severity String?
2911
+ lastUsedAt DateTime?
2912
+ createdAt DateTime @default(now())
2913
+ updatedAt DateTime @updatedAt
2914
+ }
2915
+
2916
+ model DeploymentRelease {
2917
+ id String @id @default(cuid())
2918
+ version String @unique
2919
+ environment String?
2920
+ deployedAt DateTime
2921
+ status String
2922
+ healthy Boolean @default(true)
2923
+ notes String?
2924
+ createdAt DateTime @default(now())
2925
+ }
2926
+
2927
+ model RollbackEvent {
2928
+ id String @id @default(cuid())
2929
+ deploymentVersion String
2930
+ rolledBackTo String
2931
+ reason String
2932
+ initiatedBy String?
2933
+ status String
2934
+ durationSeconds Int?
2935
+ createdAt DateTime @default(now())
2936
+
2937
+ @@index([deploymentVersion, createdAt])
2938
+ }
2939
+
2940
+ model GdprRequest {
2941
+ id String @id @default(cuid())
2942
+ userId Int?
2943
+ email String
2944
+ requestType String
2945
+ status String
2946
+ requestedAt DateTime @default(now())
2947
+ completedAt DateTime?
2948
+
2949
+ @@index([userId, status])
2950
+ @@index([email])
2951
+ }
2952
+
2953
+ model DataRetentionPolicy {
2954
+ id String @id @default(cuid())
2955
+ name String
2956
+ retention String
2957
+ description String?
2958
+ status String
2959
+ lastRunAt DateTime?
2960
+ nextRunAt DateTime?
2961
+ createdAt DateTime @default(now())
2962
+ updatedAt DateTime @updatedAt
2963
+ }
2964
+
2965
+ model DataExportConfig {
2966
+ id String @id @default(cuid())
2967
+ name String
2968
+ format String
2969
+ tables Json
2970
+ schedule String?
2971
+ status String
2972
+ lastExportAt DateTime?
2973
+ createdAt DateTime @default(now())
2974
+ updatedAt DateTime @updatedAt
2975
+
2976
+ runs DataExportRun[]
2977
+ }
2978
+
2979
+ model DataExportRun {
2980
+ id String @id @default(cuid())
2981
+ configId String?
2982
+ exportName String
2983
+ format String
2984
+ rowCount Int @default(0)
2985
+ sizeBytes Int?
2986
+ exportedBy String?
2987
+ status String
2988
+ executedAt DateTime @default(now())
2989
+
2990
+ config DataExportConfig? @relation(fields: [configId], references: [id], onDelete: SetNull)
2991
+
2992
+ @@index([configId, executedAt])
2993
+ }
2994
+
2995
+ model DataTransformation {
2996
+ id String @id @default(cuid())
2997
+ name String
2998
+ description String?
2999
+ enabled Boolean @default(true)
3000
+ createdAt DateTime @default(now())
3001
+ updatedAt DateTime @updatedAt
3002
+ }
3003
+
3004
+ model PolicySimulation {
3005
+ id String @id @default(cuid())
3006
+ policyName String
3007
+ recordsAffected Int @default(0)
3008
+ dataSizeBytes Int?
3009
+ estimatedDurationSeconds Int?
3010
+ createdAt DateTime @default(now())
3011
+ }
3012
+
3013
+ model NotificationTemplate {
3014
+ id String @id @default(cuid())
3015
+ name String
3016
+ channelType String
3017
+ category String
3018
+ subject String?
3019
+ body String?
3020
+ uses Int @default(0)
3021
+ active Boolean @default(true)
3022
+ createdAt DateTime @default(now())
3023
+ updatedAt DateTime @updatedAt
3024
+ }
3025
+
3026
+ model NotificationCampaign {
3027
+ id String @id @default(cuid())
3028
+ campaignType String
3029
+ title String
3030
+ message String
3031
+ status String
3032
+ recipientsCount Int @default(0)
3033
+ readCount Int @default(0)
3034
+ channels Json?
3035
+ scheduledAt DateTime?
3036
+ sentAt DateTime?
3037
+ createdAt DateTime @default(now())
3038
+ updatedAt DateTime @updatedAt
3039
+ }
3040
+
3041
+ model ApiKeyRotationEvent {
3042
+ id String @id @default(cuid())
3043
+ apiKeyId String?
3044
+ keyName String
3045
+ previousKey String?
3046
+ rotatedBy String?
3047
+ reason String?
3048
+ rotatedAt DateTime @default(now())
3049
+
3050
+ @@index([apiKeyId, rotatedAt])
3051
+ }
3052
+
3053
+ model IntegrationHealthLog {
3054
+ id String @id @default(cuid())
3055
+ integrationId String?
3056
+ name String
3057
+ status String
3058
+ details String?
3059
+ resolved Boolean @default(false)
3060
+ recordedAt DateTime @default(now())
3061
+
3062
+ @@index([integrationId, recordedAt])
3063
+ }
3064
+
3065
+ model SandboxEnvironment {
3066
+ id String @id @default(cuid())
3067
+ name String
3068
+ status SandboxStatus @default(STOPPED)
3069
+ baseEnvironment DeploymentEnvironment?
3070
+ url String?
3071
+ ownerName String?
3072
+ createdById Int?
3073
+ seeded Boolean @default(false)
3074
+ dataSizeBytes Int?
3075
+ autoDestroyAt DateTime?
3076
+ lastAccessAt DateTime?
3077
+ metadata Json?
3078
+ createdAt DateTime @default(now())
3079
+ updatedAt DateTime @updatedAt
3080
+
3081
+ createdBy User? @relation("SandboxCreator", fields: [createdById], references: [id], onDelete: SetNull)
3082
+ promotions SandboxPromotion[]
3083
+ seedRuns DataSeedRun[]
3084
+
3085
+ @@index([status, createdAt])
3086
+ @@index([createdById])
3087
+ }
3088
+
3089
+ model SandboxPromotion {
3090
+ id String @id @default(cuid())
3091
+ sandboxId String
3092
+ promotedTo DeploymentEnvironment
3093
+ promotedById Int?
3094
+ promotedByName String?
3095
+ status SandboxPromotionStatus @default(PENDING)
3096
+ filesChanged Int @default(0)
3097
+ notes String?
3098
+ promotedAt DateTime @default(now())
3099
+
3100
+ sandbox SandboxEnvironment @relation(fields: [sandboxId], references: [id], onDelete: Cascade)
3101
+ promotedBy User? @relation("SandboxPromotionActor", fields: [promotedById], references: [id], onDelete: SetNull)
3102
+
3103
+ @@index([sandboxId, promotedAt])
3104
+ @@index([promotedById])
3105
+ }
3106
+
3107
+ model DataSeed {
3108
+ id String @id @default(cuid())
3109
+ name String
3110
+ description String?
3111
+ category String?
3112
+ sizeBytes Int?
3113
+ recordCount Int @default(0)
3114
+ metadata Json?
3115
+ lastRunAt DateTime?
3116
+ active Boolean @default(true)
3117
+ createdAt DateTime @default(now())
3118
+ updatedAt DateTime @updatedAt
3119
+
3120
+ runs DataSeedRun[]
3121
+
3122
+ @@index([category, active])
3123
+ }
3124
+
3125
+ model DataSeedRun {
3126
+ id String @id @default(cuid())
3127
+ seedId String
3128
+ sandboxId String?
3129
+ triggeredById Int?
3130
+ status String
3131
+ recordsCreated Int @default(0)
3132
+ executedAt DateTime @default(now())
3133
+ completedAt DateTime?
3134
+
3135
+ seed DataSeed @relation(fields: [seedId], references: [id], onDelete: Cascade)
3136
+ sandbox SandboxEnvironment? @relation(fields: [sandboxId], references: [id], onDelete: SetNull)
3137
+ triggeredBy User? @relation("DataSeedRunActor", fields: [triggeredById], references: [id], onDelete: SetNull)
3138
+
3139
+ @@index([seedId, executedAt])
3140
+ @@index([sandboxId])
3141
+ @@index([triggeredById])
3142
+ }
3143
+
3144
+ model FeatureFlag {
3145
+ id String @id @default(cuid())
3146
+ key String @unique
3147
+ name String
3148
+ description String?
3149
+ status FeatureFlagStatus @default(DISABLED)
3150
+ environment DeploymentEnvironment @default(DEVELOPMENT)
3151
+ rollout Int @default(0)
3152
+ targeting String?
3153
+ lastModifiedBy String?
3154
+ createdAt DateTime @default(now())
3155
+ updatedAt DateTime @updatedAt
3156
+
3157
+ rules FeatureFlagRule[]
3158
+ experiments FeatureExperiment[]
3159
+
3160
+ @@index([environment, status])
3161
+ }
3162
+
3163
+ model FeatureFlagRule {
3164
+ id String @id @default(cuid())
3165
+ featureFlagId String
3166
+ name String
3167
+ condition String
3168
+ action String
3169
+ enabled Boolean @default(true)
3170
+ metadata Json?
3171
+ createdAt DateTime @default(now())
3172
+
3173
+ featureFlag FeatureFlag @relation(fields: [featureFlagId], references: [id], onDelete: Cascade)
3174
+
3175
+ @@index([featureFlagId, enabled])
3176
+ }
3177
+
3178
+ model FeatureExperiment {
3179
+ id String @id @default(cuid())
3180
+ featureFlagId String?
3181
+ name String
3182
+ hypothesis String?
3183
+ status ExperimentStatus @default(DRAFT)
3184
+ startDate DateTime?
3185
+ endDate DateTime?
3186
+ significance Float?
3187
+ winner String?
3188
+ variants Json?
3189
+ metadata Json?
3190
+ createdAt DateTime @default(now())
3191
+
3192
+ featureFlag FeatureFlag? @relation(fields: [featureFlagId], references: [id], onDelete: SetNull)
3193
+
3194
+ @@index([featureFlagId])
3195
+ @@index([status, startDate])
3196
+ }
3197
+
3198
+ model SpacedRepetitionConfig {
3199
+ id String @id @default("default")
3200
+ masteryThreshold Int @default(90)
3201
+ initialReviewHours Int @default(24)
3202
+ difficultyWeighting Float @default(1.5)
3203
+ dailyReviewLimit Int @default(50)
3204
+ adaptiveDifficulty Boolean @default(true)
3205
+ updatedAt DateTime @updatedAt
3206
+ }
3207
+
3208
+ model RewardRule {
3209
+ id String @id @default(cuid())
3210
+ scope RewardScope
3211
+ name String
3212
+ description String?
3213
+ status RewardStatus @default(DRAFT)
3214
+ rewardType String?
3215
+ conditions Json?
3216
+ rewards Json?
3217
+ startsAt DateTime?
3218
+ endsAt DateTime?
3219
+ createdById Int?
3220
+ createdAt DateTime @default(now())
3221
+ updatedAt DateTime @updatedAt
3222
+
3223
+ createdBy User? @relation("RewardRuleCreator", fields: [createdById], references: [id], onDelete: SetNull)
3224
+
3225
+ @@index([scope, status])
3226
+ @@index([createdById])
3227
+ }
3228
+
3229
+ model Role {
3230
+ id String @id @default(cuid())
3231
+ name String @unique
3232
+ description String?
3233
+ color String?
3234
+ createdAt DateTime @default(now())
3235
+ updatedAt DateTime @updatedAt
3236
+
3237
+ permissions RolePermission[]
3238
+ assignments UserRoleAssignment[]
3239
+ }
3240
+
3241
+ model Permission {
3242
+ id String @id @default(cuid())
3243
+ key String @unique
3244
+ groupName String
3245
+ name String
3246
+ description String?
3247
+
3248
+ roles RolePermission[]
3249
+
3250
+ @@index([groupName])
3251
+ }
3252
+
3253
+ model RolePermission {
3254
+ id String @id @default(cuid())
3255
+ roleId String
3256
+ permissionId String
3257
+
3258
+ role Role @relation(fields: [roleId], references: [id], onDelete: Cascade)
3259
+ permission Permission @relation(fields: [permissionId], references: [id], onDelete: Cascade)
3260
+
3261
+ @@unique([roleId, permissionId])
3262
+ }
3263
+
3264
+ model UserRoleAssignment {
3265
+ id String @id @default(cuid())
3266
+ userId Int
3267
+ roleId String
3268
+ createdAt DateTime @default(now())
3269
+
3270
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
3271
+ role Role @relation(fields: [roleId], references: [id], onDelete: Cascade)
3272
+
3273
+ @@unique([userId, roleId])
3274
+ }
3275
+
3276
+ model AuditLog {
3277
+ id String @id @default(cuid())
3278
+ actorId Int?
3279
+ action String
3280
+ entityType String
3281
+ entityId String?
3282
+ details String?
3283
+ severity AuditSeverity @default(INFO)
3284
+ ipAddress String?
3285
+ createdAt DateTime @default(now())
3286
+
3287
+ actor User? @relation(fields: [actorId], references: [id], onDelete: SetNull)
3288
+
3289
+ @@index([actorId])
3290
+ @@index([entityType, createdAt])
3291
+ }
3292
+
3293
+ model PromotionCode {
3294
+ id String @id @default(cuid())
3295
+ code String @unique
3296
+ createdById Int?
3297
+ applicablePlanId String?
3298
+ promotionType PromotionType
3299
+ value Float
3300
+ maxUses Int?
3301
+ usageCount Int @default(0)
3302
+ active Boolean @default(true)
3303
+ startsAt DateTime?
3304
+ expiresAt DateTime?
3305
+ metadata Json?
3306
+ createdAt DateTime @default(now())
3307
+ updatedAt DateTime @updatedAt
3308
+
3309
+ createdBy User? @relation("PromotionCodeCreator", fields: [createdById], references: [id], onDelete: SetNull)
3310
+ applicablePlan SubscriptionPlan? @relation(fields: [applicablePlanId], references: [id], onDelete: SetNull)
3311
+ subscriptions UserSubscription[]
3312
+
3313
+ @@index([createdById])
3314
+ @@index([applicablePlanId])
3315
+ @@index([active, expiresAt])
3316
+ }
3317
+
3318
+ model SubscriptionPlan {
3319
+ id String @id @default(cuid())
3320
+ name String
3321
+ description String?
3322
+ price Float
3323
+ interval BillingInterval
3324
+ active Boolean @default(true)
3325
+ features Json?
3326
+ createdAt DateTime @default(now())
3327
+ updatedAt DateTime @updatedAt
3328
+
3329
+ subscriptions UserSubscription[]
3330
+ promotionCodes PromotionCode[]
3331
+ }
3332
+
3333
+ model UserSubscription {
3334
+ id String @id @default(cuid())
3335
+ userId Int
3336
+ planId String
3337
+ promotionCodeId String?
3338
+ status SubscriptionStatus @default(TRIALING)
3339
+ startedAt DateTime @default(now())
3340
+ currentPeriodEnd DateTime?
3341
+ cancelledAt DateTime?
3342
+ metadata Json?
3343
+
3344
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
3345
+ plan SubscriptionPlan @relation(fields: [planId], references: [id], onDelete: Cascade)
3346
+ promotionCode PromotionCode? @relation(fields: [promotionCodeId], references: [id], onDelete: SetNull)
3347
+ paymentTransactions PaymentTransaction[]
3348
+
3349
+ @@index([userId, status])
3350
+ @@index([planId])
3351
+ @@index([promotionCodeId])
3352
+ }
3353
+
3354
+ model PaymentTransaction {
3355
+ id String @id @default(cuid())
3356
+ userId Int
3357
+ subscriptionId String?
3358
+ amount Float
3359
+ currency String @default("USD")
3360
+ status PaymentStatus @default(PENDING)
3361
+ provider String?
3362
+ reference String? @unique
3363
+ description String?
3364
+ createdAt DateTime @default(now())
3365
+ updatedAt DateTime @updatedAt
3366
+
3367
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
3368
+ subscription UserSubscription? @relation(fields: [subscriptionId], references: [id], onDelete: SetNull)
3369
+ refunds RefundRequest[]
3370
+
3371
+ @@index([userId, createdAt])
3372
+ @@index([subscriptionId])
3373
+ }
3374
+
3375
+ model RefundRequest {
3376
+ id String @id @default(cuid())
3377
+ requesterId Int
3378
+ transactionId String
3379
+ reason String
3380
+ status RefundStatus @default(REQUESTED)
3381
+ requestedAt DateTime @default(now())
3382
+ resolvedAt DateTime?
3383
+
3384
+ requester User @relation("RefundRequester", fields: [requesterId], references: [id], onDelete: Cascade)
3385
+ transaction PaymentTransaction @relation(fields: [transactionId], references: [id], onDelete: Cascade)
3386
+
3387
+ @@index([requesterId])
3388
+ @@index([transactionId])
3389
+ }
3390
+
3391
+ model ApiKey {
3392
+ id String @id @default(cuid())
3393
+ name String
3394
+ keyPrefix String @unique
3395
+ secretHash String
3396
+ status String
3397
+ createdById Int?
3398
+ lastUsedAt DateTime?
3399
+ expiresAt DateTime?
3400
+ createdAt DateTime @default(now())
3401
+
3402
+ @@index([createdById])
3403
+ }
3404
+
3405
+ model WebhookEndpoint {
3406
+ id String @id @default(cuid())
3407
+ name String
3408
+ url String
3409
+ secretHash String?
3410
+ events Json
3411
+ enabled Boolean @default(true)
3412
+ createdById Int?
3413
+ createdAt DateTime @default(now())
3414
+ updatedAt DateTime @updatedAt
3415
+
3416
+ deliveries WebhookDelivery[]
3417
+
3418
+ @@index([createdById])
3419
+ }
3420
+
3421
+ model WebhookDelivery {
3422
+ id String @id @default(cuid())
3423
+ endpointId String
3424
+ eventType String
3425
+ payload Json
3426
+ responseCode Int?
3427
+ deliveredAt DateTime?
3428
+ status String
3429
+ createdAt DateTime @default(now())
3430
+
3431
+ endpoint WebhookEndpoint @relation(fields: [endpointId], references: [id], onDelete: Cascade)
3432
+
3433
+ @@index([endpointId, createdAt])
3434
+ }
3435
+
3436
+ model Integration {
3437
+ id String @id @default(cuid())
3438
+ name String
3439
+ category String?
3440
+ status String
3441
+ apiVersion String?
3442
+ environment DeploymentEnvironment?
3443
+ dataPoints Int @default(0)
3444
+ healthScore Int?
3445
+ config Json?
3446
+ lastSyncAt DateTime?
3447
+ lastHealthAt DateTime?
3448
+ createdAt DateTime @default(now())
3449
+ updatedAt DateTime @updatedAt
3450
+ }
3451
+
3452
+ model Plugin {
3453
+ id String @id @default(cuid())
3454
+ name String @unique
3455
+ version String
3456
+ latestVersion String?
3457
+ author String?
3458
+ category String?
3459
+ description String?
3460
+ status String?
3461
+ installed Boolean @default(false)
3462
+ enabled Boolean @default(false)
3463
+ hasUpdate Boolean @default(false)
3464
+ rating Float?
3465
+ downloads Int @default(0)
3466
+ priceLabel String?
3467
+ featured Boolean @default(false)
3468
+ marketplaceUrl String?
3469
+ config Json?
3470
+ createdAt DateTime @default(now())
3471
+ updatedAt DateTime @updatedAt
3472
+
3473
+ @@index([category, status])
3474
+ }
3475
+
3476
+ model BackupStorageLocation {
3477
+ id String @id @default(cuid())
3478
+ name String
3479
+ type String
3480
+ path String
3481
+ status String
3482
+ region String?
3483
+ usedBytes Int?
3484
+ totalBytes Int?
3485
+ metadata Json?
3486
+ createdAt DateTime @default(now())
3487
+ updatedAt DateTime @updatedAt
3488
+
3489
+ backupJobs BackupJob[]
3490
+ backupSchedules BackupSchedule[]
3491
+ restoreRuns BackupRestoreRun[]
3492
+
3493
+ @@index([type, status])
3494
+ }
3495
+
3496
+ model BackupSchedule {
3497
+ id String @id @default(cuid())
3498
+ name String
3499
+ backupType BackupType
3500
+ scheduleExpression String
3501
+ nextRunAt DateTime?
3502
+ enabled Boolean @default(true)
3503
+ retentionDays Int?
3504
+ compressionEnabled Boolean @default(true)
3505
+ encryptionEnabled Boolean @default(true)
3506
+ storageLocationId String?
3507
+ createdAt DateTime @default(now())
3508
+ updatedAt DateTime @updatedAt
3509
+
3510
+ storageLocation BackupStorageLocation? @relation(fields: [storageLocationId], references: [id], onDelete: SetNull)
3511
+ backupJobs BackupJob[]
3512
+
3513
+ @@index([storageLocationId])
3514
+ @@index([enabled, nextRunAt])
3515
+ }
3516
+
3517
+ model BackupJob {
3518
+ id String @id @default(cuid())
3519
+ name String
3520
+ backupType BackupType?
3521
+ status String
3522
+ location String?
3523
+ sizeBytes Int?
3524
+ durationSeconds Int?
3525
+ retentionDays Int?
3526
+ compressed Boolean @default(false)
3527
+ encrypted Boolean @default(false)
3528
+ storageLocationId String?
3529
+ scheduleId String?
3530
+ startedAt DateTime?
3531
+ completedAt DateTime?
3532
+ metadata Json?
3533
+ createdAt DateTime @default(now())
3534
+
3535
+ storageLocation BackupStorageLocation? @relation(fields: [storageLocationId], references: [id], onDelete: SetNull)
3536
+ schedule BackupSchedule? @relation(fields: [scheduleId], references: [id], onDelete: SetNull)
3537
+ restores BackupRestoreRun[]
3538
+
3539
+ @@index([storageLocationId])
3540
+ @@index([scheduleId])
3541
+ @@index([backupType, createdAt])
3542
+ }
3543
+
3544
+ model BackupRestoreRun {
3545
+ id String @id @default(cuid())
3546
+ backupJobId String?
3547
+ storageLocationId String?
3548
+ initiatedById Int?
3549
+ targetEnvironment DeploymentEnvironment?
3550
+ status String
3551
+ notes String?
3552
+ startedAt DateTime @default(now())
3553
+ completedAt DateTime?
3554
+
3555
+ backupJob BackupJob? @relation(fields: [backupJobId], references: [id], onDelete: SetNull)
3556
+ storageLocation BackupStorageLocation? @relation(fields: [storageLocationId], references: [id], onDelete: SetNull)
3557
+ initiatedBy User? @relation("BackupRestoreInitiator", fields: [initiatedById], references: [id], onDelete: SetNull)
3558
+
3559
+ @@index([backupJobId])
3560
+ @@index([storageLocationId])
3561
+ @@index([initiatedById, startedAt])
3562
+ }
3563
+
3564
+ model DataExportJob {
3565
+ id String @id @default(cuid())
3566
+ name String
3567
+ status String
3568
+ format String
3569
+ requestedById Int?
3570
+ filters Json?
3571
+ createdAt DateTime @default(now())
3572
+ completedAt DateTime?
3573
+
3574
+ @@index([requestedById])
3575
+ }
3576
+
3577
+ model GameServerNode {
3578
+ id String @id @default(cuid())
3579
+ name String @unique
3580
+ region String
3581
+ status GameServerStatus @default(HEALTHY)
3582
+ websocketConnections Int @default(0)
3583
+ loadPercent Float?
3584
+ latencyMs Int?
3585
+ throughputPerSecond Int?
3586
+ errorRate Float?
3587
+ metadata Json?
3588
+ lastHeartbeatAt DateTime?
3589
+ createdAt DateTime @default(now())
3590
+ updatedAt DateTime @updatedAt
3591
+
3592
+ metrics GameServerMetricSnapshot[]
3593
+
3594
+ @@index([region, status])
3595
+ }
3596
+
3597
+ model GameServerMetricSnapshot {
3598
+ id String @id @default(cuid())
3599
+ nodeId String
3600
+ websocketConnections Int?
3601
+ loadPercent Float?
3602
+ latencyMs Int?
3603
+ throughputPerSecond Int?
3604
+ errorRate Float?
3605
+ capturedAt DateTime @default(now())
3606
+
3607
+ node GameServerNode @relation(fields: [nodeId], references: [id], onDelete: Cascade)
3608
+
3609
+ @@index([nodeId, capturedAt])
3610
+ }
3611
+
3612
+ model Game {
3613
+ id String @id @default(cuid())
3614
+ hostUserId Int?
3615
+ authorId String
3616
+ code String @unique
3617
+ status GameStatus @default(WAITING)
3618
+ createdAt DateTime @default(now())
3619
+ startedAt DateTime?
3620
+ endedAt DateTime?
3621
+ quizId String
3622
+
3623
+ hostUser User? @relation("HostedGames", fields: [hostUserId], references: [id], onDelete: SetNull)
3624
+ quiz Quiz @relation(fields: [quizId], references: [id], onDelete: Cascade)
3625
+ results GameResult[]
3626
+
3627
+ @@index([quizId])
3628
+ @@index([hostUserId])
3629
+ }
3630
+
3631
+ model GameResult {
3632
+ id String @id @default(cuid())
3633
+ score Int @default(0)
3634
+ correctAnswers Int @default(0)
3635
+ gameId String
3636
+ userId Int
3637
+ createdAt DateTime @default(now())
3638
+ answers Json?
3639
+
3640
+ game Game @relation(fields: [gameId], references: [id], onDelete: Cascade)
3641
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
3642
+
3643
+ @@unique([gameId, userId])
3644
+ @@index([gameId])
3645
+ @@index([userId])
3646
+ }
3647
+
3648
+ model Lobby {
3649
+ id String @id @default(cuid())
3650
+ code String @unique
3651
+ hostId Int
3652
+ quizId String
3653
+ name String?
3654
+ mode LobbyMode @default(COMPETITIVE)
3655
+ region String?
3656
+ isPrivate Boolean @default(false)
3657
+ maxPlayers Int @default(15)
3658
+ waitingSince DateTime?
3659
+ spectatorsCount Int @default(0)
3660
+ status LobbyStatus @default(WAITING)
3661
+ createdAt DateTime @default(now())
3662
+ startedAt DateTime?
3663
+ endedAt DateTime?
3664
+
3665
+ host User @relation("HostedLobbies", fields: [hostId], references: [id], onDelete: Cascade)
3666
+ quiz Quiz @relation(fields: [quizId], references: [id], onDelete: Cascade)
3667
+ participants Participant[]
3668
+
3669
+ @@index([quizId])
3670
+ @@index([hostId])
3671
+ @@map("lobbies")
3672
+ }
3673
+
3674
+ model Participant {
3675
+ id Int @id @default(autoincrement())
3676
+ lobbyId String
3677
+ userId Int
3678
+ displayUsername String?
3679
+ avatar String?
3680
+ kicked Boolean? @default(false)
3681
+ status ParticipantStatus @default(LOADING)
3682
+ banReason String?
3683
+ banExpires DateTime?
3684
+ finalScore Int @default(0)
3685
+ rank Int?
3686
+ joinedAt DateTime @default(now())
3687
+
3688
+ lobby Lobby @relation(fields: [lobbyId], references: [id], onDelete: Cascade)
3689
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
3690
+
3691
+ @@unique([lobbyId, userId])
3692
+ @@index([userId])
3693
+ @@map("participants")
3694
+ }
3695
+
3696
+ model TrivitEvent {
3697
+ id String @id @default(cuid())
3698
+ hostId Int?
3699
+ subjectId String?
3700
+ title String
3701
+ description String?
3702
+ lobbyCode String? @unique
3703
+ status TrivitStatus @default(DRAFT)
3704
+ scheduledAt DateTime?
3705
+ startedAt DateTime?
3706
+ endedAt DateTime?
3707
+ maxPlayers Int?
3708
+ config Json?
3709
+ createdAt DateTime @default(now())
3710
+ updatedAt DateTime @updatedAt
3711
+
3712
+ host User? @relation("HostedTrivits", fields: [hostId], references: [id], onDelete: SetNull)
3713
+ subject Subject? @relation(fields: [subjectId], references: [id], onDelete: SetNull)
3714
+ participants TrivitParticipant[]
3715
+
3716
+ @@index([hostId])
3717
+ @@index([subjectId])
3718
+ @@index([status, scheduledAt])
3719
+ }
3720
+
3721
+ model TrivitParticipant {
3722
+ id String @id @default(cuid())
3723
+ trivitId String
3724
+ userId Int
3725
+ score Int @default(0)
3726
+ accuracy Float?
3727
+ rank Int?
3728
+ joinedAt DateTime @default(now())
3729
+
3730
+ trivit TrivitEvent @relation(fields: [trivitId], references: [id], onDelete: Cascade)
3731
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
3732
+
3733
+ @@unique([trivitId, userId])
3734
+ @@index([userId])
3735
+ }