model AppConfiguration { id String @id @default(cuid()) updatedAt DateTime @updatedAt name String url String theme String? authRequireEmailVerification Boolean @default(false) authRequireOrganization Boolean @default(true) authRequireName Boolean @default(true) analyticsEnabled Boolean @default(true) analyticsSimpleAnalytics Boolean @default(false) analyticsPlausibleAnalytics Boolean @default(false) subscriptionRequired Boolean @default(true) subscriptionAllowSubscribeBeforeSignUp Boolean @default(true) subscriptionAllowSignUpBeforeSubscribe Boolean @default(true) cookiesEnabled Boolean @default(false) metricsEnabled Boolean @default(false) metricsLogToConsole Boolean @default(false) metricsSaveToDatabase Boolean @default(false) metricsIgnoreUrls String? brandingLogo String? brandingLogoDarkMode String? brandingIcon String? brandingIconDarkMode String? brandingFavicon String? headScripts String? bodyScripts String? emailProvider String? emailFromEmail String? emailFromName String? emailSupportEmail String? } model Log { id String @id @default(cuid()) createdAt DateTime @default(now()) tenantId String? userId String? apiKeyId String? rowId String? url String action String details String? commentId String? apiKey ApiKey? @relation(fields: [apiKeyId], references: [id], onDelete: Cascade) comment RowComment? @relation(fields: [commentId], references: [id]) row Row? @relation(fields: [rowId], references: [id]) tenant Tenant? @relation(fields: [tenantId], references: [id], onDelete: Cascade) user User? @relation(fields: [userId], references: [id], onDelete: Cascade) webhookLogs EntityWebhookLog[] } model AuditEvent { id String @id @default(cuid()) createdAt DateTime @default(now()) tenantId String? actorId String? actorType String actorEmail String? actorIp String? actorUserAgent String? action String targetType String? targetId String? requestId String? sourceModule String? outcome String @default("success") dataSensitivity String @default("internal") regulatoryScope String[] @default([]) metadata Json? previousHash String? eventHash String? @@index([tenantId, createdAt], name: "audit_event_tenant_created_at_idx") @@index([actorId, createdAt], name: "audit_event_actor_created_at_idx") @@index([action, createdAt], name: "audit_event_action_created_at_idx") @@index([requestId], name: "audit_event_request_id_idx") @@index([tenantId, action, createdAt], name: "audit_event_tenant_action_created_at_idx") @@index([tenantId, outcome, createdAt], name: "audit_event_tenant_outcome_created_at_idx") } model IdempotencyKey { id String @id @default(cuid()) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt expiresAt DateTime? scope String key String requestHash String responseStatus Int responseBody Json? responseHeaders Json? @@unique([scope, key], name: "idempotency_key_scope_key") @@index([expiresAt], name: "idempotency_key_expires_at_idx") } model ApiKey { id String @id @default(cuid()) createdAt DateTime @default(now()) createdByUserId String tenantId String key String @default(uuid()) keyHash String? keyPrefix String? keyLastFour String? hashedAt DateTime? keyVersion Int @default(1) alias String expires DateTime? active Boolean createdByUser User @relation(fields: [createdByUserId], references: [id], onDelete: Cascade) tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade) entities ApiKeyEntity[] apiKeyLogs ApiKeyLog[] logs Log[] createdRows Row[] tenantIpAddresses TenantIpAddress[] @@unique([tenantId, alias]) @@unique([tenantId, key]) @@unique([keyHash], name: "api_key_hash_key") @@index([key]) } model ApiKeyLog { id String @id @default(cuid()) createdAt DateTime @default(now()) apiKeyId String? apiKey ApiKey? @relation(fields: [apiKeyId], references: [id]) tenantId String? tenant Tenant? @relation(fields: [tenantId], references: [id], onDelete: Cascade) ip String endpoint String method String params String status Int? duration Int? error String? type String? alias String? tenantName String? @@index([tenantId], name: "api_key_log_tenant") @@index([tenantId, createdAt], name: "api_key_log_tenant_created_at") @@index([tenantId, type], name: "api_key_log_tenant_type") @@index([apiKeyId, tenantId, createdAt(sort: Desc)], name: "api_key_log_key_tenant_created_at") } model Event { id String @id @default(cuid()) createdAt DateTime @default(now()) tenantId String? tenant Tenant? @relation(fields: [tenantId], references: [id]) userId String? user User? @relation(fields: [userId], references: [id]) name String data String resource String? description String? attempts EventWebhookAttempt[] } model EventWebhookAttempt { id String @id @default(cuid()) createdAt DateTime @default(now()) startedAt DateTime? finishedAt DateTime? eventId String event Event @relation(fields: [eventId], references: [id], onDelete: Cascade) endpoint String success Boolean? status Int? message String? body String? } model MetricLog { id String @id @default(cuid()) createdAt DateTime @default(now()) env String type String // loader, action route String url String function String duration Int userId String? tenantId String? user User? @relation(fields: [userId], references: [id], onDelete: Cascade) tenant Tenant? @relation(fields: [tenantId], references: [id], onDelete: Cascade) } model FileUploadProgress { id String @id @default(cuid()) fileName String progressServer Int progressStorage Int url String? error String? chunks FileChunk[] } model FileChunk { id Int @id @default(autoincrement()) fileUploadId String fileUpload FileUploadProgress @relation(fields: [fileUploadId], references: [id], onDelete: Cascade) index Int data Bytes } model FileObject { id String @id @default(cuid()) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt provider String bucket String key String contentType String byteSize Int checksumSha256 String? publicUrl String? uploadedAt DateTime? lastSignedUploadAt DateTime? lastSignedDownloadAt DateTime? metadata Json? @@unique([provider, bucket, key], name: "file_object_provider_bucket_key") @@index([provider, bucket], name: "file_object_provider_bucket_idx") @@index([uploadedAt], name: "file_object_uploaded_at_idx") } /// Privacy (AD-22): `ip` speichert einen HMAC-SHA256-Pseudonym-Hash der Client-IP, KEINE Klartext-IP. /// Persistiert wird nur das Land (countryCode/countryName); regionCode/city/zipCode/latitude/longitude /// bleiben bewusst leer. Auflösung lokal (DB-IP Lite), kein externer Transfer. model IpAddress { id String @id @default(cuid()) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt ip String @unique provider String type String countryCode String countryName String regionCode String regionName String city String zipCode String latitude Decimal? longitude Decimal? metadata String logs IpAddressLog[] } /// Privacy (AD-22): `ip` ist der HMAC-Pseudonym-Hash der Client-IP, kein Klartext. model IpAddressLog { id String @id @default(cuid()) createdAt DateTime @default(now()) ip String url String action String description String success Boolean error String? metadata String? ipAddressId String? ipAddress IpAddress? @relation(fields: [ipAddressId], references: [id]) } model Widget { id String @id @default(cuid()) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt tenantId String? name String appearance String metadata String tenant Tenant? @relation(fields: [tenantId], references: [id], onDelete: Cascade) @@unique([tenantId, name]) } model Credential { id String @id @default(cuid()) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt name String @unique value Json }