Files
Kordant/packages/shared-db/prisma/schema.prisma

598 lines
15 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[]
// 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[]
propertyWatchlistItems PropertyWatchlistItem[]
propertySnapshots PropertySnapshot[]
propertyChanges PropertyChange[]
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])
propertyChange PropertyChange? @relation("PropertyAlerts", fields: [propertyChangeId], references: [id])
propertyChangeId String?
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])
}
// ============================================
// Waitlist & Marketing Models
// ============================================
model WaitlistEntry {
id String @id @default(uuid())
email String
name String?
source String? // landing_page, blog, referral, social, paid_search
tier SubscriptionTier? // interest level
utmSource String?
utmMedium String?
utmCampaign String?
metadata Json? // Browser, device, location, etc.
// Conversion tracking
convertedAt DateTime?
convertedToUserId String?
convertedToSubscriptionId String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([email])
@@index([source])
@@index([createdAt])
}
model BlogPost {
id String @id @default(uuid())
slug String @unique
title String
excerpt String?
content String
authorName String?
coverImageUrl String?
tags String[] // Array of tag strings
published Boolean @default(false)
publishedAt DateTime?
viewCount Int @default(0)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([slug])
@@index([published, publishedAt])
@@index([tags])
}
// ============================================
// Home Title Property Monitoring Models
// ============================================
model PropertyWatchlistItem {
id String @id @default(uuid())
subscriptionId String
address String
parcelId String
ownerName String
streetAddress String
city String
state String
zipCode String
latitude Float?
longitude Float?
isActive Boolean @default(true)
subscription Subscription @relation(fields: [subscriptionId], references: [id], onDelete: Cascade)
snapshots PropertySnapshot[]
changes PropertyChange[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@unique([subscriptionId, parcelId])
@@index([subscriptionId])
@@index([parcelId])
@@index([address])
}
model PropertySnapshot {
id String @id @default(uuid())
propertyWatchlistItemId String
subscriptionId String
ownerName String
ownerAddress String?
assessmentValue Int?
assessedYear Int?
taxAmount Float?
taxYear Int?
lienData Json? // Array of liens with amounts, types, dates
deedData Json? // Latest deed information
metadata Json? // Additional property data from sources
propertyWatchlistItem PropertyWatchlistItem @relation(fields: [propertyWatchlistItemId], references: [id], onDelete: Cascade)
subscription Subscription @relation(fields: [subscriptionId], references: [id], onDelete: Cascade)
changes PropertyChange[] @relation("SnapshotChanges")
snapshotDate DateTime
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([propertyWatchlistItemId])
@@index([subscriptionId])
@@index([snapshotDate])
}
model PropertyChange {
id String @id @default(uuid())
propertyWatchlistItemId String?
subscriptionId String
snapshotId String? // Reference to the snapshot where change was detected
changeType PropertyChangeType
severity PropertyChangeSeverity @default(info)
previousValue Json? // Before state
newValue Json? // After state
diff Json? // Computed diff between states
title String // Short description
description String // Detailed explanation
isResolved Boolean @default(false)
resolvedAt DateTime?
resolvedByUserId String?
propertyWatchlistItem PropertyWatchlistItem? @relation(fields: [propertyWatchlistItemId], references: [id])
subscription Subscription @relation(fields: [subscriptionId], references: [id], onDelete: Cascade)
snapshot PropertySnapshot? @relation("SnapshotChanges", fields: [snapshotId], references: [id])
alerts Alert[] @relation("PropertyAlerts")
detectedAt DateTime
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([propertyWatchlistItemId])
@@index([subscriptionId])
@@index([changeType])
@@index([severity])
@@index([detectedAt])
@@index([isResolved])
}
enum PropertyChangeType {
ownership_transfer
deed_change
lien_filing
tax_change
assessment_change
}
enum PropertyChangeSeverity {
info
warning
critical
}