// 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 }