721 lines
21 KiB
Plaintext
721 lines
21 KiB
Plaintext
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])
|
|
}
|