Files
HiveGrid-Wireframes/hivegrid-cloud/prisma/schema/files.prisma
T

163 lines
4.8 KiB
Plaintext

// P4 Files (AK-P4a) — Files platform primitive.
//
// AD-21: one self-contained schema file per module. `tenantId` /
// `createdByUserId` / `assignedToUserId` are intentionally BARE String scalars
// (no `@relation`) so this file has zero merge-conflict surface against
// `core.prisma`/`accounts.prisma` (no back-relation fields on Tenant/User
// needed). Referential integrity is carried by app-level scoping
// (withTenantScope, §7) plus the RLS policies from the merge migration (§8).
//
// Deliberately NOT reused: the legacy boilerplate models `FileObject`,
// `FileChunk`, `FileUploadProgress` in core.prisma. The models here are named
// differently (File, FileEntityLink, FileUpload, FileUploadChunk) to avoid a
// name collision and any core edit.
//
// Scope v1 (designer brief 2026-06-12): NO versioning (FileVersion = v2),
// no Office/Video previews, no resume upload, no OCR.
enum FilePreviewStatus {
pending
generating
ready
failed
not_supported
}
enum FileScanStatus {
pending
clean
infected
skipped
}
enum FileSource {
manual_upload
customer_portal
form_submission
email_attachment
automation
api
}
enum FileUploadStatus {
in_progress
completed
failed
expired
cancelled
}
enum FileEntityType {
contact
company
lead
deal
project
user
form_submission
order
task
appointment
}
/// A stored file, scoped per tenant. `storageKey` points at the original object
/// in the private Hetzner bucket (AD-23); delivery is only via the auth-proxy
/// route. Soft-delete via `archivedAt` (archive flag). Hard-delete is a separate
/// action (files.delete).
model File {
id String @id @default(cuid())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
tenantId String
createdByUserId String
displayName String
mimeType String
sizeBytes Int
storageKey String
checksumSha256 String?
previewStatus FilePreviewStatus @default(pending)
previewStorageKey String?
thumbnailStorageKey String?
scanStatus FileScanStatus @default(pending)
assignedToUserId String?
tags String[] @default([])
notes String?
source FileSource @default(manual_upload)
// Customer-portal upload prepared (DoD 12): nullable attribution, full portal
// flow = v2. Defaults to empty/invisible.
createdByPortalUserId String?
isDemoData Boolean @default(false)
archivedAt DateTime?
links FileEntityLink[]
@@index([tenantId, archivedAt, createdAt(sort: Desc)])
@@index([tenantId, createdByUserId])
@@index([tenantId, assignedToUserId])
@@index([tenantId, checksumSha256])
}
/// Polymorphic link of a file to any entity (cross-cutting). `entityType` +
/// `entityId` address the target entity, `linkRole` is an optional role label
/// (e.g. "contract", "photo").
model FileEntityLink {
id String @id @default(cuid())
createdAt DateTime @default(now())
tenantId String
createdByUserId String
fileId String
entityType FileEntityType
entityId String
linkRole String?
file File @relation(fields: [fileId], references: [id], onDelete: Cascade)
@@unique([fileId, entityType, entityId])
@@index([tenantId, entityType, entityId])
@@index([tenantId, fileId])
}
/// Chunked-upload session (replaces spec `FileUploadProgress`, renamed to avoid
/// the core collision). No resume in v1 — this session only carries progress up
/// to `complete`. `expiresAt` = now + 24h; a cleanup cron sweeps expired
/// sessions + temp chunks.
model FileUpload {
id String @id @default(cuid())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
tenantId String
createdByUserId String
originalFilename String
totalSizeBytes Int
mimeType String
totalChunks Int
receivedChunks Int @default(0)
status FileUploadStatus @default(in_progress)
targetFileId String?
expiresAt DateTime
failureReason String?
chunks FileUploadChunk[]
@@index([tenantId, createdByUserId, status])
@@index([expiresAt])
}
/// One uploaded chunk of a `FileUpload` session (replaces spec `FileChunk`).
/// Bytes live in temp storage (`storageKey`), not in the DB.
model FileUploadChunk {
id String @id @default(cuid())
tenantId String
uploadId String
chunkIndex Int
storageKey String
sizeBytes Int
checksumSha256 String?
receivedAt DateTime @default(now())
upload FileUpload @relation(fields: [uploadId], references: [id], onDelete: Cascade)
@@unique([uploadId, chunkIndex])
@@index([tenantId, uploadId])
}