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 } enum AppReleasePlatform { ANDROID WINDOWS } enum PushPlatform { WEB ANDROID IOS } model AppRelease { id String @id @default(uuid()) platform AppReleasePlatform version String versionCode Int fileName String storageKey String @unique fileSize BigInt sha256 String releaseNotes String? isPublished Boolean @default(true) createdById String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@unique([platform, version]) @@unique([platform, versionCode]) @@index([platform, isPublished, createdAt]) } 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) isSystemAccount Boolean @default(false) linkedBotId String? @unique e2ePublicKey String? isVerified Boolean @default(false) verificationIcon String? verifiedAt DateTime? status UserStatus @default(ACTIVE) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt deletedAt DateTime? deletionRequestedAt DateTime? pinCode PinCode? sessions Session[] devices Device[] signInEvents SignInEvent[] activityLogs UserActivityLog[] 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[] pushDeviceTokens PushDeviceToken[] chatRoomMembers ChatRoomMember[] chatMessages ChatMessage[] chatPollVotes ChatPollVote[] totpSecret UserTotpSecret? createdOAuthClients OAuthClient[] @relation("OAuthClientCreator") ownedBots Bot[] @relation("BotOwner") linkedBot Bot? @relation("BotSystemUser", fields: [linkedBotId], references: [id], onDelete: SetNull) } 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 lastActivityAt DateTime @default(now()) 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 UserActivityLog { id String @id @default(uuid()) userId String action String title String detail String? entityType String? entityId String? ipAddress String? userAgent String? actorId String? createdAt DateTime @default(now()) user User @relation(fields: [userId], references: [id], onDelete: Cascade) @@index([userId, createdAt]) @@index([userId, action]) } 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[] nonce 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 PushDeviceToken { id String @id @default(uuid()) userId String token String @unique platform PushPlatform @default(WEB) deviceLabel String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt user User @relation(fields: [userId], references: [id], onDelete: Cascade) @@index([userId]) } model ChatRoom { id String @id @default(uuid()) groupId String type String name String peerUserId String? botUsername String? isE2E Boolean @default(false) 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[] botChats BotChat[] @@index([groupId, type]) @@index([groupId, type, peerUserId]) } model ChatRoomMember { id String @id @default(uuid()) roomId String userId String notificationsMuted Boolean @default(false) pinnedAt DateTime? 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]) @@index([userId, pinnedAt]) } model ChatMessage { id String @id @default(uuid()) roomId String senderId String type String content String? isEncrypted Boolean @default(false) replyToId String? metadata Json? storageKey String? mimeType String? isPinned Boolean @default(false) pinnedAt DateTime? pinnedById 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]) @@index([roomId, isPinned, pinnedAt]) } 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 } model Bot { id String @id @default(uuid()) name String username String @unique tokenHash String @unique tokenPrefix String ownerId String description String? aboutText String? botPicUrl String? menuButton Json? webAppUrl String? webhookUrl String? webhookSecretToken String? isActive Boolean @default(true) isSystemBot Boolean @default(false) nextMessageId Int @default(1) nextUpdateId Int @default(1) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt owner User @relation("BotOwner", fields: [ownerId], references: [id], onDelete: Cascade) linkedSystemUser User? @relation("BotSystemUser") chats BotChat[] messages BotMessage[] inboundMessages BotInboundMessage[] updates BotUpdate[] chatMenuButtons ChatMenuButton[] @@index([ownerId]) @@index([tokenHash]) @@index([isActive]) } model BotChat { id String @id @default(uuid()) botId String recipientUserId String? roomId String? telegramChatId BigInt chatType String @default("private") nextInboundMessageId Int @default(1) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt bot Bot @relation(fields: [botId], references: [id], onDelete: Cascade) room ChatRoom? @relation(fields: [roomId], references: [id], onDelete: Cascade) messages BotMessage[] inboundMessages BotInboundMessage[] menuButton ChatMenuButton? @@unique([botId, recipientUserId]) @@unique([botId, roomId]) @@unique([botId, telegramChatId]) @@index([botId]) @@index([roomId]) } model ChatMenuButton { id String @id @default(uuid()) botId String botChatId String @unique menuButton Json createdAt DateTime @default(now()) updatedAt DateTime @updatedAt bot Bot @relation(fields: [botId], references: [id], onDelete: Cascade) botChat BotChat @relation(fields: [botChatId], references: [id], onDelete: Cascade) @@unique([botId, botChatId]) @@index([botId]) } model BotMessage { id String @id @default(uuid()) botId String botChatId String telegramMsgId Int messageType String @default("text") text String? mediaUrl String? payload Json? isPinned Boolean @default(false) pinnedAt DateTime? createdAt DateTime @default(now()) editedAt DateTime? bot Bot @relation(fields: [botId], references: [id], onDelete: Cascade) botChat BotChat @relation(fields: [botChatId], references: [id], onDelete: Cascade) @@unique([botId, telegramMsgId]) @@index([botChatId]) @@index([botChatId, isPinned, pinnedAt]) } model BotInboundMessage { id String @id @default(uuid()) botId String botChatId String senderUserId String telegramMsgId Int text String? payload Json? createdAt DateTime @default(now()) bot Bot @relation(fields: [botId], references: [id], onDelete: Cascade) botChat BotChat @relation(fields: [botChatId], references: [id], onDelete: Cascade) @@unique([botChatId, telegramMsgId]) @@index([botId, createdAt]) } model BotUpdate { id String @id @default(uuid()) botId String updateId Int payload Json createdAt DateTime @default(now()) bot Bot @relation(fields: [botId], references: [id], onDelete: Cascade) @@unique([botId, updateId]) @@index([botId, updateId]) }