Projektstart

This commit is contained in:
2026-01-22 16:40:19 +01:00
parent 43c83e96bb
commit 85dee61a4d
25 changed files with 533 additions and 157 deletions

View File

@@ -49,7 +49,12 @@ docker compose up --build
- `GET /admin/exports` (admin) - `GET /admin/exports` (admin)
- `GET /admin/exports/:id` (admin) - `GET /admin/exports/:id` (admin)
- `GET /admin/exports/:id/download` (admin) - `GET /admin/exports/:id/download` (admin)
- `POST /admin/exports/purge` (admin)
- `DELETE /admin/exports/:id` (admin)
- `GET /jobs/exports/:id/stream` (auth, SSE) - `GET /jobs/exports/:id/stream` (auth, SSE)
Export queue:
- ZIP exports are queued via Redis/BullMQ and processed by the worker container.
- `DELETE /admin/tenants/:id` (admin) - `DELETE /admin/tenants/:id` (admin)
OAuth: OAuth:
@@ -70,8 +75,10 @@ UI:
```bash ```bash
cd backend cd backend
DATABASE_URL=postgresql://mailcleaner:mailcleaner@localhost:5432/mailcleaner \\ DATABASE_URL=postgresql://mailcleaner:mailcleaner@localhost:5432/mailcleaner \\
SEED_EMAIL=admin@simplemailcleaner.local \\ SEED_ADMIN_EMAIL=admin@simplemailcleaner.local \\
SEED_PASSWORD=change-me-now \\ SEED_ADMIN_PASSWORD=change-me-now \\
SEED_TENANT=Default Tenant \\
SEED_TENANT_ID=seed-tenant \\
npm run prisma:seed npm run prisma:seed
``` ```
- DSGVO: data storage is designed for tenant isolation; encryption at rest will be added. - DSGVO: data storage is designed for tenant isolation; encryption at rest will be added.
@@ -82,3 +89,8 @@ npm run prisma:seed
Export settings: Export settings:
- `EXPORT_DIR` (default `/tmp/mailcleaner-exports`) - `EXPORT_DIR` (default `/tmp/mailcleaner-exports`)
- `EXPORT_TTL_HOURS` (default `24`) - `EXPORT_TTL_HOURS` (default `24`)
Proxy settings (Nginx Proxy Manager):
- `TRUST_PROXY=true`
- `VITE_API_URL=https://your-domain.tld`
- `GOOGLE_REDIRECT_URI=https://your-domain.tld/oauth/gmail/callback`

14
backend/.env Normal file
View File

@@ -0,0 +1,14 @@
NODE_ENV=development
PORT=8000
DATABASE_URL=postgresql://mailcleaner:mailcleaner@localhost:5432/mailcleaner
REDIS_URL=redis://localhost:6379
JWT_SECRET=change-me-super-secret
GOOGLE_CLIENT_ID=
GOOGLE_CLIENT_SECRET=
GOOGLE_REDIRECT_URI=http://localhost:8000/oauth/gmail/callback
EXPORT_DIR=/tmp/mailcleaner-exports
EXPORT_TTL_HOURS=24
SEED_ADMIN_EMAIL=admin@simplemailcleaner.local
SEED_ADMIN_PASSWORD=change-me-now
SEED_TENANT=Default Tenant
SEED_TENANT_ID=seed-tenant

View File

@@ -8,3 +8,9 @@ GOOGLE_CLIENT_SECRET=
GOOGLE_REDIRECT_URI=http://localhost:8000/oauth/gmail/callback GOOGLE_REDIRECT_URI=http://localhost:8000/oauth/gmail/callback
EXPORT_DIR=/tmp/mailcleaner-exports EXPORT_DIR=/tmp/mailcleaner-exports
EXPORT_TTL_HOURS=24 EXPORT_TTL_HOURS=24
TRUST_PROXY=false
VITE_API_URL=https://example.com
SEED_ADMIN_EMAIL=admin@simplemailcleaner.local
SEED_ADMIN_PASSWORD=change-me-now
SEED_TENANT=Default Tenant
SEED_TENANT_ID=seed-tenant

File diff suppressed because one or more lines are too long

View File

@@ -136,6 +136,7 @@ exports.Prisma.ExportJobScalarFieldEnum = {
status: 'status', status: 'status',
format: 'format', format: 'format',
scope: 'scope', scope: 'scope',
progress: 'progress',
filePath: 'filePath', filePath: 'filePath',
error: 'error', error: 'error',
expiresAt: 'expiresAt', expiresAt: 'expiresAt',
@@ -171,6 +172,8 @@ exports.Prisma.MailboxAccountScalarFieldEnum = {
oauthAccessToken: 'oauthAccessToken', oauthAccessToken: 'oauthAccessToken',
oauthExpiresAt: 'oauthExpiresAt', oauthExpiresAt: 'oauthExpiresAt',
providerUserId: 'providerUserId', providerUserId: 'providerUserId',
oauthLastCheckedAt: 'oauthLastCheckedAt',
oauthLastErrorCode: 'oauthLastErrorCode',
appPassword: 'appPassword', appPassword: 'appPassword',
createdAt: 'createdAt', createdAt: 'createdAt',
updatedAt: 'updatedAt' updatedAt: 'updatedAt'

View File

@@ -3144,16 +3144,27 @@ export namespace Prisma {
export type AggregateExportJob = { export type AggregateExportJob = {
_count: ExportJobCountAggregateOutputType | null _count: ExportJobCountAggregateOutputType | null
_avg: ExportJobAvgAggregateOutputType | null
_sum: ExportJobSumAggregateOutputType | null
_min: ExportJobMinAggregateOutputType | null _min: ExportJobMinAggregateOutputType | null
_max: ExportJobMaxAggregateOutputType | null _max: ExportJobMaxAggregateOutputType | null
} }
export type ExportJobAvgAggregateOutputType = {
progress: number | null
}
export type ExportJobSumAggregateOutputType = {
progress: number | null
}
export type ExportJobMinAggregateOutputType = { export type ExportJobMinAggregateOutputType = {
id: string | null id: string | null
tenantId: string | null tenantId: string | null
status: $Enums.ExportStatus | null status: $Enums.ExportStatus | null
format: string | null format: string | null
scope: string | null scope: string | null
progress: number | null
filePath: string | null filePath: string | null
error: string | null error: string | null
expiresAt: Date | null expiresAt: Date | null
@@ -3167,6 +3178,7 @@ export namespace Prisma {
status: $Enums.ExportStatus | null status: $Enums.ExportStatus | null
format: string | null format: string | null
scope: string | null scope: string | null
progress: number | null
filePath: string | null filePath: string | null
error: string | null error: string | null
expiresAt: Date | null expiresAt: Date | null
@@ -3180,6 +3192,7 @@ export namespace Prisma {
status: number status: number
format: number format: number
scope: number scope: number
progress: number
filePath: number filePath: number
error: number error: number
expiresAt: number expiresAt: number
@@ -3189,12 +3202,21 @@ export namespace Prisma {
} }
export type ExportJobAvgAggregateInputType = {
progress?: true
}
export type ExportJobSumAggregateInputType = {
progress?: true
}
export type ExportJobMinAggregateInputType = { export type ExportJobMinAggregateInputType = {
id?: true id?: true
tenantId?: true tenantId?: true
status?: true status?: true
format?: true format?: true
scope?: true scope?: true
progress?: true
filePath?: true filePath?: true
error?: true error?: true
expiresAt?: true expiresAt?: true
@@ -3208,6 +3230,7 @@ export namespace Prisma {
status?: true status?: true
format?: true format?: true
scope?: true scope?: true
progress?: true
filePath?: true filePath?: true
error?: true error?: true
expiresAt?: true expiresAt?: true
@@ -3221,6 +3244,7 @@ export namespace Prisma {
status?: true status?: true
format?: true format?: true
scope?: true scope?: true
progress?: true
filePath?: true filePath?: true
error?: true error?: true
expiresAt?: true expiresAt?: true
@@ -3264,6 +3288,18 @@ export namespace Prisma {
* Count returned ExportJobs * Count returned ExportJobs
**/ **/
_count?: true | ExportJobCountAggregateInputType _count?: true | ExportJobCountAggregateInputType
/**
* {@link https://www.prisma.io/docs/concepts/components/prisma-client/aggregations Aggregation Docs}
*
* Select which fields to average
**/
_avg?: ExportJobAvgAggregateInputType
/**
* {@link https://www.prisma.io/docs/concepts/components/prisma-client/aggregations Aggregation Docs}
*
* Select which fields to sum
**/
_sum?: ExportJobSumAggregateInputType
/** /**
* {@link https://www.prisma.io/docs/concepts/components/prisma-client/aggregations Aggregation Docs} * {@link https://www.prisma.io/docs/concepts/components/prisma-client/aggregations Aggregation Docs}
* *
@@ -3297,6 +3333,8 @@ export namespace Prisma {
take?: number take?: number
skip?: number skip?: number
_count?: ExportJobCountAggregateInputType | true _count?: ExportJobCountAggregateInputType | true
_avg?: ExportJobAvgAggregateInputType
_sum?: ExportJobSumAggregateInputType
_min?: ExportJobMinAggregateInputType _min?: ExportJobMinAggregateInputType
_max?: ExportJobMaxAggregateInputType _max?: ExportJobMaxAggregateInputType
} }
@@ -3307,12 +3345,15 @@ export namespace Prisma {
status: $Enums.ExportStatus status: $Enums.ExportStatus
format: string format: string
scope: string scope: string
progress: number
filePath: string | null filePath: string | null
error: string | null error: string | null
expiresAt: Date | null expiresAt: Date | null
createdAt: Date createdAt: Date
updatedAt: Date updatedAt: Date
_count: ExportJobCountAggregateOutputType | null _count: ExportJobCountAggregateOutputType | null
_avg: ExportJobAvgAggregateOutputType | null
_sum: ExportJobSumAggregateOutputType | null
_min: ExportJobMinAggregateOutputType | null _min: ExportJobMinAggregateOutputType | null
_max: ExportJobMaxAggregateOutputType | null _max: ExportJobMaxAggregateOutputType | null
} }
@@ -3337,6 +3378,7 @@ export namespace Prisma {
status?: boolean status?: boolean
format?: boolean format?: boolean
scope?: boolean scope?: boolean
progress?: boolean
filePath?: boolean filePath?: boolean
error?: boolean error?: boolean
expiresAt?: boolean expiresAt?: boolean
@@ -3351,6 +3393,7 @@ export namespace Prisma {
status?: boolean status?: boolean
format?: boolean format?: boolean
scope?: boolean scope?: boolean
progress?: boolean
filePath?: boolean filePath?: boolean
error?: boolean error?: boolean
expiresAt?: boolean expiresAt?: boolean
@@ -3365,6 +3408,7 @@ export namespace Prisma {
status?: boolean status?: boolean
format?: boolean format?: boolean
scope?: boolean scope?: boolean
progress?: boolean
filePath?: boolean filePath?: boolean
error?: boolean error?: boolean
expiresAt?: boolean expiresAt?: boolean
@@ -3390,6 +3434,7 @@ export namespace Prisma {
status: $Enums.ExportStatus status: $Enums.ExportStatus
format: string format: string
scope: string scope: string
progress: number
filePath: string | null filePath: string | null
error: string | null error: string | null
expiresAt: Date | null expiresAt: Date | null
@@ -3794,6 +3839,7 @@ export namespace Prisma {
readonly status: FieldRef<"ExportJob", 'ExportStatus'> readonly status: FieldRef<"ExportJob", 'ExportStatus'>
readonly format: FieldRef<"ExportJob", 'String'> readonly format: FieldRef<"ExportJob", 'String'>
readonly scope: FieldRef<"ExportJob", 'String'> readonly scope: FieldRef<"ExportJob", 'String'>
readonly progress: FieldRef<"ExportJob", 'Int'>
readonly filePath: FieldRef<"ExportJob", 'String'> readonly filePath: FieldRef<"ExportJob", 'String'>
readonly error: FieldRef<"ExportJob", 'String'> readonly error: FieldRef<"ExportJob", 'String'>
readonly expiresAt: FieldRef<"ExportJob", 'DateTime'> readonly expiresAt: FieldRef<"ExportJob", 'DateTime'>
@@ -5139,6 +5185,8 @@ export namespace Prisma {
oauthAccessToken: string | null oauthAccessToken: string | null
oauthExpiresAt: Date | null oauthExpiresAt: Date | null
providerUserId: string | null providerUserId: string | null
oauthLastCheckedAt: Date | null
oauthLastErrorCode: string | null
appPassword: string | null appPassword: string | null
createdAt: Date | null createdAt: Date | null
updatedAt: Date | null updatedAt: Date | null
@@ -5161,6 +5209,8 @@ export namespace Prisma {
oauthAccessToken: string | null oauthAccessToken: string | null
oauthExpiresAt: Date | null oauthExpiresAt: Date | null
providerUserId: string | null providerUserId: string | null
oauthLastCheckedAt: Date | null
oauthLastErrorCode: string | null
appPassword: string | null appPassword: string | null
createdAt: Date | null createdAt: Date | null
updatedAt: Date | null updatedAt: Date | null
@@ -5183,6 +5233,8 @@ export namespace Prisma {
oauthAccessToken: number oauthAccessToken: number
oauthExpiresAt: number oauthExpiresAt: number
providerUserId: number providerUserId: number
oauthLastCheckedAt: number
oauthLastErrorCode: number
appPassword: number appPassword: number
createdAt: number createdAt: number
updatedAt: number updatedAt: number
@@ -5217,6 +5269,8 @@ export namespace Prisma {
oauthAccessToken?: true oauthAccessToken?: true
oauthExpiresAt?: true oauthExpiresAt?: true
providerUserId?: true providerUserId?: true
oauthLastCheckedAt?: true
oauthLastErrorCode?: true
appPassword?: true appPassword?: true
createdAt?: true createdAt?: true
updatedAt?: true updatedAt?: true
@@ -5239,6 +5293,8 @@ export namespace Prisma {
oauthAccessToken?: true oauthAccessToken?: true
oauthExpiresAt?: true oauthExpiresAt?: true
providerUserId?: true providerUserId?: true
oauthLastCheckedAt?: true
oauthLastErrorCode?: true
appPassword?: true appPassword?: true
createdAt?: true createdAt?: true
updatedAt?: true updatedAt?: true
@@ -5261,6 +5317,8 @@ export namespace Prisma {
oauthAccessToken?: true oauthAccessToken?: true
oauthExpiresAt?: true oauthExpiresAt?: true
providerUserId?: true providerUserId?: true
oauthLastCheckedAt?: true
oauthLastErrorCode?: true
appPassword?: true appPassword?: true
createdAt?: true createdAt?: true
updatedAt?: true updatedAt?: true
@@ -5370,6 +5428,8 @@ export namespace Prisma {
oauthAccessToken: string | null oauthAccessToken: string | null
oauthExpiresAt: Date | null oauthExpiresAt: Date | null
providerUserId: string | null providerUserId: string | null
oauthLastCheckedAt: Date | null
oauthLastErrorCode: string | null
appPassword: string | null appPassword: string | null
createdAt: Date createdAt: Date
updatedAt: Date updatedAt: Date
@@ -5411,6 +5471,8 @@ export namespace Prisma {
oauthAccessToken?: boolean oauthAccessToken?: boolean
oauthExpiresAt?: boolean oauthExpiresAt?: boolean
providerUserId?: boolean providerUserId?: boolean
oauthLastCheckedAt?: boolean
oauthLastErrorCode?: boolean
appPassword?: boolean appPassword?: boolean
createdAt?: boolean createdAt?: boolean
updatedAt?: boolean updatedAt?: boolean
@@ -5437,6 +5499,8 @@ export namespace Prisma {
oauthAccessToken?: boolean oauthAccessToken?: boolean
oauthExpiresAt?: boolean oauthExpiresAt?: boolean
providerUserId?: boolean providerUserId?: boolean
oauthLastCheckedAt?: boolean
oauthLastErrorCode?: boolean
appPassword?: boolean appPassword?: boolean
createdAt?: boolean createdAt?: boolean
updatedAt?: boolean updatedAt?: boolean
@@ -5460,6 +5524,8 @@ export namespace Prisma {
oauthAccessToken?: boolean oauthAccessToken?: boolean
oauthExpiresAt?: boolean oauthExpiresAt?: boolean
providerUserId?: boolean providerUserId?: boolean
oauthLastCheckedAt?: boolean
oauthLastErrorCode?: boolean
appPassword?: boolean appPassword?: boolean
createdAt?: boolean createdAt?: boolean
updatedAt?: boolean updatedAt?: boolean
@@ -5499,6 +5565,8 @@ export namespace Prisma {
oauthAccessToken: string | null oauthAccessToken: string | null
oauthExpiresAt: Date | null oauthExpiresAt: Date | null
providerUserId: string | null providerUserId: string | null
oauthLastCheckedAt: Date | null
oauthLastErrorCode: string | null
appPassword: string | null appPassword: string | null
createdAt: Date createdAt: Date
updatedAt: Date updatedAt: Date
@@ -5914,6 +5982,8 @@ export namespace Prisma {
readonly oauthAccessToken: FieldRef<"MailboxAccount", 'String'> readonly oauthAccessToken: FieldRef<"MailboxAccount", 'String'>
readonly oauthExpiresAt: FieldRef<"MailboxAccount", 'DateTime'> readonly oauthExpiresAt: FieldRef<"MailboxAccount", 'DateTime'>
readonly providerUserId: FieldRef<"MailboxAccount", 'String'> readonly providerUserId: FieldRef<"MailboxAccount", 'String'>
readonly oauthLastCheckedAt: FieldRef<"MailboxAccount", 'DateTime'>
readonly oauthLastErrorCode: FieldRef<"MailboxAccount", 'String'>
readonly appPassword: FieldRef<"MailboxAccount", 'String'> readonly appPassword: FieldRef<"MailboxAccount", 'String'>
readonly createdAt: FieldRef<"MailboxAccount", 'DateTime'> readonly createdAt: FieldRef<"MailboxAccount", 'DateTime'>
readonly updatedAt: FieldRef<"MailboxAccount", 'DateTime'> readonly updatedAt: FieldRef<"MailboxAccount", 'DateTime'>
@@ -14118,6 +14188,7 @@ export namespace Prisma {
status: 'status', status: 'status',
format: 'format', format: 'format',
scope: 'scope', scope: 'scope',
progress: 'progress',
filePath: 'filePath', filePath: 'filePath',
error: 'error', error: 'error',
expiresAt: 'expiresAt', expiresAt: 'expiresAt',
@@ -14159,6 +14230,8 @@ export namespace Prisma {
oauthAccessToken: 'oauthAccessToken', oauthAccessToken: 'oauthAccessToken',
oauthExpiresAt: 'oauthExpiresAt', oauthExpiresAt: 'oauthExpiresAt',
providerUserId: 'providerUserId', providerUserId: 'providerUserId',
oauthLastCheckedAt: 'oauthLastCheckedAt',
oauthLastErrorCode: 'oauthLastErrorCode',
appPassword: 'appPassword', appPassword: 'appPassword',
createdAt: 'createdAt', createdAt: 'createdAt',
updatedAt: 'updatedAt' updatedAt: 'updatedAt'
@@ -14347,6 +14420,20 @@ export namespace Prisma {
/**
* Reference to a field of type 'Int'
*/
export type IntFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel, 'Int'>
/**
* Reference to a field of type 'Int[]'
*/
export type ListIntFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel, 'Int[]'>
/** /**
* Reference to a field of type 'UserRole' * Reference to a field of type 'UserRole'
*/ */
@@ -14375,20 +14462,6 @@ export namespace Prisma {
/**
* Reference to a field of type 'Int'
*/
export type IntFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel, 'Int'>
/**
* Reference to a field of type 'Int[]'
*/
export type ListIntFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel, 'Int[]'>
/** /**
* Reference to a field of type 'RuleConditionType' * Reference to a field of type 'RuleConditionType'
*/ */
@@ -14524,6 +14597,7 @@ export namespace Prisma {
status?: EnumExportStatusFilter<"ExportJob"> | $Enums.ExportStatus status?: EnumExportStatusFilter<"ExportJob"> | $Enums.ExportStatus
format?: StringFilter<"ExportJob"> | string format?: StringFilter<"ExportJob"> | string
scope?: StringFilter<"ExportJob"> | string scope?: StringFilter<"ExportJob"> | string
progress?: IntFilter<"ExportJob"> | number
filePath?: StringNullableFilter<"ExportJob"> | string | null filePath?: StringNullableFilter<"ExportJob"> | string | null
error?: StringNullableFilter<"ExportJob"> | string | null error?: StringNullableFilter<"ExportJob"> | string | null
expiresAt?: DateTimeNullableFilter<"ExportJob"> | Date | string | null expiresAt?: DateTimeNullableFilter<"ExportJob"> | Date | string | null
@@ -14538,6 +14612,7 @@ export namespace Prisma {
status?: SortOrder status?: SortOrder
format?: SortOrder format?: SortOrder
scope?: SortOrder scope?: SortOrder
progress?: SortOrder
filePath?: SortOrderInput | SortOrder filePath?: SortOrderInput | SortOrder
error?: SortOrderInput | SortOrder error?: SortOrderInput | SortOrder
expiresAt?: SortOrderInput | SortOrder expiresAt?: SortOrderInput | SortOrder
@@ -14555,6 +14630,7 @@ export namespace Prisma {
status?: EnumExportStatusFilter<"ExportJob"> | $Enums.ExportStatus status?: EnumExportStatusFilter<"ExportJob"> | $Enums.ExportStatus
format?: StringFilter<"ExportJob"> | string format?: StringFilter<"ExportJob"> | string
scope?: StringFilter<"ExportJob"> | string scope?: StringFilter<"ExportJob"> | string
progress?: IntFilter<"ExportJob"> | number
filePath?: StringNullableFilter<"ExportJob"> | string | null filePath?: StringNullableFilter<"ExportJob"> | string | null
error?: StringNullableFilter<"ExportJob"> | string | null error?: StringNullableFilter<"ExportJob"> | string | null
expiresAt?: DateTimeNullableFilter<"ExportJob"> | Date | string | null expiresAt?: DateTimeNullableFilter<"ExportJob"> | Date | string | null
@@ -14569,14 +14645,17 @@ export namespace Prisma {
status?: SortOrder status?: SortOrder
format?: SortOrder format?: SortOrder
scope?: SortOrder scope?: SortOrder
progress?: SortOrder
filePath?: SortOrderInput | SortOrder filePath?: SortOrderInput | SortOrder
error?: SortOrderInput | SortOrder error?: SortOrderInput | SortOrder
expiresAt?: SortOrderInput | SortOrder expiresAt?: SortOrderInput | SortOrder
createdAt?: SortOrder createdAt?: SortOrder
updatedAt?: SortOrder updatedAt?: SortOrder
_count?: ExportJobCountOrderByAggregateInput _count?: ExportJobCountOrderByAggregateInput
_avg?: ExportJobAvgOrderByAggregateInput
_max?: ExportJobMaxOrderByAggregateInput _max?: ExportJobMaxOrderByAggregateInput
_min?: ExportJobMinOrderByAggregateInput _min?: ExportJobMinOrderByAggregateInput
_sum?: ExportJobSumOrderByAggregateInput
} }
export type ExportJobScalarWhereWithAggregatesInput = { export type ExportJobScalarWhereWithAggregatesInput = {
@@ -14588,6 +14667,7 @@ export namespace Prisma {
status?: EnumExportStatusWithAggregatesFilter<"ExportJob"> | $Enums.ExportStatus status?: EnumExportStatusWithAggregatesFilter<"ExportJob"> | $Enums.ExportStatus
format?: StringWithAggregatesFilter<"ExportJob"> | string format?: StringWithAggregatesFilter<"ExportJob"> | string
scope?: StringWithAggregatesFilter<"ExportJob"> | string scope?: StringWithAggregatesFilter<"ExportJob"> | string
progress?: IntWithAggregatesFilter<"ExportJob"> | number
filePath?: StringNullableWithAggregatesFilter<"ExportJob"> | string | null filePath?: StringNullableWithAggregatesFilter<"ExportJob"> | string | null
error?: StringNullableWithAggregatesFilter<"ExportJob"> | string | null error?: StringNullableWithAggregatesFilter<"ExportJob"> | string | null
expiresAt?: DateTimeNullableWithAggregatesFilter<"ExportJob"> | Date | string | null expiresAt?: DateTimeNullableWithAggregatesFilter<"ExportJob"> | Date | string | null
@@ -14685,6 +14765,8 @@ export namespace Prisma {
oauthAccessToken?: StringNullableFilter<"MailboxAccount"> | string | null oauthAccessToken?: StringNullableFilter<"MailboxAccount"> | string | null
oauthExpiresAt?: DateTimeNullableFilter<"MailboxAccount"> | Date | string | null oauthExpiresAt?: DateTimeNullableFilter<"MailboxAccount"> | Date | string | null
providerUserId?: StringNullableFilter<"MailboxAccount"> | string | null providerUserId?: StringNullableFilter<"MailboxAccount"> | string | null
oauthLastCheckedAt?: DateTimeNullableFilter<"MailboxAccount"> | Date | string | null
oauthLastErrorCode?: StringNullableFilter<"MailboxAccount"> | string | null
appPassword?: StringNullableFilter<"MailboxAccount"> | string | null appPassword?: StringNullableFilter<"MailboxAccount"> | string | null
createdAt?: DateTimeFilter<"MailboxAccount"> | Date | string createdAt?: DateTimeFilter<"MailboxAccount"> | Date | string
updatedAt?: DateTimeFilter<"MailboxAccount"> | Date | string updatedAt?: DateTimeFilter<"MailboxAccount"> | Date | string
@@ -14710,6 +14792,8 @@ export namespace Prisma {
oauthAccessToken?: SortOrderInput | SortOrder oauthAccessToken?: SortOrderInput | SortOrder
oauthExpiresAt?: SortOrderInput | SortOrder oauthExpiresAt?: SortOrderInput | SortOrder
providerUserId?: SortOrderInput | SortOrder providerUserId?: SortOrderInput | SortOrder
oauthLastCheckedAt?: SortOrderInput | SortOrder
oauthLastErrorCode?: SortOrderInput | SortOrder
appPassword?: SortOrderInput | SortOrder appPassword?: SortOrderInput | SortOrder
createdAt?: SortOrder createdAt?: SortOrder
updatedAt?: SortOrder updatedAt?: SortOrder
@@ -14738,6 +14822,8 @@ export namespace Prisma {
oauthAccessToken?: StringNullableFilter<"MailboxAccount"> | string | null oauthAccessToken?: StringNullableFilter<"MailboxAccount"> | string | null
oauthExpiresAt?: DateTimeNullableFilter<"MailboxAccount"> | Date | string | null oauthExpiresAt?: DateTimeNullableFilter<"MailboxAccount"> | Date | string | null
providerUserId?: StringNullableFilter<"MailboxAccount"> | string | null providerUserId?: StringNullableFilter<"MailboxAccount"> | string | null
oauthLastCheckedAt?: DateTimeNullableFilter<"MailboxAccount"> | Date | string | null
oauthLastErrorCode?: StringNullableFilter<"MailboxAccount"> | string | null
appPassword?: StringNullableFilter<"MailboxAccount"> | string | null appPassword?: StringNullableFilter<"MailboxAccount"> | string | null
createdAt?: DateTimeFilter<"MailboxAccount"> | Date | string createdAt?: DateTimeFilter<"MailboxAccount"> | Date | string
updatedAt?: DateTimeFilter<"MailboxAccount"> | Date | string updatedAt?: DateTimeFilter<"MailboxAccount"> | Date | string
@@ -14763,6 +14849,8 @@ export namespace Prisma {
oauthAccessToken?: SortOrderInput | SortOrder oauthAccessToken?: SortOrderInput | SortOrder
oauthExpiresAt?: SortOrderInput | SortOrder oauthExpiresAt?: SortOrderInput | SortOrder
providerUserId?: SortOrderInput | SortOrder providerUserId?: SortOrderInput | SortOrder
oauthLastCheckedAt?: SortOrderInput | SortOrder
oauthLastErrorCode?: SortOrderInput | SortOrder
appPassword?: SortOrderInput | SortOrder appPassword?: SortOrderInput | SortOrder
createdAt?: SortOrder createdAt?: SortOrder
updatedAt?: SortOrder updatedAt?: SortOrder
@@ -14793,6 +14881,8 @@ export namespace Prisma {
oauthAccessToken?: StringNullableWithAggregatesFilter<"MailboxAccount"> | string | null oauthAccessToken?: StringNullableWithAggregatesFilter<"MailboxAccount"> | string | null
oauthExpiresAt?: DateTimeNullableWithAggregatesFilter<"MailboxAccount"> | Date | string | null oauthExpiresAt?: DateTimeNullableWithAggregatesFilter<"MailboxAccount"> | Date | string | null
providerUserId?: StringNullableWithAggregatesFilter<"MailboxAccount"> | string | null providerUserId?: StringNullableWithAggregatesFilter<"MailboxAccount"> | string | null
oauthLastCheckedAt?: DateTimeNullableWithAggregatesFilter<"MailboxAccount"> | Date | string | null
oauthLastErrorCode?: StringNullableWithAggregatesFilter<"MailboxAccount"> | string | null
appPassword?: StringNullableWithAggregatesFilter<"MailboxAccount"> | string | null appPassword?: StringNullableWithAggregatesFilter<"MailboxAccount"> | string | null
createdAt?: DateTimeWithAggregatesFilter<"MailboxAccount"> | Date | string createdAt?: DateTimeWithAggregatesFilter<"MailboxAccount"> | Date | string
updatedAt?: DateTimeWithAggregatesFilter<"MailboxAccount"> | Date | string updatedAt?: DateTimeWithAggregatesFilter<"MailboxAccount"> | Date | string
@@ -15409,6 +15499,7 @@ export namespace Prisma {
status?: $Enums.ExportStatus status?: $Enums.ExportStatus
format: string format: string
scope: string scope: string
progress?: number
filePath?: string | null filePath?: string | null
error?: string | null error?: string | null
expiresAt?: Date | string | null expiresAt?: Date | string | null
@@ -15423,6 +15514,7 @@ export namespace Prisma {
status?: $Enums.ExportStatus status?: $Enums.ExportStatus
format: string format: string
scope: string scope: string
progress?: number
filePath?: string | null filePath?: string | null
error?: string | null error?: string | null
expiresAt?: Date | string | null expiresAt?: Date | string | null
@@ -15435,6 +15527,7 @@ export namespace Prisma {
status?: EnumExportStatusFieldUpdateOperationsInput | $Enums.ExportStatus status?: EnumExportStatusFieldUpdateOperationsInput | $Enums.ExportStatus
format?: StringFieldUpdateOperationsInput | string format?: StringFieldUpdateOperationsInput | string
scope?: StringFieldUpdateOperationsInput | string scope?: StringFieldUpdateOperationsInput | string
progress?: IntFieldUpdateOperationsInput | number
filePath?: NullableStringFieldUpdateOperationsInput | string | null filePath?: NullableStringFieldUpdateOperationsInput | string | null
error?: NullableStringFieldUpdateOperationsInput | string | null error?: NullableStringFieldUpdateOperationsInput | string | null
expiresAt?: NullableDateTimeFieldUpdateOperationsInput | Date | string | null expiresAt?: NullableDateTimeFieldUpdateOperationsInput | Date | string | null
@@ -15449,6 +15542,7 @@ export namespace Prisma {
status?: EnumExportStatusFieldUpdateOperationsInput | $Enums.ExportStatus status?: EnumExportStatusFieldUpdateOperationsInput | $Enums.ExportStatus
format?: StringFieldUpdateOperationsInput | string format?: StringFieldUpdateOperationsInput | string
scope?: StringFieldUpdateOperationsInput | string scope?: StringFieldUpdateOperationsInput | string
progress?: IntFieldUpdateOperationsInput | number
filePath?: NullableStringFieldUpdateOperationsInput | string | null filePath?: NullableStringFieldUpdateOperationsInput | string | null
error?: NullableStringFieldUpdateOperationsInput | string | null error?: NullableStringFieldUpdateOperationsInput | string | null
expiresAt?: NullableDateTimeFieldUpdateOperationsInput | Date | string | null expiresAt?: NullableDateTimeFieldUpdateOperationsInput | Date | string | null
@@ -15462,6 +15556,7 @@ export namespace Prisma {
status?: $Enums.ExportStatus status?: $Enums.ExportStatus
format: string format: string
scope: string scope: string
progress?: number
filePath?: string | null filePath?: string | null
error?: string | null error?: string | null
expiresAt?: Date | string | null expiresAt?: Date | string | null
@@ -15474,6 +15569,7 @@ export namespace Prisma {
status?: EnumExportStatusFieldUpdateOperationsInput | $Enums.ExportStatus status?: EnumExportStatusFieldUpdateOperationsInput | $Enums.ExportStatus
format?: StringFieldUpdateOperationsInput | string format?: StringFieldUpdateOperationsInput | string
scope?: StringFieldUpdateOperationsInput | string scope?: StringFieldUpdateOperationsInput | string
progress?: IntFieldUpdateOperationsInput | number
filePath?: NullableStringFieldUpdateOperationsInput | string | null filePath?: NullableStringFieldUpdateOperationsInput | string | null
error?: NullableStringFieldUpdateOperationsInput | string | null error?: NullableStringFieldUpdateOperationsInput | string | null
expiresAt?: NullableDateTimeFieldUpdateOperationsInput | Date | string | null expiresAt?: NullableDateTimeFieldUpdateOperationsInput | Date | string | null
@@ -15487,6 +15583,7 @@ export namespace Prisma {
status?: EnumExportStatusFieldUpdateOperationsInput | $Enums.ExportStatus status?: EnumExportStatusFieldUpdateOperationsInput | $Enums.ExportStatus
format?: StringFieldUpdateOperationsInput | string format?: StringFieldUpdateOperationsInput | string
scope?: StringFieldUpdateOperationsInput | string scope?: StringFieldUpdateOperationsInput | string
progress?: IntFieldUpdateOperationsInput | number
filePath?: NullableStringFieldUpdateOperationsInput | string | null filePath?: NullableStringFieldUpdateOperationsInput | string | null
error?: NullableStringFieldUpdateOperationsInput | string | null error?: NullableStringFieldUpdateOperationsInput | string | null
expiresAt?: NullableDateTimeFieldUpdateOperationsInput | Date | string | null expiresAt?: NullableDateTimeFieldUpdateOperationsInput | Date | string | null
@@ -15586,6 +15683,8 @@ export namespace Prisma {
oauthAccessToken?: string | null oauthAccessToken?: string | null
oauthExpiresAt?: Date | string | null oauthExpiresAt?: Date | string | null
providerUserId?: string | null providerUserId?: string | null
oauthLastCheckedAt?: Date | string | null
oauthLastErrorCode?: string | null
appPassword?: string | null appPassword?: string | null
createdAt?: Date | string createdAt?: Date | string
updatedAt?: Date | string updatedAt?: Date | string
@@ -15611,6 +15710,8 @@ export namespace Prisma {
oauthAccessToken?: string | null oauthAccessToken?: string | null
oauthExpiresAt?: Date | string | null oauthExpiresAt?: Date | string | null
providerUserId?: string | null providerUserId?: string | null
oauthLastCheckedAt?: Date | string | null
oauthLastErrorCode?: string | null
appPassword?: string | null appPassword?: string | null
createdAt?: Date | string createdAt?: Date | string
updatedAt?: Date | string updatedAt?: Date | string
@@ -15634,6 +15735,8 @@ export namespace Prisma {
oauthAccessToken?: NullableStringFieldUpdateOperationsInput | string | null oauthAccessToken?: NullableStringFieldUpdateOperationsInput | string | null
oauthExpiresAt?: NullableDateTimeFieldUpdateOperationsInput | Date | string | null oauthExpiresAt?: NullableDateTimeFieldUpdateOperationsInput | Date | string | null
providerUserId?: NullableStringFieldUpdateOperationsInput | string | null providerUserId?: NullableStringFieldUpdateOperationsInput | string | null
oauthLastCheckedAt?: NullableDateTimeFieldUpdateOperationsInput | Date | string | null
oauthLastErrorCode?: NullableStringFieldUpdateOperationsInput | string | null
appPassword?: NullableStringFieldUpdateOperationsInput | string | null appPassword?: NullableStringFieldUpdateOperationsInput | string | null
createdAt?: DateTimeFieldUpdateOperationsInput | Date | string createdAt?: DateTimeFieldUpdateOperationsInput | Date | string
updatedAt?: DateTimeFieldUpdateOperationsInput | Date | string updatedAt?: DateTimeFieldUpdateOperationsInput | Date | string
@@ -15659,6 +15762,8 @@ export namespace Prisma {
oauthAccessToken?: NullableStringFieldUpdateOperationsInput | string | null oauthAccessToken?: NullableStringFieldUpdateOperationsInput | string | null
oauthExpiresAt?: NullableDateTimeFieldUpdateOperationsInput | Date | string | null oauthExpiresAt?: NullableDateTimeFieldUpdateOperationsInput | Date | string | null
providerUserId?: NullableStringFieldUpdateOperationsInput | string | null providerUserId?: NullableStringFieldUpdateOperationsInput | string | null
oauthLastCheckedAt?: NullableDateTimeFieldUpdateOperationsInput | Date | string | null
oauthLastErrorCode?: NullableStringFieldUpdateOperationsInput | string | null
appPassword?: NullableStringFieldUpdateOperationsInput | string | null appPassword?: NullableStringFieldUpdateOperationsInput | string | null
createdAt?: DateTimeFieldUpdateOperationsInput | Date | string createdAt?: DateTimeFieldUpdateOperationsInput | Date | string
updatedAt?: DateTimeFieldUpdateOperationsInput | Date | string updatedAt?: DateTimeFieldUpdateOperationsInput | Date | string
@@ -15683,6 +15788,8 @@ export namespace Prisma {
oauthAccessToken?: string | null oauthAccessToken?: string | null
oauthExpiresAt?: Date | string | null oauthExpiresAt?: Date | string | null
providerUserId?: string | null providerUserId?: string | null
oauthLastCheckedAt?: Date | string | null
oauthLastErrorCode?: string | null
appPassword?: string | null appPassword?: string | null
createdAt?: Date | string createdAt?: Date | string
updatedAt?: Date | string updatedAt?: Date | string
@@ -15704,6 +15811,8 @@ export namespace Prisma {
oauthAccessToken?: NullableStringFieldUpdateOperationsInput | string | null oauthAccessToken?: NullableStringFieldUpdateOperationsInput | string | null
oauthExpiresAt?: NullableDateTimeFieldUpdateOperationsInput | Date | string | null oauthExpiresAt?: NullableDateTimeFieldUpdateOperationsInput | Date | string | null
providerUserId?: NullableStringFieldUpdateOperationsInput | string | null providerUserId?: NullableStringFieldUpdateOperationsInput | string | null
oauthLastCheckedAt?: NullableDateTimeFieldUpdateOperationsInput | Date | string | null
oauthLastErrorCode?: NullableStringFieldUpdateOperationsInput | string | null
appPassword?: NullableStringFieldUpdateOperationsInput | string | null appPassword?: NullableStringFieldUpdateOperationsInput | string | null
createdAt?: DateTimeFieldUpdateOperationsInput | Date | string createdAt?: DateTimeFieldUpdateOperationsInput | Date | string
updatedAt?: DateTimeFieldUpdateOperationsInput | Date | string updatedAt?: DateTimeFieldUpdateOperationsInput | Date | string
@@ -15726,6 +15835,8 @@ export namespace Prisma {
oauthAccessToken?: NullableStringFieldUpdateOperationsInput | string | null oauthAccessToken?: NullableStringFieldUpdateOperationsInput | string | null
oauthExpiresAt?: NullableDateTimeFieldUpdateOperationsInput | Date | string | null oauthExpiresAt?: NullableDateTimeFieldUpdateOperationsInput | Date | string | null
providerUserId?: NullableStringFieldUpdateOperationsInput | string | null providerUserId?: NullableStringFieldUpdateOperationsInput | string | null
oauthLastCheckedAt?: NullableDateTimeFieldUpdateOperationsInput | Date | string | null
oauthLastErrorCode?: NullableStringFieldUpdateOperationsInput | string | null
appPassword?: NullableStringFieldUpdateOperationsInput | string | null appPassword?: NullableStringFieldUpdateOperationsInput | string | null
createdAt?: DateTimeFieldUpdateOperationsInput | Date | string createdAt?: DateTimeFieldUpdateOperationsInput | Date | string
updatedAt?: DateTimeFieldUpdateOperationsInput | Date | string updatedAt?: DateTimeFieldUpdateOperationsInput | Date | string
@@ -16440,6 +16551,17 @@ export namespace Prisma {
not?: NestedEnumExportStatusFilter<$PrismaModel> | $Enums.ExportStatus not?: NestedEnumExportStatusFilter<$PrismaModel> | $Enums.ExportStatus
} }
export type IntFilter<$PrismaModel = never> = {
equals?: number | IntFieldRefInput<$PrismaModel>
in?: number[] | ListIntFieldRefInput<$PrismaModel>
notIn?: number[] | ListIntFieldRefInput<$PrismaModel>
lt?: number | IntFieldRefInput<$PrismaModel>
lte?: number | IntFieldRefInput<$PrismaModel>
gt?: number | IntFieldRefInput<$PrismaModel>
gte?: number | IntFieldRefInput<$PrismaModel>
not?: NestedIntFilter<$PrismaModel> | number
}
export type StringNullableFilter<$PrismaModel = never> = { export type StringNullableFilter<$PrismaModel = never> = {
equals?: string | StringFieldRefInput<$PrismaModel> | null equals?: string | StringFieldRefInput<$PrismaModel> | null
in?: string[] | ListStringFieldRefInput<$PrismaModel> | null in?: string[] | ListStringFieldRefInput<$PrismaModel> | null
@@ -16482,6 +16604,7 @@ export namespace Prisma {
status?: SortOrder status?: SortOrder
format?: SortOrder format?: SortOrder
scope?: SortOrder scope?: SortOrder
progress?: SortOrder
filePath?: SortOrder filePath?: SortOrder
error?: SortOrder error?: SortOrder
expiresAt?: SortOrder expiresAt?: SortOrder
@@ -16489,12 +16612,17 @@ export namespace Prisma {
updatedAt?: SortOrder updatedAt?: SortOrder
} }
export type ExportJobAvgOrderByAggregateInput = {
progress?: SortOrder
}
export type ExportJobMaxOrderByAggregateInput = { export type ExportJobMaxOrderByAggregateInput = {
id?: SortOrder id?: SortOrder
tenantId?: SortOrder tenantId?: SortOrder
status?: SortOrder status?: SortOrder
format?: SortOrder format?: SortOrder
scope?: SortOrder scope?: SortOrder
progress?: SortOrder
filePath?: SortOrder filePath?: SortOrder
error?: SortOrder error?: SortOrder
expiresAt?: SortOrder expiresAt?: SortOrder
@@ -16508,6 +16636,7 @@ export namespace Prisma {
status?: SortOrder status?: SortOrder
format?: SortOrder format?: SortOrder
scope?: SortOrder scope?: SortOrder
progress?: SortOrder
filePath?: SortOrder filePath?: SortOrder
error?: SortOrder error?: SortOrder
expiresAt?: SortOrder expiresAt?: SortOrder
@@ -16515,6 +16644,10 @@ export namespace Prisma {
updatedAt?: SortOrder updatedAt?: SortOrder
} }
export type ExportJobSumOrderByAggregateInput = {
progress?: SortOrder
}
export type EnumExportStatusWithAggregatesFilter<$PrismaModel = never> = { export type EnumExportStatusWithAggregatesFilter<$PrismaModel = never> = {
equals?: $Enums.ExportStatus | EnumExportStatusFieldRefInput<$PrismaModel> equals?: $Enums.ExportStatus | EnumExportStatusFieldRefInput<$PrismaModel>
in?: $Enums.ExportStatus[] | ListEnumExportStatusFieldRefInput<$PrismaModel> in?: $Enums.ExportStatus[] | ListEnumExportStatusFieldRefInput<$PrismaModel>
@@ -16525,6 +16658,22 @@ export namespace Prisma {
_max?: NestedEnumExportStatusFilter<$PrismaModel> _max?: NestedEnumExportStatusFilter<$PrismaModel>
} }
export type IntWithAggregatesFilter<$PrismaModel = never> = {
equals?: number | IntFieldRefInput<$PrismaModel>
in?: number[] | ListIntFieldRefInput<$PrismaModel>
notIn?: number[] | ListIntFieldRefInput<$PrismaModel>
lt?: number | IntFieldRefInput<$PrismaModel>
lte?: number | IntFieldRefInput<$PrismaModel>
gt?: number | IntFieldRefInput<$PrismaModel>
gte?: number | IntFieldRefInput<$PrismaModel>
not?: NestedIntWithAggregatesFilter<$PrismaModel> | number
_count?: NestedIntFilter<$PrismaModel>
_avg?: NestedFloatFilter<$PrismaModel>
_sum?: NestedIntFilter<$PrismaModel>
_min?: NestedIntFilter<$PrismaModel>
_max?: NestedIntFilter<$PrismaModel>
}
export type StringNullableWithAggregatesFilter<$PrismaModel = never> = { export type StringNullableWithAggregatesFilter<$PrismaModel = never> = {
equals?: string | StringFieldRefInput<$PrismaModel> | null equals?: string | StringFieldRefInput<$PrismaModel> | null
in?: string[] | ListStringFieldRefInput<$PrismaModel> | null in?: string[] | ListStringFieldRefInput<$PrismaModel> | null
@@ -16614,17 +16763,6 @@ export namespace Prisma {
not?: NestedEnumMailProviderFilter<$PrismaModel> | $Enums.MailProvider not?: NestedEnumMailProviderFilter<$PrismaModel> | $Enums.MailProvider
} }
export type IntFilter<$PrismaModel = never> = {
equals?: number | IntFieldRefInput<$PrismaModel>
in?: number[] | ListIntFieldRefInput<$PrismaModel>
notIn?: number[] | ListIntFieldRefInput<$PrismaModel>
lt?: number | IntFieldRefInput<$PrismaModel>
lte?: number | IntFieldRefInput<$PrismaModel>
gt?: number | IntFieldRefInput<$PrismaModel>
gte?: number | IntFieldRefInput<$PrismaModel>
not?: NestedIntFilter<$PrismaModel> | number
}
export type IntNullableFilter<$PrismaModel = never> = { export type IntNullableFilter<$PrismaModel = never> = {
equals?: number | IntFieldRefInput<$PrismaModel> | null equals?: number | IntFieldRefInput<$PrismaModel> | null
in?: number[] | ListIntFieldRefInput<$PrismaModel> | null in?: number[] | ListIntFieldRefInput<$PrismaModel> | null
@@ -16668,6 +16806,8 @@ export namespace Prisma {
oauthAccessToken?: SortOrder oauthAccessToken?: SortOrder
oauthExpiresAt?: SortOrder oauthExpiresAt?: SortOrder
providerUserId?: SortOrder providerUserId?: SortOrder
oauthLastCheckedAt?: SortOrder
oauthLastErrorCode?: SortOrder
appPassword?: SortOrder appPassword?: SortOrder
createdAt?: SortOrder createdAt?: SortOrder
updatedAt?: SortOrder updatedAt?: SortOrder
@@ -16695,6 +16835,8 @@ export namespace Prisma {
oauthAccessToken?: SortOrder oauthAccessToken?: SortOrder
oauthExpiresAt?: SortOrder oauthExpiresAt?: SortOrder
providerUserId?: SortOrder providerUserId?: SortOrder
oauthLastCheckedAt?: SortOrder
oauthLastErrorCode?: SortOrder
appPassword?: SortOrder appPassword?: SortOrder
createdAt?: SortOrder createdAt?: SortOrder
updatedAt?: SortOrder updatedAt?: SortOrder
@@ -16717,6 +16859,8 @@ export namespace Prisma {
oauthAccessToken?: SortOrder oauthAccessToken?: SortOrder
oauthExpiresAt?: SortOrder oauthExpiresAt?: SortOrder
providerUserId?: SortOrder providerUserId?: SortOrder
oauthLastCheckedAt?: SortOrder
oauthLastErrorCode?: SortOrder
appPassword?: SortOrder appPassword?: SortOrder
createdAt?: SortOrder createdAt?: SortOrder
updatedAt?: SortOrder updatedAt?: SortOrder
@@ -16737,22 +16881,6 @@ export namespace Prisma {
_max?: NestedEnumMailProviderFilter<$PrismaModel> _max?: NestedEnumMailProviderFilter<$PrismaModel>
} }
export type IntWithAggregatesFilter<$PrismaModel = never> = {
equals?: number | IntFieldRefInput<$PrismaModel>
in?: number[] | ListIntFieldRefInput<$PrismaModel>
notIn?: number[] | ListIntFieldRefInput<$PrismaModel>
lt?: number | IntFieldRefInput<$PrismaModel>
lte?: number | IntFieldRefInput<$PrismaModel>
gt?: number | IntFieldRefInput<$PrismaModel>
gte?: number | IntFieldRefInput<$PrismaModel>
not?: NestedIntWithAggregatesFilter<$PrismaModel> | number
_count?: NestedIntFilter<$PrismaModel>
_avg?: NestedFloatFilter<$PrismaModel>
_sum?: NestedIntFilter<$PrismaModel>
_min?: NestedIntFilter<$PrismaModel>
_max?: NestedIntFilter<$PrismaModel>
}
export type IntNullableWithAggregatesFilter<$PrismaModel = never> = { export type IntNullableWithAggregatesFilter<$PrismaModel = never> = {
equals?: number | IntFieldRefInput<$PrismaModel> | null equals?: number | IntFieldRefInput<$PrismaModel> | null
in?: number[] | ListIntFieldRefInput<$PrismaModel> | null in?: number[] | ListIntFieldRefInput<$PrismaModel> | null
@@ -17372,6 +17500,14 @@ export namespace Prisma {
set?: $Enums.ExportStatus set?: $Enums.ExportStatus
} }
export type IntFieldUpdateOperationsInput = {
set?: number
increment?: number
decrement?: number
multiply?: number
divide?: number
}
export type NullableStringFieldUpdateOperationsInput = { export type NullableStringFieldUpdateOperationsInput = {
set?: string | null set?: string | null
} }
@@ -17444,14 +17580,6 @@ export namespace Prisma {
set?: $Enums.MailProvider set?: $Enums.MailProvider
} }
export type IntFieldUpdateOperationsInput = {
set?: number
increment?: number
decrement?: number
multiply?: number
divide?: number
}
export type NullableIntFieldUpdateOperationsInput = { export type NullableIntFieldUpdateOperationsInput = {
set?: number | null set?: number | null
increment?: number increment?: number
@@ -17998,6 +18126,33 @@ export namespace Prisma {
_max?: NestedEnumExportStatusFilter<$PrismaModel> _max?: NestedEnumExportStatusFilter<$PrismaModel>
} }
export type NestedIntWithAggregatesFilter<$PrismaModel = never> = {
equals?: number | IntFieldRefInput<$PrismaModel>
in?: number[] | ListIntFieldRefInput<$PrismaModel>
notIn?: number[] | ListIntFieldRefInput<$PrismaModel>
lt?: number | IntFieldRefInput<$PrismaModel>
lte?: number | IntFieldRefInput<$PrismaModel>
gt?: number | IntFieldRefInput<$PrismaModel>
gte?: number | IntFieldRefInput<$PrismaModel>
not?: NestedIntWithAggregatesFilter<$PrismaModel> | number
_count?: NestedIntFilter<$PrismaModel>
_avg?: NestedFloatFilter<$PrismaModel>
_sum?: NestedIntFilter<$PrismaModel>
_min?: NestedIntFilter<$PrismaModel>
_max?: NestedIntFilter<$PrismaModel>
}
export type NestedFloatFilter<$PrismaModel = never> = {
equals?: number | FloatFieldRefInput<$PrismaModel>
in?: number[] | ListFloatFieldRefInput<$PrismaModel>
notIn?: number[] | ListFloatFieldRefInput<$PrismaModel>
lt?: number | FloatFieldRefInput<$PrismaModel>
lte?: number | FloatFieldRefInput<$PrismaModel>
gt?: number | FloatFieldRefInput<$PrismaModel>
gte?: number | FloatFieldRefInput<$PrismaModel>
not?: NestedFloatFilter<$PrismaModel> | number
}
export type NestedStringNullableWithAggregatesFilter<$PrismaModel = never> = { export type NestedStringNullableWithAggregatesFilter<$PrismaModel = never> = {
equals?: string | StringFieldRefInput<$PrismaModel> | null equals?: string | StringFieldRefInput<$PrismaModel> | null
in?: string[] | ListStringFieldRefInput<$PrismaModel> | null in?: string[] | ListStringFieldRefInput<$PrismaModel> | null
@@ -18079,33 +18234,6 @@ export namespace Prisma {
_max?: NestedEnumMailProviderFilter<$PrismaModel> _max?: NestedEnumMailProviderFilter<$PrismaModel>
} }
export type NestedIntWithAggregatesFilter<$PrismaModel = never> = {
equals?: number | IntFieldRefInput<$PrismaModel>
in?: number[] | ListIntFieldRefInput<$PrismaModel>
notIn?: number[] | ListIntFieldRefInput<$PrismaModel>
lt?: number | IntFieldRefInput<$PrismaModel>
lte?: number | IntFieldRefInput<$PrismaModel>
gt?: number | IntFieldRefInput<$PrismaModel>
gte?: number | IntFieldRefInput<$PrismaModel>
not?: NestedIntWithAggregatesFilter<$PrismaModel> | number
_count?: NestedIntFilter<$PrismaModel>
_avg?: NestedFloatFilter<$PrismaModel>
_sum?: NestedIntFilter<$PrismaModel>
_min?: NestedIntFilter<$PrismaModel>
_max?: NestedIntFilter<$PrismaModel>
}
export type NestedFloatFilter<$PrismaModel = never> = {
equals?: number | FloatFieldRefInput<$PrismaModel>
in?: number[] | ListFloatFieldRefInput<$PrismaModel>
notIn?: number[] | ListFloatFieldRefInput<$PrismaModel>
lt?: number | FloatFieldRefInput<$PrismaModel>
lte?: number | FloatFieldRefInput<$PrismaModel>
gt?: number | FloatFieldRefInput<$PrismaModel>
gte?: number | FloatFieldRefInput<$PrismaModel>
not?: NestedFloatFilter<$PrismaModel> | number
}
export type NestedIntNullableWithAggregatesFilter<$PrismaModel = never> = { export type NestedIntNullableWithAggregatesFilter<$PrismaModel = never> = {
equals?: number | IntFieldRefInput<$PrismaModel> | null equals?: number | IntFieldRefInput<$PrismaModel> | null
in?: number[] | ListIntFieldRefInput<$PrismaModel> | null in?: number[] | ListIntFieldRefInput<$PrismaModel> | null
@@ -18197,6 +18325,7 @@ export namespace Prisma {
status?: $Enums.ExportStatus status?: $Enums.ExportStatus
format: string format: string
scope: string scope: string
progress?: number
filePath?: string | null filePath?: string | null
error?: string | null error?: string | null
expiresAt?: Date | string | null expiresAt?: Date | string | null
@@ -18209,6 +18338,7 @@ export namespace Prisma {
status?: $Enums.ExportStatus status?: $Enums.ExportStatus
format: string format: string
scope: string scope: string
progress?: number
filePath?: string | null filePath?: string | null
error?: string | null error?: string | null
expiresAt?: Date | string | null expiresAt?: Date | string | null
@@ -18272,6 +18402,8 @@ export namespace Prisma {
oauthAccessToken?: string | null oauthAccessToken?: string | null
oauthExpiresAt?: Date | string | null oauthExpiresAt?: Date | string | null
providerUserId?: string | null providerUserId?: string | null
oauthLastCheckedAt?: Date | string | null
oauthLastErrorCode?: string | null
appPassword?: string | null appPassword?: string | null
createdAt?: Date | string createdAt?: Date | string
updatedAt?: Date | string updatedAt?: Date | string
@@ -18295,6 +18427,8 @@ export namespace Prisma {
oauthAccessToken?: string | null oauthAccessToken?: string | null
oauthExpiresAt?: Date | string | null oauthExpiresAt?: Date | string | null
providerUserId?: string | null providerUserId?: string | null
oauthLastCheckedAt?: Date | string | null
oauthLastErrorCode?: string | null
appPassword?: string | null appPassword?: string | null
createdAt?: Date | string createdAt?: Date | string
updatedAt?: Date | string updatedAt?: Date | string
@@ -18407,6 +18541,7 @@ export namespace Prisma {
status?: EnumExportStatusFilter<"ExportJob"> | $Enums.ExportStatus status?: EnumExportStatusFilter<"ExportJob"> | $Enums.ExportStatus
format?: StringFilter<"ExportJob"> | string format?: StringFilter<"ExportJob"> | string
scope?: StringFilter<"ExportJob"> | string scope?: StringFilter<"ExportJob"> | string
progress?: IntFilter<"ExportJob"> | number
filePath?: StringNullableFilter<"ExportJob"> | string | null filePath?: StringNullableFilter<"ExportJob"> | string | null
error?: StringNullableFilter<"ExportJob"> | string | null error?: StringNullableFilter<"ExportJob"> | string | null
expiresAt?: DateTimeNullableFilter<"ExportJob"> | Date | string | null expiresAt?: DateTimeNullableFilter<"ExportJob"> | Date | string | null
@@ -18480,6 +18615,8 @@ export namespace Prisma {
oauthAccessToken?: StringNullableFilter<"MailboxAccount"> | string | null oauthAccessToken?: StringNullableFilter<"MailboxAccount"> | string | null
oauthExpiresAt?: DateTimeNullableFilter<"MailboxAccount"> | Date | string | null oauthExpiresAt?: DateTimeNullableFilter<"MailboxAccount"> | Date | string | null
providerUserId?: StringNullableFilter<"MailboxAccount"> | string | null providerUserId?: StringNullableFilter<"MailboxAccount"> | string | null
oauthLastCheckedAt?: DateTimeNullableFilter<"MailboxAccount"> | Date | string | null
oauthLastErrorCode?: StringNullableFilter<"MailboxAccount"> | string | null
appPassword?: StringNullableFilter<"MailboxAccount"> | string | null appPassword?: StringNullableFilter<"MailboxAccount"> | string | null
createdAt?: DateTimeFilter<"MailboxAccount"> | Date | string createdAt?: DateTimeFilter<"MailboxAccount"> | Date | string
updatedAt?: DateTimeFilter<"MailboxAccount"> | Date | string updatedAt?: DateTimeFilter<"MailboxAccount"> | Date | string
@@ -18866,6 +19003,8 @@ export namespace Prisma {
oauthAccessToken?: string | null oauthAccessToken?: string | null
oauthExpiresAt?: Date | string | null oauthExpiresAt?: Date | string | null
providerUserId?: string | null providerUserId?: string | null
oauthLastCheckedAt?: Date | string | null
oauthLastErrorCode?: string | null
appPassword?: string | null appPassword?: string | null
createdAt?: Date | string createdAt?: Date | string
updatedAt?: Date | string updatedAt?: Date | string
@@ -18890,6 +19029,8 @@ export namespace Prisma {
oauthAccessToken?: string | null oauthAccessToken?: string | null
oauthExpiresAt?: Date | string | null oauthExpiresAt?: Date | string | null
providerUserId?: string | null providerUserId?: string | null
oauthLastCheckedAt?: Date | string | null
oauthLastErrorCode?: string | null
appPassword?: string | null appPassword?: string | null
createdAt?: Date | string createdAt?: Date | string
updatedAt?: Date | string updatedAt?: Date | string
@@ -18962,6 +19103,8 @@ export namespace Prisma {
oauthAccessToken?: NullableStringFieldUpdateOperationsInput | string | null oauthAccessToken?: NullableStringFieldUpdateOperationsInput | string | null
oauthExpiresAt?: NullableDateTimeFieldUpdateOperationsInput | Date | string | null oauthExpiresAt?: NullableDateTimeFieldUpdateOperationsInput | Date | string | null
providerUserId?: NullableStringFieldUpdateOperationsInput | string | null providerUserId?: NullableStringFieldUpdateOperationsInput | string | null
oauthLastCheckedAt?: NullableDateTimeFieldUpdateOperationsInput | Date | string | null
oauthLastErrorCode?: NullableStringFieldUpdateOperationsInput | string | null
appPassword?: NullableStringFieldUpdateOperationsInput | string | null appPassword?: NullableStringFieldUpdateOperationsInput | string | null
createdAt?: DateTimeFieldUpdateOperationsInput | Date | string createdAt?: DateTimeFieldUpdateOperationsInput | Date | string
updatedAt?: DateTimeFieldUpdateOperationsInput | Date | string updatedAt?: DateTimeFieldUpdateOperationsInput | Date | string
@@ -18986,6 +19129,8 @@ export namespace Prisma {
oauthAccessToken?: NullableStringFieldUpdateOperationsInput | string | null oauthAccessToken?: NullableStringFieldUpdateOperationsInput | string | null
oauthExpiresAt?: NullableDateTimeFieldUpdateOperationsInput | Date | string | null oauthExpiresAt?: NullableDateTimeFieldUpdateOperationsInput | Date | string | null
providerUserId?: NullableStringFieldUpdateOperationsInput | string | null providerUserId?: NullableStringFieldUpdateOperationsInput | string | null
oauthLastCheckedAt?: NullableDateTimeFieldUpdateOperationsInput | Date | string | null
oauthLastErrorCode?: NullableStringFieldUpdateOperationsInput | string | null
appPassword?: NullableStringFieldUpdateOperationsInput | string | null appPassword?: NullableStringFieldUpdateOperationsInput | string | null
createdAt?: DateTimeFieldUpdateOperationsInput | Date | string createdAt?: DateTimeFieldUpdateOperationsInput | Date | string
updatedAt?: DateTimeFieldUpdateOperationsInput | Date | string updatedAt?: DateTimeFieldUpdateOperationsInput | Date | string
@@ -19393,6 +19538,8 @@ export namespace Prisma {
oauthAccessToken?: string | null oauthAccessToken?: string | null
oauthExpiresAt?: Date | string | null oauthExpiresAt?: Date | string | null
providerUserId?: string | null providerUserId?: string | null
oauthLastCheckedAt?: Date | string | null
oauthLastErrorCode?: string | null
appPassword?: string | null appPassword?: string | null
createdAt?: Date | string createdAt?: Date | string
updatedAt?: Date | string updatedAt?: Date | string
@@ -19417,6 +19564,8 @@ export namespace Prisma {
oauthAccessToken?: string | null oauthAccessToken?: string | null
oauthExpiresAt?: Date | string | null oauthExpiresAt?: Date | string | null
providerUserId?: string | null providerUserId?: string | null
oauthLastCheckedAt?: Date | string | null
oauthLastErrorCode?: string | null
appPassword?: string | null appPassword?: string | null
createdAt?: Date | string createdAt?: Date | string
updatedAt?: Date | string updatedAt?: Date | string
@@ -19544,6 +19693,8 @@ export namespace Prisma {
oauthAccessToken?: NullableStringFieldUpdateOperationsInput | string | null oauthAccessToken?: NullableStringFieldUpdateOperationsInput | string | null
oauthExpiresAt?: NullableDateTimeFieldUpdateOperationsInput | Date | string | null oauthExpiresAt?: NullableDateTimeFieldUpdateOperationsInput | Date | string | null
providerUserId?: NullableStringFieldUpdateOperationsInput | string | null providerUserId?: NullableStringFieldUpdateOperationsInput | string | null
oauthLastCheckedAt?: NullableDateTimeFieldUpdateOperationsInput | Date | string | null
oauthLastErrorCode?: NullableStringFieldUpdateOperationsInput | string | null
appPassword?: NullableStringFieldUpdateOperationsInput | string | null appPassword?: NullableStringFieldUpdateOperationsInput | string | null
createdAt?: DateTimeFieldUpdateOperationsInput | Date | string createdAt?: DateTimeFieldUpdateOperationsInput | Date | string
updatedAt?: DateTimeFieldUpdateOperationsInput | Date | string updatedAt?: DateTimeFieldUpdateOperationsInput | Date | string
@@ -19568,6 +19719,8 @@ export namespace Prisma {
oauthAccessToken?: NullableStringFieldUpdateOperationsInput | string | null oauthAccessToken?: NullableStringFieldUpdateOperationsInput | string | null
oauthExpiresAt?: NullableDateTimeFieldUpdateOperationsInput | Date | string | null oauthExpiresAt?: NullableDateTimeFieldUpdateOperationsInput | Date | string | null
providerUserId?: NullableStringFieldUpdateOperationsInput | string | null providerUserId?: NullableStringFieldUpdateOperationsInput | string | null
oauthLastCheckedAt?: NullableDateTimeFieldUpdateOperationsInput | Date | string | null
oauthLastErrorCode?: NullableStringFieldUpdateOperationsInput | string | null
appPassword?: NullableStringFieldUpdateOperationsInput | string | null appPassword?: NullableStringFieldUpdateOperationsInput | string | null
createdAt?: DateTimeFieldUpdateOperationsInput | Date | string createdAt?: DateTimeFieldUpdateOperationsInput | Date | string
updatedAt?: DateTimeFieldUpdateOperationsInput | Date | string updatedAt?: DateTimeFieldUpdateOperationsInput | Date | string
@@ -19788,6 +19941,7 @@ export namespace Prisma {
status?: $Enums.ExportStatus status?: $Enums.ExportStatus
format: string format: string
scope: string scope: string
progress?: number
filePath?: string | null filePath?: string | null
error?: string | null error?: string | null
expiresAt?: Date | string | null expiresAt?: Date | string | null
@@ -19821,6 +19975,8 @@ export namespace Prisma {
oauthAccessToken?: string | null oauthAccessToken?: string | null
oauthExpiresAt?: Date | string | null oauthExpiresAt?: Date | string | null
providerUserId?: string | null providerUserId?: string | null
oauthLastCheckedAt?: Date | string | null
oauthLastErrorCode?: string | null
appPassword?: string | null appPassword?: string | null
createdAt?: Date | string createdAt?: Date | string
updatedAt?: Date | string updatedAt?: Date | string
@@ -19852,6 +20008,7 @@ export namespace Prisma {
status?: EnumExportStatusFieldUpdateOperationsInput | $Enums.ExportStatus status?: EnumExportStatusFieldUpdateOperationsInput | $Enums.ExportStatus
format?: StringFieldUpdateOperationsInput | string format?: StringFieldUpdateOperationsInput | string
scope?: StringFieldUpdateOperationsInput | string scope?: StringFieldUpdateOperationsInput | string
progress?: IntFieldUpdateOperationsInput | number
filePath?: NullableStringFieldUpdateOperationsInput | string | null filePath?: NullableStringFieldUpdateOperationsInput | string | null
error?: NullableStringFieldUpdateOperationsInput | string | null error?: NullableStringFieldUpdateOperationsInput | string | null
expiresAt?: NullableDateTimeFieldUpdateOperationsInput | Date | string | null expiresAt?: NullableDateTimeFieldUpdateOperationsInput | Date | string | null
@@ -19864,6 +20021,7 @@ export namespace Prisma {
status?: EnumExportStatusFieldUpdateOperationsInput | $Enums.ExportStatus status?: EnumExportStatusFieldUpdateOperationsInput | $Enums.ExportStatus
format?: StringFieldUpdateOperationsInput | string format?: StringFieldUpdateOperationsInput | string
scope?: StringFieldUpdateOperationsInput | string scope?: StringFieldUpdateOperationsInput | string
progress?: IntFieldUpdateOperationsInput | number
filePath?: NullableStringFieldUpdateOperationsInput | string | null filePath?: NullableStringFieldUpdateOperationsInput | string | null
error?: NullableStringFieldUpdateOperationsInput | string | null error?: NullableStringFieldUpdateOperationsInput | string | null
expiresAt?: NullableDateTimeFieldUpdateOperationsInput | Date | string | null expiresAt?: NullableDateTimeFieldUpdateOperationsInput | Date | string | null
@@ -19876,6 +20034,7 @@ export namespace Prisma {
status?: EnumExportStatusFieldUpdateOperationsInput | $Enums.ExportStatus status?: EnumExportStatusFieldUpdateOperationsInput | $Enums.ExportStatus
format?: StringFieldUpdateOperationsInput | string format?: StringFieldUpdateOperationsInput | string
scope?: StringFieldUpdateOperationsInput | string scope?: StringFieldUpdateOperationsInput | string
progress?: IntFieldUpdateOperationsInput | number
filePath?: NullableStringFieldUpdateOperationsInput | string | null filePath?: NullableStringFieldUpdateOperationsInput | string | null
error?: NullableStringFieldUpdateOperationsInput | string | null error?: NullableStringFieldUpdateOperationsInput | string | null
expiresAt?: NullableDateTimeFieldUpdateOperationsInput | Date | string | null expiresAt?: NullableDateTimeFieldUpdateOperationsInput | Date | string | null
@@ -19929,6 +20088,8 @@ export namespace Prisma {
oauthAccessToken?: NullableStringFieldUpdateOperationsInput | string | null oauthAccessToken?: NullableStringFieldUpdateOperationsInput | string | null
oauthExpiresAt?: NullableDateTimeFieldUpdateOperationsInput | Date | string | null oauthExpiresAt?: NullableDateTimeFieldUpdateOperationsInput | Date | string | null
providerUserId?: NullableStringFieldUpdateOperationsInput | string | null providerUserId?: NullableStringFieldUpdateOperationsInput | string | null
oauthLastCheckedAt?: NullableDateTimeFieldUpdateOperationsInput | Date | string | null
oauthLastErrorCode?: NullableStringFieldUpdateOperationsInput | string | null
appPassword?: NullableStringFieldUpdateOperationsInput | string | null appPassword?: NullableStringFieldUpdateOperationsInput | string | null
createdAt?: DateTimeFieldUpdateOperationsInput | Date | string createdAt?: DateTimeFieldUpdateOperationsInput | Date | string
updatedAt?: DateTimeFieldUpdateOperationsInput | Date | string updatedAt?: DateTimeFieldUpdateOperationsInput | Date | string
@@ -19952,6 +20113,8 @@ export namespace Prisma {
oauthAccessToken?: NullableStringFieldUpdateOperationsInput | string | null oauthAccessToken?: NullableStringFieldUpdateOperationsInput | string | null
oauthExpiresAt?: NullableDateTimeFieldUpdateOperationsInput | Date | string | null oauthExpiresAt?: NullableDateTimeFieldUpdateOperationsInput | Date | string | null
providerUserId?: NullableStringFieldUpdateOperationsInput | string | null providerUserId?: NullableStringFieldUpdateOperationsInput | string | null
oauthLastCheckedAt?: NullableDateTimeFieldUpdateOperationsInput | Date | string | null
oauthLastErrorCode?: NullableStringFieldUpdateOperationsInput | string | null
appPassword?: NullableStringFieldUpdateOperationsInput | string | null appPassword?: NullableStringFieldUpdateOperationsInput | string | null
createdAt?: DateTimeFieldUpdateOperationsInput | Date | string createdAt?: DateTimeFieldUpdateOperationsInput | Date | string
updatedAt?: DateTimeFieldUpdateOperationsInput | Date | string updatedAt?: DateTimeFieldUpdateOperationsInput | Date | string
@@ -19975,6 +20138,8 @@ export namespace Prisma {
oauthAccessToken?: NullableStringFieldUpdateOperationsInput | string | null oauthAccessToken?: NullableStringFieldUpdateOperationsInput | string | null
oauthExpiresAt?: NullableDateTimeFieldUpdateOperationsInput | Date | string | null oauthExpiresAt?: NullableDateTimeFieldUpdateOperationsInput | Date | string | null
providerUserId?: NullableStringFieldUpdateOperationsInput | string | null providerUserId?: NullableStringFieldUpdateOperationsInput | string | null
oauthLastCheckedAt?: NullableDateTimeFieldUpdateOperationsInput | Date | string | null
oauthLastErrorCode?: NullableStringFieldUpdateOperationsInput | string | null
appPassword?: NullableStringFieldUpdateOperationsInput | string | null appPassword?: NullableStringFieldUpdateOperationsInput | string | null
createdAt?: DateTimeFieldUpdateOperationsInput | Date | string createdAt?: DateTimeFieldUpdateOperationsInput | Date | string
updatedAt?: DateTimeFieldUpdateOperationsInput | Date | string updatedAt?: DateTimeFieldUpdateOperationsInput | Date | string

File diff suppressed because one or more lines are too long

View File

@@ -1,5 +1,5 @@
{ {
"name": "prisma-client-e73c910a086b88f8fcb9eefa67d9d9c4f84c5390da0c21a4dde9378c21e5e10d", "name": "prisma-client-6c67176f7022092b3b8f46007c6286b76456763ea6fcd4c80a580e5070b636e6",
"main": "index.js", "main": "index.js",
"types": "index.d.ts", "types": "index.d.ts",
"browser": "index-browser.js", "browser": "index-browser.js",

View File

@@ -68,6 +68,7 @@ model ExportJob {
status ExportStatus @default(QUEUED) status ExportStatus @default(QUEUED)
format String format String
scope String scope String
progress Int @default(0)
filePath String? filePath String?
error String? error String?
expiresAt DateTime? expiresAt DateTime?
@@ -93,25 +94,27 @@ model User {
} }
model MailboxAccount { model MailboxAccount {
id String @id @default(cuid()) id String @id @default(cuid())
tenantId String tenantId String
email String email String
provider MailProvider provider MailProvider
isActive Boolean @default(true) isActive Boolean @default(true)
imapHost String imapHost String
imapPort Int imapPort Int
imapTLS Boolean imapTLS Boolean
smtpHost String? smtpHost String?
smtpPort Int? smtpPort Int?
smtpTLS Boolean? smtpTLS Boolean?
oauthToken String? oauthToken String?
oauthRefreshToken String? oauthRefreshToken String?
oauthAccessToken String? oauthAccessToken String?
oauthExpiresAt DateTime? oauthExpiresAt DateTime?
providerUserId String? providerUserId String?
appPassword String? oauthLastCheckedAt DateTime?
createdAt DateTime @default(now()) oauthLastErrorCode String?
updatedAt DateTime @updatedAt appPassword String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
tenant Tenant @relation(fields: [tenantId], references: [id]) tenant Tenant @relation(fields: [tenantId], references: [id])
folders MailboxFolder[] folders MailboxFolder[]

View File

@@ -136,6 +136,7 @@ exports.Prisma.ExportJobScalarFieldEnum = {
status: 'status', status: 'status',
format: 'format', format: 'format',
scope: 'scope', scope: 'scope',
progress: 'progress',
filePath: 'filePath', filePath: 'filePath',
error: 'error', error: 'error',
expiresAt: 'expiresAt', expiresAt: 'expiresAt',
@@ -171,6 +172,8 @@ exports.Prisma.MailboxAccountScalarFieldEnum = {
oauthAccessToken: 'oauthAccessToken', oauthAccessToken: 'oauthAccessToken',
oauthExpiresAt: 'oauthExpiresAt', oauthExpiresAt: 'oauthExpiresAt',
providerUserId: 'providerUserId', providerUserId: 'providerUserId',
oauthLastCheckedAt: 'oauthLastCheckedAt',
oauthLastErrorCode: 'oauthLastErrorCode',
appPassword: 'appPassword', appPassword: 'appPassword',
createdAt: 'createdAt', createdAt: 'createdAt',
updatedAt: 'updatedAt' updatedAt: 'updatedAt'

View File

@@ -0,0 +1,6 @@
-- AlterTable
ALTER TABLE "ExportJob" ADD COLUMN "progress" INTEGER NOT NULL DEFAULT 0;
-- AlterTable
ALTER TABLE "MailboxAccount" ADD COLUMN "oauthLastCheckedAt" TIMESTAMP(3),
ADD COLUMN "oauthLastErrorCode" TEXT;

View File

@@ -68,6 +68,7 @@ model ExportJob {
status ExportStatus @default(QUEUED) status ExportStatus @default(QUEUED)
format String format String
scope String scope String
progress Int @default(0)
filePath String? filePath String?
error String? error String?
expiresAt DateTime? expiresAt DateTime?
@@ -109,6 +110,8 @@ model MailboxAccount {
oauthAccessToken String? oauthAccessToken String?
oauthExpiresAt DateTime? oauthExpiresAt DateTime?
providerUserId String? providerUserId String?
oauthLastCheckedAt DateTime?
oauthLastErrorCode String?
appPassword String? appPassword String?
createdAt DateTime @default(now()) createdAt DateTime @default(now())
updatedAt DateTime @updatedAt updatedAt DateTime @updatedAt

View File

@@ -4,27 +4,34 @@ import { PrismaClient, UserRole } from "@prisma/client";
const prisma = new PrismaClient(); const prisma = new PrismaClient();
const tenantName = process.env.SEED_TENANT ?? "Default Tenant"; const tenantName = process.env.SEED_TENANT ?? "Default Tenant";
const email = process.env.SEED_EMAIL ?? "admin@example.com"; const email = process.env.SEED_ADMIN_EMAIL ?? process.env.SEED_EMAIL ?? "admin@example.com";
const password = process.env.SEED_PASSWORD ?? "change-me-please"; const password = process.env.SEED_ADMIN_PASSWORD ?? process.env.SEED_PASSWORD ?? "change-me-please";
const role = (process.env.SEED_ROLE as UserRole | undefined) ?? "ADMIN"; const role = (process.env.SEED_ROLE as UserRole | undefined) ?? "ADMIN";
const main = async () => { const main = async () => {
const hash = await argon2.hash(password);
const tenantId = process.env.SEED_TENANT_ID ?? "seed-tenant";
const tenant = await prisma.tenant.upsert({
where: { id: tenantId },
update: { name: tenantName },
create: { id: tenantId, name: tenantName }
});
const existing = await prisma.user.findUnique({ where: { email } }); const existing = await prisma.user.findUnique({ where: { email } });
if (existing) { if (existing) {
process.stdout.write("Seed user already exists.\n"); await prisma.user.update({
where: { email },
data: { password: hash, role, tenantId: tenant.id }
});
process.stdout.write("Seed admin updated.\n");
return; return;
} }
const hash = await argon2.hash(password);
const tenant = await prisma.tenant.create({
data: { name: tenantName }
});
await prisma.user.create({ await prisma.user.create({
data: { tenantId: tenant.id, email, password: hash, role } data: { tenantId: tenant.id, email, password: hash, role }
}); });
process.stdout.write("Seeded tenant and user.\n"); process.stdout.write("Seeded admin user.\n");
}; };
main() main()

View File

@@ -8,7 +8,7 @@ import { pipeline } from "node:stream/promises";
const EXPORT_DIR = process.env.EXPORT_DIR ?? "/tmp/mailcleaner-exports"; const EXPORT_DIR = process.env.EXPORT_DIR ?? "/tmp/mailcleaner-exports";
const EXPORT_TTL_HOURS = Number(process.env.EXPORT_TTL_HOURS ?? 24); const EXPORT_TTL_HOURS = Number(process.env.EXPORT_TTL_HOURS ?? 24);
const buildExport = async (jobId: string) => { export const buildExport = async (jobId: string) => {
const job = await prisma.exportJob.findUnique({ where: { id: jobId }, include: { tenant: true } }); const job = await prisma.exportJob.findUnique({ where: { id: jobId }, include: { tenant: true } });
if (!job) throw new Error("Export job not found"); if (!job) throw new Error("Export job not found");
@@ -23,6 +23,8 @@ const buildExport = async (jobId: string) => {
}); });
if (!tenant) throw new Error("Tenant not found"); if (!tenant) throw new Error("Tenant not found");
await prisma.exportJob.update({ where: { id: job.id }, data: { progress: 20 } });
const scope = job.scope; const scope = job.scope;
const payload: Record<string, unknown> = {}; const payload: Record<string, unknown> = {};
if (scope === "all" || scope === "users") payload.users = tenant.users; if (scope === "all" || scope === "users") payload.users = tenant.users;
@@ -31,6 +33,8 @@ const buildExport = async (jobId: string) => {
if (scope === "all" || scope === "rules") payload.rules = tenant.rules; if (scope === "all" || scope === "rules") payload.rules = tenant.rules;
if (scope === "all") payload.tenant = { id: tenant.id, name: tenant.name }; if (scope === "all") payload.tenant = { id: tenant.id, name: tenant.name };
await prisma.exportJob.update({ where: { id: job.id }, data: { progress: 50 } });
await mkdir(EXPORT_DIR, { recursive: true }); await mkdir(EXPORT_DIR, { recursive: true });
const fileName = `${job.id}.${job.format === "zip" ? "zip" : "json"}`; const fileName = `${job.id}.${job.format === "zip" ? "zip" : "json"}`;
const filePath = `${EXPORT_DIR}/${fileName}`; const filePath = `${EXPORT_DIR}/${fileName}`;
@@ -47,32 +51,21 @@ const buildExport = async (jobId: string) => {
await writeFile(filePath, JSON.stringify(payload, null, 2)); await writeFile(filePath, JSON.stringify(payload, null, 2));
} }
await prisma.exportJob.update({ where: { id: job.id }, data: { progress: 90 } });
await prisma.exportJob.update({ await prisma.exportJob.update({
where: { id: job.id }, where: { id: job.id },
data: { status: "DONE", filePath, expiresAt: new Date(Date.now() + EXPORT_TTL_HOURS * 3600 * 1000) } data: { status: "DONE", filePath, progress: 100, expiresAt: new Date(Date.now() + EXPORT_TTL_HOURS * 3600 * 1000) }
}); });
}; };
export const processExportQueue = async () => { export const runExportJob = async (jobId: string) => {
while (true) { await prisma.exportJob.update({ where: { id: jobId }, data: { status: "RUNNING", progress: 5 } });
const job = await prisma.exportJob.findFirst({ await buildExport(jobId);
where: { status: "QUEUED" }, };
orderBy: { createdAt: "asc" }
}); export const startExportCleanupLoop = () => {
if (!job) { setInterval(() => {
await cleanupExpiredExports(); cleanupExpiredExports().catch(() => undefined);
await new Promise((resolve) => setTimeout(resolve, 2000)); }, 60000);
continue;
}
await prisma.exportJob.update({ where: { id: job.id }, data: { status: "RUNNING" } });
try {
await buildExport(job.id);
} catch (err) {
await prisma.exportJob.update({
where: { id: job.id },
data: { status: "FAILED", error: err instanceof Error ? err.message : String(err) }
});
}
}
}; };

View File

@@ -2,9 +2,10 @@ import { FastifyInstance } from "fastify";
import { z } from "zod"; import { z } from "zod";
import { prisma } from "../db.js"; import { prisma } from "../db.js";
import { logJobEvent } from "../queue/jobEvents.js"; import { logJobEvent } from "../queue/jobEvents.js";
import { queueCleanupJob, removeQueueJob } from "../queue/queue.js"; import { queueCleanupJob, removeQueueJob, queueExportJob } from "../queue/queue.js";
import { createReadStream } from "node:fs"; import { createReadStream } from "node:fs";
import { access } from "node:fs/promises"; import { access, unlink } from "node:fs/promises";
import { cleanupExpiredExports } from "./exportCleanup.js";
const roleSchema = z.object({ const roleSchema = z.object({
role: z.enum(["USER", "ADMIN"]) role: z.enum(["USER", "ADMIN"])
@@ -74,6 +75,7 @@ export async function adminRoutes(app: FastifyInstance) {
scope: scope scope: scope
} }
}); });
await queueExportJob(exportJob.id);
return { jobId: exportJob.id }; return { jobId: exportJob.id };
} }
@@ -111,8 +113,10 @@ export async function adminRoutes(app: FastifyInstance) {
return reply.send(createReadStream(job.filePath)); return reply.send(createReadStream(job.filePath));
}); });
app.get("/exports", async () => { app.get("/exports", async (request) => {
const query = request.query as { status?: string };
const exports = await prisma.exportJob.findMany({ const exports = await prisma.exportJob.findMany({
where: query.status ? { status: query.status as "QUEUED" | "RUNNING" | "DONE" | "FAILED" } : undefined,
orderBy: { createdAt: "desc" } orderBy: { createdAt: "desc" }
}); });
const sanitized = exports.map((job) => ({ const sanitized = exports.map((job) => ({
@@ -120,12 +124,33 @@ export async function adminRoutes(app: FastifyInstance) {
status: job.status, status: job.status,
format: job.format, format: job.format,
scope: job.scope, scope: job.scope,
progress: job.progress,
expiresAt: job.expiresAt, expiresAt: job.expiresAt,
createdAt: job.createdAt createdAt: job.createdAt
})); }));
return { exports: sanitized }; return { exports: sanitized };
}); });
app.post("/exports/purge", async () => {
await cleanupExpiredExports();
return { success: true };
});
app.delete("/exports/:id", async (request, reply) => {
const params = request.params as { id: string };
const job = await prisma.exportJob.findUnique({ where: { id: params.id } });
if (!job) return reply.code(404).send({ message: "Export job not found" });
if (job.filePath) {
try {
await unlink(job.filePath);
} catch {
// ignore
}
}
await prisma.exportJob.delete({ where: { id: job.id } });
return { success: true };
});
app.delete("/tenants/:id", async (request, reply) => { app.delete("/tenants/:id", async (request, reply) => {
const params = request.params as { id: string }; const params = request.params as { id: string };
const tenant = await prisma.tenant.findUnique({ where: { id: params.id } }); const tenant = await prisma.tenant.findUnique({ where: { id: params.id } });
@@ -241,7 +266,11 @@ export async function adminRoutes(app: FastifyInstance) {
tenant: account.tenant ? { id: account.tenant.id, name: account.tenant.name } : null, tenant: account.tenant ? { id: account.tenant.id, name: account.tenant.name } : null,
imapHost: account.imapHost, imapHost: account.imapHost,
imapPort: account.imapPort, imapPort: account.imapPort,
imapTLS: account.imapTLS imapTLS: account.imapTLS,
oauthExpiresAt: account.oauthExpiresAt,
oauthLastCheckedAt: account.oauthLastCheckedAt,
oauthLastErrorCode: account.oauthLastErrorCode,
hasOauth: Boolean(account.oauthRefreshToken || account.oauthAccessToken)
})); }));
return { accounts: sanitized }; return { accounts: sanitized };

View File

@@ -8,7 +8,8 @@ const envSchema = z.object({
JWT_SECRET: z.string().min(12), JWT_SECRET: z.string().min(12),
GOOGLE_CLIENT_ID: z.string().optional(), GOOGLE_CLIENT_ID: z.string().optional(),
GOOGLE_CLIENT_SECRET: z.string().optional(), GOOGLE_CLIENT_SECRET: z.string().optional(),
GOOGLE_REDIRECT_URI: z.string().optional() GOOGLE_REDIRECT_URI: z.string().optional(),
TRUST_PROXY: z.coerce.boolean().default(false)
}); });
export type AppConfig = z.infer<typeof envSchema>; export type AppConfig = z.infer<typeof envSchema>;
@@ -21,5 +22,6 @@ export const config = envSchema.parse({
JWT_SECRET: process.env.JWT_SECRET, JWT_SECRET: process.env.JWT_SECRET,
GOOGLE_CLIENT_ID: process.env.GOOGLE_CLIENT_ID, GOOGLE_CLIENT_ID: process.env.GOOGLE_CLIENT_ID,
GOOGLE_CLIENT_SECRET: process.env.GOOGLE_CLIENT_SECRET, GOOGLE_CLIENT_SECRET: process.env.GOOGLE_CLIENT_SECRET,
GOOGLE_REDIRECT_URI: process.env.GOOGLE_REDIRECT_URI GOOGLE_REDIRECT_URI: process.env.GOOGLE_REDIRECT_URI,
TRUST_PROXY: process.env.TRUST_PROXY
}); });

View File

@@ -77,9 +77,17 @@ export async function oauthRoutes(app: FastifyInstance) {
try { try {
const refreshed = await refreshGmailTokens(account); const refreshed = await refreshGmailTokens(account);
await pingGmail(account); await pingGmail(account);
await prisma.mailboxAccount.update({
where: { id: account.id },
data: { oauthLastCheckedAt: new Date(), oauthLastErrorCode: null }
});
return { connected: true, healthy: true, expiresAt: refreshed.expiresAt ?? account.oauthExpiresAt }; return { connected: true, healthy: true, expiresAt: refreshed.expiresAt ?? account.oauthExpiresAt };
} catch (err) { } catch (err) {
const error = mapGmailError(err); const error = mapGmailError(err);
await prisma.mailboxAccount.update({
where: { id: account.id },
data: { oauthLastCheckedAt: new Date(), oauthLastErrorCode: error.code }
});
return { connected: true, healthy: false, expiresAt: account.oauthExpiresAt, error }; return { connected: true, healthy: false, expiresAt: account.oauthExpiresAt, error };
} }
}); });

View File

@@ -22,7 +22,8 @@ const app = Fastify({
target: "pino-pretty", target: "pino-pretty",
options: { colorize: true } options: { colorize: true }
} }
} },
trustProxy: config.TRUST_PROXY
}); });
await app.register(cors, { origin: true }); await app.register(cors, { origin: true });

View File

@@ -3,6 +3,7 @@ import IORedis from "ioredis";
import { config } from "../config.js"; import { config } from "../config.js";
let cleanupQueue: Queue | null = null; let cleanupQueue: Queue | null = null;
let exportQueue: Queue | null = null;
const getConnection = () => new IORedis(config.REDIS_URL, { maxRetriesPerRequest: null }); const getConnection = () => new IORedis(config.REDIS_URL, { maxRetriesPerRequest: null });
@@ -15,6 +16,15 @@ export const getCleanupQueue = () => {
return cleanupQueue; return cleanupQueue;
}; };
export const getExportQueue = () => {
if (!exportQueue) {
exportQueue = new Queue("export", {
connection: getConnection()
});
}
return exportQueue;
};
export const queueCleanupJob = async (cleanupJobId: string, mailboxAccountId: string) => { export const queueCleanupJob = async (cleanupJobId: string, mailboxAccountId: string) => {
const queue = getCleanupQueue(); const queue = getCleanupQueue();
await queue.add( await queue.add(
@@ -24,6 +34,15 @@ export const queueCleanupJob = async (cleanupJobId: string, mailboxAccountId: st
); );
}; };
export const queueExportJob = async (exportJobId: string) => {
const queue = getExportQueue();
await queue.add(
"export",
{ exportJobId },
{ jobId: exportJobId }
);
};
export const getQueueJob = async (jobId: string) => { export const getQueueJob = async (jobId: string) => {
const queue = getCleanupQueue(); const queue = getCleanupQueue();
return queue.getJob(jobId); return queue.getJob(jobId);

View File

@@ -100,12 +100,14 @@ export async function queueRoutes(app: FastifyInstance) {
}); });
let lastStatus = ""; let lastStatus = "";
let lastProgress = -1;
const timer = setInterval(async () => { const timer = setInterval(async () => {
const latest = await prisma.exportJob.findUnique({ where: { id: params.id } }); const latest = await prisma.exportJob.findUnique({ where: { id: params.id } });
if (!latest) return; if (!latest) return;
if (latest.status !== lastStatus) { if (latest.status !== lastStatus || latest.progress !== lastProgress) {
reply.raw.write(`data: ${JSON.stringify(latest)}\n\n`); reply.raw.write(`data: ${JSON.stringify(latest)}\n\n`);
lastStatus = latest.status; lastStatus = latest.status;
lastProgress = latest.progress ?? 0;
} }
if (latest.status === "DONE" || latest.status === "FAILED") { if (latest.status === "DONE" || latest.status === "FAILED") {
clearInterval(timer); clearInterval(timer);

View File

@@ -4,7 +4,7 @@ import { prisma } from "./db.js";
import { config } from "./config.js"; import { config } from "./config.js";
import { runCleanup } from "./mail/cleanup.js"; import { runCleanup } from "./mail/cleanup.js";
import { logJobEvent } from "./queue/jobEvents.js"; import { logJobEvent } from "./queue/jobEvents.js";
import { processExportQueue } from "./admin/exportWorker.js"; import { runExportJob, startExportCleanupLoop } from "./admin/exportWorker.js";
const connection = new IORedis(config.REDIS_URL, { maxRetriesPerRequest: null }); const connection = new IORedis(config.REDIS_URL, { maxRetriesPerRequest: null });
@@ -32,6 +32,26 @@ const worker = new Worker(
{ connection } { connection }
); );
const exportWorker = new Worker(
"export",
async (job) => {
const { exportJobId } = job.data as { exportJobId: string };
await runExportJob(exportJobId);
return { ok: true };
},
{ connection }
);
exportWorker.on("failed", async (job, err) => {
if (!job) return;
const exportJobId = job.data?.exportJobId as string | undefined;
if (!exportJobId) return;
await prisma.exportJob.update({
where: { id: exportJobId },
data: { status: "FAILED", error: err.message }
});
});
worker.on("failed", async (job, err) => { worker.on("failed", async (job, err) => {
if (!job) return; if (!job) return;
const cleanupJobId = job.data?.cleanupJobId as string | undefined; const cleanupJobId = job.data?.cleanupJobId as string | undefined;
@@ -47,6 +67,4 @@ worker.on("failed", async (job, err) => {
}); });
process.stdout.write("[worker] cleanup worker ready\n"); process.stdout.write("[worker] cleanup worker ready\n");
processExportQueue().catch((err) => { startExportCleanupLoop();
process.stderr.write(`[worker] export worker failed: ${err instanceof Error ? err.message : String(err)}\n`);
});

View File

@@ -32,6 +32,11 @@ services:
GOOGLE_REDIRECT_URI: ${GOOGLE_REDIRECT_URI:-http://localhost:8000/oauth/gmail/callback} GOOGLE_REDIRECT_URI: ${GOOGLE_REDIRECT_URI:-http://localhost:8000/oauth/gmail/callback}
EXPORT_DIR: /tmp/mailcleaner-exports EXPORT_DIR: /tmp/mailcleaner-exports
EXPORT_TTL_HOURS: 24 EXPORT_TTL_HOURS: 24
TRUST_PROXY: ${TRUST_PROXY:-false}
SEED_ADMIN_EMAIL: ${SEED_ADMIN_EMAIL:-admin@simplemailcleaner.local}
SEED_ADMIN_PASSWORD: ${SEED_ADMIN_PASSWORD:-change-me-now}
SEED_TENANT: ${SEED_TENANT:-Default Tenant}
SEED_TENANT_ID: ${SEED_TENANT_ID:-seed-tenant}
depends_on: depends_on:
- postgres - postgres
- redis - redis
@@ -54,6 +59,11 @@ services:
GOOGLE_REDIRECT_URI: ${GOOGLE_REDIRECT_URI:-http://localhost:8000/oauth/gmail/callback} GOOGLE_REDIRECT_URI: ${GOOGLE_REDIRECT_URI:-http://localhost:8000/oauth/gmail/callback}
EXPORT_DIR: /tmp/mailcleaner-exports EXPORT_DIR: /tmp/mailcleaner-exports
EXPORT_TTL_HOURS: 24 EXPORT_TTL_HOURS: 24
TRUST_PROXY: ${TRUST_PROXY:-false}
SEED_ADMIN_EMAIL: ${SEED_ADMIN_EMAIL:-admin@simplemailcleaner.local}
SEED_ADMIN_PASSWORD: ${SEED_ADMIN_PASSWORD:-change-me-now}
SEED_TENANT: ${SEED_TENANT:-Default Tenant}
SEED_TENANT_ID: ${SEED_TENANT_ID:-seed-tenant}
depends_on: depends_on:
- postgres - postgres
- redis - redis
@@ -67,7 +77,7 @@ services:
dockerfile: Dockerfile dockerfile: Dockerfile
environment: environment:
PORT: 3000 PORT: 3000
VITE_API_URL: http://localhost:8000 VITE_API_URL: ${VITE_API_URL:-http://localhost:8000}
depends_on: depends_on:
- api - api
ports: ports:

View File

@@ -24,6 +24,10 @@ type Account = {
email: string; email: string;
provider: string; provider: string;
isActive: boolean; isActive: boolean;
hasOauth?: boolean;
oauthExpiresAt?: string | null;
oauthLastCheckedAt?: string | null;
oauthLastErrorCode?: string | null;
tenant?: { id: string; name: string } | null; tenant?: { id: string; name: string } | null;
}; };
@@ -52,7 +56,8 @@ export default function AdminPanel({ token, onImpersonate }: Props) {
const [exportTenantId, setExportTenantId] = useState<string | null>(null); const [exportTenantId, setExportTenantId] = useState<string | null>(null);
const [exportStatus, setExportStatus] = useState<"idle" | "loading" | "done" | "failed">("idle"); const [exportStatus, setExportStatus] = useState<"idle" | "loading" | "done" | "failed">("idle");
const [exportJobId, setExportJobId] = useState<string | null>(null); const [exportJobId, setExportJobId] = useState<string | null>(null);
const [exportHistory, setExportHistory] = useState<{ id: string; status: string; expiresAt?: string | null; createdAt?: string }[]>([]); const [exportHistory, setExportHistory] = useState<{ id: string; status: string; expiresAt?: string | null; createdAt?: string; progress?: number }[]>([]);
const [exportFilter, setExportFilter] = useState<"all" | "active" | "done" | "failed" | "expired">("all");
const [exportScope, setExportScope] = useState<"all" | "users" | "accounts" | "jobs" | "rules">("all"); const [exportScope, setExportScope] = useState<"all" | "users" | "accounts" | "jobs" | "rules">("all");
const [exportFormat, setExportFormat] = useState<"json" | "csv" | "zip">("json"); const [exportFormat, setExportFormat] = useState<"json" | "csv" | "zip">("json");
const [tenantSort, setTenantSort] = useState<"recent" | "oldest" | "name">("recent"); const [tenantSort, setTenantSort] = useState<"recent" | "oldest" | "name">("recent");
@@ -72,8 +77,8 @@ export default function AdminPanel({ token, onImpersonate }: Props) {
const jobsData = await apiFetch("/admin/jobs", {}, token); const jobsData = await apiFetch("/admin/jobs", {}, token);
setJobs(jobsData.jobs ?? []); setJobs(jobsData.jobs ?? []);
const exportsData = await apiFetch("/admin/exports", {}, token); const exportsData = await apiFetch("/admin/exports", {}, token);
setExportHistory(exportsData.exports ?? []); setExportHistory(exportsData.exports ?? []);
}; };
useEffect(() => { useEffect(() => {
@@ -107,7 +112,7 @@ export default function AdminPanel({ token, onImpersonate }: Props) {
source.onmessage = async (event) => { source.onmessage = async (event) => {
const data = JSON.parse(event.data); const data = JSON.parse(event.data);
setExportHistory((prev) => setExportHistory((prev) =>
prev.map((item) => (item.id === data.id ? { ...item, status: data.status, expiresAt: data.expiresAt } : item)) prev.map((item) => (item.id === data.id ? { ...item, status: data.status, expiresAt: data.expiresAt, progress: data.progress } : item))
); );
if (data.status === "DONE") { if (data.status === "DONE") {
const response = await downloadExport(token, result.jobId); const response = await downloadExport(token, result.jobId);
@@ -213,6 +218,14 @@ export default function AdminPanel({ token, onImpersonate }: Props) {
const usersSorted = sortBy(users, userSort, (u) => u.email); const usersSorted = sortBy(users, userSort, (u) => u.email);
const accountsSorted = sortBy(accounts, accountSort, (a) => a.email); const accountsSorted = sortBy(accounts, accountSort, (a) => a.email);
const jobsSorted = sortBy(jobs, jobSort, (j) => j.status); const jobsSorted = sortBy(jobs, jobSort, (j) => j.status);
const exportsFiltered = exportHistory.filter((item) => {
const expired = item.expiresAt ? new Date(item.expiresAt) < new Date() : false;
if (exportFilter === "expired") return expired;
if (exportFilter === "active") return item.status === "QUEUED" || item.status === "RUNNING";
if (exportFilter === "done") return item.status === "DONE";
if (exportFilter === "failed") return item.status === "FAILED";
return true;
});
const mapJobStatus = (status: string) => { const mapJobStatus = (status: string) => {
switch (status) { switch (status) {
case "RUNNING": case "RUNNING":
@@ -317,7 +330,28 @@ export default function AdminPanel({ token, onImpersonate }: Props) {
{exportHistory.length > 0 && ( {exportHistory.length > 0 && (
<div className="export-history"> <div className="export-history">
<h4>{t("exportHistory")}</h4> <h4>{t("exportHistory")}</h4>
{exportHistory.map((item) => ( <div className="filter-row">
<label>
{t("adminSortLabel")}
<select value={exportFilter} onChange={(event) => setExportFilter(event.target.value as typeof exportFilter)}>
<option value="all">{t("adminExportAll")}</option>
<option value="active">{t("exportStatusRunning")}</option>
<option value="done">{t("exportStatusDone")}</option>
<option value="failed">{t("exportStatusFailed")}</option>
<option value="expired">{t("exportStatusExpired")}</option>
</select>
</label>
<button
className="ghost"
onClick={async () => {
await apiFetch("/admin/exports/purge", { method: "POST" }, token);
loadAll().catch(() => undefined);
}}
>
{t("adminExportPurge")}
</button>
</div>
{exportsFiltered.map((item) => (
<div key={item.id} className="list-item"> <div key={item.id} className="list-item">
<div> <div>
<strong>{item.id.slice(0, 6)}</strong> <strong>{item.id.slice(0, 6)}</strong>
@@ -332,6 +366,9 @@ export default function AdminPanel({ token, onImpersonate }: Props) {
? t("exportStatusDone") ? t("exportStatusDone")
: t("exportStatusFailed")} : t("exportStatusFailed")}
</p> </p>
{item.progress !== undefined && (
<p>{t("exportProgress", { progress: item.progress })}</p>
)}
</div> </div>
<div className="inline-actions"> <div className="inline-actions">
<span> <span>
@@ -351,6 +388,15 @@ export default function AdminPanel({ token, onImpersonate }: Props) {
> >
{t("exportDownload")} {t("exportDownload")}
</button> </button>
<button
className="ghost"
onClick={async () => {
await apiFetch(`/admin/exports/${item.id}`, { method: "DELETE" }, token);
loadAll().catch(() => undefined);
}}
>
{t("delete")}
</button>
</div> </div>
</div> </div>
))} ))}
@@ -442,6 +488,22 @@ export default function AdminPanel({ token, onImpersonate }: Props) {
</span> </span>
</div> </div>
<p>{account.provider} · {account.tenant?.name ?? "-"}</p> <p>{account.provider} · {account.tenant?.name ?? "-"}</p>
{account.hasOauth && (
<p className="status-note">
{t("statusLabel")}: {account.oauthLastErrorCode ? t("badgeUnhealthy") : t("badgeHealthy")} ·{" "}
{t("adminExpiresAt")}: {account.oauthExpiresAt ? new Date(account.oauthExpiresAt).toLocaleString() : t("oauthStatusUnknown")} ·{" "}
{t("adminLastConnected")}: {account.oauthLastCheckedAt ? new Date(account.oauthLastCheckedAt).toLocaleString() : t("oauthStatusUnknown")}
</p>
)}
{account.oauthLastErrorCode && (
<p className="status-note">
{account.oauthLastErrorCode === "invalid_grant"
? t("oauthErrorInvalidGrant")
: account.oauthLastErrorCode === "token_expired"
? t("oauthErrorExpired")
: t("oauthErrorUnknown")}
</p>
)}
</div> </div>
<button className="ghost" onClick={() => toggleAccount(account)}> <button className="ghost" onClick={() => toggleAccount(account)}>
{account.isActive ? t("adminDisable") : t("adminEnable")} {account.isActive ? t("adminDisable") : t("adminEnable")}

View File

@@ -151,5 +151,7 @@
"exportStatusRunning": "Läuft", "exportStatusRunning": "Läuft",
"exportStatusDone": "Fertig", "exportStatusDone": "Fertig",
"exportStatusFailed": "Fehlgeschlagen", "exportStatusFailed": "Fehlgeschlagen",
"exportStatusExpired": "Abgelaufen" "exportStatusExpired": "Abgelaufen",
"adminExportPurge": "Abgelaufene löschen",
"exportProgress": "Fortschritt {{progress}}%"
} }

View File

@@ -151,5 +151,7 @@
"exportStatusRunning": "Running", "exportStatusRunning": "Running",
"exportStatusDone": "Done", "exportStatusDone": "Done",
"exportStatusFailed": "Failed", "exportStatusFailed": "Failed",
"exportStatusExpired": "Expired" "exportStatusExpired": "Expired",
"adminExportPurge": "Purge expired",
"exportProgress": "Progress {{progress}}%"
} }