import prisma from "@shieldai/db"; import { WatchListService } from "./watchlist/WatchListService"; import { HIBPService } from "./hibp/HIBPService"; import { MatchingEngine } from "./matching/MatchingEngine"; import { AlertPipeline } from "./alerts/AlertPipeline"; import { DataSource, ScanJobStatus } from "@shieldai/types"; export class ScanService { private watchList: WatchListService; private hibp: HIBPService; private matching: MatchingEngine; private alerts: AlertPipeline; constructor() { this.watchList = new WatchListService(); this.hibp = new HIBPService(process.env.HIBP_API_KEY); this.matching = new MatchingEngine(); this.alerts = new AlertPipeline(); } async runScan(userId: string, source?: DataSource): Promise { const job = await prisma.scanJob.create({ data: { userId, status: ScanJobStatus.RUNNING, source: source || DataSource.HIBP, }, }); let resultCount = 0; try { const activeItems = await this.watchList.getActiveItems(userId); for (const item of activeItems) { if (item.identifierType === "EMAIL") { const breaches = await this.hibp.checkEmail(item.identifierValue); for (const breach of breaches) { const severity = this.hibp.getSeverity(breach); const matched = await this.matching.matchExposure( item.id, DataSource.HIBP, breach.name, breach.breachDate, breach.dataClasses, severity, `Breach: ${breach.title}. Domain: ${breach.domain}. PwnCount: ${breach.pwnCount}` ); if (matched) { resultCount++; await this.alerts.createAlert(userId, matched.dataSource + ":" + breach.name, severity); } } } } await prisma.scanJob.update({ where: { id: job.id }, data: { status: ScanJobStatus.COMPLETED, resultCount, completedAt: new Date(), }, }); } catch (err) { const message = err instanceof Error ? err.message : String(err); await prisma.scanJob.update({ where: { id: job.id }, data: { status: ScanJobStatus.FAILED, errorMessage: message, completedAt: new Date(), }, }); } return resultCount; } async getScanHistory(userId: string, limit = 20) { return prisma.scanJob.findMany({ where: { userId }, orderBy: { createdAt: "desc" }, take: limit, }); } }