Spaces:
Runtime error
Runtime error
| generator client { | |
| provider = "prisma-client-js" | |
| } | |
| datasource db { | |
| provider = "postgresql" | |
| } | |
| // ================ ENUMS ================= | |
| enum USER_STATUS { | |
| PENDING_EMAIL_VERIFICATION | |
| PENDING_PHONE_VERIFICATION | |
| PENDING_PROFILE_COMPLETION | |
| ENABLED | |
| DISABLED | |
| SUSPENDED | |
| INVITED | |
| } | |
| enum SESSION_STATUS { | |
| CURRENT | |
| EXPIRED | |
| REVOKED | |
| } | |
| enum OTP_TYPE { | |
| SMS | |
| } | |
| enum OTP_PURPOSE { | |
| EMAIL_VERIFICATION | |
| PHONE_VERIFICATION | |
| PASSWORD_RESET | |
| LOGIN_2FA | |
| } | |
| enum UPLOAD_STATUS { | |
| UPLOADING | |
| PROCESSING | |
| COMPLETED | |
| FAILED | |
| } | |
| enum NOTIFICATION_TYPE { | |
| NEW_MOVIE | |
| NEW_SERIES | |
| NEW_EPISODE | |
| COMMENT_REPLY | |
| COMMENT_LIKE | |
| CONTENT_LIKE | |
| ACCOUNT_UPDATE | |
| SUBSCRIPTION_REMINDER | |
| RECOMMENDATION | |
| } | |
| enum CONTENT_TYPE { | |
| MOVIE | |
| SERIES | |
| EPISODE | |
| } | |
| enum TWO_FACTOR_METHOD_TYPE { | |
| AUTHENTICATOR // TOTP | |
| PASSKEY // WebAuthn / FIDO2 | |
| PUSH_APPROVAL // Mobile yes/no | |
| } | |
| enum PUSH_APPROVAL_STATUS { | |
| PENDING | |
| APPROVED | |
| DENIED | |
| EXPIRED | |
| } | |
| enum PLATFORM_TYPE { | |
| ANDROID | |
| IOS | |
| WEB | |
| } | |
| enum URL_SHORTENER_PROVIDER { | |
| SHLINK | |
| TRIM_KUTT | |
| } | |
| // ============ USER MANAGEMENT ============ | |
| model User { | |
| id String (uuid()) | |
| uniqueId String | |
| email String? | |
| phone String? | |
| code String? | |
| firstName String? | |
| lastName String? | |
| firstNameLastName String? | |
| lastNameFirstName String? | |
| profileImage String? | |
| bio String? .Text | |
| dateOfBirth DateTime? .Timestamp(3) | |
| country String? | |
| city String? | |
| status USER_STATUS (PENDING_EMAIL_VERIFICATION) | |
| emailVerified Boolean (false) | |
| phoneVerified Boolean (false) | |
| lastLogin DateTime? .Timestamp(3) | |
| createdAt DateTime (now()) .Timestamp(3) | |
| updatedAt DateTime .Timestamp(3) | |
| isDeleted Boolean (false) | |
| // Relations | |
| sessions UserSession[] | |
| credentials UserCredential[] | |
| otps UserOtp[] | |
| interests UserInterest[] | |
| watchHistories WatchHistory[] | |
| notifications Notification[] | |
| playlists Playlist[] | |
| likes Like[] | |
| ratings Rating[] | |
| comments Comment[] | |
| commentLikes CommentLike[] | |
| @("users") | |
| } | |
| model UserSession { | |
| id String (uuid()) | |
| userId String | |
| accessToken String .Text | |
| refreshToken String .Text | |
| ipAddress String? | |
| userAgent String? .Text | |
| geoIpCountry String? | |
| city String? | |
| state String? | |
| latitude Float? | |
| longitude Float? | |
| status SESSION_STATUS (CURRENT) | |
| loginAt DateTime? .Timestamp(3) | |
| logoutAt DateTime? .Timestamp(3) | |
| expiredAt DateTime? .Timestamp(3) | |
| createdAt DateTime (now()) .Timestamp(3) | |
| updatedAt DateTime .Timestamp(3) | |
| isDeleted Boolean (false) | |
| // Relations | |
| user User (fields: [userId], references: [id], onDelete: Cascade) | |
| @("user_sessions") | |
| } | |
| model UserCredential { | |
| id String (uuid()) | |
| userId String | |
| password String | |
| createdAt DateTime (now()) .Timestamp(3) | |
| updatedAt DateTime .Timestamp(3) | |
| isDeleted Boolean (false) | |
| // Relations | |
| user User (fields: [userId], references: [id], onDelete: Cascade) | |
| @("user_credentials") | |
| } | |
| model UserOtp { | |
| id String (uuid()) | |
| userId String | |
| otp String | |
| type OTP_TYPE | |
| purpose OTP_PURPOSE | |
| expiresAt DateTime .Timestamp(3) | |
| verified Boolean (false) | |
| verifiedAt DateTime? .Timestamp(3) | |
| attempts Int (0) | |
| createdAt DateTime (now()) .Timestamp(3) | |
| updatedAt DateTime .Timestamp(3) | |
| isDeleted Boolean (false) | |
| // Relations | |
| user User (fields: [userId], references: [id], onDelete: Cascade) | |
| @([userId, type, verified]) | |
| @("user_otps") | |
| } | |
| model UserInterest { | |
| id String (uuid()) | |
| userId String | |
| genreId String | |
| createdAt DateTime (now()) .Timestamp(3) | |
| // Relations | |
| user User (fields: [userId], references: [id], onDelete: Cascade) | |
| genre Genre (fields: [genreId], references: [id], onDelete: Cascade) | |
| @([userId, genreId]) | |
| @([userId]) | |
| @([genreId]) | |
| @("user_interests") | |
| } | |
| // ============ SUPER ADMIN ============ | |
| model SuperAdmin { | |
| id String (uuid()) | |
| email String | |
| name String | |
| profileImage String? | |
| isTwoFactorEnabled Boolean (false) | |
| createdAt DateTime (now()) .Timestamp(3) | |
| updatedAt DateTime .Timestamp(3) | |
| isDeleted Boolean (false) | |
| // Relations | |
| sessions SuperAdminSession[] | |
| credentials SuperAdminCredential[] | |
| twoFactorMethods SuperAdminTwoFactorMethod[] | |
| recoveryCodes SuperAdminRecoveryCode[] | |
| trustedDevices SuperAdminTrustedDevice[] | |
| webAuthnCredentials SuperAdminWebAuthnCredential[] | |
| pushDevices SuperAdminPushDevice[] | |
| pushApprovalChallenges SuperAdminPushApprovalChallenge[] | |
| @("super_admins") | |
| } | |
| model SuperAdminSession { | |
| id String (uuid()) | |
| superAdminId String | |
| accessToken String .Text | |
| refreshToken String .Text | |
| ipAddress String? | |
| userAgent String? .Text | |
| geoIpCountry String? | |
| city String? | |
| state String? | |
| latitude Float? | |
| longitude Float? | |
| isFromAdmin Boolean (false) | |
| status SESSION_STATUS (CURRENT) | |
| loginAt DateTime? .Timestamp(3) | |
| expiredAt DateTime? .Timestamp(3) | |
| createdAt DateTime (now()) .Timestamp(3) | |
| updatedAt DateTime .Timestamp(3) | |
| isDeleted Boolean (false) | |
| // Relations | |
| superAdmin SuperAdmin (fields: [superAdminId], references: [id], onDelete: Cascade) | |
| @("super_admin_sessions") | |
| } | |
| model SuperAdminCredential { | |
| id String (uuid()) | |
| superAdminId String | |
| password String | |
| createdAt DateTime (now()) .Timestamp(3) | |
| updatedAt DateTime .Timestamp(3) | |
| isDeleted Boolean (false) | |
| // Relations | |
| superAdmin SuperAdmin (fields: [superAdminId], references: [id], onDelete: Cascade) | |
| @("super_admin_credentials") | |
| } | |
| // ============ SUPER ADMIN 2FA ============ | |
| model SuperAdminTwoFactorMethod { | |
| id String (uuid()) | |
| superAdminId String | |
| type TWO_FACTOR_METHOD_TYPE | |
| isEnabled Boolean (false) | |
| isPrimary Boolean (false) | |
| createdAt DateTime (now()) .Timestamp(3) | |
| updatedAt DateTime .Timestamp(3) | |
| isDeleted Boolean (false) | |
| // Relations | |
| superAdmin SuperAdmin (fields: [superAdminId], references: [id], onDelete: Cascade) | |
| emailTwoFactor SuperAdminEmailTwoFactor? | |
| totpTwoFactor SuperAdminTotpTwoFactor? | |
| passkeyTwoFactor SuperAdminPasskeyTwoFactor? | |
| pushTwoFactor SuperAdminPushTwoFactor? | |
| @([superAdminId, type]) | |
| @([superAdminId]) | |
| @("super_admin_two_factor_methods") | |
| } | |
| model SuperAdminEmailTwoFactor { | |
| id String (uuid()) | |
| methodId String | |
| lastCodeHash String? | |
| lastCodeExpiresAt DateTime? .Timestamp(3) | |
| lastSentAt DateTime? .Timestamp(3) | |
| failedAttempts Int (0) | |
| createdAt DateTime (now()) .Timestamp(3) | |
| updatedAt DateTime .Timestamp(3) | |
| isDeleted Boolean (false) | |
| // Relations | |
| method SuperAdminTwoFactorMethod (fields: [methodId], references: [id], onDelete: Cascade) | |
| @("super_admin_email_two_factor") | |
| } | |
| model SuperAdminTotpTwoFactor { | |
| id String (uuid()) | |
| methodId String | |
| secretEncrypted String | |
| confirmedAt DateTime? .Timestamp(3) | |
| createdAt DateTime (now()) .Timestamp(3) | |
| updatedAt DateTime .Timestamp(3) | |
| isDeleted Boolean (false) | |
| // Relations | |
| method SuperAdminTwoFactorMethod (fields: [methodId], references: [id], onDelete: Cascade) | |
| @("super_admin_totp_two_factor") | |
| } | |
| model SuperAdminPasskeyTwoFactor { | |
| id String (uuid()) | |
| methodId String | |
| createdAt DateTime (now()) .Timestamp(3) | |
| updatedAt DateTime .Timestamp(3) | |
| isDeleted Boolean (false) | |
| // Relations | |
| method SuperAdminTwoFactorMethod (fields: [methodId], references: [id], onDelete: Cascade) | |
| @("super_admin_passkey_two_factor") | |
| } | |
| model SuperAdminPushTwoFactor { | |
| id String (uuid()) | |
| methodId String | |
| createdAt DateTime (now()) .Timestamp(3) | |
| updatedAt DateTime .Timestamp(3) | |
| isDeleted Boolean (false) | |
| // Relations | |
| method SuperAdminTwoFactorMethod (fields: [methodId], references: [id], onDelete: Cascade) | |
| @("super_admin_push_two_factor") | |
| } | |
| model SuperAdminRecoveryCode { | |
| id String (uuid()) | |
| superAdminId String | |
| codeHash String | |
| usedAt DateTime? .Timestamp(3) | |
| createdAt DateTime (now()) .Timestamp(3) | |
| isDeleted Boolean (false) | |
| // Relations | |
| superAdmin SuperAdmin (fields: [superAdminId], references: [id], onDelete: Cascade) | |
| @([superAdminId]) | |
| @("super_admin_recovery_codes") | |
| } | |
| model SuperAdminWebAuthnCredential { | |
| id String (uuid()) | |
| superAdminId String | |
| credentialId String | |
| publicKey String .Text | |
| counter Int (0) | |
| deviceName String? | |
| createdAt DateTime (now()) .Timestamp(3) | |
| lastUsedAt DateTime? .Timestamp(3) | |
| isDeleted Boolean (false) | |
| // Relations | |
| superAdmin SuperAdmin (fields: [superAdminId], references: [id], onDelete: Cascade) | |
| @([superAdminId]) | |
| @("super_admin_webauthn_credentials") | |
| } | |
| model SuperAdminTrustedDevice { | |
| id String (uuid()) | |
| superAdminId String | |
| deviceId String | |
| deviceName String? | |
| ipAddress String? | |
| userAgent String? .Text | |
| lastUsedAt DateTime (now()) .Timestamp(3) | |
| expiresAt DateTime .Timestamp(3) | |
| createdAt DateTime (now()) .Timestamp(3) | |
| isDeleted Boolean (false) | |
| // Relations | |
| superAdmin SuperAdmin (fields: [superAdminId], references: [id], onDelete: Cascade) | |
| @([superAdminId]) | |
| @([deviceId]) | |
| @("super_admin_trusted_devices") | |
| } | |
| model SuperAdminPushDevice { | |
| id String (uuid()) | |
| superAdminId String | |
| pushToken String | |
| platform PLATFORM_TYPE | |
| deviceName String? | |
| lastSeenAt DateTime (now()) .Timestamp(3) | |
| createdAt DateTime (now()) .Timestamp(3) | |
| updatedAt DateTime .Timestamp(3) | |
| isDeleted Boolean (false) | |
| // Relations | |
| superAdmin SuperAdmin (fields: [superAdminId], references: [id], onDelete: Cascade) | |
| @([superAdminId]) | |
| @("super_admin_push_devices") | |
| } | |
| model SuperAdminPushApprovalChallenge { | |
| id String (uuid()) | |
| superAdminId String | |
| status PUSH_APPROVAL_STATUS (PENDING) | |
| ipAddress String? | |
| userAgent String? .Text | |
| location String? | |
| deviceInfo String? | |
| expiresAt DateTime .Timestamp(3) | |
| approvedAt DateTime? .Timestamp(3) | |
| deniedAt DateTime? .Timestamp(3) | |
| createdAt DateTime (now()) .Timestamp(3) | |
| isDeleted Boolean (false) | |
| // Relations | |
| superAdmin SuperAdmin (fields: [superAdminId], references: [id], onDelete: Cascade) | |
| @([superAdminId]) | |
| @([status]) | |
| @("super_admin_push_approval_challenges") | |
| } | |
| // ============ MOVIES ============ | |
| model Movie { | |
| id String (uuid()) | |
| title String | |
| slug String | |
| description String? .Text | |
| thumbnailUrl String | |
| bannerUrl String? | |
| trailerUrl String? | |
| rating String? | |
| releaseYear Int? | |
| language String? | |
| duration Int? | |
| imdbId String? | |
| tmdbId String? | |
| isFeatured Boolean (false) | |
| uploadStatus UPLOAD_STATUS (COMPLETED) | |
| views Int (0) | |
| createdAt DateTime (now()) .Timestamp(3) | |
| updatedAt DateTime .Timestamp(3) | |
| isDeleted Boolean (false) | |
| // Relations | |
| genres MovieGenre[] | |
| tags MovieTag[] | |
| video MovieVideo? | |
| comments Comment[] | |
| likes Like[] | |
| ratings Rating[] | |
| playlistItems PlaylistItem[] | |
| watchHistory WatchHistory[] | |
| @([isDeleted]) | |
| @([isFeatured]) | |
| @([slug]) | |
| @("movies") | |
| } | |
| model MovieVideo { | |
| id String (uuid()) | |
| movieId String | |
| videoUrl String | |
| videoKey String? | |
| fileSize BigInt? | |
| quality String? | |
| uploadStatus UPLOAD_STATUS (COMPLETED) | |
| createdAt DateTime (now()) .Timestamp(3) | |
| updatedAt DateTime .Timestamp(3) | |
| isDeleted Boolean (false) | |
| // Relations | |
| movie Movie (fields: [movieId], references: [id], onDelete: Cascade) | |
| @([isDeleted]) | |
| @("movie_videos") | |
| } | |
| model MovieGenre { | |
| id String (uuid()) | |
| movieId String | |
| genreId String | |
| createdAt DateTime (now()) .Timestamp(3) | |
| // Relations | |
| movie Movie (fields: [movieId], references: [id], onDelete: Cascade) | |
| genre Genre (fields: [genreId], references: [id], onDelete: Cascade) | |
| @([movieId, genreId]) | |
| @([movieId]) | |
| @([genreId]) | |
| @("movie_genres") | |
| } | |
| // ============ SERIES ============ | |
| model Series { | |
| id String (uuid()) | |
| title String | |
| slug String | |
| description String? .Text | |
| thumbnailUrl String | |
| bannerUrl String? | |
| trailerUrl String? | |
| rating String? | |
| releaseYear Int? | |
| language String? | |
| imdbId String? | |
| tmdbId String? | |
| isFeatured Boolean (false) | |
| uploadStatus UPLOAD_STATUS (COMPLETED) | |
| views Int (0) | |
| createdAt DateTime (now()) .Timestamp(3) | |
| updatedAt DateTime .Timestamp(3) | |
| isDeleted Boolean (false) | |
| // Relations | |
| genres SeriesGenre[] | |
| tags SeriesTag[] | |
| seasons Season[] | |
| comments Comment[] | |
| likes Like[] | |
| ratings Rating[] | |
| playlistItems PlaylistItem[] | |
| watchHistory WatchHistory[] | |
| @([isDeleted]) | |
| @([isFeatured]) | |
| @([slug]) | |
| @("series") | |
| } | |
| model Season { | |
| id String (uuid()) | |
| seriesId String | |
| seasonNumber Int | |
| title String | |
| description String? .Text | |
| thumbnailUrl String? | |
| releaseYear Int? | |
| createdAt DateTime (now()) .Timestamp(3) | |
| updatedAt DateTime .Timestamp(3) | |
| isDeleted Boolean (false) | |
| // Relations | |
| series Series (fields: [seriesId], references: [id], onDelete: Cascade) | |
| episodes Episode[] | |
| @([seriesId, seasonNumber]) | |
| @([seriesId]) | |
| @("seasons") | |
| } | |
| model Episode { | |
| id String (uuid()) | |
| seasonId String | |
| episodeNumber Int | |
| title String | |
| description String? .Text | |
| thumbnailUrl String? | |
| duration Int | |
| createdAt DateTime (now()) .Timestamp(3) | |
| updatedAt DateTime .Timestamp(3) | |
| isDeleted Boolean (false) | |
| // Relations | |
| season Season (fields: [seasonId], references: [id], onDelete: Cascade) | |
| video EpisodeVideo? | |
| comments Comment[] | |
| likes Like[] | |
| ratings Rating[] | |
| playlistItems PlaylistItem[] | |
| watchHistory WatchHistory[] | |
| @([seasonId, episodeNumber]) | |
| @([seasonId]) | |
| @("episodes") | |
| } | |
| model EpisodeVideo { | |
| id String (uuid()) | |
| episodeId String | |
| videoUrl String | |
| videoKey String? | |
| fileSize BigInt? | |
| quality String? | |
| uploadStatus UPLOAD_STATUS (COMPLETED) | |
| createdAt DateTime (now()) .Timestamp(3) | |
| updatedAt DateTime .Timestamp(3) | |
| isDeleted Boolean (false) | |
| // Relations | |
| episode Episode (fields: [episodeId], references: [id], onDelete: Cascade) | |
| @([isDeleted]) | |
| @("episode_videos") | |
| } | |
| model SeriesGenre { | |
| id String (uuid()) | |
| seriesId String | |
| genreId String | |
| createdAt DateTime (now()) .Timestamp(3) | |
| // Relations | |
| series Series (fields: [seriesId], references: [id], onDelete: Cascade) | |
| genre Genre (fields: [genreId], references: [id], onDelete: Cascade) | |
| @([seriesId, genreId]) | |
| @([seriesId]) | |
| @([genreId]) | |
| @("series_genres") | |
| } | |
| // ============ GENRES ============ | |
| model Genre { | |
| id String (uuid()) | |
| name String | |
| slug String | |
| description String? .Text | |
| icon String? | |
| sortOrder Int (0) | |
| createdAt DateTime (now()) .Timestamp(3) | |
| updatedAt DateTime .Timestamp(3) | |
| isDeleted Boolean (false) | |
| // Relations | |
| movieGenres MovieGenre[] | |
| seriesGenres SeriesGenre[] | |
| userInterests UserInterest[] | |
| @([slug]) | |
| @("genres") | |
| } | |
| // ============ UNIFIED COMMENT ============ | |
| model Comment { | |
| id String (uuid()) | |
| content String .Text | |
| userId String | |
| contentType CONTENT_TYPE | |
| movieId String? | |
| seriesId String? | |
| episodeId String? | |
| parentId String? | |
| createdAt DateTime (now()) .Timestamp(3) | |
| updatedAt DateTime .Timestamp(3) | |
| isDeleted Boolean (false) | |
| // Relations | |
| user User (fields: [userId], references: [id], onDelete: Cascade) | |
| movie Movie? (fields: [movieId], references: [id], onDelete: Cascade) | |
| series Series? (fields: [seriesId], references: [id], onDelete: Cascade) | |
| episode Episode? (fields: [episodeId], references: [id], onDelete: Cascade) | |
| parent Comment? ("CommentReplies", fields: [parentId], references: [id], onDelete: Cascade) | |
| replies Comment[] ("CommentReplies") | |
| likes CommentLike[] | |
| @([movieId, isDeleted]) | |
| @([seriesId, isDeleted]) | |
| @([episodeId, isDeleted]) | |
| @([userId, isDeleted]) | |
| @([contentType, isDeleted]) | |
| @("comments") | |
| } | |
| // ============ UNIFIED LIKE ============ | |
| model Like { | |
| id String (uuid()) | |
| userId String | |
| contentType CONTENT_TYPE | |
| movieId String? | |
| seriesId String? | |
| episodeId String? | |
| isLike Boolean | |
| createdAt DateTime (now()) .Timestamp(3) | |
| // Relations | |
| user User (fields: [userId], references: [id], onDelete: Cascade) | |
| movie Movie? (fields: [movieId], references: [id], onDelete: Cascade) | |
| series Series? (fields: [seriesId], references: [id], onDelete: Cascade) | |
| episode Episode? (fields: [episodeId], references: [id], onDelete: Cascade) | |
| // Only one of movieId/seriesId/episodeId should be non-null per row (enforce in code) | |
| @([userId, movieId]) | |
| @([userId, seriesId]) | |
| @([userId, episodeId]) | |
| @([movieId, contentType]) | |
| @([seriesId, contentType]) | |
| @([episodeId, contentType]) | |
| @("likes") | |
| } | |
| // ============ COMMENT LIKE ============ | |
| model CommentLike { | |
| id String (uuid()) | |
| userId String | |
| commentId String | |
| isLike Boolean | |
| createdAt DateTime (now()) .Timestamp(3) | |
| // Relations | |
| user User (fields: [userId], references: [id], onDelete: Cascade) | |
| comment Comment (fields: [commentId], references: [id], onDelete: Cascade) | |
| @([userId, commentId]) | |
| @([commentId]) | |
| @("comment_likes") | |
| } | |
| // ============ PLAYLISTS ============ | |
| model Playlist { | |
| id String (uuid()) | |
| name String | |
| description String? .Text | |
| userId String | |
| createdAt DateTime (now()) .Timestamp(3) | |
| updatedAt DateTime .Timestamp(3) | |
| isDeleted Boolean (false) | |
| // Relations | |
| user User (fields: [userId], references: [id], onDelete: Cascade) | |
| items PlaylistItem[] | |
| @([userId, isDeleted]) | |
| @("playlists") | |
| } | |
| model PlaylistItem { | |
| id String (uuid()) | |
| playlistId String | |
| contentType CONTENT_TYPE | |
| movieId String? | |
| seriesId String? | |
| episodeId String? | |
| position Int | |
| createdAt DateTime (now()) .Timestamp(3) | |
| // Relations | |
| playlist Playlist (fields: [playlistId], references: [id], onDelete: Cascade) | |
| movie Movie? (fields: [movieId], references: [id], onDelete: Cascade) | |
| series Series? (fields: [seriesId], references: [id], onDelete: Cascade) | |
| episode Episode? (fields: [episodeId], references: [id], onDelete: Cascade) | |
| // Only one of movieId/seriesId/episodeId should be non-null per row (enforce in code) | |
| @([playlistId, movieId]) | |
| @([playlistId, seriesId]) | |
| @([playlistId, episodeId]) | |
| @([playlistId]) | |
| @([movieId, contentType]) | |
| @([seriesId, contentType]) | |
| @([episodeId, contentType]) | |
| @("playlist_items") | |
| } | |
| // ============ WATCH HISTORY ============ | |
| model WatchHistory { | |
| id String (uuid()) | |
| userId String | |
| contentType CONTENT_TYPE | |
| movieId String? | |
| seriesId String? | |
| episodeId String? | |
| watchedAt DateTime (now()) .Timestamp(3) | |
| watchTime Int | |
| duration Int | |
| completed Boolean (false) | |
| // Relations | |
| user User (fields: [userId], references: [id], onDelete: Cascade) | |
| movie Movie? (fields: [movieId], references: [id], onDelete: Cascade) | |
| series Series? (fields: [seriesId], references: [id], onDelete: Cascade) | |
| episode Episode? (fields: [episodeId], references: [id], onDelete: Cascade) | |
| @([userId, contentType]) | |
| @([movieId]) | |
| @([seriesId]) | |
| @([episodeId]) | |
| @("watch_history") | |
| } | |
| // ============ NOTIFICATIONS ============ | |
| model Notification { | |
| id String (uuid()) | |
| userId String | |
| type NOTIFICATION_TYPE | |
| title String | |
| message String .Text | |
| read Boolean (false) | |
| data Json? | |
| createdAt DateTime (now()) .Timestamp(3) | |
| isDeleted Boolean (false) | |
| // Relations | |
| user User (fields: [userId], references: [id], onDelete: Cascade) | |
| @([userId, isDeleted]) | |
| @([userId, read, isDeleted]) | |
| @("notifications") | |
| } | |
| // ============ RATINGS (Star Ratings 1-5) ============ | |
| model Rating { | |
| id String (uuid()) | |
| userId String | |
| contentType CONTENT_TYPE | |
| movieId String? | |
| seriesId String? | |
| episodeId String? | |
| value Int | |
| createdAt DateTime (now()) .Timestamp(3) | |
| updatedAt DateTime .Timestamp(3) | |
| // Relations | |
| user User (fields: [userId], references: [id], onDelete: Cascade) | |
| movie Movie? (fields: [movieId], references: [id], onDelete: Cascade) | |
| series Series? (fields: [seriesId], references: [id], onDelete: Cascade) | |
| episode Episode? (fields: [episodeId], references: [id], onDelete: Cascade) | |
| // Only one of movieId/seriesId/episodeId should be non-null per row (enforce in code) | |
| @([userId, movieId]) | |
| @([userId, seriesId]) | |
| @([userId, episodeId]) | |
| @([movieId, contentType]) | |
| @([seriesId, contentType]) | |
| @([episodeId, contentType]) | |
| @("ratings") | |
| } | |
| // ============ UPLOADS ============ | |
| model Upload { | |
| id String (uuid()) | |
| fileName String | |
| fileType String // 'image', 'video', 'document' | |
| originalSize Int? | |
| // For videos (HLS) | |
| hlsUrl String? // URL to playlist.m3u8 | |
| hlsFolderId String? // folder containing HLS files | |
| hlsSegments Json? // Array of segment URLs | |
| videoDuration Float? // Duration in seconds | |
| status String ("pending") // pending, processing, completed, failed | |
| errorMessage String? | |
| uploadedAt DateTime (now()) .Timestamp(3) | |
| processedAt DateTime? | |
| metadata Json? | |
| @([status]) | |
| @([fileType]) | |
| @([uploadedAt]) | |
| @("uploads") | |
| } | |
| // ============ TAGS (Flexible Content Tags) ============ | |
| model Tag { | |
| id String (uuid()) | |
| name String | |
| slug String | |
| createdAt DateTime (now()) .Timestamp(3) | |
| updatedAt DateTime .Timestamp(3) | |
| isDeleted Boolean (false) | |
| // Relations | |
| movieTags MovieTag[] | |
| seriesTags SeriesTag[] | |
| @([slug]) | |
| @("tags") | |
| } | |
| model MovieTag { | |
| id String (uuid()) | |
| movieId String | |
| tagId String | |
| createdAt DateTime (now()) .Timestamp(3) | |
| // Relations | |
| movie Movie (fields: [movieId], references: [id], onDelete: Cascade) | |
| tag Tag (fields: [tagId], references: [id], onDelete: Cascade) | |
| @([movieId, tagId]) | |
| @([movieId]) | |
| @([tagId]) | |
| @("movie_tags") | |
| } | |
| model SeriesTag { | |
| id String (uuid()) | |
| seriesId String | |
| tagId String | |
| createdAt DateTime (now()) .Timestamp(3) | |
| // Relations | |
| series Series (fields: [seriesId], references: [id], onDelete: Cascade) | |
| tag Tag (fields: [tagId], references: [id], onDelete: Cascade) | |
| @([seriesId, tagId]) | |
| @([seriesId]) | |
| @([tagId]) | |
| @("series_tags") | |
| } | |
| // ============ SHORT URL ============ | |
| model ShortUrl { | |
| id String (uuid()) | |
| longUrl String .Text | |
| shortUrl String | |
| shortCode String? | |
| externalId String? // Provider's internal ID (e.g., Kutt's ID) | |
| provider URL_SHORTENER_PROVIDER | |
| createdAt DateTime (now()) .Timestamp(3) | |
| updatedAt DateTime .Timestamp(3) | |
| isDeleted Boolean (false) | |
| @([provider]) | |
| @("short_urls") | |
| } | |