231 lines
7.3 KiB
Plaintext
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])
|
|
}
|