215 lines
6.3 KiB
Plaintext
215 lines
6.3 KiB
Plaintext
generator client {
|
|
provider = "prisma-client-js"
|
|
}
|
|
|
|
datasource db {
|
|
provider = "postgresql"
|
|
}
|
|
|
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
// Enums
|
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
|
|
enum UserRole {
|
|
ADMIN
|
|
USER
|
|
}
|
|
|
|
enum RequestState {
|
|
PENDING
|
|
CHANGES_REQUESTED
|
|
APPROVED
|
|
REJECTED
|
|
EXPIRED
|
|
CONSUMED
|
|
CANCELLED
|
|
}
|
|
|
|
enum NotificationType {
|
|
REQUEST_CREATED
|
|
REQUEST_UPDATED
|
|
REQUEST_APPROVED
|
|
REQUEST_REJECTED
|
|
CHANGES_REQUESTED
|
|
REQUEST_CONSUMED
|
|
REQUEST_CANCELLED
|
|
REQUEST_EXPIRED
|
|
AGENT_DISABLED
|
|
ADMIN_ACTION
|
|
SETTINGS_CHANGED
|
|
}
|
|
|
|
enum GlobalSettingType {
|
|
registration_enabled
|
|
requests_enabled
|
|
}
|
|
|
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
// Models
|
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
|
|
/// A global (admin-only) platform setting, keyed by type.
|
|
model GlobalSetting {
|
|
type GlobalSettingType @id
|
|
value String
|
|
updatedAt DateTime @updatedAt
|
|
}
|
|
|
|
/// A human account. The first registered user becomes ADMIN.
|
|
model User {
|
|
id Int @id @default(autoincrement())
|
|
/// Normalized (lowercased) username — enforces case-insensitive uniqueness.
|
|
username String @unique
|
|
/// Original-cased display handle shown in the UI.
|
|
displayName String
|
|
password String
|
|
role UserRole @default(USER)
|
|
|
|
// Optional TOTP two-factor auth
|
|
totpSecret String?
|
|
totpEnabled Boolean @default(false)
|
|
|
|
// Admin can disable a human account entirely
|
|
disabled Boolean @default(false)
|
|
|
|
// Auto-delete of old change requests (opt-in, min 7 days, default 30)
|
|
autoDeleteEnabled Boolean @default(false)
|
|
autoDeleteDays Int @default(30)
|
|
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
sessions Session[]
|
|
agents Agent[]
|
|
changeRequests ChangeRequest[] @relation("Owner")
|
|
decisions ChangeRequest[] @relation("Approver")
|
|
notifications Notification[]
|
|
auditLogs AuditLog[] @relation("AuditActor")
|
|
}
|
|
|
|
/// A browser session for a human account (cookie-based auth).
|
|
model Session {
|
|
id Int @id @default(autoincrement())
|
|
hash String @unique
|
|
userId Int
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
createdAt DateTime @default(now())
|
|
|
|
@@index([userId])
|
|
}
|
|
|
|
/// An AGENT account. Fully managed by a human owner. Holds exactly one API key.
|
|
model Agent {
|
|
id Int @id @default(autoincrement())
|
|
name String
|
|
description String?
|
|
website String?
|
|
iconUrl String?
|
|
/// The single API key the agent authenticates with.
|
|
apiKey String @unique
|
|
disabled Boolean @default(false)
|
|
/// Max simultaneous pending requests (1..10, human-configurable, default 5).
|
|
maxPendingRequests Int @default(5)
|
|
|
|
ownerId Int
|
|
owner User @relation(fields: [ownerId], references: [id], onDelete: Cascade)
|
|
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
changeRequests ChangeRequest[]
|
|
|
|
@@index([ownerId])
|
|
}
|
|
|
|
/// A structured change request submitted by an agent for human approval.
|
|
model ChangeRequest {
|
|
id Int @id @default(autoincrement())
|
|
/// Public UUID (v4) exposed to agents as request_id.
|
|
publicId String @unique @default(uuid())
|
|
|
|
title String
|
|
description String?
|
|
/// Normalized changes array (diffs normalized, canonical ordering).
|
|
changes Json
|
|
/// Original changes exactly as submitted (audit trail).
|
|
rawChanges Json
|
|
metadata Json?
|
|
/// SHA-256 hash over the canonical (title + description + normalized changes).
|
|
contentHash String
|
|
|
|
state RequestState @default(PENDING)
|
|
|
|
expiresAt DateTime
|
|
|
|
// Decision fields
|
|
comment String?
|
|
decidedAt DateTime?
|
|
approverId Int?
|
|
approver User? @relation("Approver", fields: [approverId], references: [id], onDelete: SetNull)
|
|
/// HMAC-SHA256 signature of the approval receipt (platform-signed decision).
|
|
signature String?
|
|
receiptIssuedAt DateTime?
|
|
|
|
consumedAt DateTime?
|
|
cancelledAt DateTime?
|
|
|
|
// Update tracking for the CHANGES_REQUESTED → resubmit loop
|
|
updateCount Int @default(0)
|
|
/// True after an agent resubmits following a CHANGES_REQUESTED decision.
|
|
resubmitted Boolean @default(false)
|
|
lastAgentUpdateAt DateTime?
|
|
|
|
agentId Int
|
|
agent Agent @relation(fields: [agentId], references: [id], onDelete: Cascade)
|
|
|
|
// The human who owns/reviews this request (the agent's owner at creation time).
|
|
userId Int
|
|
user User @relation("Owner", fields: [userId], references: [id], onDelete: Cascade)
|
|
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@index([userId, state])
|
|
@@index([agentId, state])
|
|
@@index([createdAt])
|
|
}
|
|
|
|
/// An in-app notification for a human account.
|
|
model Notification {
|
|
id Int @id @default(autoincrement())
|
|
type NotificationType
|
|
title String
|
|
message String
|
|
/// Optional link to a change request (publicId).
|
|
requestPublicId String?
|
|
read Boolean @default(false)
|
|
|
|
userId Int
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
@@index([userId, read])
|
|
@@index([userId, createdAt])
|
|
}
|
|
|
|
/// An audit log entry. Records privileged/admin actions and payload views.
|
|
model AuditLog {
|
|
id Int @id @default(autoincrement())
|
|
action String
|
|
/// Human-readable detail / JSON string of context.
|
|
detail String?
|
|
/// Optional target references.
|
|
targetType String?
|
|
targetId String?
|
|
|
|
actorId Int?
|
|
actor User? @relation("AuditActor", fields: [actorId], references: [id], onDelete: SetNull)
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
@@index([createdAt])
|
|
@@index([actorId])
|
|
}
|