Files
HiveGrid-Wireframes/03 Quellen und Referenzen/hivegrid-cloud/prisma/schema/appsEngine.prisma
T

231 lines
7.3 KiB
Plaintext

// R-P5 (P5) Apps-Engine & Integrations-Framework — aligned to ENTITY-MODEL
// Domäne 4 (AD-20 Pivot-Vorrang). Replaces the stock boilerplate models.
//
// AD-21: self-contained schema. `tenantId` / `createdByUserId` are BARE String
// scalars (no `@relation` to Tenant/User) → zero merge-conflict surface against
// core.prisma/accounts.prisma. Intra-module relations (App→DataConnection→
// AppSyncRun→Log/Item) stay inside this file.
//
// `App` is GLOBAL (a definition/catalog table, no tenantId, no RLS). Everything
// tenant-facing (DataConnection + sync rows) is tenant-scoped + RLS
// (prisma/rls/apps-engine-isolation.sql, deferred activation like the other
// modules). Credentials are stored ENVELOPE-ENCRYPTED via the per-tenant DEK
// (R-P5 Phase 1: packages/db/src/crypto/tenant-envelope.server.ts), never plain.
//
// Scope (AD-26 recut): connection types oauth2 / api_key / rest_api only have a
// v1 flow; `windows_client` (KWP) stays in the datamodel but has NO v1 UI/logic.
// Concrete app adapters (Gmail/Outlook/AirCall/Mollie) belong to P14/P15/P16/P20,
// not here — P5 ships only the generic framework + the sample-data-sync adapter.
enum AppCategory {
erp
communication
calendar
signature
payment
ki
}
enum AppConnectionType {
oauth2
api_key
rest_api
windows_client
}
enum DataConnectionStatus {
pending
active
degraded
broken
revoked
}
enum AppSyncKind {
manual
scheduled
webhook
}
enum AppSyncStatus {
pending
running
completed
partial
failed
cancelled
}
enum AppSyncLogSeverity {
info
success
warning
error
}
enum AppSyncItemOperation {
created
updated
deleted
skipped
}
enum AppSyncItemStatus {
pending
synced
failed
skipped
}
enum AppEntityBindingState {
active
quarantined
missing
ignored
unlinked
}
/// Global catalog definition of an integratable app (no tenant, no RLS). The
/// `connectionSchema` drives the auto-generated credential form; `connectionType`
/// selects the connect flow. New apps are registered by the build-time adapter
/// scan + this catalog (drift-check warns in the platform admin).
model App {
id String @id @default(cuid())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
key String @unique
title String
category AppCategory
connectionType AppConnectionType
connectionSchema Json @default("{}")
phase String?
icon String?
isActive Boolean @default(true)
@@index([category])
}
/// A tenant's connection to an App. Multiple per (tenant, app) with a user
/// `label` (e.g. three Gmail mailboxes). Credentials live encrypted in
/// `credentialsCiphertext`. Disconnect sets `connectionStatus = revoked` (no
/// hard-delete → sync history preserved). Soft-delete via `archivedAt`.
model DataConnection {
id String @id @default(cuid())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
tenantId String
// The app's stable catalog `key` (bare scalar per AD-21; matches the adapter
// registry + INTEGRATION_CATALOG). App is a global catalog, not an FK target.
appId String
label String
// AES-256-GCM per-tenant DEK envelope (AD-8). Never selected into list VMs.
credentialsCiphertext String?
connectionStatus DataConnectionStatus @default(pending)
lastHealthCheck DateTime?
// i18n key describing the current health state (Klartext, M002/M003).
healthMessageKey String?
tokenExpiresAt DateTime?
createdByUserId String?
isDemoData Boolean @default(false)
archivedAt DateTime?
syncRuns AppSyncRun[]
entityBindings AppEntityBinding[]
@@index([tenantId, appId, archivedAt])
@@index([tenantId, connectionStatus])
@@index([tenantId, tokenExpiresAt])
}
/// One sync run for a DataConnection. `summaryText` is the Klartext summary.
model AppSyncRun {
id String @id @default(cuid())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
tenantId String
dataConnectionId String
kind AppSyncKind @default(manual)
status AppSyncStatus @default(pending)
itemsTotal Int @default(0)
itemsSucceeded Int @default(0)
itemsFailed Int @default(0)
summaryText String?
startedAt DateTime?
completedAt DateTime?
createdByUserId String?
dataConnection DataConnection @relation(fields: [dataConnectionId], references: [id], onDelete: Cascade)
logs AppSyncRunLog[]
items AppSyncItem[]
@@index([tenantId, dataConnectionId, createdAt(sort: Desc)])
@@index([tenantId, status])
}
/// A log line of a sync run (Klartext `messageText` + optional `techDetails`).
model AppSyncRunLog {
id String @id @default(cuid())
createdAt DateTime @default(now())
tenantId String
syncRunId String
severity AppSyncLogSeverity @default(info)
messageText String
techDetails String?
syncRun AppSyncRun @relation(fields: [syncRunId], references: [id], onDelete: Cascade)
@@index([tenantId, syncRunId])
}
/// A single synced object: maps a HiveGrid entity (`entityType`/`entityId`) to an
/// external system id (`externalId`). Polymorphic.
model AppSyncItem {
id String @id @default(cuid())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
tenantId String
syncRunId String
entityType String
entityId String?
externalId String
operation AppSyncItemOperation @default(created)
status AppSyncItemStatus @default(pending)
errorText String?
syncRun AppSyncRun @relation(fields: [syncRunId], references: [id], onDelete: Cascade)
@@index([tenantId, syncRunId])
@@index([tenantId, entityType, entityId])
@@index([tenantId, externalId])
}
/// Durable provider-neutral identity mapping. AppSyncItem remains the history
/// of one run; this row survives runs, reconnects and process restarts.
model AppEntityBinding {
id String @id @default(cuid())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
tenantId String
dataConnectionId String
externalEntityType String
localEntityType String
externalId String
localId String
state AppEntityBindingState @default(active)
normalizedSnapshot Json
fingerprint String
overwrittenLocalSnapshot Json?
revision Int @default(1)
lastSeenAt DateTime?
lastSyncedAt DateTime?
missingSince DateTime?
unlinkedAt DateTime?
dataConnection DataConnection @relation(fields: [dataConnectionId], references: [id], onDelete: Cascade)
@@unique([tenantId, dataConnectionId, externalEntityType, externalId])
@@unique([tenantId, dataConnectionId, localEntityType, localId])
@@index([tenantId, dataConnectionId, state, lastSyncedAt])
@@index([tenantId, localEntityType, localId, state])
}