Consolidate @shieldai/db and @shieldsai/shared-db packages (FRE-4603)

- Merged singleton pattern + type exports from shared-db
- Kept FieldEncryptionService from original db package
- Upgraded to Prisma v6.2.0 (newer version)
- Adopted shared-db's complete schema for multi-service platform
- Updated 17 consumer imports across darkwatch, voiceprint, jobs, api
- Standardized on @shieldai/db namespace

Files changed:
- packages/db/package.json (v0.1.0 → v0.2.0)
- packages/db/src/index.ts (consolidated exports)
- packages/db/prisma/schema.prisma (merged schema)
- packages/db/prisma/seed.ts (updated for new schema)
- 17 consumer files updated

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
2026-05-02 15:06:02 -04:00
parent 93ff4885ee
commit 24bc9c235f
20 changed files with 465 additions and 371 deletions

View File

@@ -2,7 +2,7 @@ import { describe, it, expect, beforeEach, vi } from 'vitest';
import { SMSClassifierService } from '../services/spamshield/spamshield.service'; import { SMSClassifierService } from '../services/spamshield/spamshield.service';
// Mock shared-db before anything else (Prisma client is not generated in test env) // Mock shared-db before anything else (Prisma client is not generated in test env)
vi.mock('@shieldsai/shared-db', () => ({ vi.mock('@shieldai/db', () => ({
prisma: {}, prisma: {},
SpamFeedback: {}, SpamFeedback: {},
})); }));

View File

@@ -1,5 +1,5 @@
import { FastifyInstance, FastifyRequest, FastifyReply } from 'fastify'; import { FastifyInstance, FastifyRequest, FastifyReply } from 'fastify';
import { prisma, SubscriptionTier } from '@shieldsai/shared-db'; import { prisma, SubscriptionTier } from '@shieldai/db';
import { tierConfig, SubscriptionTier as BillingTier } from '@shieldsai/shared-billing'; import { tierConfig, SubscriptionTier as BillingTier } from '@shieldsai/shared-billing';
import { import {
watchlistService, watchlistService,

View File

@@ -1,4 +1,4 @@
import { prisma, AlertType, AlertSeverity } from '@shieldsai/shared-db'; import { prisma, AlertType, AlertSeverity } from '@shieldai/db';
import { import {
NotificationService, NotificationService,
NotificationPriority, NotificationPriority,

View File

@@ -1,4 +1,4 @@
import { prisma, ExposureSource, ExposureSeverity, WatchlistType } from '@shieldsai/shared-db'; import { prisma, ExposureSource, ExposureSeverity, WatchlistType } from '@shieldai/db';
import { createHash } from 'crypto'; import { createHash } from 'crypto';
function hashIdentifier(identifier: string): string { function hashIdentifier(identifier: string): string {

View File

@@ -1,4 +1,4 @@
import { prisma, SubscriptionTier, SubscriptionStatus } from '@shieldsai/shared-db'; import { prisma, SubscriptionTier, SubscriptionStatus } from '@shieldai/db';
import { tierConfig } from '@shieldsai/shared-billing'; import { tierConfig } from '@shieldsai/shared-billing';
import { darkwatchScanQueue } from '@shieldsai/jobs'; import { darkwatchScanQueue } from '@shieldsai/jobs';
import { randomUUID } from 'crypto'; import { randomUUID } from 'crypto';

View File

@@ -1,4 +1,4 @@
import { prisma, WatchlistType } from '@shieldsai/shared-db'; import { prisma, WatchlistType } from '@shieldai/db';
import { createHash } from 'crypto'; import { createHash } from 'crypto';
export function normalizeValue(type: WatchlistType, value: string): string { export function normalizeValue(type: WatchlistType, value: string): string {

View File

@@ -1,4 +1,4 @@
import { prisma, ExposureSource, ExposureSeverity, WatchlistType, AlertType, AlertSeverity } from '@shieldsai/shared-db'; import { prisma, ExposureSource, ExposureSeverity, WatchlistType, AlertType, AlertSeverity } from '@shieldai/db';
import { createHash } from 'crypto'; import { createHash } from 'crypto';
import { mixpanelService, EventType } from '@shieldsai/shared-analytics'; import { mixpanelService, EventType } from '@shieldsai/shared-analytics';

View File

@@ -1,4 +1,4 @@
import { prisma, SpamFeedback } from '@shieldsai/shared-db'; import { prisma, SpamFeedback } from '@shieldai/db';
import { spamShieldEnv, SpamDecision, spamFeatureFlags, defaultScores, metadataLimits } from './spamshield.config'; import { spamShieldEnv, SpamDecision, spamFeatureFlags, defaultScores, metadataLimits } from './spamshield.config';
import { createHash } from 'crypto'; import { createHash } from 'crypto';
import { spamAuditLogger, hashPhoneNumber } from './spamshield.audit-logger'; import { spamAuditLogger, hashPhoneNumber } from './spamshield.audit-logger';

View File

@@ -1,4 +1,4 @@
import { prisma, VoiceEnrollment, VoiceAnalysis } from '@shieldsai/shared-db'; import { prisma, VoiceEnrollment, VoiceAnalysis } from '@shieldai/db';
import { import {
voicePrintEnv, voicePrintEnv,
AnalysisJobStatus, AnalysisJobStatus,

View File

@@ -1,21 +1,26 @@
{ {
"name": "@shieldai/db", "name": "@shieldai/db",
"version": "0.1.0", "version": "0.2.0",
"main": "./dist/index.js", "type": "module",
"types": "./dist/index.js", "main": "./src/index.ts",
"types": "./src/index.ts",
"scripts": { "scripts": {
"build": "prisma generate && tsc", "build": "prisma generate && tsc",
"db:migrate": "prisma migrate dev", "db:migrate": "prisma migrate dev",
"db:seed": "tsx prisma/seed.ts", "db:seed": "tsx prisma/seed.ts",
"db:studio": "prisma studio", "db:studio": "prisma studio",
"db:push": "prisma db push",
"db:format": "prisma format",
"generate": "prisma generate" "generate": "prisma generate"
}, },
"dependencies": { "dependencies": {
"@prisma/client": "^6.2.0", "@prisma/client": "^6.2.0",
"prisma": "^6.2.0" "prisma": "^6.2.0",
"zod": "^4.3.6"
}, },
"devDependencies": { "devDependencies": {
"tsx": "^4.19.0" "tsx": "^4.19.0",
"typescript": "^5.3.3"
}, },
"exports": { "exports": {
".": "./src/index.ts" ".": "./src/index.ts"

View File

@@ -1,3 +1,6 @@
// Prisma schema for ShieldAI
// All models for the multi-service SaaS platform
generator client { generator client {
provider = "prisma-client-js" provider = "prisma-client-js"
} }
@@ -7,406 +10,428 @@ datasource db {
url = env("DATABASE_URL") url = env("DATABASE_URL")
} }
enum SubscriptionTier { // ============================================
BASIC // User & Authentication Models
PLUS // ============================================
PREMIUM
}
enum IdentifierType {
EMAIL
PHONE
SSN
}
enum WatchListStatus {
ACTIVE
PAUSED
}
enum Severity {
LOW
INFO
MEDIUM
WARNING
HIGH
CRITICAL
}
enum AlertChannel {
EMAIL
PUSH
SMS
}
enum AlertStatus {
PENDING
SENT
READ
}
enum ScanJobStatus {
PENDING
RUNNING
COMPLETED
FAILED
}
enum DataSource {
HIBP
SECURITY_TRAILS
CENSYS
SHODAN
HONEYPOT
}
enum AnalysisJobStatus {
PENDING
RUNNING
COMPLETED
FAILED
}
enum AnalysisType {
SYNTHETIC_DETECTION
VOICE_MATCH
BATCH
}
enum DetectionVerdict {
NATURAL
SYNTHETIC
UNCERTAIN
}
model User { model User {
id String @id @default(uuid()) id String @id @default(uuid())
email String @unique email String @unique
name String? emailVerified DateTime?
subscriptionTier SubscriptionTier @default(BASIC) name String?
familyGroupId String? image String?
watchListItems WatchListItem[] role UserRole @default(user)
alerts Alert[]
scanJobs ScanJob[] // Relationships
scanSchedules ScanSchedule[] accounts Account[]
sessions Session[]
familyGroups FamilyGroupMember[]
familyGroupOwned FamilyGroup[] @relation("FamilyGroupOwner")
subscriptions Subscription[]
alerts Alert[]
voiceEnrollments VoiceEnrollment[] voiceEnrollments VoiceEnrollment[]
analysisJobs AnalysisJob[] voiceAnalyses VoiceAnalysis[]
spamFeedback SpamFeedback[] spamFeedback SpamFeedback[]
spamCallAnalyses SpamCallAnalysis[] spamRules SpamRule[]
spamAuditLogs SpamAuditLog[]
normalizedAlerts NormalizedAlert[] // Audit
correlationGroups CorrelationGroup[] createdAt DateTime @default(now())
createdAt DateTime @default(now()) updatedAt DateTime @updatedAt
updatedAt DateTime @updatedAt
@@index([email]) @@index([email])
@@index([role])
} }
model WatchListItem { enum UserRole {
id String @id @default(uuid()) 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 userId String
user User @relation(fields: [userId], references: [id], onDelete: Cascade) familyGroupId String?
identifierType IdentifierType stripeId String? @unique
identifierValue String tier SubscriptionTier @default(basic)
identifierHash String @unique status SubscriptionStatus @default(active)
status WatchListStatus @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[] exposures Exposure[]
createdAt DateTime @default(now()) alerts Alert[]
updatedAt DateTime @updatedAt
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([userId]) @@index([userId])
@@index([identifierHash]) @@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 { model Exposure {
id String @id @default(uuid()) id String @id @default(uuid())
watchListItemId String subscriptionId String
watchListItem WatchListItem @relation(fields: [watchListItemId], references: [id], onDelete: Cascade) watchlistItemId String?
dataSource DataSource source ExposureSource
breachName String dataType WatchlistType
exposedAt DateTime identifier String
dataType String[] identifierHash String
severity Severity severity ExposureSeverity @default(info)
details String? metadata Json? // Additional source-specific data
contentHash String @unique isFirstTime Boolean @default(false)
alert Alert?
subscription Subscription @relation(fields: [subscriptionId], references: [id], onDelete: Cascade)
watchlistItem WatchlistItem? @relation(fields: [watchlistItemId], references: [id])
alerts Alert[]
detectedAt DateTime
createdAt DateTime @default(now()) createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([watchListItemId]) @@index([subscriptionId])
@@index([contentHash]) @@index([watchlistItemId])
@@index([dataSource]) @@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 { model Alert {
id String @id @default(uuid()) id String @id @default(uuid())
userId String subscriptionId String
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
exposureId String @unique
exposure Exposure @relation(fields: [exposureId], references: [id], onDelete: Cascade)
severity Severity
channel AlertChannel
status AlertStatus @default(PENDING)
dedupKey String
sentAt DateTime?
createdAt DateTime @default(now())
@@index([userId, status])
@@index([dedupKey])
}
model ScanJob {
id String @id @default(uuid())
userId String
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
status ScanJobStatus @default(PENDING)
source DataSource?
resultCount Int @default(0)
errorMessage String?
scheduledBy String?
webhookEvents WebhookEvent[]
completedAt DateTime?
createdAt DateTime @default(now())
@@index([userId, status])
@@index([createdAt])
}
enum ScheduleStatus {
ACTIVE
PAUSED
}
model ScanSchedule {
id String @id @default(uuid())
userId String userId String
user User @relation(fields: [userId], references: [id], onDelete: Cascade) exposureId String?
intervalMinutes Int // minutes between scans type AlertType
cronExpression String // cron expression for scheduling title String
status ScheduleStatus @default(ACTIVE) message String
lastScanAt DateTime? severity AlertSeverity @default(info)
nextScanAt DateTime? isRead Boolean @default(false)
createdAt DateTime @default(now()) readAt DateTime?
updatedAt DateTime @updatedAt 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
@@unique([userId]) @@index([subscriptionId])
@@index([status]) @@index([userId])
} @@index([isRead])
enum WebhookEventType {
SCAN_TRIGGER
BREACH_DETECTED
SUBSCRIPTION_CHANGE
}
model WebhookEvent {
id String @id @default(uuid())
eventType WebhookEventType
payload String
source String?
signature String?
processed Boolean @default(false)
processedAt DateTime?
scanJobId String?
scanJob ScanJob? @relation(fields: [scanJobId], references: [id], onDelete: SetNull)
createdAt DateTime @default(now())
@@index([eventType, processed])
@@index([createdAt]) @@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 { model VoiceEnrollment {
id String @id @default(uuid()) id String @id @default(uuid())
userId String userId String
user User @relation(fields: [userId], references: [id], onDelete: Cascade) name String
label String voiceHash String // FAISS embedding hash
embeddingVector Float[] audioMetadata Json? // Sample rate, duration, etc.
embeddingDim Int @default(192)
audioFilePath String? user User @relation(fields: [userId], references: [id], onDelete: Cascade)
sampleRate Int @default(16000) analyses VoiceAnalysis[]
durationSec Float?
createdAt DateTime @default(now()) isActive Boolean @default(true)
updatedAt DateTime @updatedAt createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([userId]) @@index([userId])
@@index([embeddingDim]) @@index([voiceHash])
} }
model AnalysisJob { model VoiceAnalysis {
id String @id @default(uuid()) id String @id @default(uuid())
userId String enrollmentId String?
user User @relation(fields: [userId], references: [id], onDelete: Cascade) userId String
analysisType AnalysisType audioHash String // Content hash of audio file
audioFilePath String isSynthetic Boolean
status AnalysisJobStatus @default(PENDING) confidence Float // 0.0 to 1.0
result AnalysisResult? analysisResult Json // Full ML analysis results
errorMessage String? audioUrl String // S3 storage URL
completedAt DateTime?
createdAt DateTime @default(now()) enrollment VoiceEnrollment? @relation(fields: [enrollmentId], references: [id])
user User @relation(fields: [userId], references: [id])
createdAt DateTime @default(now())
@@index([userId, status]) @@index([userId])
@@index([createdAt]) @@index([enrollmentId])
@@index([audioHash])
} }
model AnalysisResult { // ============================================
id String @id @default(uuid()) // SpamShield Models (Spam Detection)
analysisJobId String @unique // ============================================
analysisJob AnalysisJob @relation(fields: [analysisJobId], references: [id], onDelete: Cascade)
syntheticScore Float
verdict DetectionVerdict
matchedEnrollmentId String?
matchedSimilarity Float?
confidence Float
processingTimeMs Int
modelVersion String?
metadata String?
createdAt DateTime @default(now())
@@index([analysisJobId])
@@index([verdict])
}
enum SpamDecision {
BLOCK
FLAG
ALLOW
}
model SpamFeedback { model SpamFeedback {
id String @id @default(uuid()) id String @id @default(uuid())
userId String userId String
user User @relation(fields: [userId], references: [id], onDelete: Cascade) phoneNumber String
phoneNumber String // AES-256 encrypted PII phoneNumberHash String // SHA-256 hash
phoneNumberHash String // SHA-256 hash for anonymized lookup isSpam Boolean
isSpam Boolean confidence Float? // ML model confidence
label String? feedbackType FeedbackType
metadata String? // Unbounded JSON metadata Json? // Call duration, time, etc.
createdAt DateTime @default(now())
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([userId]) @@index([userId])
@@index([phoneNumberHash]) @@index([phoneNumberHash])
@@index([createdAt]) @@index([isSpam])
} }
model SpamCallAnalysis { enum FeedbackType {
id String @id @default(uuid()) initial_detection
userId String user_confirmation
user User @relation(fields: [userId], references: [id], onDelete: Cascade) user_rejection
phoneNumber String auto_learned
callTimestamp DateTime
hiyaReputationScore Float?
truecallerSpamScore Float?
decision SpamDecision
confidence Float
ruleMatches String[] // IDs of matched rules
auditLogs SpamAuditLog[]
createdAt DateTime @default(now())
@@index([userId])
@@index([phoneNumber])
@@index([callTimestamp])
} }
model SpamRule { model SpamRule {
id String @id @default(uuid()) id String @id @default(uuid())
name String @unique userId String?
pattern String @db.VarChar(500) // Regex pattern - validated for ReDoS at application layer isGlobal Boolean @default(false)
decision SpamDecision ruleType RuleType
description String? pattern String
isActive Boolean @default(true) action RuleAction
priority Int @default(0) priority Int @default(0)
createdAt DateTime @default(now()) isActive Boolean @default(true)
updatedAt DateTime @updatedAt
user User? @relation(fields: [userId], references: [id], onDelete: Cascade)
@@index([isActive])
@@index([priority]) createdAt DateTime @default(now())
} updatedAt DateTime @updatedAt
model SpamAuditLog {
id String @id @default(uuid())
analysisId String?
analysis SpamCallAnalysis? @relation(fields: [analysisId], references: [id], onDelete: SetNull)
userId String
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
phoneNumber String
decision SpamDecision
reason String
ruleId String?
createdAt DateTime @default(now())
@@index([userId]) @@index([userId])
@@index([createdAt]) @@index([isGlobal])
@@index([decision]) @@index([ruleType])
} }
enum AlertSource { enum RuleType {
DARKWATCH phoneNumber
SPAMSHIELD areaCode
VOICEPRINT prefix
CALL_ANALYSIS pattern
reputation
} }
enum AlertCategory { enum RuleAction {
BREACH_EXPOSURE block
SPAM_CALL flag
SPAM_SMS allow
SYNTHETIC_VOICE challenge
VOICE_MISMATCH
CALL_QUALITY
CALL_ANOMALY
CALL_EVENT
} }
enum CorrelationStatus { // ============================================
ACTIVE // Audit & Analytics Models
RESOLVED // ============================================
FALSE_POSITIVE
}
enum EntityType { model AuditLog {
PHONE_NUMBER id String @id @default(uuid())
EMAIL userId String?
USER_ID action String
CALL_ID resource String
IP_ADDRESS resourceId String?
} changes Json? // Before/after values
metadata Json?
ipAddress String?
userAgent String?
createdAt DateTime @default(now())
model NormalizedAlert { @@index([userId])
id String @id @default(uuid()) @@index([action])
source AlertSource @@index([resource])
category AlertCategory
severity Severity
userId String
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
title String
description String
entities Json // [{ type: EntityType, value: string }]
sourceAlertId String
groupId String?
correlationGroup CorrelationGroup? @relation(fields: [groupId], references: [id], onDelete: SetNull)
payload Json
createdAt DateTime @default(now())
@@index([userId, createdAt])
@@index([groupId])
@@index([sourceAlertId])
@@index([source])
@@index([severity])
}
model CorrelationGroup {
id String @id @default(uuid())
userId String
user User @relation(fields: [userId], references: [id], onDelete: Cascade, map: "corr_user_idx")
entities Json // [{ type: EntityType, value: string }]
highestSeverity Severity
status CorrelationStatus @default(ACTIVE)
alertCount Int @default(0)
alerts NormalizedAlert[]
summary String?
resolvedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([userId, status])
@@index([createdAt]) @@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])
}

View File

@@ -7,7 +7,7 @@ async function main() {
create: { create: {
email: "dev@shieldai.local", email: "dev@shieldai.local",
name: "Dev User", name: "Dev User",
subscriptionTier: "PREMIUM", role: "user",
}, },
}); });

View File

@@ -1,7 +1,71 @@
// ============================================
// Consolidated @shieldai/db package
// ============================================
// Merges functionality from:
// - @shieldai/db (Prisma v6.2.0, FieldEncryptionService)
// - @shieldsai/shared-db (singleton pattern, type exports)
// ============================================
import { PrismaClient } from '@prisma/client'; import { PrismaClient } from '@prisma/client';
import { FieldEncryptionService } from './services/field-encryption.service'; import { FieldEncryptionService } from './services/field-encryption.service';
export const prisma = new PrismaClient(); // ============================================
// Singleton Pattern (from shared-db)
// ============================================
const globalForPrisma = globalThis as unknown as {
prisma: PrismaClient | undefined;
};
export const prisma =
globalForPrisma.prisma ??
new PrismaClient({
log: process.env.NODE_ENV === 'development' ? ['query', 'error', 'warn'] : ['error'],
});
if (process.env.NODE_ENV === 'development') {
globalForPrisma.prisma = prisma;
}
export default prisma; export default prisma;
// ============================================
// Services (from @shieldai/db)
// ============================================
export { FieldEncryptionService }; export { FieldEncryptionService };
// ============================================
// Type Exports (from shared-db)
// ============================================
export type {
User,
Account,
Session,
FamilyGroup,
FamilyGroupMember,
Subscription,
WatchlistItem,
Exposure,
Alert,
VoiceEnrollment,
VoiceAnalysis,
SpamFeedback,
SpamRule,
AuditLog,
KPISnapshot,
UserRole,
FamilyMemberRole,
SubscriptionTier,
SubscriptionStatus,
WatchlistType,
ExposureSource,
ExposureSeverity,
AlertType,
AlertSeverity,
AlertChannel,
FeedbackType,
RuleType,
RuleAction,
} from '@prisma/client';
export * as PrismaModels from '@prisma/client';
export type { PrismaClient }; export type { PrismaClient };

View File

@@ -1,4 +1,4 @@
import { prisma, SubscriptionTier } from '@shieldsai/shared-db'; import { prisma, SubscriptionTier } from '@shieldai/db';
import { Queue, Worker, Job } from 'bullmq'; import { Queue, Worker, Job } from 'bullmq';
import { Redis } from 'ioredis'; import { Redis } from 'ioredis';
import { tierConfig, getTierFeatures } from '@shieldsai/shared-billing'; import { tierConfig, getTierFeatures } from '@shieldsai/shared-billing';

View File

@@ -1,4 +1,4 @@
import { prisma, AlertType, AlertSeverity } from '@shieldsai/shared-db'; import { prisma, AlertType, AlertSeverity } from '@shieldai/db';
import { import {
NotificationService, NotificationService,
NotificationPriority, NotificationPriority,

View File

@@ -1,4 +1,4 @@
import { prisma, ExposureSource, ExposureSeverity, WatchlistType } from '@shieldsai/shared-db'; import { prisma, ExposureSource, ExposureSeverity, WatchlistType } from '@shieldai/db';
import { createHash } from 'crypto'; import { createHash } from 'crypto';
function hashIdentifier(identifier: string): string { function hashIdentifier(identifier: string): string {

View File

@@ -1,4 +1,4 @@
import { prisma, SubscriptionTier, SubscriptionStatus } from '@shieldsai/shared-db'; import { prisma, SubscriptionTier, SubscriptionStatus } from '@shieldai/db';
import { tierConfig } from '@shieldsai/shared-billing'; import { tierConfig } from '@shieldsai/shared-billing';
import { darkwatchScanQueue } from '@shieldsai/jobs'; import { darkwatchScanQueue } from '@shieldsai/jobs';
import { randomUUID } from 'crypto'; import { randomUUID } from 'crypto';

View File

@@ -1,4 +1,4 @@
import { prisma, WatchlistType } from '@shieldsai/shared-db'; import { prisma, WatchlistType } from '@shieldai/db';
import { createHash } from 'crypto'; import { createHash } from 'crypto';
export function normalizeValue(type: WatchlistType, value: string): string { export function normalizeValue(type: WatchlistType, value: string): string {

View File

@@ -1,4 +1,4 @@
import { prisma, ExposureSource, ExposureSeverity, WatchlistType, AlertType, AlertSeverity } from '@shieldsai/shared-db'; import { prisma, ExposureSource, ExposureSeverity, WatchlistType, AlertType, AlertSeverity } from '@shieldai/db';
import { createHash } from 'crypto'; import { createHash } from 'crypto';
import { mixpanelService, EventType } from '@shieldsai/shared-analytics'; import { mixpanelService, EventType } from '@shieldsai/shared-analytics';

View File

@@ -1,4 +1,4 @@
import { prisma, VoiceEnrollment, VoiceAnalysis } from '@shieldsai/shared-db'; import { prisma, VoiceEnrollment, VoiceAnalysis } from '@shieldai/db';
import { import {
voicePrintEnv, voicePrintEnv,
AnalysisJobStatus, AnalysisJobStatus,