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?
@@ -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

@@ -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");
@@ -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}}%"
} }