diff --git a/.env b/.env index 18e75385..48907b0a 100644 --- a/.env +++ b/.env @@ -35,3 +35,5 @@ SEED_ADMIN_EMAIL=admin@simplemailcleaner.local SEED_ADMIN_PASSWORD=change-me-now SEED_TENANT=Default Tenant SEED_TENANT_ID=seed-tenant +SEED_ENABLED=true +SEED_FORCE_PASSWORD_UPDATE=false diff --git a/.env.example b/.env.example index fdfba237..1f210d65 100644 --- a/.env.example +++ b/.env.example @@ -35,3 +35,5 @@ SEED_ADMIN_EMAIL=admin@simplemailcleaner.local SEED_ADMIN_PASSWORD=change-me-now SEED_TENANT=Default Tenant SEED_TENANT_ID=seed-tenant +SEED_ENABLED=true +SEED_FORCE_PASSWORD_UPDATE=false diff --git a/backend/node_modules/.prisma/client/edge.js b/backend/node_modules/.prisma/client/edge.js index 27e7f1b8..899b64e6 100644 --- a/backend/node_modules/.prisma/client/edge.js +++ b/backend/node_modules/.prisma/client/edge.js @@ -225,6 +225,13 @@ exports.Prisma.CleanupJobEventScalarFieldEnum = { createdAt: 'createdAt' }; +exports.Prisma.AppSettingScalarFieldEnum = { + id: 'id', + key: 'key', + value: 'value', + updatedAt: 'updatedAt' +}; + exports.Prisma.SortOrder = { asc: 'asc', desc: 'desc' @@ -292,7 +299,8 @@ exports.Prisma.ModelName = { RuleAction: 'RuleAction', CleanupJob: 'CleanupJob', UnsubscribeAttempt: 'UnsubscribeAttempt', - CleanupJobEvent: 'CleanupJobEvent' + CleanupJobEvent: 'CleanupJobEvent', + AppSetting: 'AppSetting' }; /** * Create the Client @@ -305,7 +313,7 @@ const config = { "value": "prisma-client-js" }, "output": { - "value": "/root/mailcleaner/backend/node_modules/@prisma/client", + "value": "/app/node_modules/@prisma/client", "fromEnvVar": null }, "config": { @@ -319,10 +327,11 @@ const config = { } ], "previewFeatures": [], - "sourceFilePath": "/root/mailcleaner/backend/prisma/schema.prisma" + "sourceFilePath": "/app/prisma/schema.prisma" }, "relativeEnvPaths": { - "rootEnvPath": null + "rootEnvPath": null, + "schemaEnvPath": "../../../.env" }, "relativePath": "../../../prisma", "clientVersion": "5.22.0", @@ -331,6 +340,7 @@ const config = { "db" ], "activeProvider": "postgresql", + "postinstall": false, "inlineDatasources": { "db": { "url": { @@ -339,13 +349,13 @@ const config = { } } }, - "inlineSchema": "generator client {\n provider = \"prisma-client-js\"\n}\n\ndatasource db {\n provider = \"postgresql\"\n url = env(\"DATABASE_URL\")\n}\n\nenum MailProvider {\n GMAIL\n GMX\n WEBDE\n}\n\nenum UserRole {\n USER\n ADMIN\n}\n\nenum JobStatus {\n QUEUED\n RUNNING\n SUCCEEDED\n FAILED\n CANCELED\n}\n\nenum RuleActionType {\n MOVE\n DELETE\n ARCHIVE\n LABEL\n}\n\nenum RuleConditionType {\n HEADER\n SUBJECT\n FROM\n LIST_UNSUBSCRIBE\n LIST_ID\n}\n\nmodel Tenant {\n id String @id @default(cuid())\n name String\n isActive Boolean @default(true)\n exportJobs ExportJob[]\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n users User[]\n mailboxAccounts MailboxAccount[]\n rules Rule[]\n jobs CleanupJob[]\n}\n\nenum ExportStatus {\n QUEUED\n RUNNING\n DONE\n FAILED\n}\n\nmodel ExportJob {\n id String @id @default(cuid())\n tenantId String\n status ExportStatus @default(QUEUED)\n format String\n scope String\n progress Int @default(0)\n filePath String?\n error String?\n expiresAt DateTime?\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n tenant Tenant @relation(fields: [tenantId], references: [id])\n\n @@index([tenantId])\n}\n\nmodel User {\n id String @id @default(cuid())\n tenantId String\n email String @unique\n password String\n role UserRole @default(USER)\n isActive Boolean @default(true)\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n tenant Tenant @relation(fields: [tenantId], references: [id])\n}\n\nmodel MailboxAccount {\n id String @id @default(cuid())\n tenantId String\n email String\n provider MailProvider\n isActive Boolean @default(true)\n imapHost String\n imapPort Int\n imapTLS Boolean\n smtpHost String?\n smtpPort Int?\n smtpTLS Boolean?\n oauthToken String?\n oauthRefreshToken String?\n oauthAccessToken String?\n oauthExpiresAt DateTime?\n providerUserId String?\n oauthLastCheckedAt DateTime?\n oauthLastErrorCode String?\n appPassword String?\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n tenant Tenant @relation(fields: [tenantId], references: [id])\n folders MailboxFolder[]\n jobs CleanupJob[]\n\n @@index([tenantId])\n}\n\nmodel MailboxFolder {\n id String @id @default(cuid())\n mailboxAccountId String\n name String\n remoteId String?\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n mailboxAccount MailboxAccount @relation(fields: [mailboxAccountId], references: [id])\n mailItems MailItem[]\n\n @@index([mailboxAccountId])\n}\n\nmodel MailItem {\n id String @id @default(cuid())\n folderId String\n messageId String\n subject String?\n from String?\n receivedAt DateTime?\n listId String?\n listUnsubscribe String?\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n folder MailboxFolder @relation(fields: [folderId], references: [id])\n\n @@index([folderId])\n @@index([messageId])\n}\n\nmodel Rule {\n id String @id @default(cuid())\n tenantId String\n name String\n enabled Boolean @default(true)\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n tenant Tenant @relation(fields: [tenantId], references: [id])\n conditions RuleCondition[]\n actions RuleAction[]\n\n @@index([tenantId])\n}\n\nmodel RuleCondition {\n id String @id @default(cuid())\n ruleId String\n type RuleConditionType\n value String\n\n rule Rule @relation(fields: [ruleId], references: [id])\n\n @@index([ruleId])\n}\n\nmodel RuleAction {\n id String @id @default(cuid())\n ruleId String\n type RuleActionType\n target String?\n\n rule Rule @relation(fields: [ruleId], references: [id])\n\n @@index([ruleId])\n}\n\nmodel CleanupJob {\n id String @id @default(cuid())\n tenantId String\n mailboxAccountId String\n status JobStatus @default(QUEUED)\n dryRun Boolean @default(true)\n unsubscribeEnabled Boolean @default(true)\n routingEnabled Boolean @default(true)\n startedAt DateTime?\n finishedAt DateTime?\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n tenant Tenant @relation(fields: [tenantId], references: [id])\n mailboxAccount MailboxAccount @relation(fields: [mailboxAccountId], references: [id])\n unsubscribeAttempts UnsubscribeAttempt[]\n events CleanupJobEvent[]\n\n @@index([tenantId])\n @@index([mailboxAccountId])\n}\n\nmodel UnsubscribeAttempt {\n id String @id @default(cuid())\n jobId String\n mailItemId String?\n method String\n target String\n status String\n createdAt DateTime @default(now())\n\n job CleanupJob @relation(fields: [jobId], references: [id])\n\n @@index([jobId])\n}\n\nmodel CleanupJobEvent {\n id String @id @default(cuid())\n jobId String\n level String\n message String\n progress Int?\n createdAt DateTime @default(now())\n\n job CleanupJob @relation(fields: [jobId], references: [id])\n\n @@index([jobId])\n}\n", - "inlineSchemaHash": "8215bb521bb81721fac5b205bca5af7a38690fa4cc8087b4b16d7967edb43359", + "inlineSchema": "generator client {\n provider = \"prisma-client-js\"\n}\n\ndatasource db {\n provider = \"postgresql\"\n url = env(\"DATABASE_URL\")\n}\n\nenum MailProvider {\n GMAIL\n GMX\n WEBDE\n}\n\nenum UserRole {\n USER\n ADMIN\n}\n\nenum JobStatus {\n QUEUED\n RUNNING\n SUCCEEDED\n FAILED\n CANCELED\n}\n\nenum RuleActionType {\n MOVE\n DELETE\n ARCHIVE\n LABEL\n}\n\nenum RuleConditionType {\n HEADER\n SUBJECT\n FROM\n LIST_UNSUBSCRIBE\n LIST_ID\n}\n\nenum ExportStatus {\n QUEUED\n RUNNING\n DONE\n FAILED\n}\n\nmodel Tenant {\n id String @id @default(cuid())\n name String\n isActive Boolean @default(true)\n exportJobs ExportJob[]\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n users User[]\n mailboxAccounts MailboxAccount[]\n rules Rule[]\n jobs CleanupJob[]\n}\n\nmodel ExportJob {\n id String @id @default(cuid())\n tenantId String\n status ExportStatus @default(QUEUED)\n format String\n scope String\n progress Int @default(0)\n filePath String?\n error String?\n expiresAt DateTime?\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n tenant Tenant @relation(fields: [tenantId], references: [id])\n\n @@index([tenantId])\n}\n\nmodel User {\n id String @id @default(cuid())\n tenantId String\n email String @unique\n password String\n role UserRole @default(USER)\n isActive Boolean @default(true)\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n tenant Tenant @relation(fields: [tenantId], references: [id])\n}\n\nmodel MailboxAccount {\n id String @id @default(cuid())\n tenantId String\n email String\n provider MailProvider\n isActive Boolean @default(true)\n imapHost String\n imapPort Int\n imapTLS Boolean\n smtpHost String?\n smtpPort Int?\n smtpTLS Boolean?\n oauthToken String?\n oauthRefreshToken String?\n oauthAccessToken String?\n oauthExpiresAt DateTime?\n providerUserId String?\n oauthLastCheckedAt DateTime?\n oauthLastErrorCode String?\n appPassword String?\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n tenant Tenant @relation(fields: [tenantId], references: [id])\n folders MailboxFolder[]\n jobs CleanupJob[]\n\n @@index([tenantId])\n}\n\nmodel MailboxFolder {\n id String @id @default(cuid())\n mailboxAccountId String\n name String\n remoteId String?\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n mailboxAccount MailboxAccount @relation(fields: [mailboxAccountId], references: [id])\n mailItems MailItem[]\n\n @@index([mailboxAccountId])\n}\n\nmodel MailItem {\n id String @id @default(cuid())\n folderId String\n messageId String\n subject String?\n from String?\n receivedAt DateTime?\n listId String?\n listUnsubscribe String?\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n folder MailboxFolder @relation(fields: [folderId], references: [id])\n\n @@index([folderId])\n @@index([messageId])\n}\n\nmodel Rule {\n id String @id @default(cuid())\n tenantId String\n name String\n enabled Boolean @default(true)\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n tenant Tenant @relation(fields: [tenantId], references: [id])\n conditions RuleCondition[]\n actions RuleAction[]\n\n @@index([tenantId])\n}\n\nmodel RuleCondition {\n id String @id @default(cuid())\n ruleId String\n type RuleConditionType\n value String\n\n rule Rule @relation(fields: [ruleId], references: [id])\n\n @@index([ruleId])\n}\n\nmodel RuleAction {\n id String @id @default(cuid())\n ruleId String\n type RuleActionType\n target String?\n\n rule Rule @relation(fields: [ruleId], references: [id])\n\n @@index([ruleId])\n}\n\nmodel CleanupJob {\n id String @id @default(cuid())\n tenantId String\n mailboxAccountId String\n status JobStatus @default(QUEUED)\n dryRun Boolean @default(true)\n unsubscribeEnabled Boolean @default(true)\n routingEnabled Boolean @default(true)\n startedAt DateTime?\n finishedAt DateTime?\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n tenant Tenant @relation(fields: [tenantId], references: [id])\n mailboxAccount MailboxAccount @relation(fields: [mailboxAccountId], references: [id])\n unsubscribeAttempts UnsubscribeAttempt[]\n events CleanupJobEvent[]\n\n @@index([tenantId])\n @@index([mailboxAccountId])\n}\n\nmodel UnsubscribeAttempt {\n id String @id @default(cuid())\n jobId String\n mailItemId String?\n method String\n target String\n status String\n createdAt DateTime @default(now())\n\n job CleanupJob @relation(fields: [jobId], references: [id])\n\n @@index([jobId])\n}\n\nmodel CleanupJobEvent {\n id String @id @default(cuid())\n jobId String\n level String\n message String\n progress Int?\n createdAt DateTime @default(now())\n\n job CleanupJob @relation(fields: [jobId], references: [id])\n\n @@index([jobId])\n}\n\nmodel AppSetting {\n id String @id @default(cuid())\n key String @unique\n value String\n updatedAt DateTime @updatedAt\n}\n", + "inlineSchemaHash": "a722a25b5f786847581c71e94e2c62c6a9680977a2bde0ff3dca71f63aec1155", "copyEngine": true } config.dirname = '/' -config.runtimeDataModel = JSON.parse("{\"models\":{\"Tenant\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"name\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"isActive\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Boolean\",\"default\":true,\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"exportJobs\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"ExportJob\",\"relationName\":\"ExportJobToTenant\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":true},{\"name\":\"users\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"User\",\"relationName\":\"TenantToUser\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"mailboxAccounts\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"MailboxAccount\",\"relationName\":\"MailboxAccountToTenant\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"rules\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Rule\",\"relationName\":\"RuleToTenant\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"jobs\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"CleanupJob\",\"relationName\":\"CleanupJobToTenant\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"ExportJob\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"tenantId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"status\",\"kind\":\"enum\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"ExportStatus\",\"default\":\"QUEUED\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"format\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"scope\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"progress\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Int\",\"default\":0,\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"filePath\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"error\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"expiresAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":true},{\"name\":\"tenant\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Tenant\",\"relationName\":\"ExportJobToTenant\",\"relationFromFields\":[\"tenantId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"User\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"tenantId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"email\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":true,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"password\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"role\",\"kind\":\"enum\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"UserRole\",\"default\":\"USER\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"isActive\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Boolean\",\"default\":true,\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":true},{\"name\":\"tenant\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Tenant\",\"relationName\":\"TenantToUser\",\"relationFromFields\":[\"tenantId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"MailboxAccount\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"tenantId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"email\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"provider\",\"kind\":\"enum\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"MailProvider\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"isActive\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Boolean\",\"default\":true,\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"imapHost\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"imapPort\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Int\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"imapTLS\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Boolean\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"smtpHost\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"smtpPort\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Int\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"smtpTLS\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Boolean\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"oauthToken\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"oauthRefreshToken\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"oauthAccessToken\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"oauthExpiresAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"providerUserId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"oauthLastCheckedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"oauthLastErrorCode\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"appPassword\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":true},{\"name\":\"tenant\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Tenant\",\"relationName\":\"MailboxAccountToTenant\",\"relationFromFields\":[\"tenantId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"folders\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"MailboxFolder\",\"relationName\":\"MailboxAccountToMailboxFolder\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"jobs\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"CleanupJob\",\"relationName\":\"CleanupJobToMailboxAccount\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"MailboxFolder\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"mailboxAccountId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"name\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"remoteId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":true},{\"name\":\"mailboxAccount\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"MailboxAccount\",\"relationName\":\"MailboxAccountToMailboxFolder\",\"relationFromFields\":[\"mailboxAccountId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"mailItems\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"MailItem\",\"relationName\":\"MailItemToMailboxFolder\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"MailItem\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"folderId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"messageId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"subject\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"from\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"receivedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"listId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"listUnsubscribe\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":true},{\"name\":\"folder\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"MailboxFolder\",\"relationName\":\"MailItemToMailboxFolder\",\"relationFromFields\":[\"folderId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"Rule\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"tenantId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"name\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"enabled\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Boolean\",\"default\":true,\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":true},{\"name\":\"tenant\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Tenant\",\"relationName\":\"RuleToTenant\",\"relationFromFields\":[\"tenantId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"conditions\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"RuleCondition\",\"relationName\":\"RuleToRuleCondition\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"actions\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"RuleAction\",\"relationName\":\"RuleToRuleAction\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"RuleCondition\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"ruleId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"type\",\"kind\":\"enum\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"RuleConditionType\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"value\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"rule\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Rule\",\"relationName\":\"RuleToRuleCondition\",\"relationFromFields\":[\"ruleId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"RuleAction\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"ruleId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"type\",\"kind\":\"enum\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"RuleActionType\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"target\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"rule\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Rule\",\"relationName\":\"RuleToRuleAction\",\"relationFromFields\":[\"ruleId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"CleanupJob\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"tenantId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"mailboxAccountId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"status\",\"kind\":\"enum\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"JobStatus\",\"default\":\"QUEUED\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"dryRun\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Boolean\",\"default\":true,\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"unsubscribeEnabled\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Boolean\",\"default\":true,\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"routingEnabled\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Boolean\",\"default\":true,\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"startedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"finishedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":true},{\"name\":\"tenant\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Tenant\",\"relationName\":\"CleanupJobToTenant\",\"relationFromFields\":[\"tenantId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"mailboxAccount\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"MailboxAccount\",\"relationName\":\"CleanupJobToMailboxAccount\",\"relationFromFields\":[\"mailboxAccountId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"unsubscribeAttempts\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"UnsubscribeAttempt\",\"relationName\":\"CleanupJobToUnsubscribeAttempt\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"events\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"CleanupJobEvent\",\"relationName\":\"CleanupJobToCleanupJobEvent\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"UnsubscribeAttempt\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"jobId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"mailItemId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"method\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"target\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"status\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"job\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"CleanupJob\",\"relationName\":\"CleanupJobToUnsubscribeAttempt\",\"relationFromFields\":[\"jobId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"CleanupJobEvent\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"jobId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"level\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"message\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"progress\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Int\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"job\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"CleanupJob\",\"relationName\":\"CleanupJobToCleanupJobEvent\",\"relationFromFields\":[\"jobId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false}},\"enums\":{\"MailProvider\":{\"values\":[{\"name\":\"GMAIL\",\"dbName\":null},{\"name\":\"GMX\",\"dbName\":null},{\"name\":\"WEBDE\",\"dbName\":null}],\"dbName\":null},\"UserRole\":{\"values\":[{\"name\":\"USER\",\"dbName\":null},{\"name\":\"ADMIN\",\"dbName\":null}],\"dbName\":null},\"JobStatus\":{\"values\":[{\"name\":\"QUEUED\",\"dbName\":null},{\"name\":\"RUNNING\",\"dbName\":null},{\"name\":\"SUCCEEDED\",\"dbName\":null},{\"name\":\"FAILED\",\"dbName\":null},{\"name\":\"CANCELED\",\"dbName\":null}],\"dbName\":null},\"RuleActionType\":{\"values\":[{\"name\":\"MOVE\",\"dbName\":null},{\"name\":\"DELETE\",\"dbName\":null},{\"name\":\"ARCHIVE\",\"dbName\":null},{\"name\":\"LABEL\",\"dbName\":null}],\"dbName\":null},\"RuleConditionType\":{\"values\":[{\"name\":\"HEADER\",\"dbName\":null},{\"name\":\"SUBJECT\",\"dbName\":null},{\"name\":\"FROM\",\"dbName\":null},{\"name\":\"LIST_UNSUBSCRIBE\",\"dbName\":null},{\"name\":\"LIST_ID\",\"dbName\":null}],\"dbName\":null},\"ExportStatus\":{\"values\":[{\"name\":\"QUEUED\",\"dbName\":null},{\"name\":\"RUNNING\",\"dbName\":null},{\"name\":\"DONE\",\"dbName\":null},{\"name\":\"FAILED\",\"dbName\":null}],\"dbName\":null}},\"types\":{}}") +config.runtimeDataModel = JSON.parse("{\"models\":{\"Tenant\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"name\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"isActive\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Boolean\",\"default\":true,\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"exportJobs\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"ExportJob\",\"relationName\":\"ExportJobToTenant\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":true},{\"name\":\"users\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"User\",\"relationName\":\"TenantToUser\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"mailboxAccounts\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"MailboxAccount\",\"relationName\":\"MailboxAccountToTenant\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"rules\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Rule\",\"relationName\":\"RuleToTenant\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"jobs\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"CleanupJob\",\"relationName\":\"CleanupJobToTenant\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"ExportJob\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"tenantId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"status\",\"kind\":\"enum\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"ExportStatus\",\"default\":\"QUEUED\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"format\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"scope\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"progress\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Int\",\"default\":0,\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"filePath\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"error\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"expiresAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":true},{\"name\":\"tenant\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Tenant\",\"relationName\":\"ExportJobToTenant\",\"relationFromFields\":[\"tenantId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"User\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"tenantId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"email\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":true,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"password\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"role\",\"kind\":\"enum\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"UserRole\",\"default\":\"USER\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"isActive\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Boolean\",\"default\":true,\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":true},{\"name\":\"tenant\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Tenant\",\"relationName\":\"TenantToUser\",\"relationFromFields\":[\"tenantId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"MailboxAccount\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"tenantId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"email\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"provider\",\"kind\":\"enum\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"MailProvider\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"isActive\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Boolean\",\"default\":true,\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"imapHost\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"imapPort\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Int\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"imapTLS\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Boolean\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"smtpHost\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"smtpPort\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Int\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"smtpTLS\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Boolean\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"oauthToken\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"oauthRefreshToken\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"oauthAccessToken\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"oauthExpiresAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"providerUserId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"oauthLastCheckedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"oauthLastErrorCode\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"appPassword\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":true},{\"name\":\"tenant\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Tenant\",\"relationName\":\"MailboxAccountToTenant\",\"relationFromFields\":[\"tenantId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"folders\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"MailboxFolder\",\"relationName\":\"MailboxAccountToMailboxFolder\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"jobs\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"CleanupJob\",\"relationName\":\"CleanupJobToMailboxAccount\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"MailboxFolder\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"mailboxAccountId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"name\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"remoteId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":true},{\"name\":\"mailboxAccount\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"MailboxAccount\",\"relationName\":\"MailboxAccountToMailboxFolder\",\"relationFromFields\":[\"mailboxAccountId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"mailItems\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"MailItem\",\"relationName\":\"MailItemToMailboxFolder\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"MailItem\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"folderId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"messageId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"subject\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"from\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"receivedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"listId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"listUnsubscribe\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":true},{\"name\":\"folder\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"MailboxFolder\",\"relationName\":\"MailItemToMailboxFolder\",\"relationFromFields\":[\"folderId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"Rule\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"tenantId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"name\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"enabled\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Boolean\",\"default\":true,\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":true},{\"name\":\"tenant\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Tenant\",\"relationName\":\"RuleToTenant\",\"relationFromFields\":[\"tenantId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"conditions\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"RuleCondition\",\"relationName\":\"RuleToRuleCondition\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"actions\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"RuleAction\",\"relationName\":\"RuleToRuleAction\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"RuleCondition\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"ruleId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"type\",\"kind\":\"enum\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"RuleConditionType\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"value\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"rule\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Rule\",\"relationName\":\"RuleToRuleCondition\",\"relationFromFields\":[\"ruleId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"RuleAction\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"ruleId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"type\",\"kind\":\"enum\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"RuleActionType\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"target\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"rule\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Rule\",\"relationName\":\"RuleToRuleAction\",\"relationFromFields\":[\"ruleId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"CleanupJob\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"tenantId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"mailboxAccountId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"status\",\"kind\":\"enum\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"JobStatus\",\"default\":\"QUEUED\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"dryRun\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Boolean\",\"default\":true,\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"unsubscribeEnabled\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Boolean\",\"default\":true,\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"routingEnabled\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Boolean\",\"default\":true,\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"startedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"finishedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":true},{\"name\":\"tenant\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Tenant\",\"relationName\":\"CleanupJobToTenant\",\"relationFromFields\":[\"tenantId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"mailboxAccount\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"MailboxAccount\",\"relationName\":\"CleanupJobToMailboxAccount\",\"relationFromFields\":[\"mailboxAccountId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"unsubscribeAttempts\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"UnsubscribeAttempt\",\"relationName\":\"CleanupJobToUnsubscribeAttempt\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"events\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"CleanupJobEvent\",\"relationName\":\"CleanupJobToCleanupJobEvent\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"UnsubscribeAttempt\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"jobId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"mailItemId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"method\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"target\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"status\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"job\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"CleanupJob\",\"relationName\":\"CleanupJobToUnsubscribeAttempt\",\"relationFromFields\":[\"jobId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"CleanupJobEvent\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"jobId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"level\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"message\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"progress\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Int\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"job\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"CleanupJob\",\"relationName\":\"CleanupJobToCleanupJobEvent\",\"relationFromFields\":[\"jobId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"AppSetting\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"key\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":true,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"value\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":true}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false}},\"enums\":{\"MailProvider\":{\"values\":[{\"name\":\"GMAIL\",\"dbName\":null},{\"name\":\"GMX\",\"dbName\":null},{\"name\":\"WEBDE\",\"dbName\":null}],\"dbName\":null},\"UserRole\":{\"values\":[{\"name\":\"USER\",\"dbName\":null},{\"name\":\"ADMIN\",\"dbName\":null}],\"dbName\":null},\"JobStatus\":{\"values\":[{\"name\":\"QUEUED\",\"dbName\":null},{\"name\":\"RUNNING\",\"dbName\":null},{\"name\":\"SUCCEEDED\",\"dbName\":null},{\"name\":\"FAILED\",\"dbName\":null},{\"name\":\"CANCELED\",\"dbName\":null}],\"dbName\":null},\"RuleActionType\":{\"values\":[{\"name\":\"MOVE\",\"dbName\":null},{\"name\":\"DELETE\",\"dbName\":null},{\"name\":\"ARCHIVE\",\"dbName\":null},{\"name\":\"LABEL\",\"dbName\":null}],\"dbName\":null},\"RuleConditionType\":{\"values\":[{\"name\":\"HEADER\",\"dbName\":null},{\"name\":\"SUBJECT\",\"dbName\":null},{\"name\":\"FROM\",\"dbName\":null},{\"name\":\"LIST_UNSUBSCRIBE\",\"dbName\":null},{\"name\":\"LIST_ID\",\"dbName\":null}],\"dbName\":null},\"ExportStatus\":{\"values\":[{\"name\":\"QUEUED\",\"dbName\":null},{\"name\":\"RUNNING\",\"dbName\":null},{\"name\":\"DONE\",\"dbName\":null},{\"name\":\"FAILED\",\"dbName\":null}],\"dbName\":null}},\"types\":{}}") defineDmmfProperty(exports.Prisma, config.runtimeDataModel) config.engineWasm = undefined diff --git a/backend/node_modules/.prisma/client/index-browser.js b/backend/node_modules/.prisma/client/index-browser.js index 73a23795..19f67e83 100644 --- a/backend/node_modules/.prisma/client/index-browser.js +++ b/backend/node_modules/.prisma/client/index-browser.js @@ -257,6 +257,13 @@ exports.Prisma.CleanupJobEventScalarFieldEnum = { createdAt: 'createdAt' }; +exports.Prisma.AppSettingScalarFieldEnum = { + id: 'id', + key: 'key', + value: 'value', + updatedAt: 'updatedAt' +}; + exports.Prisma.SortOrder = { asc: 'asc', desc: 'desc' @@ -324,7 +331,8 @@ exports.Prisma.ModelName = { RuleAction: 'RuleAction', CleanupJob: 'CleanupJob', UnsubscribeAttempt: 'UnsubscribeAttempt', - CleanupJobEvent: 'CleanupJobEvent' + CleanupJobEvent: 'CleanupJobEvent', + AppSetting: 'AppSetting' }; /** diff --git a/backend/node_modules/.prisma/client/index.d.ts b/backend/node_modules/.prisma/client/index.d.ts index 1024616c..c8a491a0 100644 --- a/backend/node_modules/.prisma/client/index.d.ts +++ b/backend/node_modules/.prisma/client/index.d.ts @@ -73,6 +73,11 @@ export type UnsubscribeAttempt = $Result.DefaultSelection +/** + * Model AppSetting + * + */ +export type AppSetting = $Result.DefaultSelection /** * Enums @@ -404,6 +409,16 @@ export class PrismaClient< * ``` */ get cleanupJobEvent(): Prisma.CleanupJobEventDelegate; + + /** + * `prisma.appSetting`: Exposes CRUD operations for the **AppSetting** model. + * Example usage: + * ```ts + * // Fetch zero or more AppSettings + * const appSettings = await prisma.appSetting.findMany() + * ``` + */ + get appSetting(): Prisma.AppSettingDelegate; } export namespace Prisma { @@ -856,7 +871,8 @@ export namespace Prisma { RuleAction: 'RuleAction', CleanupJob: 'CleanupJob', UnsubscribeAttempt: 'UnsubscribeAttempt', - CleanupJobEvent: 'CleanupJobEvent' + CleanupJobEvent: 'CleanupJobEvent', + AppSetting: 'AppSetting' }; export type ModelName = (typeof ModelName)[keyof typeof ModelName] @@ -872,7 +888,7 @@ export namespace Prisma { export type TypeMap = { meta: { - modelProps: "tenant" | "exportJob" | "user" | "mailboxAccount" | "mailboxFolder" | "mailItem" | "rule" | "ruleCondition" | "ruleAction" | "cleanupJob" | "unsubscribeAttempt" | "cleanupJobEvent" + modelProps: "tenant" | "exportJob" | "user" | "mailboxAccount" | "mailboxFolder" | "mailItem" | "rule" | "ruleCondition" | "ruleAction" | "cleanupJob" | "unsubscribeAttempt" | "cleanupJobEvent" | "appSetting" txIsolationLevel: Prisma.TransactionIsolationLevel } model: { @@ -1716,6 +1732,76 @@ export namespace Prisma { } } } + AppSetting: { + payload: Prisma.$AppSettingPayload + fields: Prisma.AppSettingFieldRefs + operations: { + findUnique: { + args: Prisma.AppSettingFindUniqueArgs + result: $Utils.PayloadToResult | null + } + findUniqueOrThrow: { + args: Prisma.AppSettingFindUniqueOrThrowArgs + result: $Utils.PayloadToResult + } + findFirst: { + args: Prisma.AppSettingFindFirstArgs + result: $Utils.PayloadToResult | null + } + findFirstOrThrow: { + args: Prisma.AppSettingFindFirstOrThrowArgs + result: $Utils.PayloadToResult + } + findMany: { + args: Prisma.AppSettingFindManyArgs + result: $Utils.PayloadToResult[] + } + create: { + args: Prisma.AppSettingCreateArgs + result: $Utils.PayloadToResult + } + createMany: { + args: Prisma.AppSettingCreateManyArgs + result: BatchPayload + } + createManyAndReturn: { + args: Prisma.AppSettingCreateManyAndReturnArgs + result: $Utils.PayloadToResult[] + } + delete: { + args: Prisma.AppSettingDeleteArgs + result: $Utils.PayloadToResult + } + update: { + args: Prisma.AppSettingUpdateArgs + result: $Utils.PayloadToResult + } + deleteMany: { + args: Prisma.AppSettingDeleteManyArgs + result: BatchPayload + } + updateMany: { + args: Prisma.AppSettingUpdateManyArgs + result: BatchPayload + } + upsert: { + args: Prisma.AppSettingUpsertArgs + result: $Utils.PayloadToResult + } + aggregate: { + args: Prisma.AppSettingAggregateArgs + result: $Utils.Optional + } + groupBy: { + args: Prisma.AppSettingGroupByArgs + result: $Utils.Optional[] + } + count: { + args: Prisma.AppSettingCountArgs + result: $Utils.Optional | number + } + } + } } } & { other: { @@ -14157,6 +14243,872 @@ export namespace Prisma { } + /** + * Model AppSetting + */ + + export type AggregateAppSetting = { + _count: AppSettingCountAggregateOutputType | null + _min: AppSettingMinAggregateOutputType | null + _max: AppSettingMaxAggregateOutputType | null + } + + export type AppSettingMinAggregateOutputType = { + id: string | null + key: string | null + value: string | null + updatedAt: Date | null + } + + export type AppSettingMaxAggregateOutputType = { + id: string | null + key: string | null + value: string | null + updatedAt: Date | null + } + + export type AppSettingCountAggregateOutputType = { + id: number + key: number + value: number + updatedAt: number + _all: number + } + + + export type AppSettingMinAggregateInputType = { + id?: true + key?: true + value?: true + updatedAt?: true + } + + export type AppSettingMaxAggregateInputType = { + id?: true + key?: true + value?: true + updatedAt?: true + } + + export type AppSettingCountAggregateInputType = { + id?: true + key?: true + value?: true + updatedAt?: true + _all?: true + } + + export type AppSettingAggregateArgs = { + /** + * Filter which AppSetting to aggregate. + */ + where?: AppSettingWhereInput + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/sorting Sorting Docs} + * + * Determine the order of AppSettings to fetch. + */ + orderBy?: AppSettingOrderByWithRelationInput | AppSettingOrderByWithRelationInput[] + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination#cursor-based-pagination Cursor Docs} + * + * Sets the start position + */ + cursor?: AppSettingWhereUniqueInput + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination Pagination Docs} + * + * Take `±n` AppSettings from the position of the cursor. + */ + take?: number + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination Pagination Docs} + * + * Skip the first `n` AppSettings. + */ + skip?: number + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/aggregations Aggregation Docs} + * + * Count returned AppSettings + **/ + _count?: true | AppSettingCountAggregateInputType + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/aggregations Aggregation Docs} + * + * Select which fields to find the minimum value + **/ + _min?: AppSettingMinAggregateInputType + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/aggregations Aggregation Docs} + * + * Select which fields to find the maximum value + **/ + _max?: AppSettingMaxAggregateInputType + } + + export type GetAppSettingAggregateType = { + [P in keyof T & keyof AggregateAppSetting]: P extends '_count' | 'count' + ? T[P] extends true + ? number + : GetScalarType + : GetScalarType + } + + + + + export type AppSettingGroupByArgs = { + where?: AppSettingWhereInput + orderBy?: AppSettingOrderByWithAggregationInput | AppSettingOrderByWithAggregationInput[] + by: AppSettingScalarFieldEnum[] | AppSettingScalarFieldEnum + having?: AppSettingScalarWhereWithAggregatesInput + take?: number + skip?: number + _count?: AppSettingCountAggregateInputType | true + _min?: AppSettingMinAggregateInputType + _max?: AppSettingMaxAggregateInputType + } + + export type AppSettingGroupByOutputType = { + id: string + key: string + value: string + updatedAt: Date + _count: AppSettingCountAggregateOutputType | null + _min: AppSettingMinAggregateOutputType | null + _max: AppSettingMaxAggregateOutputType | null + } + + type GetAppSettingGroupByPayload = Prisma.PrismaPromise< + Array< + PickEnumerable & + { + [P in ((keyof T) & (keyof AppSettingGroupByOutputType))]: P extends '_count' + ? T[P] extends boolean + ? number + : GetScalarType + : GetScalarType + } + > + > + + + export type AppSettingSelect = $Extensions.GetSelect<{ + id?: boolean + key?: boolean + value?: boolean + updatedAt?: boolean + }, ExtArgs["result"]["appSetting"]> + + export type AppSettingSelectCreateManyAndReturn = $Extensions.GetSelect<{ + id?: boolean + key?: boolean + value?: boolean + updatedAt?: boolean + }, ExtArgs["result"]["appSetting"]> + + export type AppSettingSelectScalar = { + id?: boolean + key?: boolean + value?: boolean + updatedAt?: boolean + } + + + export type $AppSettingPayload = { + name: "AppSetting" + objects: {} + scalars: $Extensions.GetPayloadResult<{ + id: string + key: string + value: string + updatedAt: Date + }, ExtArgs["result"]["appSetting"]> + composites: {} + } + + type AppSettingGetPayload = $Result.GetResult + + type AppSettingCountArgs = + Omit & { + select?: AppSettingCountAggregateInputType | true + } + + export interface AppSettingDelegate { + [K: symbol]: { types: Prisma.TypeMap['model']['AppSetting'], meta: { name: 'AppSetting' } } + /** + * Find zero or one AppSetting that matches the filter. + * @param {AppSettingFindUniqueArgs} args - Arguments to find a AppSetting + * @example + * // Get one AppSetting + * const appSetting = await prisma.appSetting.findUnique({ + * where: { + * // ... provide filter here + * } + * }) + */ + findUnique(args: SelectSubset>): Prisma__AppSettingClient<$Result.GetResult, T, "findUnique"> | null, null, ExtArgs> + + /** + * Find one AppSetting that matches the filter or throw an error with `error.code='P2025'` + * if no matches were found. + * @param {AppSettingFindUniqueOrThrowArgs} args - Arguments to find a AppSetting + * @example + * // Get one AppSetting + * const appSetting = await prisma.appSetting.findUniqueOrThrow({ + * where: { + * // ... provide filter here + * } + * }) + */ + findUniqueOrThrow(args: SelectSubset>): Prisma__AppSettingClient<$Result.GetResult, T, "findUniqueOrThrow">, never, ExtArgs> + + /** + * Find the first AppSetting that matches the filter. + * Note, that providing `undefined` is treated as the value not being there. + * Read more here: https://pris.ly/d/null-undefined + * @param {AppSettingFindFirstArgs} args - Arguments to find a AppSetting + * @example + * // Get one AppSetting + * const appSetting = await prisma.appSetting.findFirst({ + * where: { + * // ... provide filter here + * } + * }) + */ + findFirst(args?: SelectSubset>): Prisma__AppSettingClient<$Result.GetResult, T, "findFirst"> | null, null, ExtArgs> + + /** + * Find the first AppSetting that matches the filter or + * throw `PrismaKnownClientError` with `P2025` code if no matches were found. + * Note, that providing `undefined` is treated as the value not being there. + * Read more here: https://pris.ly/d/null-undefined + * @param {AppSettingFindFirstOrThrowArgs} args - Arguments to find a AppSetting + * @example + * // Get one AppSetting + * const appSetting = await prisma.appSetting.findFirstOrThrow({ + * where: { + * // ... provide filter here + * } + * }) + */ + findFirstOrThrow(args?: SelectSubset>): Prisma__AppSettingClient<$Result.GetResult, T, "findFirstOrThrow">, never, ExtArgs> + + /** + * Find zero or more AppSettings that matches the filter. + * Note, that providing `undefined` is treated as the value not being there. + * Read more here: https://pris.ly/d/null-undefined + * @param {AppSettingFindManyArgs} args - Arguments to filter and select certain fields only. + * @example + * // Get all AppSettings + * const appSettings = await prisma.appSetting.findMany() + * + * // Get first 10 AppSettings + * const appSettings = await prisma.appSetting.findMany({ take: 10 }) + * + * // Only select the `id` + * const appSettingWithIdOnly = await prisma.appSetting.findMany({ select: { id: true } }) + * + */ + findMany(args?: SelectSubset>): Prisma.PrismaPromise<$Result.GetResult, T, "findMany">> + + /** + * Create a AppSetting. + * @param {AppSettingCreateArgs} args - Arguments to create a AppSetting. + * @example + * // Create one AppSetting + * const AppSetting = await prisma.appSetting.create({ + * data: { + * // ... data to create a AppSetting + * } + * }) + * + */ + create(args: SelectSubset>): Prisma__AppSettingClient<$Result.GetResult, T, "create">, never, ExtArgs> + + /** + * Create many AppSettings. + * @param {AppSettingCreateManyArgs} args - Arguments to create many AppSettings. + * @example + * // Create many AppSettings + * const appSetting = await prisma.appSetting.createMany({ + * data: [ + * // ... provide data here + * ] + * }) + * + */ + createMany(args?: SelectSubset>): Prisma.PrismaPromise + + /** + * Create many AppSettings and returns the data saved in the database. + * @param {AppSettingCreateManyAndReturnArgs} args - Arguments to create many AppSettings. + * @example + * // Create many AppSettings + * const appSetting = await prisma.appSetting.createManyAndReturn({ + * data: [ + * // ... provide data here + * ] + * }) + * + * // Create many AppSettings and only return the `id` + * const appSettingWithIdOnly = await prisma.appSetting.createManyAndReturn({ + * select: { id: true }, + * data: [ + * // ... provide data here + * ] + * }) + * Note, that providing `undefined` is treated as the value not being there. + * Read more here: https://pris.ly/d/null-undefined + * + */ + createManyAndReturn(args?: SelectSubset>): Prisma.PrismaPromise<$Result.GetResult, T, "createManyAndReturn">> + + /** + * Delete a AppSetting. + * @param {AppSettingDeleteArgs} args - Arguments to delete one AppSetting. + * @example + * // Delete one AppSetting + * const AppSetting = await prisma.appSetting.delete({ + * where: { + * // ... filter to delete one AppSetting + * } + * }) + * + */ + delete(args: SelectSubset>): Prisma__AppSettingClient<$Result.GetResult, T, "delete">, never, ExtArgs> + + /** + * Update one AppSetting. + * @param {AppSettingUpdateArgs} args - Arguments to update one AppSetting. + * @example + * // Update one AppSetting + * const appSetting = await prisma.appSetting.update({ + * where: { + * // ... provide filter here + * }, + * data: { + * // ... provide data here + * } + * }) + * + */ + update(args: SelectSubset>): Prisma__AppSettingClient<$Result.GetResult, T, "update">, never, ExtArgs> + + /** + * Delete zero or more AppSettings. + * @param {AppSettingDeleteManyArgs} args - Arguments to filter AppSettings to delete. + * @example + * // Delete a few AppSettings + * const { count } = await prisma.appSetting.deleteMany({ + * where: { + * // ... provide filter here + * } + * }) + * + */ + deleteMany(args?: SelectSubset>): Prisma.PrismaPromise + + /** + * Update zero or more AppSettings. + * Note, that providing `undefined` is treated as the value not being there. + * Read more here: https://pris.ly/d/null-undefined + * @param {AppSettingUpdateManyArgs} args - Arguments to update one or more rows. + * @example + * // Update many AppSettings + * const appSetting = await prisma.appSetting.updateMany({ + * where: { + * // ... provide filter here + * }, + * data: { + * // ... provide data here + * } + * }) + * + */ + updateMany(args: SelectSubset>): Prisma.PrismaPromise + + /** + * Create or update one AppSetting. + * @param {AppSettingUpsertArgs} args - Arguments to update or create a AppSetting. + * @example + * // Update or create a AppSetting + * const appSetting = await prisma.appSetting.upsert({ + * create: { + * // ... data to create a AppSetting + * }, + * update: { + * // ... in case it already exists, update + * }, + * where: { + * // ... the filter for the AppSetting we want to update + * } + * }) + */ + upsert(args: SelectSubset>): Prisma__AppSettingClient<$Result.GetResult, T, "upsert">, never, ExtArgs> + + + /** + * Count the number of AppSettings. + * Note, that providing `undefined` is treated as the value not being there. + * Read more here: https://pris.ly/d/null-undefined + * @param {AppSettingCountArgs} args - Arguments to filter AppSettings to count. + * @example + * // Count the number of AppSettings + * const count = await prisma.appSetting.count({ + * where: { + * // ... the filter for the AppSettings we want to count + * } + * }) + **/ + count( + args?: Subset, + ): Prisma.PrismaPromise< + T extends $Utils.Record<'select', any> + ? T['select'] extends true + ? number + : GetScalarType + : number + > + + /** + * Allows you to perform aggregations operations on a AppSetting. + * Note, that providing `undefined` is treated as the value not being there. + * Read more here: https://pris.ly/d/null-undefined + * @param {AppSettingAggregateArgs} args - Select which aggregations you would like to apply and on what fields. + * @example + * // Ordered by age ascending + * // Where email contains prisma.io + * // Limited to the 10 users + * const aggregations = await prisma.user.aggregate({ + * _avg: { + * age: true, + * }, + * where: { + * email: { + * contains: "prisma.io", + * }, + * }, + * orderBy: { + * age: "asc", + * }, + * take: 10, + * }) + **/ + aggregate(args: Subset): Prisma.PrismaPromise> + + /** + * Group by AppSetting. + * Note, that providing `undefined` is treated as the value not being there. + * Read more here: https://pris.ly/d/null-undefined + * @param {AppSettingGroupByArgs} args - Group by arguments. + * @example + * // Group by city, order by createdAt, get count + * const result = await prisma.user.groupBy({ + * by: ['city', 'createdAt'], + * orderBy: { + * createdAt: true + * }, + * _count: { + * _all: true + * }, + * }) + * + **/ + groupBy< + T extends AppSettingGroupByArgs, + HasSelectOrTake extends Or< + Extends<'skip', Keys>, + Extends<'take', Keys> + >, + OrderByArg extends True extends HasSelectOrTake + ? { orderBy: AppSettingGroupByArgs['orderBy'] } + : { orderBy?: AppSettingGroupByArgs['orderBy'] }, + OrderFields extends ExcludeUnderscoreKeys>>, + ByFields extends MaybeTupleToUnion, + ByValid extends Has, + HavingFields extends GetHavingFields, + HavingValid extends Has, + ByEmpty extends T['by'] extends never[] ? True : False, + InputErrors extends ByEmpty extends True + ? `Error: "by" must not be empty.` + : HavingValid extends False + ? { + [P in HavingFields]: P extends ByFields + ? never + : P extends string + ? `Error: Field "${P}" used in "having" needs to be provided in "by".` + : [ + Error, + 'Field ', + P, + ` in "having" needs to be provided in "by"`, + ] + }[HavingFields] + : 'take' extends Keys + ? 'orderBy' extends Keys + ? ByValid extends True + ? {} + : { + [P in OrderFields]: P extends ByFields + ? never + : `Error: Field "${P}" in "orderBy" needs to be provided in "by"` + }[OrderFields] + : 'Error: If you provide "take", you also need to provide "orderBy"' + : 'skip' extends Keys + ? 'orderBy' extends Keys + ? ByValid extends True + ? {} + : { + [P in OrderFields]: P extends ByFields + ? never + : `Error: Field "${P}" in "orderBy" needs to be provided in "by"` + }[OrderFields] + : 'Error: If you provide "skip", you also need to provide "orderBy"' + : ByValid extends True + ? {} + : { + [P in OrderFields]: P extends ByFields + ? never + : `Error: Field "${P}" in "orderBy" needs to be provided in "by"` + }[OrderFields] + >(args: SubsetIntersection & InputErrors): {} extends InputErrors ? GetAppSettingGroupByPayload : Prisma.PrismaPromise + /** + * Fields of the AppSetting model + */ + readonly fields: AppSettingFieldRefs; + } + + /** + * The delegate class that acts as a "Promise-like" for AppSetting. + * Why is this prefixed with `Prisma__`? + * Because we want to prevent naming conflicts as mentioned in + * https://github.com/prisma/prisma-client-js/issues/707 + */ + export interface Prisma__AppSettingClient extends Prisma.PrismaPromise { + readonly [Symbol.toStringTag]: "PrismaPromise" + /** + * Attaches callbacks for the resolution and/or rejection of the Promise. + * @param onfulfilled The callback to execute when the Promise is resolved. + * @param onrejected The callback to execute when the Promise is rejected. + * @returns A Promise for the completion of which ever callback is executed. + */ + then(onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null): $Utils.JsPromise + /** + * Attaches a callback for only the rejection of the Promise. + * @param onrejected The callback to execute when the Promise is rejected. + * @returns A Promise for the completion of the callback. + */ + catch(onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null): $Utils.JsPromise + /** + * Attaches a callback that is invoked when the Promise is settled (fulfilled or rejected). The + * resolved value cannot be modified from the callback. + * @param onfinally The callback to execute when the Promise is settled (fulfilled or rejected). + * @returns A Promise for the completion of the callback. + */ + finally(onfinally?: (() => void) | undefined | null): $Utils.JsPromise + } + + + + + /** + * Fields of the AppSetting model + */ + interface AppSettingFieldRefs { + readonly id: FieldRef<"AppSetting", 'String'> + readonly key: FieldRef<"AppSetting", 'String'> + readonly value: FieldRef<"AppSetting", 'String'> + readonly updatedAt: FieldRef<"AppSetting", 'DateTime'> + } + + + // Custom InputTypes + /** + * AppSetting findUnique + */ + export type AppSettingFindUniqueArgs = { + /** + * Select specific fields to fetch from the AppSetting + */ + select?: AppSettingSelect | null + /** + * Filter, which AppSetting to fetch. + */ + where: AppSettingWhereUniqueInput + } + + /** + * AppSetting findUniqueOrThrow + */ + export type AppSettingFindUniqueOrThrowArgs = { + /** + * Select specific fields to fetch from the AppSetting + */ + select?: AppSettingSelect | null + /** + * Filter, which AppSetting to fetch. + */ + where: AppSettingWhereUniqueInput + } + + /** + * AppSetting findFirst + */ + export type AppSettingFindFirstArgs = { + /** + * Select specific fields to fetch from the AppSetting + */ + select?: AppSettingSelect | null + /** + * Filter, which AppSetting to fetch. + */ + where?: AppSettingWhereInput + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/sorting Sorting Docs} + * + * Determine the order of AppSettings to fetch. + */ + orderBy?: AppSettingOrderByWithRelationInput | AppSettingOrderByWithRelationInput[] + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination#cursor-based-pagination Cursor Docs} + * + * Sets the position for searching for AppSettings. + */ + cursor?: AppSettingWhereUniqueInput + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination Pagination Docs} + * + * Take `±n` AppSettings from the position of the cursor. + */ + take?: number + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination Pagination Docs} + * + * Skip the first `n` AppSettings. + */ + skip?: number + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/distinct Distinct Docs} + * + * Filter by unique combinations of AppSettings. + */ + distinct?: AppSettingScalarFieldEnum | AppSettingScalarFieldEnum[] + } + + /** + * AppSetting findFirstOrThrow + */ + export type AppSettingFindFirstOrThrowArgs = { + /** + * Select specific fields to fetch from the AppSetting + */ + select?: AppSettingSelect | null + /** + * Filter, which AppSetting to fetch. + */ + where?: AppSettingWhereInput + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/sorting Sorting Docs} + * + * Determine the order of AppSettings to fetch. + */ + orderBy?: AppSettingOrderByWithRelationInput | AppSettingOrderByWithRelationInput[] + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination#cursor-based-pagination Cursor Docs} + * + * Sets the position for searching for AppSettings. + */ + cursor?: AppSettingWhereUniqueInput + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination Pagination Docs} + * + * Take `±n` AppSettings from the position of the cursor. + */ + take?: number + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination Pagination Docs} + * + * Skip the first `n` AppSettings. + */ + skip?: number + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/distinct Distinct Docs} + * + * Filter by unique combinations of AppSettings. + */ + distinct?: AppSettingScalarFieldEnum | AppSettingScalarFieldEnum[] + } + + /** + * AppSetting findMany + */ + export type AppSettingFindManyArgs = { + /** + * Select specific fields to fetch from the AppSetting + */ + select?: AppSettingSelect | null + /** + * Filter, which AppSettings to fetch. + */ + where?: AppSettingWhereInput + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/sorting Sorting Docs} + * + * Determine the order of AppSettings to fetch. + */ + orderBy?: AppSettingOrderByWithRelationInput | AppSettingOrderByWithRelationInput[] + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination#cursor-based-pagination Cursor Docs} + * + * Sets the position for listing AppSettings. + */ + cursor?: AppSettingWhereUniqueInput + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination Pagination Docs} + * + * Take `±n` AppSettings from the position of the cursor. + */ + take?: number + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination Pagination Docs} + * + * Skip the first `n` AppSettings. + */ + skip?: number + distinct?: AppSettingScalarFieldEnum | AppSettingScalarFieldEnum[] + } + + /** + * AppSetting create + */ + export type AppSettingCreateArgs = { + /** + * Select specific fields to fetch from the AppSetting + */ + select?: AppSettingSelect | null + /** + * The data needed to create a AppSetting. + */ + data: XOR + } + + /** + * AppSetting createMany + */ + export type AppSettingCreateManyArgs = { + /** + * The data used to create many AppSettings. + */ + data: AppSettingCreateManyInput | AppSettingCreateManyInput[] + skipDuplicates?: boolean + } + + /** + * AppSetting createManyAndReturn + */ + export type AppSettingCreateManyAndReturnArgs = { + /** + * Select specific fields to fetch from the AppSetting + */ + select?: AppSettingSelectCreateManyAndReturn | null + /** + * The data used to create many AppSettings. + */ + data: AppSettingCreateManyInput | AppSettingCreateManyInput[] + skipDuplicates?: boolean + } + + /** + * AppSetting update + */ + export type AppSettingUpdateArgs = { + /** + * Select specific fields to fetch from the AppSetting + */ + select?: AppSettingSelect | null + /** + * The data needed to update a AppSetting. + */ + data: XOR + /** + * Choose, which AppSetting to update. + */ + where: AppSettingWhereUniqueInput + } + + /** + * AppSetting updateMany + */ + export type AppSettingUpdateManyArgs = { + /** + * The data used to update AppSettings. + */ + data: XOR + /** + * Filter which AppSettings to update + */ + where?: AppSettingWhereInput + } + + /** + * AppSetting upsert + */ + export type AppSettingUpsertArgs = { + /** + * Select specific fields to fetch from the AppSetting + */ + select?: AppSettingSelect | null + /** + * The filter to search for the AppSetting to update in case it exists. + */ + where: AppSettingWhereUniqueInput + /** + * In case the AppSetting found by the `where` argument doesn't exist, create a new AppSetting with this data. + */ + create: XOR + /** + * In case the AppSetting was found with the provided `where` argument, update it with this data. + */ + update: XOR + } + + /** + * AppSetting delete + */ + export type AppSettingDeleteArgs = { + /** + * Select specific fields to fetch from the AppSetting + */ + select?: AppSettingSelect | null + /** + * Filter which AppSetting to delete. + */ + where: AppSettingWhereUniqueInput + } + + /** + * AppSetting deleteMany + */ + export type AppSettingDeleteManyArgs = { + /** + * Filter which AppSettings to delete + */ + where?: AppSettingWhereInput + } + + /** + * AppSetting without action + */ + export type AppSettingDefaultArgs = { + /** + * Select specific fields to fetch from the AppSetting + */ + select?: AppSettingSelect | null + } + + /** * Enums */ @@ -14342,6 +15294,16 @@ export namespace Prisma { export type CleanupJobEventScalarFieldEnum = (typeof CleanupJobEventScalarFieldEnum)[keyof typeof CleanupJobEventScalarFieldEnum] + export const AppSettingScalarFieldEnum: { + id: 'id', + key: 'key', + value: 'value', + updatedAt: 'updatedAt' + }; + + export type AppSettingScalarFieldEnum = (typeof AppSettingScalarFieldEnum)[keyof typeof AppSettingScalarFieldEnum] + + export const SortOrder: { asc: 'asc', desc: 'desc' @@ -15418,6 +16380,53 @@ export namespace Prisma { createdAt?: DateTimeWithAggregatesFilter<"CleanupJobEvent"> | Date | string } + export type AppSettingWhereInput = { + AND?: AppSettingWhereInput | AppSettingWhereInput[] + OR?: AppSettingWhereInput[] + NOT?: AppSettingWhereInput | AppSettingWhereInput[] + id?: StringFilter<"AppSetting"> | string + key?: StringFilter<"AppSetting"> | string + value?: StringFilter<"AppSetting"> | string + updatedAt?: DateTimeFilter<"AppSetting"> | Date | string + } + + export type AppSettingOrderByWithRelationInput = { + id?: SortOrder + key?: SortOrder + value?: SortOrder + updatedAt?: SortOrder + } + + export type AppSettingWhereUniqueInput = Prisma.AtLeast<{ + id?: string + key?: string + AND?: AppSettingWhereInput | AppSettingWhereInput[] + OR?: AppSettingWhereInput[] + NOT?: AppSettingWhereInput | AppSettingWhereInput[] + value?: StringFilter<"AppSetting"> | string + updatedAt?: DateTimeFilter<"AppSetting"> | Date | string + }, "id" | "key"> + + export type AppSettingOrderByWithAggregationInput = { + id?: SortOrder + key?: SortOrder + value?: SortOrder + updatedAt?: SortOrder + _count?: AppSettingCountOrderByAggregateInput + _max?: AppSettingMaxOrderByAggregateInput + _min?: AppSettingMinOrderByAggregateInput + } + + export type AppSettingScalarWhereWithAggregatesInput = { + AND?: AppSettingScalarWhereWithAggregatesInput | AppSettingScalarWhereWithAggregatesInput[] + OR?: AppSettingScalarWhereWithAggregatesInput[] + NOT?: AppSettingScalarWhereWithAggregatesInput | AppSettingScalarWhereWithAggregatesInput[] + id?: StringWithAggregatesFilter<"AppSetting"> | string + key?: StringWithAggregatesFilter<"AppSetting"> | string + value?: StringWithAggregatesFilter<"AppSetting"> | string + updatedAt?: DateTimeWithAggregatesFilter<"AppSetting"> | Date | string + } + export type TenantCreateInput = { id?: string name: string @@ -16399,6 +17408,55 @@ export namespace Prisma { createdAt?: DateTimeFieldUpdateOperationsInput | Date | string } + export type AppSettingCreateInput = { + id?: string + key: string + value: string + updatedAt?: Date | string + } + + export type AppSettingUncheckedCreateInput = { + id?: string + key: string + value: string + updatedAt?: Date | string + } + + export type AppSettingUpdateInput = { + id?: StringFieldUpdateOperationsInput | string + key?: StringFieldUpdateOperationsInput | string + value?: StringFieldUpdateOperationsInput | string + updatedAt?: DateTimeFieldUpdateOperationsInput | Date | string + } + + export type AppSettingUncheckedUpdateInput = { + id?: StringFieldUpdateOperationsInput | string + key?: StringFieldUpdateOperationsInput | string + value?: StringFieldUpdateOperationsInput | string + updatedAt?: DateTimeFieldUpdateOperationsInput | Date | string + } + + export type AppSettingCreateManyInput = { + id?: string + key: string + value: string + updatedAt?: Date | string + } + + export type AppSettingUpdateManyMutationInput = { + id?: StringFieldUpdateOperationsInput | string + key?: StringFieldUpdateOperationsInput | string + value?: StringFieldUpdateOperationsInput | string + updatedAt?: DateTimeFieldUpdateOperationsInput | Date | string + } + + export type AppSettingUncheckedUpdateManyInput = { + id?: StringFieldUpdateOperationsInput | string + key?: StringFieldUpdateOperationsInput | string + value?: StringFieldUpdateOperationsInput | string + updatedAt?: DateTimeFieldUpdateOperationsInput | Date | string + } + export type StringFilter<$PrismaModel = never> = { equals?: string | StringFieldRefInput<$PrismaModel> in?: string[] | ListStringFieldRefInput<$PrismaModel> @@ -17268,6 +18326,27 @@ export namespace Prisma { progress?: SortOrder } + export type AppSettingCountOrderByAggregateInput = { + id?: SortOrder + key?: SortOrder + value?: SortOrder + updatedAt?: SortOrder + } + + export type AppSettingMaxOrderByAggregateInput = { + id?: SortOrder + key?: SortOrder + value?: SortOrder + updatedAt?: SortOrder + } + + export type AppSettingMinOrderByAggregateInput = { + id?: SortOrder + key?: SortOrder + value?: SortOrder + updatedAt?: SortOrder + } + export type ExportJobCreateNestedManyWithoutTenantInput = { create?: XOR | ExportJobCreateWithoutTenantInput[] | ExportJobUncheckedCreateWithoutTenantInput[] connectOrCreate?: ExportJobCreateOrConnectWithoutTenantInput | ExportJobCreateOrConnectWithoutTenantInput[] @@ -20543,6 +21622,10 @@ export namespace Prisma { * @deprecated Use CleanupJobEventDefaultArgs instead */ export type CleanupJobEventArgs = CleanupJobEventDefaultArgs + /** + * @deprecated Use AppSettingDefaultArgs instead + */ + export type AppSettingArgs = AppSettingDefaultArgs /** * Batch Payload for updateMany & deleteMany & createMany diff --git a/backend/node_modules/.prisma/client/index.js b/backend/node_modules/.prisma/client/index.js index 8dcda61d..4df09791 100644 --- a/backend/node_modules/.prisma/client/index.js +++ b/backend/node_modules/.prisma/client/index.js @@ -226,6 +226,13 @@ exports.Prisma.CleanupJobEventScalarFieldEnum = { createdAt: 'createdAt' }; +exports.Prisma.AppSettingScalarFieldEnum = { + id: 'id', + key: 'key', + value: 'value', + updatedAt: 'updatedAt' +}; + exports.Prisma.SortOrder = { asc: 'asc', desc: 'desc' @@ -293,7 +300,8 @@ exports.Prisma.ModelName = { RuleAction: 'RuleAction', CleanupJob: 'CleanupJob', UnsubscribeAttempt: 'UnsubscribeAttempt', - CleanupJobEvent: 'CleanupJobEvent' + CleanupJobEvent: 'CleanupJobEvent', + AppSetting: 'AppSetting' }; /** * Create the Client @@ -306,7 +314,7 @@ const config = { "value": "prisma-client-js" }, "output": { - "value": "/root/mailcleaner/backend/node_modules/@prisma/client", + "value": "/app/node_modules/@prisma/client", "fromEnvVar": null }, "config": { @@ -320,10 +328,11 @@ const config = { } ], "previewFeatures": [], - "sourceFilePath": "/root/mailcleaner/backend/prisma/schema.prisma" + "sourceFilePath": "/app/prisma/schema.prisma" }, "relativeEnvPaths": { - "rootEnvPath": null + "rootEnvPath": null, + "schemaEnvPath": "../../../.env" }, "relativePath": "../../../prisma", "clientVersion": "5.22.0", @@ -332,6 +341,7 @@ const config = { "db" ], "activeProvider": "postgresql", + "postinstall": false, "inlineDatasources": { "db": { "url": { @@ -340,8 +350,8 @@ const config = { } } }, - "inlineSchema": "generator client {\n provider = \"prisma-client-js\"\n}\n\ndatasource db {\n provider = \"postgresql\"\n url = env(\"DATABASE_URL\")\n}\n\nenum MailProvider {\n GMAIL\n GMX\n WEBDE\n}\n\nenum UserRole {\n USER\n ADMIN\n}\n\nenum JobStatus {\n QUEUED\n RUNNING\n SUCCEEDED\n FAILED\n CANCELED\n}\n\nenum RuleActionType {\n MOVE\n DELETE\n ARCHIVE\n LABEL\n}\n\nenum RuleConditionType {\n HEADER\n SUBJECT\n FROM\n LIST_UNSUBSCRIBE\n LIST_ID\n}\n\nmodel Tenant {\n id String @id @default(cuid())\n name String\n isActive Boolean @default(true)\n exportJobs ExportJob[]\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n users User[]\n mailboxAccounts MailboxAccount[]\n rules Rule[]\n jobs CleanupJob[]\n}\n\nenum ExportStatus {\n QUEUED\n RUNNING\n DONE\n FAILED\n}\n\nmodel ExportJob {\n id String @id @default(cuid())\n tenantId String\n status ExportStatus @default(QUEUED)\n format String\n scope String\n progress Int @default(0)\n filePath String?\n error String?\n expiresAt DateTime?\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n tenant Tenant @relation(fields: [tenantId], references: [id])\n\n @@index([tenantId])\n}\n\nmodel User {\n id String @id @default(cuid())\n tenantId String\n email String @unique\n password String\n role UserRole @default(USER)\n isActive Boolean @default(true)\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n tenant Tenant @relation(fields: [tenantId], references: [id])\n}\n\nmodel MailboxAccount {\n id String @id @default(cuid())\n tenantId String\n email String\n provider MailProvider\n isActive Boolean @default(true)\n imapHost String\n imapPort Int\n imapTLS Boolean\n smtpHost String?\n smtpPort Int?\n smtpTLS Boolean?\n oauthToken String?\n oauthRefreshToken String?\n oauthAccessToken String?\n oauthExpiresAt DateTime?\n providerUserId String?\n oauthLastCheckedAt DateTime?\n oauthLastErrorCode String?\n appPassword String?\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n tenant Tenant @relation(fields: [tenantId], references: [id])\n folders MailboxFolder[]\n jobs CleanupJob[]\n\n @@index([tenantId])\n}\n\nmodel MailboxFolder {\n id String @id @default(cuid())\n mailboxAccountId String\n name String\n remoteId String?\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n mailboxAccount MailboxAccount @relation(fields: [mailboxAccountId], references: [id])\n mailItems MailItem[]\n\n @@index([mailboxAccountId])\n}\n\nmodel MailItem {\n id String @id @default(cuid())\n folderId String\n messageId String\n subject String?\n from String?\n receivedAt DateTime?\n listId String?\n listUnsubscribe String?\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n folder MailboxFolder @relation(fields: [folderId], references: [id])\n\n @@index([folderId])\n @@index([messageId])\n}\n\nmodel Rule {\n id String @id @default(cuid())\n tenantId String\n name String\n enabled Boolean @default(true)\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n tenant Tenant @relation(fields: [tenantId], references: [id])\n conditions RuleCondition[]\n actions RuleAction[]\n\n @@index([tenantId])\n}\n\nmodel RuleCondition {\n id String @id @default(cuid())\n ruleId String\n type RuleConditionType\n value String\n\n rule Rule @relation(fields: [ruleId], references: [id])\n\n @@index([ruleId])\n}\n\nmodel RuleAction {\n id String @id @default(cuid())\n ruleId String\n type RuleActionType\n target String?\n\n rule Rule @relation(fields: [ruleId], references: [id])\n\n @@index([ruleId])\n}\n\nmodel CleanupJob {\n id String @id @default(cuid())\n tenantId String\n mailboxAccountId String\n status JobStatus @default(QUEUED)\n dryRun Boolean @default(true)\n unsubscribeEnabled Boolean @default(true)\n routingEnabled Boolean @default(true)\n startedAt DateTime?\n finishedAt DateTime?\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n tenant Tenant @relation(fields: [tenantId], references: [id])\n mailboxAccount MailboxAccount @relation(fields: [mailboxAccountId], references: [id])\n unsubscribeAttempts UnsubscribeAttempt[]\n events CleanupJobEvent[]\n\n @@index([tenantId])\n @@index([mailboxAccountId])\n}\n\nmodel UnsubscribeAttempt {\n id String @id @default(cuid())\n jobId String\n mailItemId String?\n method String\n target String\n status String\n createdAt DateTime @default(now())\n\n job CleanupJob @relation(fields: [jobId], references: [id])\n\n @@index([jobId])\n}\n\nmodel CleanupJobEvent {\n id String @id @default(cuid())\n jobId String\n level String\n message String\n progress Int?\n createdAt DateTime @default(now())\n\n job CleanupJob @relation(fields: [jobId], references: [id])\n\n @@index([jobId])\n}\n", - "inlineSchemaHash": "8215bb521bb81721fac5b205bca5af7a38690fa4cc8087b4b16d7967edb43359", + "inlineSchema": "generator client {\n provider = \"prisma-client-js\"\n}\n\ndatasource db {\n provider = \"postgresql\"\n url = env(\"DATABASE_URL\")\n}\n\nenum MailProvider {\n GMAIL\n GMX\n WEBDE\n}\n\nenum UserRole {\n USER\n ADMIN\n}\n\nenum JobStatus {\n QUEUED\n RUNNING\n SUCCEEDED\n FAILED\n CANCELED\n}\n\nenum RuleActionType {\n MOVE\n DELETE\n ARCHIVE\n LABEL\n}\n\nenum RuleConditionType {\n HEADER\n SUBJECT\n FROM\n LIST_UNSUBSCRIBE\n LIST_ID\n}\n\nenum ExportStatus {\n QUEUED\n RUNNING\n DONE\n FAILED\n}\n\nmodel Tenant {\n id String @id @default(cuid())\n name String\n isActive Boolean @default(true)\n exportJobs ExportJob[]\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n users User[]\n mailboxAccounts MailboxAccount[]\n rules Rule[]\n jobs CleanupJob[]\n}\n\nmodel ExportJob {\n id String @id @default(cuid())\n tenantId String\n status ExportStatus @default(QUEUED)\n format String\n scope String\n progress Int @default(0)\n filePath String?\n error String?\n expiresAt DateTime?\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n tenant Tenant @relation(fields: [tenantId], references: [id])\n\n @@index([tenantId])\n}\n\nmodel User {\n id String @id @default(cuid())\n tenantId String\n email String @unique\n password String\n role UserRole @default(USER)\n isActive Boolean @default(true)\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n tenant Tenant @relation(fields: [tenantId], references: [id])\n}\n\nmodel MailboxAccount {\n id String @id @default(cuid())\n tenantId String\n email String\n provider MailProvider\n isActive Boolean @default(true)\n imapHost String\n imapPort Int\n imapTLS Boolean\n smtpHost String?\n smtpPort Int?\n smtpTLS Boolean?\n oauthToken String?\n oauthRefreshToken String?\n oauthAccessToken String?\n oauthExpiresAt DateTime?\n providerUserId String?\n oauthLastCheckedAt DateTime?\n oauthLastErrorCode String?\n appPassword String?\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n tenant Tenant @relation(fields: [tenantId], references: [id])\n folders MailboxFolder[]\n jobs CleanupJob[]\n\n @@index([tenantId])\n}\n\nmodel MailboxFolder {\n id String @id @default(cuid())\n mailboxAccountId String\n name String\n remoteId String?\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n mailboxAccount MailboxAccount @relation(fields: [mailboxAccountId], references: [id])\n mailItems MailItem[]\n\n @@index([mailboxAccountId])\n}\n\nmodel MailItem {\n id String @id @default(cuid())\n folderId String\n messageId String\n subject String?\n from String?\n receivedAt DateTime?\n listId String?\n listUnsubscribe String?\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n folder MailboxFolder @relation(fields: [folderId], references: [id])\n\n @@index([folderId])\n @@index([messageId])\n}\n\nmodel Rule {\n id String @id @default(cuid())\n tenantId String\n name String\n enabled Boolean @default(true)\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n tenant Tenant @relation(fields: [tenantId], references: [id])\n conditions RuleCondition[]\n actions RuleAction[]\n\n @@index([tenantId])\n}\n\nmodel RuleCondition {\n id String @id @default(cuid())\n ruleId String\n type RuleConditionType\n value String\n\n rule Rule @relation(fields: [ruleId], references: [id])\n\n @@index([ruleId])\n}\n\nmodel RuleAction {\n id String @id @default(cuid())\n ruleId String\n type RuleActionType\n target String?\n\n rule Rule @relation(fields: [ruleId], references: [id])\n\n @@index([ruleId])\n}\n\nmodel CleanupJob {\n id String @id @default(cuid())\n tenantId String\n mailboxAccountId String\n status JobStatus @default(QUEUED)\n dryRun Boolean @default(true)\n unsubscribeEnabled Boolean @default(true)\n routingEnabled Boolean @default(true)\n startedAt DateTime?\n finishedAt DateTime?\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n tenant Tenant @relation(fields: [tenantId], references: [id])\n mailboxAccount MailboxAccount @relation(fields: [mailboxAccountId], references: [id])\n unsubscribeAttempts UnsubscribeAttempt[]\n events CleanupJobEvent[]\n\n @@index([tenantId])\n @@index([mailboxAccountId])\n}\n\nmodel UnsubscribeAttempt {\n id String @id @default(cuid())\n jobId String\n mailItemId String?\n method String\n target String\n status String\n createdAt DateTime @default(now())\n\n job CleanupJob @relation(fields: [jobId], references: [id])\n\n @@index([jobId])\n}\n\nmodel CleanupJobEvent {\n id String @id @default(cuid())\n jobId String\n level String\n message String\n progress Int?\n createdAt DateTime @default(now())\n\n job CleanupJob @relation(fields: [jobId], references: [id])\n\n @@index([jobId])\n}\n\nmodel AppSetting {\n id String @id @default(cuid())\n key String @unique\n value String\n updatedAt DateTime @updatedAt\n}\n", + "inlineSchemaHash": "a722a25b5f786847581c71e94e2c62c6a9680977a2bde0ff3dca71f63aec1155", "copyEngine": true } @@ -362,7 +372,7 @@ if (!fs.existsSync(path.join(__dirname, 'schema.prisma'))) { config.isBundled = true } -config.runtimeDataModel = JSON.parse("{\"models\":{\"Tenant\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"name\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"isActive\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Boolean\",\"default\":true,\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"exportJobs\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"ExportJob\",\"relationName\":\"ExportJobToTenant\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":true},{\"name\":\"users\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"User\",\"relationName\":\"TenantToUser\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"mailboxAccounts\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"MailboxAccount\",\"relationName\":\"MailboxAccountToTenant\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"rules\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Rule\",\"relationName\":\"RuleToTenant\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"jobs\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"CleanupJob\",\"relationName\":\"CleanupJobToTenant\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"ExportJob\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"tenantId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"status\",\"kind\":\"enum\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"ExportStatus\",\"default\":\"QUEUED\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"format\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"scope\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"progress\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Int\",\"default\":0,\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"filePath\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"error\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"expiresAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":true},{\"name\":\"tenant\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Tenant\",\"relationName\":\"ExportJobToTenant\",\"relationFromFields\":[\"tenantId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"User\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"tenantId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"email\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":true,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"password\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"role\",\"kind\":\"enum\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"UserRole\",\"default\":\"USER\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"isActive\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Boolean\",\"default\":true,\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":true},{\"name\":\"tenant\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Tenant\",\"relationName\":\"TenantToUser\",\"relationFromFields\":[\"tenantId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"MailboxAccount\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"tenantId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"email\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"provider\",\"kind\":\"enum\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"MailProvider\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"isActive\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Boolean\",\"default\":true,\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"imapHost\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"imapPort\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Int\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"imapTLS\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Boolean\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"smtpHost\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"smtpPort\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Int\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"smtpTLS\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Boolean\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"oauthToken\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"oauthRefreshToken\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"oauthAccessToken\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"oauthExpiresAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"providerUserId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"oauthLastCheckedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"oauthLastErrorCode\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"appPassword\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":true},{\"name\":\"tenant\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Tenant\",\"relationName\":\"MailboxAccountToTenant\",\"relationFromFields\":[\"tenantId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"folders\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"MailboxFolder\",\"relationName\":\"MailboxAccountToMailboxFolder\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"jobs\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"CleanupJob\",\"relationName\":\"CleanupJobToMailboxAccount\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"MailboxFolder\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"mailboxAccountId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"name\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"remoteId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":true},{\"name\":\"mailboxAccount\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"MailboxAccount\",\"relationName\":\"MailboxAccountToMailboxFolder\",\"relationFromFields\":[\"mailboxAccountId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"mailItems\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"MailItem\",\"relationName\":\"MailItemToMailboxFolder\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"MailItem\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"folderId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"messageId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"subject\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"from\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"receivedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"listId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"listUnsubscribe\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":true},{\"name\":\"folder\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"MailboxFolder\",\"relationName\":\"MailItemToMailboxFolder\",\"relationFromFields\":[\"folderId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"Rule\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"tenantId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"name\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"enabled\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Boolean\",\"default\":true,\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":true},{\"name\":\"tenant\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Tenant\",\"relationName\":\"RuleToTenant\",\"relationFromFields\":[\"tenantId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"conditions\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"RuleCondition\",\"relationName\":\"RuleToRuleCondition\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"actions\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"RuleAction\",\"relationName\":\"RuleToRuleAction\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"RuleCondition\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"ruleId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"type\",\"kind\":\"enum\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"RuleConditionType\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"value\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"rule\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Rule\",\"relationName\":\"RuleToRuleCondition\",\"relationFromFields\":[\"ruleId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"RuleAction\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"ruleId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"type\",\"kind\":\"enum\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"RuleActionType\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"target\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"rule\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Rule\",\"relationName\":\"RuleToRuleAction\",\"relationFromFields\":[\"ruleId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"CleanupJob\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"tenantId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"mailboxAccountId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"status\",\"kind\":\"enum\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"JobStatus\",\"default\":\"QUEUED\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"dryRun\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Boolean\",\"default\":true,\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"unsubscribeEnabled\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Boolean\",\"default\":true,\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"routingEnabled\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Boolean\",\"default\":true,\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"startedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"finishedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":true},{\"name\":\"tenant\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Tenant\",\"relationName\":\"CleanupJobToTenant\",\"relationFromFields\":[\"tenantId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"mailboxAccount\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"MailboxAccount\",\"relationName\":\"CleanupJobToMailboxAccount\",\"relationFromFields\":[\"mailboxAccountId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"unsubscribeAttempts\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"UnsubscribeAttempt\",\"relationName\":\"CleanupJobToUnsubscribeAttempt\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"events\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"CleanupJobEvent\",\"relationName\":\"CleanupJobToCleanupJobEvent\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"UnsubscribeAttempt\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"jobId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"mailItemId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"method\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"target\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"status\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"job\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"CleanupJob\",\"relationName\":\"CleanupJobToUnsubscribeAttempt\",\"relationFromFields\":[\"jobId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"CleanupJobEvent\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"jobId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"level\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"message\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"progress\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Int\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"job\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"CleanupJob\",\"relationName\":\"CleanupJobToCleanupJobEvent\",\"relationFromFields\":[\"jobId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false}},\"enums\":{\"MailProvider\":{\"values\":[{\"name\":\"GMAIL\",\"dbName\":null},{\"name\":\"GMX\",\"dbName\":null},{\"name\":\"WEBDE\",\"dbName\":null}],\"dbName\":null},\"UserRole\":{\"values\":[{\"name\":\"USER\",\"dbName\":null},{\"name\":\"ADMIN\",\"dbName\":null}],\"dbName\":null},\"JobStatus\":{\"values\":[{\"name\":\"QUEUED\",\"dbName\":null},{\"name\":\"RUNNING\",\"dbName\":null},{\"name\":\"SUCCEEDED\",\"dbName\":null},{\"name\":\"FAILED\",\"dbName\":null},{\"name\":\"CANCELED\",\"dbName\":null}],\"dbName\":null},\"RuleActionType\":{\"values\":[{\"name\":\"MOVE\",\"dbName\":null},{\"name\":\"DELETE\",\"dbName\":null},{\"name\":\"ARCHIVE\",\"dbName\":null},{\"name\":\"LABEL\",\"dbName\":null}],\"dbName\":null},\"RuleConditionType\":{\"values\":[{\"name\":\"HEADER\",\"dbName\":null},{\"name\":\"SUBJECT\",\"dbName\":null},{\"name\":\"FROM\",\"dbName\":null},{\"name\":\"LIST_UNSUBSCRIBE\",\"dbName\":null},{\"name\":\"LIST_ID\",\"dbName\":null}],\"dbName\":null},\"ExportStatus\":{\"values\":[{\"name\":\"QUEUED\",\"dbName\":null},{\"name\":\"RUNNING\",\"dbName\":null},{\"name\":\"DONE\",\"dbName\":null},{\"name\":\"FAILED\",\"dbName\":null}],\"dbName\":null}},\"types\":{}}") +config.runtimeDataModel = JSON.parse("{\"models\":{\"Tenant\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"name\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"isActive\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Boolean\",\"default\":true,\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"exportJobs\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"ExportJob\",\"relationName\":\"ExportJobToTenant\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":true},{\"name\":\"users\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"User\",\"relationName\":\"TenantToUser\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"mailboxAccounts\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"MailboxAccount\",\"relationName\":\"MailboxAccountToTenant\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"rules\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Rule\",\"relationName\":\"RuleToTenant\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"jobs\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"CleanupJob\",\"relationName\":\"CleanupJobToTenant\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"ExportJob\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"tenantId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"status\",\"kind\":\"enum\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"ExportStatus\",\"default\":\"QUEUED\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"format\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"scope\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"progress\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Int\",\"default\":0,\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"filePath\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"error\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"expiresAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":true},{\"name\":\"tenant\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Tenant\",\"relationName\":\"ExportJobToTenant\",\"relationFromFields\":[\"tenantId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"User\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"tenantId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"email\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":true,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"password\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"role\",\"kind\":\"enum\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"UserRole\",\"default\":\"USER\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"isActive\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Boolean\",\"default\":true,\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":true},{\"name\":\"tenant\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Tenant\",\"relationName\":\"TenantToUser\",\"relationFromFields\":[\"tenantId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"MailboxAccount\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"tenantId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"email\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"provider\",\"kind\":\"enum\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"MailProvider\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"isActive\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Boolean\",\"default\":true,\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"imapHost\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"imapPort\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Int\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"imapTLS\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Boolean\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"smtpHost\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"smtpPort\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Int\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"smtpTLS\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Boolean\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"oauthToken\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"oauthRefreshToken\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"oauthAccessToken\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"oauthExpiresAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"providerUserId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"oauthLastCheckedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"oauthLastErrorCode\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"appPassword\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":true},{\"name\":\"tenant\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Tenant\",\"relationName\":\"MailboxAccountToTenant\",\"relationFromFields\":[\"tenantId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"folders\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"MailboxFolder\",\"relationName\":\"MailboxAccountToMailboxFolder\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"jobs\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"CleanupJob\",\"relationName\":\"CleanupJobToMailboxAccount\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"MailboxFolder\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"mailboxAccountId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"name\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"remoteId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":true},{\"name\":\"mailboxAccount\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"MailboxAccount\",\"relationName\":\"MailboxAccountToMailboxFolder\",\"relationFromFields\":[\"mailboxAccountId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"mailItems\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"MailItem\",\"relationName\":\"MailItemToMailboxFolder\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"MailItem\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"folderId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"messageId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"subject\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"from\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"receivedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"listId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"listUnsubscribe\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":true},{\"name\":\"folder\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"MailboxFolder\",\"relationName\":\"MailItemToMailboxFolder\",\"relationFromFields\":[\"folderId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"Rule\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"tenantId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"name\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"enabled\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Boolean\",\"default\":true,\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":true},{\"name\":\"tenant\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Tenant\",\"relationName\":\"RuleToTenant\",\"relationFromFields\":[\"tenantId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"conditions\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"RuleCondition\",\"relationName\":\"RuleToRuleCondition\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"actions\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"RuleAction\",\"relationName\":\"RuleToRuleAction\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"RuleCondition\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"ruleId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"type\",\"kind\":\"enum\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"RuleConditionType\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"value\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"rule\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Rule\",\"relationName\":\"RuleToRuleCondition\",\"relationFromFields\":[\"ruleId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"RuleAction\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"ruleId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"type\",\"kind\":\"enum\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"RuleActionType\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"target\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"rule\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Rule\",\"relationName\":\"RuleToRuleAction\",\"relationFromFields\":[\"ruleId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"CleanupJob\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"tenantId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"mailboxAccountId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"status\",\"kind\":\"enum\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"JobStatus\",\"default\":\"QUEUED\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"dryRun\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Boolean\",\"default\":true,\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"unsubscribeEnabled\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Boolean\",\"default\":true,\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"routingEnabled\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Boolean\",\"default\":true,\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"startedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"finishedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":true},{\"name\":\"tenant\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Tenant\",\"relationName\":\"CleanupJobToTenant\",\"relationFromFields\":[\"tenantId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"mailboxAccount\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"MailboxAccount\",\"relationName\":\"CleanupJobToMailboxAccount\",\"relationFromFields\":[\"mailboxAccountId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"unsubscribeAttempts\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"UnsubscribeAttempt\",\"relationName\":\"CleanupJobToUnsubscribeAttempt\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"events\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"CleanupJobEvent\",\"relationName\":\"CleanupJobToCleanupJobEvent\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"UnsubscribeAttempt\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"jobId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"mailItemId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"method\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"target\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"status\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"job\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"CleanupJob\",\"relationName\":\"CleanupJobToUnsubscribeAttempt\",\"relationFromFields\":[\"jobId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"CleanupJobEvent\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"jobId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"level\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"message\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"progress\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Int\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"job\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"CleanupJob\",\"relationName\":\"CleanupJobToCleanupJobEvent\",\"relationFromFields\":[\"jobId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"AppSetting\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"key\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":true,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"value\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":true}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false}},\"enums\":{\"MailProvider\":{\"values\":[{\"name\":\"GMAIL\",\"dbName\":null},{\"name\":\"GMX\",\"dbName\":null},{\"name\":\"WEBDE\",\"dbName\":null}],\"dbName\":null},\"UserRole\":{\"values\":[{\"name\":\"USER\",\"dbName\":null},{\"name\":\"ADMIN\",\"dbName\":null}],\"dbName\":null},\"JobStatus\":{\"values\":[{\"name\":\"QUEUED\",\"dbName\":null},{\"name\":\"RUNNING\",\"dbName\":null},{\"name\":\"SUCCEEDED\",\"dbName\":null},{\"name\":\"FAILED\",\"dbName\":null},{\"name\":\"CANCELED\",\"dbName\":null}],\"dbName\":null},\"RuleActionType\":{\"values\":[{\"name\":\"MOVE\",\"dbName\":null},{\"name\":\"DELETE\",\"dbName\":null},{\"name\":\"ARCHIVE\",\"dbName\":null},{\"name\":\"LABEL\",\"dbName\":null}],\"dbName\":null},\"RuleConditionType\":{\"values\":[{\"name\":\"HEADER\",\"dbName\":null},{\"name\":\"SUBJECT\",\"dbName\":null},{\"name\":\"FROM\",\"dbName\":null},{\"name\":\"LIST_UNSUBSCRIBE\",\"dbName\":null},{\"name\":\"LIST_ID\",\"dbName\":null}],\"dbName\":null},\"ExportStatus\":{\"values\":[{\"name\":\"QUEUED\",\"dbName\":null},{\"name\":\"RUNNING\",\"dbName\":null},{\"name\":\"DONE\",\"dbName\":null},{\"name\":\"FAILED\",\"dbName\":null}],\"dbName\":null}},\"types\":{}}") defineDmmfProperty(exports.Prisma, config.runtimeDataModel) config.engineWasm = undefined diff --git a/backend/node_modules/.prisma/client/package.json b/backend/node_modules/.prisma/client/package.json index 0032622f..ed5d38f2 100644 --- a/backend/node_modules/.prisma/client/package.json +++ b/backend/node_modules/.prisma/client/package.json @@ -1,5 +1,5 @@ { - "name": "prisma-client-6c67176f7022092b3b8f46007c6286b76456763ea6fcd4c80a580e5070b636e6", + "name": "prisma-client-0dfa452a25e24864bcf3f498cd8b34074b00c8171bdce93526b6a0ab38135aa4", "main": "index.js", "types": "index.d.ts", "browser": "index-browser.js", diff --git a/backend/node_modules/.prisma/client/schema.prisma b/backend/node_modules/.prisma/client/schema.prisma index 02bfd4a2..5574bf0b 100644 --- a/backend/node_modules/.prisma/client/schema.prisma +++ b/backend/node_modules/.prisma/client/schema.prisma @@ -41,6 +41,13 @@ enum RuleConditionType { LIST_ID } +enum ExportStatus { + QUEUED + RUNNING + DONE + FAILED +} + model Tenant { id String @id @default(cuid()) name String @@ -55,13 +62,6 @@ model Tenant { jobs CleanupJob[] } -enum ExportStatus { - QUEUED - RUNNING - DONE - FAILED -} - model ExportJob { id String @id @default(cuid()) tenantId String @@ -240,3 +240,10 @@ model CleanupJobEvent { @@index([jobId]) } + +model AppSetting { + id String @id @default(cuid()) + key String @unique + value String + updatedAt DateTime @updatedAt +} diff --git a/backend/node_modules/.prisma/client/wasm.js b/backend/node_modules/.prisma/client/wasm.js index 73a23795..19f67e83 100644 --- a/backend/node_modules/.prisma/client/wasm.js +++ b/backend/node_modules/.prisma/client/wasm.js @@ -257,6 +257,13 @@ exports.Prisma.CleanupJobEventScalarFieldEnum = { createdAt: 'createdAt' }; +exports.Prisma.AppSettingScalarFieldEnum = { + id: 'id', + key: 'key', + value: 'value', + updatedAt: 'updatedAt' +}; + exports.Prisma.SortOrder = { asc: 'asc', desc: 'desc' @@ -324,7 +331,8 @@ exports.Prisma.ModelName = { RuleAction: 'RuleAction', CleanupJob: 'CleanupJob', UnsubscribeAttempt: 'UnsubscribeAttempt', - CleanupJobEvent: 'CleanupJobEvent' + CleanupJobEvent: 'CleanupJobEvent', + AppSetting: 'AppSetting' }; /** diff --git a/backend/prisma/migrations/20260122190000_app_setting/migration.sql b/backend/prisma/migrations/20260122190000_app_setting/migration.sql new file mode 100644 index 00000000..8de43d1f --- /dev/null +++ b/backend/prisma/migrations/20260122190000_app_setting/migration.sql @@ -0,0 +1,12 @@ +-- CreateTable +CREATE TABLE "AppSetting" ( + "id" TEXT NOT NULL, + "key" TEXT NOT NULL, + "value" TEXT NOT NULL, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "AppSetting_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE UNIQUE INDEX "AppSetting_key_key" ON "AppSetting"("key"); diff --git a/backend/prisma/schema.prisma b/backend/prisma/schema.prisma index 6a3f6414..6536a4a6 100644 --- a/backend/prisma/schema.prisma +++ b/backend/prisma/schema.prisma @@ -41,6 +41,13 @@ enum RuleConditionType { LIST_ID } +enum ExportStatus { + QUEUED + RUNNING + DONE + FAILED +} + model Tenant { id String @id @default(cuid()) name String @@ -55,13 +62,6 @@ model Tenant { jobs CleanupJob[] } -enum ExportStatus { - QUEUED - RUNNING - DONE - FAILED -} - model ExportJob { id String @id @default(cuid()) tenantId String @@ -240,3 +240,10 @@ model CleanupJobEvent { @@index([jobId]) } + +model AppSetting { + id String @id @default(cuid()) + key String @unique + value String + updatedAt DateTime @updatedAt +} diff --git a/backend/src/admin/routes.ts b/backend/src/admin/routes.ts index 2dcdff45..7ebbb90d 100644 --- a/backend/src/admin/routes.ts +++ b/backend/src/admin/routes.ts @@ -6,6 +6,7 @@ import { queueCleanupJob, removeQueueJob, queueExportJob } from "../queue/queue. import { createReadStream } from "node:fs"; import { access, unlink } from "node:fs/promises"; import { cleanupExpiredExports } from "./exportCleanup.js"; +import { deleteSetting, listSettings, setSetting } from "./settings.js"; const roleSchema = z.object({ role: z.enum(["USER", "ADMIN"]) @@ -19,9 +20,53 @@ const resetSchema = z.object({ password: z.string().min(10) }); +const settingsSchema = z.object({ + settings: z.record(z.string(), z.string().nullable()) +}); + +const allowedSettings = ["google.client_id", "google.client_secret", "google.redirect_uri"] as const; + export async function adminRoutes(app: FastifyInstance) { app.addHook("preHandler", app.requireAdmin); + app.get("/settings", async () => { + const keys = [...allowedSettings]; + const stored = await listSettings(keys); + const envDefaults: Record = { + "google.client_id": process.env.GOOGLE_CLIENT_ID ?? null, + "google.client_secret": process.env.GOOGLE_CLIENT_SECRET ?? null, + "google.redirect_uri": process.env.GOOGLE_REDIRECT_URI ?? null + }; + const settings = keys.reduce>((acc, key) => { + const dbValue = stored[key]; + if (dbValue !== null && dbValue !== undefined) { + acc[key] = { value: dbValue, source: "db" }; + } else if (envDefaults[key]) { + acc[key] = { value: envDefaults[key], source: "env" }; + } else { + acc[key] = { value: null, source: "unset" }; + } + return acc; + }, {}); + return { settings }; + }); + + app.put("/settings", async (request) => { + const input = settingsSchema.parse(request.body); + const entries = Object.entries(input.settings); + for (const [key, value] of entries) { + if (!allowedSettings.includes(key as (typeof allowedSettings)[number])) continue; + if (value === null || value.trim() === "") { + await deleteSetting(key); + } else { + await setSetting(key, value); + } + } + const keys = [...allowedSettings]; + const stored = await listSettings(keys); + return { settings: stored }; + }); + app.get("/tenants", async () => { const tenants = await prisma.tenant.findMany({ include: { _count: { select: { users: true, mailboxAccounts: true, jobs: true } } }, @@ -156,7 +201,18 @@ export async function adminRoutes(app: FastifyInstance) { const tenant = await prisma.tenant.findUnique({ where: { id: params.id } }); if (!tenant) return reply.code(404).send({ message: "Tenant not found" }); + const exportJobs = await prisma.exportJob.findMany({ where: { tenantId: tenant.id } }); + for (const job of exportJobs) { + if (!job.filePath) continue; + try { + await unlink(job.filePath); + } catch { + // ignore missing files + } + } + await prisma.$transaction(async (tx) => { + await tx.exportJob.deleteMany({ where: { tenantId: tenant.id } }); const jobs = await tx.cleanupJob.findMany({ where: { tenantId: tenant.id } }); const jobIds = jobs.map((job) => job.id); await tx.cleanupJobEvent.deleteMany({ where: { jobId: { in: jobIds } } }); diff --git a/backend/src/admin/settings.ts b/backend/src/admin/settings.ts new file mode 100644 index 00000000..5be7aae6 --- /dev/null +++ b/backend/src/admin/settings.ts @@ -0,0 +1,27 @@ +import { prisma } from "../db.js"; + +export const getSetting = async (key: string) => { + const setting = await prisma.appSetting.findUnique({ where: { key } }); + return setting?.value ?? null; +}; + +export const setSetting = async (key: string, value: string) => { + return prisma.appSetting.upsert({ + where: { key }, + update: { value }, + create: { key, value } + }); +}; + +export const listSettings = async (keys: string[]) => { + const settings = await prisma.appSetting.findMany({ where: { key: { in: keys } } }); + const map = new Map(settings.map((s) => [s.key, s.value])); + return keys.reduce>((acc, key) => { + acc[key] = map.get(key) ?? null; + return acc; + }, {}); +}; + +export const deleteSetting = async (key: string) => { + await prisma.appSetting.deleteMany({ where: { key } }); +}; diff --git a/backend/src/config.ts b/backend/src/config.ts index 1916acdd..978e27f5 100644 --- a/backend/src/config.ts +++ b/backend/src/config.ts @@ -9,7 +9,13 @@ const envSchema = z.object({ GOOGLE_CLIENT_ID: z.string().optional(), GOOGLE_CLIENT_SECRET: z.string().optional(), GOOGLE_REDIRECT_URI: z.string().optional(), - TRUST_PROXY: z.coerce.boolean().default(false) + TRUST_PROXY: z.coerce.boolean().default(false), + SEED_ENABLED: z.coerce.boolean().default(true), + SEED_TENANT: z.string().default("Default Tenant"), + SEED_TENANT_ID: z.string().default("seed-tenant"), + SEED_ADMIN_EMAIL: z.string().email().optional(), + SEED_ADMIN_PASSWORD: z.string().min(10).optional(), + SEED_FORCE_PASSWORD_UPDATE: z.coerce.boolean().default(false) }); export type AppConfig = z.infer; @@ -23,7 +29,13 @@ const parsed = envSchema.safeParse({ GOOGLE_CLIENT_ID: process.env.GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET: process.env.GOOGLE_CLIENT_SECRET, GOOGLE_REDIRECT_URI: process.env.GOOGLE_REDIRECT_URI, - TRUST_PROXY: process.env.TRUST_PROXY + TRUST_PROXY: process.env.TRUST_PROXY, + SEED_ENABLED: process.env.SEED_ENABLED, + SEED_TENANT: process.env.SEED_TENANT, + SEED_TENANT_ID: process.env.SEED_TENANT_ID, + SEED_ADMIN_EMAIL: process.env.SEED_ADMIN_EMAIL, + SEED_ADMIN_PASSWORD: process.env.SEED_ADMIN_PASSWORD, + SEED_FORCE_PASSWORD_UPDATE: process.env.SEED_FORCE_PASSWORD_UPDATE }); if (!parsed.success) { diff --git a/backend/src/mail/gmail.ts b/backend/src/mail/gmail.ts index 74ae6a16..03c52d5f 100644 --- a/backend/src/mail/gmail.ts +++ b/backend/src/mail/gmail.ts @@ -2,20 +2,21 @@ import { google } from "googleapis"; import { MailboxAccount } from "@prisma/client"; import { config } from "../config.js"; import { prisma } from "../db.js"; +import { getSetting } from "../admin/settings.js"; -const getOAuthClient = () => { - if (!config.GOOGLE_CLIENT_ID || !config.GOOGLE_CLIENT_SECRET || !config.GOOGLE_REDIRECT_URI) { +const getOAuthClient = async () => { + const clientId = (await getSetting("google.client_id")) ?? config.GOOGLE_CLIENT_ID; + const clientSecret = (await getSetting("google.client_secret")) ?? config.GOOGLE_CLIENT_SECRET; + const redirectUri = (await getSetting("google.redirect_uri")) ?? config.GOOGLE_REDIRECT_URI; + + if (!clientId || !clientSecret || !redirectUri) { throw new Error("Google OAuth config missing"); } - return new google.auth.OAuth2( - config.GOOGLE_CLIENT_ID, - config.GOOGLE_CLIENT_SECRET, - config.GOOGLE_REDIRECT_URI - ); + return new google.auth.OAuth2(clientId, clientSecret, redirectUri); }; -export const getGmailAuthUrl = (state: string) => { - const client = getOAuthClient(); +export const getGmailAuthUrl = async (state: string) => { + const client = await getOAuthClient(); return client.generateAuthUrl({ access_type: "offline", prompt: "consent", @@ -28,7 +29,7 @@ export const getGmailAuthUrl = (state: string) => { }; export const exchangeGmailCode = async (code: string) => { - const client = getOAuthClient(); + const client = await getOAuthClient(); const { tokens } = await client.getToken(code); return tokens; }; @@ -49,7 +50,7 @@ export const gmailClientForAccount = async (account: MailboxAccount) => { throw new Error("Gmail OAuth not configured"); } - const client = getOAuthClient(); + const client = await getOAuthClient(); client.setCredentials({ refresh_token: account.oauthRefreshToken ?? undefined, access_token: account.oauthAccessToken ?? undefined, diff --git a/backend/src/mail/oauthRoutes.ts b/backend/src/mail/oauthRoutes.ts index 9e59f34e..f04c6094 100644 --- a/backend/src/mail/oauthRoutes.ts +++ b/backend/src/mail/oauthRoutes.ts @@ -9,7 +9,7 @@ const urlSchema = z.object({ accountId: z.string() }); export async function oauthRoutes(app: FastifyInstance) { app.addHook("preHandler", app.authenticate); - app.post("/gmail/url", async (request) => { + app.post("/gmail/url", async (request, reply) => { const input = urlSchema.parse(request.body); const account = await prisma.mailboxAccount.findFirst({ where: { id: input.accountId, tenantId: request.user.tenantId, provider: "GMAIL" } @@ -19,8 +19,12 @@ export async function oauthRoutes(app: FastifyInstance) { } const state = `${account.id}:${request.user.tenantId}`; - const url = getGmailAuthUrl(state); - return { url }; + try { + const url = await getGmailAuthUrl(state); + return { url }; + } catch { + return reply.code(400).send({ message: "Google OAuth config missing" }); + } }); app.get("/gmail/callback", async (request, reply) => { @@ -37,7 +41,12 @@ export async function oauthRoutes(app: FastifyInstance) { return reply.code(404).send({ message: "Account not found" }); } - const tokens = await exchangeGmailCode(query.code); + let tokens; + try { + tokens = await exchangeGmailCode(query.code); + } catch { + return reply.code(400).send({ message: "Google OAuth config missing" }); + } await storeGmailTokens(account.id, { access_token: tokens.access_token ?? undefined, refresh_token: tokens.refresh_token ?? undefined, diff --git a/backend/src/main.ts b/backend/src/main.ts index b157731b..ad65213c 100644 --- a/backend/src/main.ts +++ b/backend/src/main.ts @@ -15,6 +15,7 @@ import { queueRoutes } from "./queue/routes.js"; import { rulesRoutes } from "./rules/routes.js"; import { adminRoutes } from "./admin/routes.js"; import { oauthRoutes } from "./mail/oauthRoutes.js"; +import { ensureSeedData } from "./seed.js"; const app = Fastify({ logger: { @@ -26,7 +27,11 @@ const app = Fastify({ trustProxy: config.TRUST_PROXY }); -await app.register(cors, { origin: true }); +await app.register(cors, { + origin: true, + methods: ["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"], + allowedHeaders: ["Authorization", "Content-Type"] +}); await app.register(helmet); await app.register(jwt, { secret: config.JWT_SECRET }); await app.register(authPlugin); @@ -48,6 +53,8 @@ await app.register(rulesRoutes, { prefix: "/rules" }); await app.register(adminRoutes, { prefix: "/admin" }); await app.register(oauthRoutes, { prefix: "/oauth" }); +await ensureSeedData(); + const start = async () => { try { await app.listen({ port: config.PORT, host: "0.0.0.0" }); diff --git a/backend/src/seed.ts b/backend/src/seed.ts new file mode 100644 index 00000000..b3a45e08 --- /dev/null +++ b/backend/src/seed.ts @@ -0,0 +1,39 @@ +import argon2 from "argon2"; +import { prisma } from "./db.js"; +import { config } from "./config.js"; + +export const ensureSeedData = async () => { + if (!config.SEED_ENABLED) return; + if (!config.SEED_ADMIN_EMAIL || !config.SEED_ADMIN_PASSWORD) return; + + const existingUser = await prisma.user.findUnique({ where: { email: config.SEED_ADMIN_EMAIL } }); + if (existingUser) { + const updates: { role?: "ADMIN"; isActive?: boolean; password?: string } = {}; + if (existingUser.role !== "ADMIN") updates.role = "ADMIN"; + if (!existingUser.isActive) updates.isActive = true; + if (config.SEED_FORCE_PASSWORD_UPDATE) { + updates.password = await argon2.hash(config.SEED_ADMIN_PASSWORD); + } + if (Object.keys(updates).length) { + await prisma.user.update({ where: { id: existingUser.id }, data: updates }); + } + return; + } + + const tenant = await prisma.tenant.upsert({ + where: { id: config.SEED_TENANT_ID }, + update: { name: config.SEED_TENANT, isActive: true }, + create: { id: config.SEED_TENANT_ID, name: config.SEED_TENANT, isActive: true } + }); + + const hashed = await argon2.hash(config.SEED_ADMIN_PASSWORD); + await prisma.user.create({ + data: { + tenantId: tenant.id, + email: config.SEED_ADMIN_EMAIL, + password: hashed, + role: "ADMIN", + isActive: true + } + }); +}; diff --git a/docker-compose.yml b/docker-compose.yml index c05dff64..967d13ee 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -42,6 +42,7 @@ services: depends_on: - postgres - redis + command: ["sh", "-c", "npm run prisma:generate && npm run dev"] ports: - "${API_PORT:-8000}:${API_PORT:-8000}" volumes: @@ -71,7 +72,7 @@ services: depends_on: - postgres - redis - command: ["npm", "run", "worker:dev"] + command: ["sh", "-c", "npm run prisma:generate && npm run worker:dev"] volumes: - ./backend:/app diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index df32e229..f11a6d3a 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -2,6 +2,7 @@ import { useEffect, useMemo, useState } from "react"; import { useTranslation } from "react-i18next"; import { apiFetch, createEventSource } from "./api"; import AdminPanel from "./admin"; +import { useToast } from "./toast"; const languages = [ { code: "de", label: "Deutsch" }, @@ -47,9 +48,15 @@ const defaultAction = { type: "MOVE", target: "Newsletter" }; export default function App() { const { t, i18n } = useTranslation(); + const { pushToast } = useToast(); const [activeLang, setActiveLang] = useState(i18n.language); const [token, setToken] = useState(localStorage.getItem("token") ?? ""); - const [authMode, setAuthMode] = useState<"login" | "register">("login"); + const [showAdmin, setShowAdmin] = useState( + localStorage.getItem("ui.showAdmin") === "true" + ); + const [authMode, setAuthMode] = useState<"login" | "register">( + (localStorage.getItem("ui.authMode") as "login" | "register") ?? "login" + ); const [authEmail, setAuthEmail] = useState(""); const [authPassword, setAuthPassword] = useState(""); const [tenantName, setTenantName] = useState(""); @@ -58,12 +65,15 @@ export default function App() { const [accounts, setAccounts] = useState([]); const [rules, setRules] = useState([]); const [jobs, setJobs] = useState([]); - const [selectedJobId, setSelectedJobId] = useState(null); + const [selectedJobId, setSelectedJobId] = useState( + localStorage.getItem("ui.selectedJobId") + ); const [events, setEvents] = useState([]); const [accountEmail, setAccountEmail] = useState(""); const [accountProvider, setAccountProvider] = useState("GMAIL"); const [accountPassword, setAccountPassword] = useState(""); + const [showProviderHelp, setShowProviderHelp] = useState(false); const [ruleName, setRuleName] = useState(""); const [ruleEnabled, setRuleEnabled] = useState(true); @@ -82,9 +92,28 @@ export default function App() { const isAuthenticated = useMemo(() => Boolean(token), [token]); + const getErrorMessage = (err: unknown) => { + if (err instanceof Error) { + try { + const parsed = JSON.parse(err.message) as { message?: string }; + if (parsed?.message) return parsed.message; + } catch { + // ignore parsing + } + return err.message; + } + return t("toastGenericError"); + }; + const loadInitial = async (authToken: string) => { const me = await apiFetch("/tenants/me", {}, authToken); setUser(me.user); + if (me.user?.role === "ADMIN") { + const stored = localStorage.getItem("ui.showAdmin"); + if (stored === null) { + setShowAdmin(false); + } + } setTenant(me.tenant); const accountsData = await apiFetch("/mail/accounts", {}, authToken); @@ -119,12 +148,32 @@ export default function App() { useEffect(() => { if (!token) return; - loadInitial(token).catch(() => { - setToken(""); - localStorage.removeItem("token"); + loadInitial(token).catch((err: unknown) => { + const status = (err as { status?: number }).status; + if (status === 401 || status === 403) { + setToken(""); + localStorage.removeItem("token"); + pushToast(t("toastSessionExpired"), "info"); + } }); }, [token]); + useEffect(() => { + localStorage.setItem("ui.showAdmin", String(showAdmin)); + }, [showAdmin]); + + useEffect(() => { + localStorage.setItem("ui.authMode", authMode); + }, [authMode]); + + useEffect(() => { + if (selectedJobId) { + localStorage.setItem("ui.selectedJobId", selectedJobId); + } else { + localStorage.removeItem("ui.selectedJobId"); + } + }, [selectedJobId]); + useEffect(() => { if (!token) return; const interval = setInterval(() => { @@ -156,91 +205,117 @@ export default function App() { }, [selectedJobId, token]); const handleAuth = async () => { - if (authMode === "login") { + try { + if (authMode === "login") { + const result = await apiFetch( + "/auth/login", + { + method: "POST", + body: JSON.stringify({ email: authEmail, password: authPassword }) + } + ); + localStorage.setItem("token", result.token); + setToken(result.token); + pushToast(t("toastLoginSuccess"), "success"); + return; + } + const result = await apiFetch( - "/auth/login", + "/auth/register", { method: "POST", - body: JSON.stringify({ email: authEmail, password: authPassword }) + body: JSON.stringify({ tenantName, email: authEmail, password: authPassword }) } ); localStorage.setItem("token", result.token); setToken(result.token); - return; + pushToast(t("toastRegisterSuccess"), "success"); + } catch (err) { + pushToast(getErrorMessage(err), "error"); } - - const result = await apiFetch( - "/auth/register", - { - method: "POST", - body: JSON.stringify({ tenantName, email: authEmail, password: authPassword }) - } - ); - localStorage.setItem("token", result.token); - setToken(result.token); }; const handleAddAccount = async () => { - const result = await apiFetch( - "/mail/accounts", - { - method: "POST", - body: JSON.stringify({ - email: accountEmail, - provider: accountProvider, - appPassword: accountPassword || undefined - }) - }, - token - ); - setAccounts((prev) => [...prev, result.account]); - setAccountEmail(""); - setAccountPassword(""); + try { + const result = await apiFetch( + "/mail/accounts", + { + method: "POST", + body: JSON.stringify({ + email: accountEmail, + provider: accountProvider, + appPassword: accountPassword || undefined + }) + }, + token + ); + setAccounts((prev) => [...prev, result.account]); + setAccountEmail(""); + setAccountPassword(""); + pushToast(t("toastMailboxAdded"), "success"); + } catch (err) { + pushToast(getErrorMessage(err), "error"); + } }; const handleAddRule = async () => { - const result = await apiFetch( - "/rules", - { - method: "POST", - body: JSON.stringify({ - name: ruleName, - enabled: ruleEnabled, - conditions, - actions - }) - }, - token - ); - setRules((prev) => [...prev, result.rule]); - setRuleName(""); - setConditions([{ ...defaultCondition }]); - setActions([{ ...defaultAction }]); + try { + const result = await apiFetch( + "/rules", + { + method: "POST", + body: JSON.stringify({ + name: ruleName, + enabled: ruleEnabled, + conditions, + actions + }) + }, + token + ); + setRules((prev) => [...prev, result.rule]); + setRuleName(""); + setConditions([{ ...defaultCondition }]); + setActions([{ ...defaultAction }]); + pushToast(t("toastRuleSaved"), "success"); + } catch (err) { + pushToast(getErrorMessage(err), "error"); + } }; const handleDeleteRule = async (ruleId: string) => { - await apiFetch(`/rules/${ruleId}`, { method: "DELETE" }, token); - setRules((prev) => prev.filter((rule) => rule.id !== ruleId)); + try { + await apiFetch(`/rules/${ruleId}`, { method: "DELETE" }, token); + setRules((prev) => prev.filter((rule) => rule.id !== ruleId)); + pushToast(t("toastRuleDeleted"), "info"); + } catch (err) { + pushToast(getErrorMessage(err), "error"); + } }; const handleStartCleanup = async () => { - const result = await apiFetch( - "/mail/cleanup", - { - method: "POST", - body: JSON.stringify({ - mailboxAccountId: cleanupAccountId, - dryRun, - unsubscribeEnabled, - routingEnabled - }) - }, - token - ); - const jobsData = await apiFetch("/jobs", {}, token); - setJobs(jobsData.jobs ?? []); - setSelectedJobId(result.jobId); - setEvents([]); + try { + const result = await apiFetch( + "/mail/cleanup", + { + method: "POST", + body: JSON.stringify({ + mailboxAccountId: cleanupAccountId, + dryRun, + unsubscribeEnabled, + routingEnabled + }) + }, + token + ); + const jobsData = await apiFetch("/jobs", {}, token); + setJobs(jobsData.jobs ?? []); + setSelectedJobId(result.jobId); + setEvents([]); + pushToast(t("toastCleanupStarted"), "success"); + } catch (err) { + pushToast(getErrorMessage(err), "error"); + } }; const handleLogout = () => { @@ -248,6 +323,7 @@ export default function App() { localStorage.removeItem("token"); setUser(null); setTenant(null); + pushToast(t("toastLoggedOut"), "info"); }; const addCondition = () => setConditions((prev) => [...prev, { ...defaultCondition }]); @@ -276,16 +352,26 @@ export default function App() { }; const startGmailOauth = async (accountId: string) => { - const result = await apiFetch( - "/oauth/gmail/url", - { method: "POST", body: JSON.stringify({ accountId }) }, - token - ); - if (result.url) { - window.location.href = result.url; + try { + const result = await apiFetch( + "/oauth/gmail/url", + { method: "POST", body: JSON.stringify({ accountId }) }, + token + ); + if (result.url) { + window.location.href = result.url; + } + } catch (err) { + pushToast(getErrorMessage(err), "error"); } }; + const providerHint = () => { + if (accountProvider === "GMAIL") return t("providerHintGmail"); + if (accountProvider === "GMX") return t("providerHintGmx"); + return t("providerHintWebde"); + }; + if (!isAuthenticated) { return (
@@ -295,8 +381,7 @@ export default function App() {

{t("appName")}

{t("tagline")}

-
- {t("language")} +
{languages.map((lang) => ( ))}
@@ -358,8 +443,8 @@ export default function App() {

{t("appName")}

{tenant?.name ?? t("tenantFallback")}

-
- {user?.email ?? ""} +
+ {user?.email ?? ""}
{languages.map((lang) => ( ))} @@ -413,9 +498,42 @@ export default function App() {
- {user?.role === "ADMIN" && } + {user?.role === "ADMIN" && ( +
+ + {t("admin")} +
+ )} -
+ {user?.role === "ADMIN" && showAdmin ? ( +
+
+
+

{t("adminConsole")}

+

{t("adminConsoleHint")}

+
+
+ +
+ ) : ( +
+
+
+

{t("userWorkspace")}

+

{t("userWorkspaceHint")}

+
+
+
+ )} + + {!showAdmin && ( +

{t("mailboxAdd")}

setAccountPassword(event.target.value)} /> - - {accountProvider === "GMAIL" && cleanupAccountId && ( - - )} + + {accountProvider === "GMAIL" && cleanupAccountId && ( + + )} +
@@ -453,29 +581,33 @@ export default function App() { ))} - - - - +
+ + + +
+
+ +
@@ -485,10 +617,12 @@ export default function App() { value={ruleName} onChange={(event) => setRuleName(event.target.value)} /> - +
+ +

{t("rulesConditions")}

{conditions.map((condition, idx) => ( @@ -522,7 +656,7 @@ export default function App() { />
))} - +

{t("rulesActions")}

@@ -556,15 +690,19 @@ export default function App() { />
))} - + +
+
+
- + )} -
+ {!showAdmin && ( +

{t("adminMailboxStatus")}

{accounts.map((account) => ( @@ -613,8 +751,10 @@ export default function App() { ))}
+ )} -
+ {!showAdmin && ( +

{t("rulesOverview")}

{rules.map((rule) => ( @@ -658,7 +798,28 @@ export default function App() { )}
+ )} + {showProviderHelp && ( +
setShowProviderHelp(false)}> +
event.stopPropagation()}> +
+

{t("providerHelpTitle")}

+ +
+
+

{t("providerGmail")}

+

{t("providerHelpGmail")}

+

{t("providerGmx")}

+

{t("providerHelpGmx")}

+

{t("providerWebde")}

+

{t("providerHelpWebde")}

+
+
+
+ )} ); } diff --git a/frontend/src/admin.tsx b/frontend/src/admin.tsx index 239dc96a..24ec42be 100644 --- a/frontend/src/admin.tsx +++ b/frontend/src/admin.tsx @@ -3,6 +3,7 @@ import { apiFetch, createEventSourceFor } from "./api"; import { downloadFile } from "./export"; import { downloadExport } from "./exportHistory"; import { useTranslation } from "react-i18next"; +import { useToast } from "./toast"; type Tenant = { id: string; @@ -39,6 +40,11 @@ type Job = { mailboxAccount?: { id: string; email: string } | null; }; +type SettingValue = { + value: string | null; + source: "db" | "env" | "unset"; +}; + type Props = { token: string; onImpersonate: (token: string) => void; @@ -46,11 +52,14 @@ type Props = { export default function AdminPanel({ token, onImpersonate }: Props) { const { t } = useTranslation(); + const { pushToast } = useToast(); const [tenants, setTenants] = useState([]); const [users, setUsers] = useState([]); const [accounts, setAccounts] = useState([]); const [jobs, setJobs] = useState([]); - const [activeTab, setActiveTab] = useState<"tenants" | "users" | "accounts" | "jobs">("tenants"); + const [activeTab, setActiveTab] = useState<"tenants" | "users" | "accounts" | "jobs" | "settings">( + (localStorage.getItem("ui.adminTab") as "tenants" | "users" | "accounts" | "jobs" | "settings") ?? "tenants" + ); const [resetUserId, setResetUserId] = useState(null); const [resetPassword, setResetPassword] = useState(""); const [exportTenantId, setExportTenantId] = useState(null); @@ -64,6 +73,14 @@ export default function AdminPanel({ token, onImpersonate }: Props) { const [userSort, setUserSort] = useState<"recent" | "oldest" | "email">("recent"); const [accountSort, setAccountSort] = useState<"recent" | "oldest" | "email">("recent"); const [jobSort, setJobSort] = useState<"recent" | "oldest" | "status">("recent"); + const [settings, setSettings] = useState>({}); + const [settingsDraft, setSettingsDraft] = useState({ + googleClientId: "", + googleClientSecret: "", + googleRedirectUri: "" + }); + const [settingsStatus, setSettingsStatus] = useState<"idle" | "saving" | "saved" | "error">("idle"); + const [showGoogleSecret, setShowGoogleSecret] = useState(false); const loadAll = async () => { const tenantData = await apiFetch("/admin/tenants", {}, token); @@ -77,59 +94,108 @@ export default function AdminPanel({ token, onImpersonate }: Props) { const jobsData = await apiFetch("/admin/jobs", {}, token); setJobs(jobsData.jobs ?? []); - const exportsData = await apiFetch("/admin/exports", {}, token); - setExportHistory(exportsData.exports ?? []); + + const exportsData = await apiFetch("/admin/exports", {}, token); + setExportHistory(exportsData.exports ?? []); + + try { + await loadSettings(); + } catch { + // ignore settings fetch failures in initial load + } + }; + + const getErrorMessage = (err: unknown) => { + if (err instanceof Error) { + try { + const parsed = JSON.parse(err.message) as { message?: string }; + if (parsed?.message) return parsed.message; + } catch { + // ignore parsing + } + return err.message; + } + return t("toastGenericError"); }; useEffect(() => { loadAll().catch(() => undefined); }, []); + useEffect(() => { + localStorage.setItem("ui.adminTab", activeTab); + }, [activeTab]); + + const loadSettings = async () => { + const data = await apiFetch("/admin/settings", {}, token); + const next = data.settings ?? {}; + setSettings(next); + setSettingsDraft({ + googleClientId: next["google.client_id"]?.value ?? "", + googleClientSecret: next["google.client_secret"]?.value ?? "", + googleRedirectUri: next["google.redirect_uri"]?.value ?? "" + }); + }; + const toggleTenant = async (tenant: Tenant) => { - const result = await apiFetch( - `/admin/tenants/${tenant.id}`, - { method: "PUT", body: JSON.stringify({ isActive: !tenant.isActive }) }, - token - ); - setTenants((prev) => prev.map((item) => (item.id === tenant.id ? result.tenant : item))); + try { + const result = await apiFetch( + `/admin/tenants/${tenant.id}`, + { method: "PUT", body: JSON.stringify({ isActive: !tenant.isActive }) }, + token + ); + setTenants((prev) => prev.map((item) => (item.id === tenant.id ? result.tenant : item))); + pushToast(t("toastTenantUpdated"), "success"); + } catch (err) { + pushToast(getErrorMessage(err), "error"); + } }; const exportTenant = async (tenant: Tenant) => { - setExportTenantId(tenant.id); - setExportStatus("loading"); - if (exportFormat === "json") { - const result = await apiFetch(`/admin/tenants/${tenant.id}/export?scope=${exportScope}`, {}, token); - const blob = new Blob([JSON.stringify(result, null, 2)], { type: "application/json" }); - downloadFile(blob, `tenant-${tenant.id}.json`); - } else if (exportFormat === "csv") { - await exportTenantCsv(tenant); - return; - } else { - const result = await apiFetch(`/admin/tenants/${tenant.id}/export?format=zip&scope=${exportScope}`, {}, token); - setExportJobId(result.jobId); - setExportHistory((prev) => [{ id: result.jobId, status: "QUEUED" }, ...prev]); - const source = createEventSourceFor(`exports/${result.jobId}`, token); - source.onmessage = async (event) => { - const data = JSON.parse(event.data); - setExportHistory((prev) => - prev.map((item) => (item.id === data.id ? { ...item, status: data.status, expiresAt: data.expiresAt, progress: data.progress } : item)) - ); - if (data.status === "DONE") { - const response = await downloadExport(token, result.jobId); - const blob = await response.blob(); - downloadFile(blob, `tenant-${tenant.id}.zip`); - setExportStatus("done"); - source.close(); - setTimeout(() => setExportStatus("idle"), 1500); - } else if (data.status === "FAILED") { - setExportStatus("failed"); - source.close(); - } - }; - return; + try { + setExportTenantId(tenant.id); + setExportStatus("loading"); + if (exportFormat === "json") { + const result = await apiFetch(`/admin/tenants/${tenant.id}/export?scope=${exportScope}`, {}, token); + const blob = new Blob([JSON.stringify(result, null, 2)], { type: "application/json" }); + downloadFile(blob, `tenant-${tenant.id}.json`); + pushToast(t("toastExportReady"), "success"); + } else if (exportFormat === "csv") { + await exportTenantCsv(tenant); + return; + } else { + const result = await apiFetch(`/admin/tenants/${tenant.id}/export?format=zip&scope=${exportScope}`, {}, token); + setExportJobId(result.jobId); + setExportHistory((prev) => [{ id: result.jobId, status: "QUEUED" }, ...prev]); + pushToast(t("toastExportQueued"), "info"); + const source = createEventSourceFor(`exports/${result.jobId}`, token); + source.onmessage = async (event) => { + const data = JSON.parse(event.data); + setExportHistory((prev) => + prev.map((item) => (item.id === data.id ? { ...item, status: data.status, expiresAt: data.expiresAt, progress: data.progress } : item)) + ); + if (data.status === "DONE") { + const response = await downloadExport(token, result.jobId); + const blob = await response.blob(); + downloadFile(blob, `tenant-${tenant.id}.zip`); + setExportStatus("done"); + pushToast(t("toastExportReady"), "success"); + source.close(); + setTimeout(() => setExportStatus("idle"), 1500); + } else if (data.status === "FAILED") { + setExportStatus("failed"); + pushToast(t("toastExportFailed"), "error"); + source.close(); + } + }; + return; + } + setExportStatus("done"); + setTimeout(() => setExportStatus("idle"), 1500); + } catch (err) { + setExportStatus("failed"); + pushToast(getErrorMessage(err), "error"); } - setExportStatus("done"); - setTimeout(() => setExportStatus("idle"), 1500); }; const exportTenantCsv = async (tenant: Tenant) => { @@ -139,69 +205,113 @@ export default function AdminPanel({ token, onImpersonate }: Props) { const response = await fetch(`${base}/admin/tenants/${tenant.id}/export?format=csv&scope=${exportScope}`, { headers: { Authorization: `Bearer ${token}` } }); + if (!response.ok) { + throw new Error(await response.text()); + } const text = await response.text(); const blob = new Blob([text], { type: "text/csv" }); downloadFile(blob, `tenant-${tenant.id}.csv`); setExportStatus("done"); setTimeout(() => setExportStatus("idle"), 1500); + pushToast(t("toastExportReady"), "success"); }; const deleteTenant = async (tenant: Tenant) => { if (!confirm(t("adminDeleteConfirm", { name: tenant.name }))) return; - await apiFetch(`/admin/tenants/${tenant.id}`, { method: "DELETE" }, token); - setTenants((prev) => prev.filter((item) => item.id !== tenant.id)); + try { + await apiFetch(`/admin/tenants/${tenant.id}`, { method: "DELETE" }, token); + setTenants((prev) => prev.filter((item) => item.id !== tenant.id)); + pushToast(t("toastTenantDeleted"), "info"); + } catch (err) { + pushToast(getErrorMessage(err), "error"); + } }; const toggleUser = async (user: User) => { - const result = await apiFetch( - `/admin/users/${user.id}`, - { method: "PUT", body: JSON.stringify({ isActive: !user.isActive }) }, - token - ); - setUsers((prev) => prev.map((item) => (item.id === user.id ? result.user : item))); + try { + const result = await apiFetch( + `/admin/users/${user.id}`, + { method: "PUT", body: JSON.stringify({ isActive: !user.isActive }) }, + token + ); + setUsers((prev) => prev.map((item) => (item.id === user.id ? result.user : item))); + pushToast(t("toastUserUpdated"), "success"); + } catch (err) { + pushToast(getErrorMessage(err), "error"); + } }; const toggleAccount = async (account: Account) => { - const result = await apiFetch( - `/admin/accounts/${account.id}`, - { method: "PUT", body: JSON.stringify({ isActive: !account.isActive }) }, - token - ); - setAccounts((prev) => prev.map((item) => (item.id === account.id ? result.account : item))); + try { + const result = await apiFetch( + `/admin/accounts/${account.id}`, + { method: "PUT", body: JSON.stringify({ isActive: !account.isActive }) }, + token + ); + setAccounts((prev) => prev.map((item) => (item.id === account.id ? result.account : item))); + pushToast(t("toastAccountUpdated"), "success"); + } catch (err) { + pushToast(getErrorMessage(err), "error"); + } }; const setRole = async (user: User, role: "USER" | "ADMIN") => { - const result = await apiFetch( - `/admin/users/${user.id}/role`, - { method: "PUT", body: JSON.stringify({ role }) }, - token - ); - setUsers((prev) => prev.map((item) => (item.id === user.id ? result.user : item))); + try { + const result = await apiFetch( + `/admin/users/${user.id}/role`, + { method: "PUT", body: JSON.stringify({ role }) }, + token + ); + setUsers((prev) => prev.map((item) => (item.id === user.id ? result.user : item))); + pushToast(t("toastRoleUpdated"), "success"); + } catch (err) { + pushToast(getErrorMessage(err), "error"); + } }; const impersonate = async (user: User) => { - const result = await apiFetch(`/admin/impersonate/${user.id}`, { method: "POST" }, token); - onImpersonate(result.token); + try { + const result = await apiFetch(`/admin/impersonate/${user.id}`, { method: "POST" }, token); + onImpersonate(result.token); + pushToast(t("toastImpersonate"), "info"); + } catch (err) { + pushToast(getErrorMessage(err), "error"); + } }; const resetPasswordForUser = async () => { if (!resetUserId || resetPassword.length < 10) return; - await apiFetch(`/admin/users/${resetUserId}/reset`, { - method: "POST", - body: JSON.stringify({ password: resetPassword }) - }, token); - setResetUserId(null); - setResetPassword(""); + try { + await apiFetch(`/admin/users/${resetUserId}/reset`, { + method: "POST", + body: JSON.stringify({ password: resetPassword }) + }, token); + setResetUserId(null); + setResetPassword(""); + pushToast(t("toastPasswordReset"), "success"); + } catch (err) { + pushToast(getErrorMessage(err), "error"); + } }; const cancelJob = async (job: Job) => { - await apiFetch(`/admin/jobs/${job.id}/cancel`, { method: "POST" }, token); - setJobs((prev) => prev.map((item) => (item.id === job.id ? { ...item, status: "CANCELED" } : item))); + try { + await apiFetch(`/admin/jobs/${job.id}/cancel`, { method: "POST" }, token); + setJobs((prev) => prev.map((item) => (item.id === job.id ? { ...item, status: "CANCELED" } : item))); + pushToast(t("toastJobCanceled"), "info"); + } catch (err) { + pushToast(getErrorMessage(err), "error"); + } }; const retryJob = async (job: Job) => { - await apiFetch(`/admin/jobs/${job.id}/retry`, { method: "POST" }, token); - loadAll().catch(() => undefined); + try { + await apiFetch(`/admin/jobs/${job.id}/retry`, { method: "POST" }, token); + loadAll().catch(() => undefined); + pushToast(t("toastJobRetry"), "success"); + } catch (err) { + pushToast(getErrorMessage(err), "error"); + } }; const sortBy = (items: T[], mode: string, getKey: (item: T) => string) => { @@ -243,10 +353,37 @@ export default function AdminPanel({ token, onImpersonate }: Props) { } }; + const saveSettings = async () => { + try { + setSettingsStatus("saving"); + await apiFetch( + "/admin/settings", + { + method: "PUT", + body: JSON.stringify({ + settings: { + "google.client_id": settingsDraft.googleClientId, + "google.client_secret": settingsDraft.googleClientSecret, + "google.redirect_uri": settingsDraft.googleRedirectUri + } + }) + }, + token + ); + await loadSettings(); + setSettingsStatus("saved"); + setTimeout(() => setSettingsStatus("idle"), 1500); + pushToast(t("toastSettingsSaved"), "success"); + } catch { + setSettingsStatus("error"); + pushToast(t("toastSettingsFailed"), "error"); + } + }; + return (
- {(["tenants", "users", "accounts", "jobs"] as const).map((tab) => ( + {(["tenants", "users", "accounts", "jobs", "settings"] as const).map((tab) => ( + + {settingsStatus === "saved" && {t("adminSettingsSaved")}} + {settingsStatus === "error" && {t("adminSettingsError")}} +
+
+ {t("adminSettingsSource", { + id: settings["google.client_id"]?.source ?? "unset", + secret: settings["google.client_secret"]?.source ?? "unset", + redirect: settings["google.redirect_uri"]?.source ?? "unset" + })} +
+ + + )}
); } diff --git a/frontend/src/api.ts b/frontend/src/api.ts index 7438ac75..1966ea11 100644 --- a/frontend/src/api.ts +++ b/frontend/src/api.ts @@ -2,7 +2,10 @@ const apiUrl = import.meta.env.VITE_API_URL ?? "http://localhost:8000"; export const apiFetch = async (path: string, options: RequestInit = {}, token?: string) => { const headers = new Headers(options.headers ?? {}); - headers.set("Content-Type", "application/json"); + const hasBody = options.body !== undefined && options.body !== null; + if (hasBody && !headers.has("Content-Type")) { + headers.set("Content-Type", "application/json"); + } if (token) { headers.set("Authorization", `Bearer ${token}`); } @@ -14,7 +17,9 @@ export const apiFetch = async (path: string, options: RequestInit = {}, token?: if (!response.ok) { const message = await response.text(); - throw new Error(message || `Request failed: ${response.status}`); + const error = new Error(message || `Request failed: ${response.status}`) as Error & { status?: number }; + error.status = response.status; + throw error; } return response.json(); diff --git a/frontend/src/i18n.ts b/frontend/src/i18n.ts index aa324d1d..22c6b8a0 100644 --- a/frontend/src/i18n.ts +++ b/frontend/src/i18n.ts @@ -8,7 +8,7 @@ i18n.use(initReactI18next).init({ en: { translation: en }, de: { translation: de } }, - lng: "de", + lng: typeof window !== "undefined" && window.navigator.language.toLowerCase().startsWith("de") ? "de" : "en", fallbackLng: "en", interpolation: { escapeValue: false } }); diff --git a/frontend/src/locales/de/translation.json b/frontend/src/locales/de/translation.json index a2dc228e..d27678e3 100644 --- a/frontend/src/locales/de/translation.json +++ b/frontend/src/locales/de/translation.json @@ -31,6 +31,7 @@ "adminUsers": "User", "adminAccounts": "Accounts", "adminJobs": "Jobs", + "adminSettings": "Einstellungen", "adminExport": "Export", "adminDisable": "Deaktivieren", "adminEnable": "Aktivieren", @@ -43,6 +44,19 @@ "adminResetPlaceholder": "Neues Passwort (min 10 Zeichen)", "adminCancel": "Abbrechen", "adminConfirmReset": "Reset", + "adminSettingsHint": "OAuth-Einstellungen aus der Umgebung überschreiben. Leer lassen, um auf .env zurückzufallen.", + "adminGoogleSettings": "Google OAuth", + "adminGoogleSettingsHelp": "Lege in der Google Cloud Console einen OAuth‑Client an. Trage die Redirect‑URL unten als erlaubte Weiterleitungs‑URL ein. Consent Screen auf „extern“ setzen und Gmail API aktivieren.", + "adminGoogleClientId": "Client-ID", + "adminGoogleClientSecret": "Client-Secret", + "adminGoogleRedirectUri": "Redirect-URL", + "adminSaveSettings": "Einstellungen speichern", + "adminSaving": "Speichert...", + "adminSettingsSaved": "Gespeichert", + "adminSettingsError": "Speichern fehlgeschlagen", + "adminShowSecret": "Secret anzeigen", + "adminHideSecret": "Secret verbergen", + "adminSettingsSource": "Quellen - Client-ID: {{id}}, Secret: {{secret}}, Redirect: {{redirect}}", "adminRetry": "Retry", "adminCancelJob": "Cancel", "adminMailboxStatus": "Mailbox Status", @@ -63,7 +77,7 @@ "rulesActions": "Aktionen", "rulesAddCondition": "+ Bedingung", "rulesAddAction": "+ Aktion", - "rulesSave": "Rule speichern", + "rulesSave": "Regel speichern", "rulesOverview": "Regeln Übersicht", "jobsTitle": "Jobs", "jobEvents": "Job Events", @@ -83,6 +97,15 @@ "countAccounts": "{{count}} Accounts", "countJobs": "{{count}} Jobs", "placeholderEmail": "email@example.com", + "providerHintGmail": "Gmail nutzt OAuth. Du kannst das Passwort leer lassen und per OAuth verbinden.", + "providerHintGmx": "GMX nutzt IMAP. Gib hier dein App‑Passwort oder IMAP‑Passwort ein.", + "providerHintWebde": "web.de nutzt IMAP. Gib hier dein App‑Passwort oder IMAP‑Passwort ein.", + "providerHelp": "Hilfe zur Mailbox‑Einrichtung", + "providerHelpTitle": "Mailbox‑Einrichtung", + "providerHelpGmail": "Erstelle einen Google OAuth‑Client und verbinde per OAuth‑Button. Das Passwortfeld ist bei OAuth nicht nötig.", + "providerHelpGmx": "Aktiviere IMAP in den GMX‑Einstellungen und erstelle ein App‑Passwort. Dieses Passwort hier verwenden.", + "providerHelpWebde": "Aktiviere IMAP in den web.de‑Einstellungen und erstelle ein App‑Passwort. Dieses Passwort hier verwenden.", + "close": "Schließen", "providerGmail": "Gmail", "providerGmx": "GMX", "providerWebde": "web.de", @@ -153,5 +176,34 @@ "exportStatusFailed": "Fehlgeschlagen", "exportStatusExpired": "Abgelaufen", "adminExportPurge": "Abgelaufene löschen", - "exportProgress": "Fortschritt {{progress}}%" + "exportProgress": "Fortschritt {{progress}}%", + "adminConsole": "Admin-Konsole", + "adminConsoleHint": "Globale Steuerung für Tenants, User, Accounts, Exporte und Jobs.", + "userWorkspace": "User-Bereich", + "userWorkspaceHint": "Alles darunter betrifft nur dein eigenes Postfach und deine Regeln.", + "toastGenericError": "Etwas ist schiefgelaufen.", + "toastSessionExpired": "Session abgelaufen. Bitte erneut einloggen.", + "toastLoginSuccess": "Erfolgreich eingeloggt.", + "toastRegisterSuccess": "Account erfolgreich erstellt.", + "toastMailboxAdded": "Mailbox hinzugefügt.", + "toastRuleSaved": "Regel gespeichert.", + "toastRuleDeleted": "Regel gelöscht.", + "toastCleanupStarted": "Bereinigung gestartet.", + "toastLoggedOut": "Ausgeloggt.", + "toastExportQueued": "Export in Warteschlange.", + "toastExportReady": "Export bereit.", + "toastExportFailed": "Export fehlgeschlagen.", + "toastExportPurged": "Abgelaufene Exporte entfernt.", + "toastExportDeleted": "Export gelöscht.", + "toastTenantUpdated": "Tenant aktualisiert.", + "toastTenantDeleted": "Tenant gelöscht.", + "toastUserUpdated": "User aktualisiert.", + "toastAccountUpdated": "Account aktualisiert.", + "toastRoleUpdated": "Rolle aktualisiert.", + "toastImpersonate": "Impersonation gestartet.", + "toastPasswordReset": "Passwort zurückgesetzt.", + "toastJobCanceled": "Job abgebrochen.", + "toastJobRetry": "Job neu gestartet.", + "toastSettingsSaved": "Einstellungen gespeichert.", + "toastSettingsFailed": "Einstellungen konnten nicht gespeichert werden." } diff --git a/frontend/src/locales/en/translation.json b/frontend/src/locales/en/translation.json index c572e95d..f4fa1415 100644 --- a/frontend/src/locales/en/translation.json +++ b/frontend/src/locales/en/translation.json @@ -31,6 +31,7 @@ "adminUsers": "Users", "adminAccounts": "Accounts", "adminJobs": "Jobs", + "adminSettings": "Settings", "adminExport": "Export", "adminDisable": "Disable", "adminEnable": "Enable", @@ -43,6 +44,19 @@ "adminResetPlaceholder": "New password (min 10 characters)", "adminCancel": "Cancel", "adminConfirmReset": "Reset", + "adminSettingsHint": "Override OAuth settings stored in the environment. Leave empty to fall back to .env.", + "adminGoogleSettings": "Google OAuth", + "adminGoogleSettingsHelp": "Create an OAuth client in Google Cloud Console. Add the Redirect URL below as an authorized redirect URI. Use OAuth consent screen in external mode and enable Gmail API.", + "adminGoogleClientId": "Client ID", + "adminGoogleClientSecret": "Client secret", + "adminGoogleRedirectUri": "Redirect URL", + "adminSaveSettings": "Save settings", + "adminSaving": "Saving...", + "adminSettingsSaved": "Saved", + "adminSettingsError": "Save failed", + "adminShowSecret": "Show secret", + "adminHideSecret": "Hide secret", + "adminSettingsSource": "Sources - Client ID: {{id}}, Secret: {{secret}}, Redirect: {{redirect}}", "adminRetry": "Retry", "adminCancelJob": "Cancel", "adminMailboxStatus": "Mailbox status", @@ -83,6 +97,15 @@ "countAccounts": "{{count}} accounts", "countJobs": "{{count}} jobs", "placeholderEmail": "email@example.com", + "providerHintGmail": "Gmail uses OAuth. You can leave the password empty and connect via the OAuth button.", + "providerHintGmx": "GMX uses IMAP. Enter your app password or IMAP password for this mailbox.", + "providerHintWebde": "web.de uses IMAP. Enter your app password or IMAP password for this mailbox.", + "providerHelp": "Help for mailbox setup", + "providerHelpTitle": "Mailbox setup help", + "providerHelpGmail": "Create a Google OAuth Client and connect via the OAuth button. The password field is not required when OAuth is used.", + "providerHelpGmx": "Enable IMAP in your GMX settings and create an app password. Use that password here.", + "providerHelpWebde": "Enable IMAP in your web.de account settings and create an app password. Use that password here.", + "close": "Close", "providerGmail": "Gmail", "providerGmx": "GMX", "providerWebde": "web.de", @@ -153,5 +176,34 @@ "exportStatusFailed": "Failed", "exportStatusExpired": "Expired", "adminExportPurge": "Purge expired", - "exportProgress": "Progress {{progress}}%" + "exportProgress": "Progress {{progress}}%", + "adminConsole": "Admin console", + "adminConsoleHint": "Global controls for tenants, users, accounts, exports, and jobs.", + "userWorkspace": "User workspace", + "userWorkspaceHint": "Everything below affects only your own mailbox and rules.", + "toastGenericError": "Something went wrong.", + "toastSessionExpired": "Session expired. Please log in again.", + "toastLoginSuccess": "Logged in successfully.", + "toastRegisterSuccess": "Account created successfully.", + "toastMailboxAdded": "Mailbox added.", + "toastRuleSaved": "Rule saved.", + "toastRuleDeleted": "Rule deleted.", + "toastCleanupStarted": "Cleanup job started.", + "toastLoggedOut": "Logged out.", + "toastExportQueued": "Export queued.", + "toastExportReady": "Export ready.", + "toastExportFailed": "Export failed.", + "toastExportPurged": "Expired exports purged.", + "toastExportDeleted": "Export deleted.", + "toastTenantUpdated": "Tenant updated.", + "toastTenantDeleted": "Tenant deleted.", + "toastUserUpdated": "User updated.", + "toastAccountUpdated": "Account updated.", + "toastRoleUpdated": "Role updated.", + "toastImpersonate": "Impersonation started.", + "toastPasswordReset": "Password reset.", + "toastJobCanceled": "Job canceled.", + "toastJobRetry": "Job retried.", + "toastSettingsSaved": "Settings saved.", + "toastSettingsFailed": "Settings save failed." } diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx index 178a4711..c9e7b7d8 100644 --- a/frontend/src/main.tsx +++ b/frontend/src/main.tsx @@ -3,9 +3,12 @@ import ReactDOM from "react-dom/client"; import "./i18n"; import "./styles.css"; import App from "./App"; +import { ToastProvider } from "./toast"; ReactDOM.createRoot(document.getElementById("root")!).render( - + + + ); diff --git a/frontend/src/styles.css b/frontend/src/styles.css index eefcbfc7..15bfb7e2 100644 --- a/frontend/src/styles.css +++ b/frontend/src/styles.css @@ -2,13 +2,13 @@ :root { color-scheme: light; - --bg: #f4f6f3; - --bg-accent: #eef1ea; - --ink: #111413; - --muted: #5b615b; - --primary: #0f766e; - --primary-strong: #0b5f59; - --accent: #e2b644; + --bg: #f3f6fb; + --bg-accent: #e9f0ff; + --ink: #101827; + --muted: #5a6375; + --primary: #2563eb; + --primary-strong: #1d4ed8; + --accent: #7c3aed; --card: #ffffff; --border: rgba(17, 20, 19, 0.08); --shadow: 0 16px 40px rgba(17, 20, 19, 0.08); @@ -27,6 +27,66 @@ body { min-height: 100vh; } +.toast-container { + position: fixed; + top: 20px; + right: 20px; + display: grid; + gap: 12px; + z-index: 9999; + max-width: min(360px, 90vw); +} + +.toast { + background: #ffffff; + border: 1px solid var(--border); + border-left: 4px solid var(--primary); + border-radius: 12px; + padding: 12px 14px; + display: flex; + align-items: center; + gap: 12px; + box-shadow: 0 16px 40px rgba(17, 20, 19, 0.12); + animation: toast-in 0.2s ease-out; +} + +.toast span { + flex: 1; + font-size: 14px; + color: var(--ink); +} + +.toast button { + border: none; + background: transparent; + font-size: 18px; + cursor: pointer; + color: var(--muted); +} + +.toast-success { + border-left-color: #1d4ed8; +} + +.toast-error { + border-left-color: #dc2626; +} + +.toast-info { + border-left-color: #2563eb; +} + +@keyframes toast-in { + from { + opacity: 0; + transform: translateY(-6px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + .app { max-width: 1200px; margin: 0 auto; @@ -60,40 +120,35 @@ h1 { font-size: 16px; } -.lang { - background: var(--card); - border: 1px solid var(--border); - border-radius: 16px; - padding: 12px 16px; - box-shadow: var(--shadow); - min-width: 200px; +.lang-compact { + display: flex; + flex-direction: column; + align-items: flex-end; + gap: 6px; } -.lang span { - display: block; - font-size: 11px; - text-transform: uppercase; - letter-spacing: 0.12em; +.lang-compact .user-label { + font-size: 12px; color: var(--muted); - margin-bottom: 10px; } .lang-buttons { display: flex; - gap: 8px; + gap: 6px; flex-wrap: wrap; } -.lang button { +.lang-compact button { border: 1px solid var(--border); - background: transparent; - padding: 6px 10px; + background: #fff; + padding: 4px 8px; border-radius: 999px; cursor: pointer; + font-size: 12px; font-weight: 600; } -.lang button.active { +.lang-compact button.active { background: var(--primary); color: #fff; border-color: var(--primary); @@ -132,7 +187,7 @@ button.primary { border-radius: 12px; font-weight: 700; cursor: pointer; - box-shadow: 0 10px 24px rgba(15, 118, 110, 0.25); + box-shadow: 0 10px 24px rgba(37, 99, 235, 0.25); } button.ghost { @@ -165,7 +220,7 @@ button.ghost { } .progress-bar { - background: rgba(15, 118, 110, 0.12); + background: rgba(37, 99, 235, 0.12); border-radius: 999px; height: 8px; overflow: hidden; @@ -204,6 +259,7 @@ button.ghost { grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 16px; margin-bottom: 28px; + align-items: stretch; } .card { @@ -212,10 +268,80 @@ button.ghost { border-radius: 18px; padding: 18px; box-shadow: 0 12px 28px rgba(17, 20, 19, 0.06); - display: grid; + display: flex; + flex-direction: column; gap: 10px; } +.card-actions { + margin-top: auto; + display: grid; + gap: 8px; +} + +.card button.primary, +.card button.ghost, +.card .add-button { + width: 100%; + height: 40px; + font-size: 13px; + padding: 0 14px; +} + +.card button { + font-size: 13px; + line-height: 1.1; +} + +.panel { + background: var(--bg-accent); + border: 1px solid var(--border); + border-radius: 16px; + padding: 16px; + display: grid; + gap: 12px; +} + +.section-block { + margin: 16px 0 18px; + padding: 12px 14px; + border-radius: 16px; + border: 1px dashed var(--border); + background: rgba(255, 255, 255, 0.6); +} + +.admin-only { + margin: 16px 0 24px; + padding: 16px; + border-radius: 18px; + border: 1px solid rgba(37, 99, 235, 0.2); + background: #f7f9ff; + box-shadow: var(--shadow); +} + +.admin-switch { + display: flex; + align-items: center; + gap: 10px; + margin-bottom: 12px; +} + +.section-header { + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; +} + +.section-header h3 { + font-size: 18px; +} + +.section-header p { + color: var(--muted); + font-size: 13px; +} + .card h3 { margin-bottom: 4px; } @@ -237,7 +363,9 @@ button.ghost { input, select { width: 100%; - padding: 9px 12px; + padding: 8px 12px; + min-height: 40px; + height: 40px; border-radius: 10px; border: 1px solid var(--border); font-family: inherit; @@ -245,10 +373,37 @@ select { } .toggle { - display: flex; + display: grid; + grid-template-columns: 16px 1fr; align-items: center; - gap: 8px; + gap: 10px; font-size: 14px; + line-height: 1.2; + margin: 0; +} + +.toggle input { + margin: 0; + justify-self: start; +} + +.toggle-group { + display: grid; + gap: 8px; +} + +.field-row { + display: grid; + grid-template-columns: 140px 1fr; + gap: 12px; + align-items: center; + font-size: 13px; +} + +@media (max-width: 720px) { + .field-row { + grid-template-columns: 1fr; + } } .row { @@ -263,6 +418,30 @@ select { gap: 8px; } +.rule-actions { + display: grid; + gap: 8px; +} + +.add-button { + border: 1px solid rgba(37, 99, 235, 0.28); + background: linear-gradient(135deg, #f2f6ff, #e2ebff); + color: var(--primary-strong); + padding: 8px 14px; + border-radius: 999px; + font-weight: 600; + cursor: pointer; + width: fit-content; + transition: transform 0.15s ease, box-shadow 0.15s ease, border-color 0.15s ease; + box-shadow: 0 8px 18px rgba(37, 99, 235, 0.12); +} + +.add-button:hover { + transform: translateY(-1px); + border-color: rgba(37, 99, 235, 0.45); + box-shadow: 0 12px 22px rgba(37, 99, 235, 0.18); +} + .list-item { display: flex; justify-content: space-between; @@ -282,7 +461,7 @@ select { .event { padding: 10px 12px; border-radius: 10px; - background: rgba(15, 118, 110, 0.12); + background: rgba(37, 99, 235, 0.12); display: grid; grid-template-columns: 40px 1fr; gap: 12px; @@ -290,7 +469,7 @@ select { } .event.error { - background: rgba(226, 182, 68, 0.25); + background: rgba(124, 58, 237, 0.2); } .auth-panel { @@ -306,6 +485,51 @@ select { gap: 10px; } +.hint-text { + font-size: 12px; + color: var(--muted); + line-height: 1.5; +} + +.modal-backdrop { + position: fixed; + inset: 0; + background: rgba(9, 16, 32, 0.45); + display: grid; + place-items: center; + padding: 20px; + z-index: 1200; +} + +.modal { + width: min(640px, 92vw); + background: #fff; + border-radius: 18px; + border: 1px solid var(--border); + box-shadow: 0 24px 60px rgba(17, 20, 19, 0.25); + padding: 20px; + display: grid; + gap: 14px; +} + +.modal-header { + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; +} + +.modal-body { + display: grid; + gap: 10px; + font-size: 14px; + color: var(--ink); +} + +.modal-body h4 { + margin-top: 6px; +} + .admin-panel { margin: 16px 0 32px; display: grid; @@ -401,13 +625,13 @@ select { font-size: 10px; text-transform: uppercase; letter-spacing: 0.12em; - background: rgba(15, 118, 110, 0.16); + background: rgba(37, 99, 235, 0.16); color: var(--primary-strong); } .status-badge.missing { - background: rgba(226, 182, 68, 0.25); - color: #8a5d00; + background: rgba(124, 58, 237, 0.18); + color: #5b21b6; } button:disabled { diff --git a/frontend/src/toast.tsx b/frontend/src/toast.tsx new file mode 100644 index 00000000..aebc30bd --- /dev/null +++ b/frontend/src/toast.tsx @@ -0,0 +1,67 @@ +import { createContext, useCallback, useContext, useMemo, useRef, useState } from "react"; + +type ToastVariant = "success" | "error" | "info"; + +export type Toast = { + id: string; + message: string; + variant: ToastVariant; +}; + +type ToastContextValue = { + toasts: Toast[]; + pushToast: (message: string, variant?: ToastVariant, durationMs?: number) => void; + dismissToast: (id: string) => void; +}; + +const ToastContext = createContext(null); + +export const ToastProvider = ({ children }: { children: React.ReactNode }) => { + const [toasts, setToasts] = useState([]); + const timers = useRef(new Map()); + + const dismissToast = useCallback((id: string) => { + setToasts((prev) => prev.filter((toast) => toast.id !== id)); + const timer = timers.current.get(id); + if (timer) { + window.clearTimeout(timer); + timers.current.delete(id); + } + }, []); + + const pushToast = useCallback( + (message: string, variant: ToastVariant = "info", durationMs = 4000) => { + const id = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`; + setToasts((prev) => [...prev, { id, message, variant }]); + const timer = window.setTimeout(() => dismissToast(id), durationMs); + timers.current.set(id, timer); + }, + [dismissToast] + ); + + const value = useMemo(() => ({ toasts, pushToast, dismissToast }), [toasts, pushToast, dismissToast]); + + return ( + + {children} +
+ {toasts.map((toast) => ( +
+ {toast.message} + +
+ ))} +
+
+ ); +}; + +export const useToast = () => { + const ctx = useContext(ToastContext); + if (!ctx) { + throw new Error("useToast must be used within ToastProvider"); + } + return ctx; +}; diff --git a/screenshot/Screenshot 2026-01-22 194754.png b/screenshot/Screenshot 2026-01-22 194754.png new file mode 100644 index 00000000..51a6dc58 Binary files /dev/null and b/screenshot/Screenshot 2026-01-22 194754.png differ