42daa87e5e
- Prisma: domain String? added to CalendarSource model (db push applied) - DTOs: domain in CreateCalendarSourceDto, UpdateCalendarSourceDto, new TestCalendarSourceConfigDto - Service: domain in SOURCE_SAFE_SELECT, addSource, updateSource; new testConnectionFromConfig method - Controller: POST /calendar/sources/test-config (before :id routes to avoid collision) - ExchangeProvider: domain in all source interfaces; passed as 3rd arg to EWS WebCredentials - Frontend: domain in CalendarSource/CreateSourcePayload/UpdateSourcePayload; testSourceConfig API fn - Form: domain field (Exchange-only), "Test connection" button with idle/loading/success/error states - i18n: de+en keys for formFieldDomain, formFieldDomainHint, formTestConnection, formTesting, formTestSuccess, formTestFailed Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
257 lines
7.5 KiB
Plaintext
257 lines
7.5 KiB
Plaintext
datasource db {
|
|
provider = "postgresql"
|
|
url = env("DATABASE_URL")
|
|
}
|
|
|
|
generator client {
|
|
provider = "prisma-client-js"
|
|
}
|
|
|
|
model Tenant {
|
|
id String @id @default(uuid())
|
|
name String
|
|
slug String @unique
|
|
isActive Boolean @default(true)
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
users User[]
|
|
ldapConfig LdapConfig?
|
|
}
|
|
|
|
enum Role {
|
|
SUPER_ADMIN
|
|
ADMIN
|
|
USER
|
|
}
|
|
|
|
model User {
|
|
id String @id @default(uuid())
|
|
username String @unique
|
|
email String @unique
|
|
passwordHash String?
|
|
displayName String?
|
|
role Role @default(USER)
|
|
isActive Boolean @default(true)
|
|
mustChangePassword Boolean @default(false)
|
|
ldapDn String?
|
|
tenantId String
|
|
tenant Tenant @relation(fields: [tenantId], references: [id])
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
lastLoginAt DateTime?
|
|
avatarPath String?
|
|
accentColor String?
|
|
passwordResetTokens PasswordResetToken[]
|
|
|
|
@@index([tenantId])
|
|
@@index([username])
|
|
@@index([email])
|
|
}
|
|
|
|
model PasswordResetToken {
|
|
id String @id @default(uuid())
|
|
token String @unique
|
|
userId String
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
expiresAt DateTime
|
|
usedAt DateTime?
|
|
createdAt DateTime @default(now())
|
|
}
|
|
|
|
model LdapConfig {
|
|
id String @id @default(uuid())
|
|
tenantId String @unique
|
|
tenant Tenant @relation(fields: [tenantId], references: [id])
|
|
serverUrl String
|
|
baseDn String
|
|
bindDn String
|
|
bindPassword String
|
|
searchFilter String @default("(objectClass=person)")
|
|
syncIntervalMin Int @default(60)
|
|
isActive Boolean @default(true)
|
|
lastSyncAt DateTime?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
fieldMappings LdapFieldMapping[]
|
|
}
|
|
|
|
model LdapFieldMapping {
|
|
id String @id @default(uuid())
|
|
ldapConfigId String
|
|
ldapConfig LdapConfig @relation(fields: [ldapConfigId], references: [id], onDelete: Cascade)
|
|
ldapField String
|
|
tesseraField String
|
|
isDefault Boolean @default(false)
|
|
createdAt DateTime @default(now())
|
|
|
|
@@unique([ldapConfigId, ldapField])
|
|
}
|
|
|
|
model Module {
|
|
id String @id @default(uuid())
|
|
slug String @unique
|
|
name String
|
|
version String
|
|
category String
|
|
description Json
|
|
icon String?
|
|
isSystem Boolean @default(false)
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
activations TenantModuleActivation[]
|
|
}
|
|
|
|
model TenantModuleActivation {
|
|
id String @id @default(uuid())
|
|
tenantId String
|
|
moduleId String
|
|
isActive Boolean @default(true)
|
|
activatedAt DateTime @default(now())
|
|
module Module @relation(fields: [moduleId], references: [id], onDelete: Cascade)
|
|
|
|
@@unique([tenantId, moduleId])
|
|
@@index([tenantId])
|
|
}
|
|
|
|
model DashboardLayout {
|
|
id String @id @default(uuid())
|
|
userId String @unique
|
|
tenantId String
|
|
layouts Json @default("{}")
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@index([tenantId])
|
|
}
|
|
|
|
model WidgetInstance {
|
|
id String @id @default(uuid())
|
|
userId String
|
|
tenantId String
|
|
widgetType String
|
|
config Json @default("{}")
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
favoriteLinks FavoriteLink[]
|
|
|
|
@@index([userId])
|
|
@@index([tenantId])
|
|
}
|
|
|
|
model SearchProvider {
|
|
id String @id @default(uuid())
|
|
userId String?
|
|
tenantId String?
|
|
name String
|
|
urlTemplate String
|
|
isDefault Boolean @default(false)
|
|
createdAt DateTime @default(now())
|
|
|
|
@@index([userId])
|
|
}
|
|
|
|
model CalendarSource {
|
|
id String @id @default(uuid())
|
|
userId String
|
|
tenantId String
|
|
name String
|
|
type String // 'caldav' | 'ics' | 'exchange'
|
|
exchangeMode String? // 'ews' | 'graph' — only for exchange type
|
|
domain String? // Exchange EWS only: Windows domain (e.g. COMPANY)
|
|
url String
|
|
username String?
|
|
encryptedPassword String? // AES-256-GCM ciphertext (iv:authTag:ciphertext hex)
|
|
color String? @default("#3B82F6")
|
|
isVisible Boolean @default(true)
|
|
syncIntervalMin Int @default(15)
|
|
lastSyncAt DateTime?
|
|
lastSyncError String?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@index([userId])
|
|
@@index([tenantId])
|
|
}
|
|
|
|
model DkvModuleConfig {
|
|
id String @id @default(uuid())
|
|
tenantId String @unique
|
|
protocol String @default("imap") // 'imap' | 'exchange'
|
|
host String?
|
|
port Int?
|
|
encryption String @default("ssl-tls") // 'none' | 'starttls' | 'ssl-tls'
|
|
folder String @default("INBOX")
|
|
senderFilter String?
|
|
pollIntervalMin Int @default(60)
|
|
isActive Boolean @default(false)
|
|
exportRecipient String?
|
|
vehicleFormatString String @default("{Marke}/{Modell}/{Kennzeichen}")
|
|
domain String? // Exchange only: Windows domain (optional)
|
|
encryptedInboxCreds String? // AES-256-GCM: JSON { username, password } encrypted
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@index([tenantId])
|
|
}
|
|
|
|
model DkvVehicleMaster {
|
|
id String @id @default(uuid())
|
|
tenantId String
|
|
kennzeichen String
|
|
marke String
|
|
modell String
|
|
fahrer String // "Vorname Nachname"
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@unique([tenantId, kennzeichen])
|
|
@@index([tenantId])
|
|
}
|
|
|
|
model DkvInvoiceHistory {
|
|
id String @id @default(uuid())
|
|
tenantId String
|
|
datumZeit DateTime @default(now())
|
|
rechnungsnummer String
|
|
anzahlFahrzeuge Int @default(0)
|
|
anzahlTransaktionen Int @default(0)
|
|
status String // 'Verarbeitet' | 'Fehler' | 'Versand fehlgeschlagen'
|
|
errorMessage String?
|
|
exportFilename String?
|
|
createdAt DateTime @default(now())
|
|
|
|
@@index([tenantId])
|
|
@@index([datumZeit])
|
|
}
|
|
|
|
model SmtpConfig {
|
|
id String @id @default(uuid())
|
|
tenantId String @unique
|
|
host String
|
|
port Int @default(587)
|
|
encryption String @default("starttls") // 'none' | 'starttls' | 'ssl-tls'
|
|
username String?
|
|
encryptedPassword String? // AES-256-GCM via CalendarCryptoService
|
|
fromAddress String
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
}
|
|
|
|
model FavoriteLink {
|
|
id String @id @default(uuid())
|
|
userId String
|
|
tenantId String
|
|
widgetId String
|
|
title String
|
|
url String
|
|
iconUrl String?
|
|
position Int @default(0)
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
widgetInstance WidgetInstance @relation(fields: [widgetId], references: [id], onDelete: Cascade)
|
|
|
|
@@index([userId])
|
|
@@index([tenantId])
|
|
@@index([widgetId])
|
|
}
|