generator client { provider = "prisma-client-js" output = "../src/generated/prisma" } datasource db { provider = "postgresql" } enum UserStatus { ACTIVE SUSPENDED DELETED } enum SessionStatus { ACTIVE LOCKED REVOKED EXPIRED } enum DeviceType { WEB IOS ANDROID DESKTOP TV SMART_SPEAKER OTHER } enum OAuthClientType { CONFIDENTIAL PUBLIC } model User { id String @id @default(uuid()) email String? @unique phone String? @unique backupEmail String? @unique backupPhone String? @unique passwordHash String? displayName String username String? @unique avatarUrl String? avatarStorageKey String? birthDate DateTime? gender String? isSuperAdmin Boolean @default(false) status UserStatus @default(ACTIVE) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt deletedAt DateTime? deletionRequestedAt DateTime? pinCode PinCode? sessions Session[] devices Device[] signInEvents SignInEvent[] authCodes AuthCode[] linkedAccounts LinkedAccount[] userRoles UserRole[] userPermissions UserPermission[] profile UserProfile? documents UserDocument[] addresses Address[] oauthConsents OAuthConsent[] oauthAuthCodes OAuthAuthorizationCode[] ownedFamilyGroups FamilyGroup[] familyMemberships FamilyMember[] sentFamilyInvites FamilyInvite[] @relation("FamilyInviteInviter") receivedFamilyInvites FamilyInvite[] @relation("FamilyInviteInvitee") notifications Notification[] chatRoomMembers ChatRoomMember[] chatMessages ChatMessage[] chatPollVotes ChatPollVote[] totpSecret UserTotpSecret? createdOAuthClients OAuthClient[] @relation("OAuthClientCreator") } model UserTotpSecret { id String @id @default(uuid()) userId String @unique secretEnc String isEnabled Boolean @default(false) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt user User @relation(fields: [userId], references: [id], onDelete: Cascade) } model UserProfile { id String @id @default(uuid()) userId String @unique firstName String? lastName String? bio String? age Int? gender String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt user User @relation(fields: [userId], references: [id], onDelete: Cascade) } model PinCode { id String @id @default(uuid()) userId String @unique hash String isEnabled Boolean @default(false) deletionRequestedAt DateTime? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt user User @relation(fields: [userId], references: [id], onDelete: Cascade) } model Session { id String @id @default(uuid()) userId String deviceId String? refreshHash String pinVerified Boolean @default(true) status SessionStatus @default(ACTIVE) ipAddress String? userAgent String? expiresAt DateTime createdAt DateTime @default(now()) updatedAt DateTime @updatedAt user User @relation(fields: [userId], references: [id], onDelete: Cascade) device Device? @relation(fields: [deviceId], references: [id], onDelete: SetNull) @@index([userId, status]) } model Device { id String @id @default(uuid()) userId String name String type DeviceType @default(OTHER) fingerprint String lastIp String? lastSeenAt DateTime @default(now()) trusted Boolean @default(false) createdAt DateTime @default(now()) user User @relation(fields: [userId], references: [id], onDelete: Cascade) sessions Session[] signInEvents SignInEvent[] @@unique([userId, fingerprint]) } model SignInEvent { id String @id @default(uuid()) userId String deviceId String? ipAddress String? userAgent String? success Boolean reason String? createdAt DateTime @default(now()) user User @relation(fields: [userId], references: [id], onDelete: Cascade) device Device? @relation(fields: [deviceId], references: [id], onDelete: SetNull) @@index([userId, createdAt]) } model LinkedAccount { id String @id @default(uuid()) userId String providerName String providerId String email String? displayName String? avatarUrl String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt user User @relation(fields: [userId], references: [id], onDelete: Cascade) @@unique([providerName, providerId]) @@index([userId]) } model Role { id String @id @default(uuid()) slug String @unique name String description String? isSystem Boolean @default(false) isDefault Boolean @default(false) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt users UserRole[] permissions RolePermission[] } model Permission { id String @id @default(uuid()) slug String @unique name String description String? createdAt DateTime @default(now()) roles RolePermission[] users UserPermission[] } model UserPermission { userId String permissionId String createdAt DateTime @default(now()) user User @relation(fields: [userId], references: [id], onDelete: Cascade) permission Permission @relation(fields: [permissionId], references: [id], onDelete: Cascade) @@id([userId, permissionId]) } model UserRole { userId String roleId String createdAt DateTime @default(now()) user User @relation(fields: [userId], references: [id], onDelete: Cascade) role Role @relation(fields: [roleId], references: [id], onDelete: Cascade) @@id([userId, roleId]) } model RolePermission { roleId String permissionId String createdAt DateTime @default(now()) role Role @relation(fields: [roleId], references: [id], onDelete: Cascade) permission Permission @relation(fields: [permissionId], references: [id], onDelete: Cascade) @@id([roleId, permissionId]) } model OAuthClient { id String @id @default(uuid()) name String clientId String @unique clientSecret String? type OAuthClientType @default(CONFIDENTIAL) redirectUris String[] scopes OAuthClientScope[] isActive Boolean @default(true) createdById String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt createdBy User? @relation("OAuthClientCreator", fields: [createdById], references: [id], onDelete: SetNull) consents OAuthConsent[] authCodes OAuthAuthorizationCode[] @@index([createdById]) } model OAuthAuthorizationCode { id String @id @default(uuid()) code String @unique userId String clientId String redirectUri String scopes String[] expiresAt DateTime consumedAt DateTime? createdAt DateTime @default(now()) user User @relation(fields: [userId], references: [id], onDelete: Cascade) client OAuthClient @relation(fields: [clientId], references: [id], onDelete: Cascade) @@index([userId, clientId]) } model OAuthScope { id String @id @default(uuid()) slug String @unique name String description String? clients OAuthClientScope[] consents OAuthConsentScope[] } model OAuthClientScope { clientId String scopeId String client OAuthClient @relation(fields: [clientId], references: [id], onDelete: Cascade) scope OAuthScope @relation(fields: [scopeId], references: [id], onDelete: Cascade) @@id([clientId, scopeId]) } model OAuthConsent { id String @id @default(uuid()) userId String clientId String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt user User @relation(fields: [userId], references: [id], onDelete: Cascade) client OAuthClient @relation(fields: [clientId], references: [id], onDelete: Cascade) scopes OAuthConsentScope[] @@unique([userId, clientId]) } model OAuthConsentScope { consentId String scopeId String consent OAuthConsent @relation(fields: [consentId], references: [id], onDelete: Cascade) scope OAuthScope @relation(fields: [scopeId], references: [id], onDelete: Cascade) @@id([consentId, scopeId]) } model UserDocument { id String @id @default(uuid()) userId String type String number String issuedAt DateTime? expiresAt DateTime? metadata Json? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt user User @relation(fields: [userId], references: [id], onDelete: Cascade) } model AuthCode { id String @id @default(uuid()) userId String? channel String target String codeHash String purpose String expiresAt DateTime usedAt DateTime? createdAt DateTime @default(now()) user User? @relation(fields: [userId], references: [id], onDelete: Cascade) @@index([target, purpose, expiresAt]) } model FamilyGroup { id String @id @default(uuid()) ownerId String name String avatarStorageKey String? hasAvatar Boolean @default(false) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt owner User @relation(fields: [ownerId], references: [id], onDelete: Cascade) members FamilyMember[] invites FamilyInvite[] chatRooms ChatRoom[] } model FamilyInvite { id String @id @default(uuid()) groupId String inviterId String inviteeUserId String? targetEmail String? targetPhone String? status String @default("PENDING") token String @unique @default(uuid()) expiresAt DateTime createdAt DateTime @default(now()) updatedAt DateTime @updatedAt group FamilyGroup @relation(fields: [groupId], references: [id], onDelete: Cascade) inviter User @relation("FamilyInviteInviter", fields: [inviterId], references: [id], onDelete: Cascade) invitee User? @relation("FamilyInviteInvitee", fields: [inviteeUserId], references: [id], onDelete: SetNull) @@index([inviteeUserId, status]) @@index([groupId, status]) } model Notification { id String @id @default(uuid()) userId String type String title String message String payload Json? isRead Boolean @default(false) createdAt DateTime @default(now()) user User @relation(fields: [userId], references: [id], onDelete: Cascade) @@index([userId, isRead, createdAt]) } model ChatRoom { id String @id @default(uuid()) groupId String type String name String avatarStorageKey String? hasAvatar Boolean @default(false) createdById String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt group FamilyGroup @relation(fields: [groupId], references: [id], onDelete: Cascade) members ChatRoomMember[] messages ChatMessage[] @@index([groupId, type]) } model ChatRoomMember { id String @id @default(uuid()) roomId String userId String notificationsMuted Boolean @default(false) lastReadAt DateTime? lastReadMessageId String? joinedAt DateTime @default(now()) room ChatRoom @relation(fields: [roomId], references: [id], onDelete: Cascade) user User @relation(fields: [userId], references: [id], onDelete: Cascade) @@unique([roomId, userId]) } model ChatMessage { id String @id @default(uuid()) roomId String senderId String type String content String? replyToId String? metadata Json? storageKey String? mimeType String? createdAt DateTime @default(now()) editedAt DateTime? deletedAt DateTime? room ChatRoom @relation(fields: [roomId], references: [id], onDelete: Cascade) sender User @relation(fields: [senderId], references: [id], onDelete: Cascade) poll ChatPoll? @@index([roomId, createdAt]) } model ChatPoll { id String @id @default(uuid()) messageId String @unique question String allowsMultiple Boolean @default(false) isAnonymous Boolean @default(false) closedAt DateTime? message ChatMessage @relation(fields: [messageId], references: [id], onDelete: Cascade) options ChatPollOption[] } model ChatPollOption { id String @id @default(uuid()) pollId String text String poll ChatPoll @relation(fields: [pollId], references: [id], onDelete: Cascade) votes ChatPollVote[] } model ChatPollVote { id String @id @default(uuid()) optionId String userId String option ChatPollOption @relation(fields: [optionId], references: [id], onDelete: Cascade) user User @relation(fields: [userId], references: [id], onDelete: Cascade) @@unique([optionId, userId]) } model FamilyMember { id String @id @default(uuid()) groupId String userId String role String @default("member") createdAt DateTime @default(now()) group FamilyGroup @relation(fields: [groupId], references: [id], onDelete: Cascade) user User @relation(fields: [userId], references: [id], onDelete: Cascade) @@unique([groupId, userId]) } model Address { id String @id @default(uuid()) userId String label String city String street String house String apartment String? comment String? latitude Float? longitude Float? fullAddress String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt user User @relation(fields: [userId], references: [id], onDelete: Cascade) @@index([userId, label]) } model SystemSetting { id String @id @default(uuid()) key String @unique value String description String? isSecret Boolean @default(false) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt } model SocialProvider { id String @id @default(uuid()) providerName String @unique clientId String clientSecret String? isEnabled Boolean @default(false) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt }