- Report service: data collection from all three engines, HTML rendering (Handlebars), PDF generation (pdfkit) - REST API: /reports endpoints for generate, history, view, PDF download, scheduling - BullMQ workers: queued report generation with retry, monthly/annual scheduler triggers - DB: SecurityReport model with Prisma schema and type exports - Email: report_ready template in shared-notifications - All dependencies wired through existing packages Co-Authored-By: Paperclip <noreply@paperclip.ing>
572 lines
14 KiB
Plaintext
572 lines
14 KiB
Plaintext
// Prisma schema for ShieldAI
|
|
// All models for the multi-service SaaS platform
|
|
|
|
generator client {
|
|
provider = "prisma-client-js"
|
|
}
|
|
|
|
datasource db {
|
|
provider = "postgresql"
|
|
url = env("DATABASE_URL")
|
|
}
|
|
|
|
// ============================================
|
|
// User & Authentication Models
|
|
// ============================================
|
|
|
|
model User {
|
|
id String @id @default(uuid())
|
|
email String @unique
|
|
emailVerified DateTime?
|
|
name String?
|
|
image String?
|
|
role UserRole @default(user)
|
|
|
|
// Relationships
|
|
accounts Account[]
|
|
sessions Session[]
|
|
familyGroups FamilyGroupMember[]
|
|
familyGroupOwned FamilyGroup[] @relation("FamilyGroupOwner")
|
|
subscriptions Subscription[]
|
|
alerts Alert[]
|
|
voiceEnrollments VoiceEnrollment[]
|
|
voiceAnalyses VoiceAnalysis[]
|
|
spamFeedback SpamFeedback[]
|
|
spamRules SpamRule[]
|
|
normalizedAlerts NormalizedAlert[]
|
|
correlationGroups CorrelationGroup[]
|
|
securityReports SecurityReport[]
|
|
|
|
// Audit
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@index([email])
|
|
@@index([role])
|
|
}
|
|
|
|
enum UserRole {
|
|
user
|
|
family_admin
|
|
family_member
|
|
support
|
|
}
|
|
|
|
model Account {
|
|
id String @id @default(uuid())
|
|
userId String
|
|
provider String
|
|
providerAccountId String
|
|
access_token String?
|
|
refresh_token String?
|
|
expires_at Int?
|
|
token_type String?
|
|
scope String?
|
|
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@unique([userId, provider, providerAccountId])
|
|
@@index([userId])
|
|
}
|
|
|
|
model Session {
|
|
id String @id @default(uuid())
|
|
userId String
|
|
sessionToken String @unique
|
|
expires DateTime
|
|
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@index([sessionToken])
|
|
@@index([userId])
|
|
}
|
|
|
|
// ============================================
|
|
// Family & Subscription Models
|
|
// ============================================
|
|
|
|
model FamilyGroup {
|
|
id String @id @default(uuid())
|
|
name String
|
|
ownerId String
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
owner User @relation("FamilyGroupOwner", fields: [ownerId], references: [id])
|
|
members FamilyGroupMember[]
|
|
subscriptions Subscription[]
|
|
|
|
@@index([ownerId])
|
|
@@index([name])
|
|
}
|
|
|
|
model FamilyGroupMember {
|
|
id String @id @default(uuid())
|
|
groupId String
|
|
userId String
|
|
role FamilyMemberRole @default(member)
|
|
joinedAt DateTime @default(now())
|
|
|
|
group FamilyGroup @relation(fields: [groupId], references: [id], onDelete: Cascade)
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@unique([groupId, userId])
|
|
@@index([groupId])
|
|
@@index([userId])
|
|
}
|
|
|
|
enum FamilyMemberRole {
|
|
owner
|
|
admin
|
|
member
|
|
}
|
|
|
|
model Subscription {
|
|
id String @id @default(uuid())
|
|
userId String
|
|
familyGroupId String?
|
|
stripeId String? @unique
|
|
tier SubscriptionTier @default(basic)
|
|
status SubscriptionStatus @default(active)
|
|
currentPeriodStart DateTime
|
|
currentPeriodEnd DateTime
|
|
cancelAtPeriodEnd Boolean @default(false)
|
|
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
familyGroup FamilyGroup? @relation(fields: [familyGroupId], references: [id])
|
|
|
|
watchlistItems WatchlistItem[]
|
|
exposures Exposure[]
|
|
alerts Alert[]
|
|
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@index([userId])
|
|
@@index([familyGroupId])
|
|
@@index([stripeId])
|
|
@@index([tier])
|
|
}
|
|
|
|
enum SubscriptionTier {
|
|
basic
|
|
plus
|
|
premium
|
|
}
|
|
|
|
enum SubscriptionStatus {
|
|
active
|
|
past_due
|
|
canceled
|
|
unpaid
|
|
trialing
|
|
}
|
|
|
|
// ============================================
|
|
// DarkWatch Models (Dark Web Monitoring)
|
|
// ============================================
|
|
|
|
model WatchlistItem {
|
|
id String @id @default(uuid())
|
|
subscriptionId String
|
|
type WatchlistType
|
|
value String
|
|
hash String // SHA-256 hash for deduplication
|
|
isActive Boolean @default(true)
|
|
|
|
subscription Subscription @relation(fields: [subscriptionId], references: [id], onDelete: Cascade)
|
|
exposures Exposure[]
|
|
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@unique([subscriptionId, type, hash])
|
|
@@index([subscriptionId])
|
|
@@index([type])
|
|
@@index([hash])
|
|
}
|
|
|
|
enum WatchlistType {
|
|
email
|
|
phoneNumber
|
|
ssn
|
|
address
|
|
domain
|
|
}
|
|
|
|
model Exposure {
|
|
id String @id @default(uuid())
|
|
subscriptionId String
|
|
watchlistItemId String?
|
|
source ExposureSource
|
|
dataType WatchlistType
|
|
identifier String
|
|
identifierHash String
|
|
severity ExposureSeverity @default(info)
|
|
metadata Json? // Additional source-specific data
|
|
isFirstTime Boolean @default(false)
|
|
|
|
subscription Subscription @relation(fields: [subscriptionId], references: [id], onDelete: Cascade)
|
|
watchlistItem WatchlistItem? @relation(fields: [watchlistItemId], references: [id])
|
|
alerts Alert[]
|
|
|
|
detectedAt DateTime
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@index([subscriptionId])
|
|
@@index([watchlistItemId])
|
|
@@index([source])
|
|
@@index([severity])
|
|
@@index([detectedAt])
|
|
}
|
|
|
|
enum ExposureSource {
|
|
hibp // Have I Been Pwned
|
|
securityTrails
|
|
censys
|
|
darkWebForum
|
|
shodan
|
|
honeypot
|
|
}
|
|
|
|
enum ExposureSeverity {
|
|
info
|
|
warning
|
|
critical
|
|
}
|
|
|
|
// ============================================
|
|
// Notification & Alert Models
|
|
// ============================================
|
|
|
|
model Alert {
|
|
id String @id @default(uuid())
|
|
subscriptionId String
|
|
userId String
|
|
exposureId String?
|
|
type AlertType
|
|
title String
|
|
message String
|
|
severity AlertSeverity @default(info)
|
|
isRead Boolean @default(false)
|
|
readAt DateTime?
|
|
channel AlertChannel[] // Array of notification channels
|
|
|
|
subscription Subscription @relation(fields: [subscriptionId], references: [id], onDelete: Cascade)
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
exposure Exposure? @relation(fields: [exposureId], references: [id])
|
|
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@index([subscriptionId])
|
|
@@index([userId])
|
|
@@index([isRead])
|
|
@@index([createdAt])
|
|
}
|
|
|
|
enum AlertType {
|
|
exposure_detected
|
|
exposure_resolved
|
|
scan_complete
|
|
subscription_changed
|
|
system_warning
|
|
}
|
|
|
|
enum AlertSeverity {
|
|
info
|
|
warning
|
|
critical
|
|
}
|
|
|
|
enum AlertChannel {
|
|
email
|
|
push
|
|
sms
|
|
}
|
|
|
|
// ============================================
|
|
// VoicePrint Models (Voice Cloning Detection)
|
|
// ============================================
|
|
|
|
model VoiceEnrollment {
|
|
id String @id @default(uuid())
|
|
userId String
|
|
name String
|
|
voiceHash String // FAISS embedding hash
|
|
audioMetadata Json? // Sample rate, duration, etc.
|
|
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
analyses VoiceAnalysis[]
|
|
|
|
isActive Boolean @default(true)
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@index([userId])
|
|
@@index([voiceHash])
|
|
}
|
|
|
|
model VoiceAnalysis {
|
|
id String @id @default(uuid())
|
|
enrollmentId String?
|
|
userId String
|
|
audioHash String // Content hash of audio file
|
|
isSynthetic Boolean
|
|
confidence Float // 0.0 to 1.0
|
|
analysisResult Json // Full ML analysis results
|
|
audioUrl String // S3 storage URL
|
|
|
|
enrollment VoiceEnrollment? @relation(fields: [enrollmentId], references: [id])
|
|
user User @relation(fields: [userId], references: [id])
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
@@index([userId])
|
|
@@index([enrollmentId])
|
|
@@index([audioHash])
|
|
}
|
|
|
|
// ============================================
|
|
// SpamShield Models (Spam Detection)
|
|
// ============================================
|
|
|
|
model SpamFeedback {
|
|
id String @id @default(uuid())
|
|
userId String
|
|
phoneNumber String
|
|
phoneNumberHash String // SHA-256 hash
|
|
isSpam Boolean
|
|
confidence Float? // ML model confidence
|
|
feedbackType FeedbackType
|
|
metadata Json? // Call duration, time, etc.
|
|
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@index([userId])
|
|
@@index([phoneNumberHash])
|
|
@@index([isSpam])
|
|
}
|
|
|
|
enum FeedbackType {
|
|
initial_detection
|
|
user_confirmation
|
|
user_rejection
|
|
auto_learned
|
|
}
|
|
|
|
model SpamRule {
|
|
id String @id @default(uuid())
|
|
userId String?
|
|
isGlobal Boolean @default(false)
|
|
ruleType RuleType
|
|
pattern String
|
|
action RuleAction
|
|
priority Int @default(0)
|
|
isActive Boolean @default(true)
|
|
|
|
user User? @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@index([userId])
|
|
@@index([isGlobal])
|
|
@@index([ruleType])
|
|
}
|
|
|
|
enum RuleType {
|
|
phoneNumber
|
|
areaCode
|
|
prefix
|
|
pattern
|
|
reputation
|
|
}
|
|
|
|
enum RuleAction {
|
|
block
|
|
flag
|
|
allow
|
|
challenge
|
|
}
|
|
|
|
// ============================================
|
|
// Audit & Analytics Models
|
|
// ============================================
|
|
|
|
model AuditLog {
|
|
id String @id @default(uuid())
|
|
userId String?
|
|
action String
|
|
resource String
|
|
resourceId String?
|
|
changes Json? // Before/after values
|
|
metadata Json?
|
|
ipAddress String?
|
|
userAgent String?
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
@@index([userId])
|
|
@@index([action])
|
|
@@index([resource])
|
|
@@index([createdAt])
|
|
}
|
|
|
|
model KPISnapshot {
|
|
id String @id @default(uuid())
|
|
date DateTime @unique
|
|
metricName String
|
|
metricValue Float
|
|
metadata Json?
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
@@index([metricName])
|
|
@@index([date])
|
|
}
|
|
|
|
// ============================================
|
|
// Cross-Service Alert Correlation Models
|
|
// ============================================
|
|
|
|
enum AlertSource {
|
|
DARKWATCH
|
|
SPAMSHIELD
|
|
VOICEPRINT
|
|
CALL_ANALYSIS
|
|
}
|
|
|
|
enum AlertCategory {
|
|
BREACH_EXPOSURE
|
|
SPAM_CALL
|
|
SPAM_SMS
|
|
SYNTHETIC_VOICE
|
|
VOICE_MISMATCH
|
|
CALL_ANOMALY
|
|
CALL_QUALITY
|
|
CALL_EVENT
|
|
}
|
|
|
|
enum NormalizedAlertSeverity {
|
|
LOW
|
|
INFO
|
|
MEDIUM
|
|
WARNING
|
|
HIGH
|
|
CRITICAL
|
|
}
|
|
|
|
enum CorrelationStatus {
|
|
ACTIVE
|
|
RESOLVED
|
|
}
|
|
|
|
model NormalizedAlert {
|
|
id String @id @default(uuid())
|
|
source AlertSource
|
|
category AlertCategory
|
|
severity NormalizedAlertSeverity
|
|
userId String
|
|
title String
|
|
description String
|
|
entities Json
|
|
sourceAlertId String
|
|
groupId String?
|
|
payload Json?
|
|
createdAt DateTime
|
|
updatedAt DateTime @default(now()) @updatedAt
|
|
|
|
correlationGroup CorrelationGroup? @relation(fields: [groupId], references: [id])
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
|
|
@@unique([sourceAlertId])
|
|
@@index([userId])
|
|
@@index([groupId])
|
|
@@index([source])
|
|
@@index([severity])
|
|
@@index([createdAt])
|
|
@@index([userId, createdAt])
|
|
}
|
|
|
|
model CorrelationGroup {
|
|
id String @id @default(uuid())
|
|
userId String
|
|
entities Json
|
|
highestSeverity NormalizedAlertSeverity
|
|
status CorrelationStatus @default(ACTIVE)
|
|
alertCount Int @default(0)
|
|
summary String?
|
|
resolvedAt DateTime?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @default(now()) @updatedAt
|
|
|
|
alerts NormalizedAlert[]
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
|
|
@@index([userId])
|
|
@@index([status])
|
|
@@index([userId, status])
|
|
@@index([createdAt])
|
|
}
|
|
|
|
// ============================================
|
|
// Report Generation Models
|
|
// ============================================
|
|
|
|
enum ReportType {
|
|
MONTHLY_PLUS
|
|
ANNUAL_PREMIUM
|
|
}
|
|
|
|
enum ReportStatus {
|
|
PENDING
|
|
GENERATING
|
|
COMPLETED
|
|
FAILED
|
|
DELIVERED
|
|
}
|
|
|
|
model SecurityReport {
|
|
id String @id @default(uuid())
|
|
userId String
|
|
subscriptionId String
|
|
reportType ReportType
|
|
status ReportStatus @default(PENDING)
|
|
periodStart DateTime
|
|
periodEnd DateTime
|
|
title String
|
|
summary String?
|
|
htmlContent String?
|
|
pdfUrl String?
|
|
dataPayload Json?
|
|
error String?
|
|
scheduledFor DateTime?
|
|
deliveredAt DateTime?
|
|
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @default(now()) @updatedAt
|
|
|
|
@@index([userId])
|
|
@@index([subscriptionId])
|
|
@@index([reportType])
|
|
@@index([status])
|
|
@@index([periodStart, periodEnd])
|
|
@@index([createdAt])
|
|
}
|