- 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>
98 lines
2.5 KiB
TypeScript
98 lines
2.5 KiB
TypeScript
import { prisma, WatchlistType } from '@shieldai/db';
|
|
import { createHash } from 'crypto';
|
|
|
|
export function normalizeValue(type: WatchlistType, value: string): string {
|
|
const trimmed = value.trim().toLowerCase();
|
|
switch (type) {
|
|
case WatchlistType.email:
|
|
return trimmed.replace(/\s+/g, '');
|
|
case WatchlistType.phoneNumber:
|
|
return trimmed.replace(/[\s\-\(\)]/g, '');
|
|
case WatchlistType.ssn:
|
|
return trimmed.replace(/-/g, '');
|
|
case WatchlistType.address:
|
|
return trimmed;
|
|
case WatchlistType.domain:
|
|
return trimmed.replace(/^https?:\/\//, '').replace(/\/.*$/, '');
|
|
default:
|
|
return trimmed;
|
|
}
|
|
}
|
|
|
|
export function hashValue(value: string): string {
|
|
return createHash('sha256').update(value).digest('hex');
|
|
}
|
|
|
|
export class WatchlistService {
|
|
async addItem(
|
|
subscriptionId: string,
|
|
type: WatchlistType,
|
|
value: string,
|
|
maxItems: number
|
|
) {
|
|
const normalized = normalizeValue(type, value);
|
|
const itemHash = hashValue(normalized);
|
|
|
|
const currentCount = await prisma.watchlistItem.count({
|
|
where: { subscriptionId, isActive: true },
|
|
});
|
|
|
|
if (currentCount >= maxItems) {
|
|
throw new Error(
|
|
`Watchlist limit reached (${maxItems} items). Upgrade your plan to add more.`
|
|
);
|
|
}
|
|
|
|
const existing = await prisma.watchlistItem.findFirst({
|
|
where: { subscriptionId, type, hash: itemHash },
|
|
});
|
|
|
|
if (existing) {
|
|
if (!existing.isActive) {
|
|
return prisma.watchlistItem.update({
|
|
where: { id: existing.id },
|
|
data: { isActive: true },
|
|
});
|
|
}
|
|
return existing;
|
|
}
|
|
|
|
return prisma.watchlistItem.create({
|
|
data: {
|
|
subscriptionId,
|
|
type,
|
|
value: normalized,
|
|
hash: itemHash,
|
|
},
|
|
});
|
|
}
|
|
|
|
async getItems(subscriptionId: string) {
|
|
return prisma.watchlistItem.findMany({
|
|
where: { subscriptionId, isActive: true },
|
|
orderBy: { createdAt: 'desc' },
|
|
});
|
|
}
|
|
|
|
async removeItem(id: string, subscriptionId: string) {
|
|
return prisma.watchlistItem.update({
|
|
where: { id },
|
|
data: { isActive: false },
|
|
});
|
|
}
|
|
|
|
async getActiveItemsForScan(subscriptionId: string) {
|
|
return prisma.watchlistItem.findMany({
|
|
where: { subscriptionId, isActive: true },
|
|
});
|
|
}
|
|
|
|
async getItemCount(subscriptionId: string) {
|
|
return prisma.watchlistItem.count({
|
|
where: { subscriptionId, isActive: true },
|
|
});
|
|
}
|
|
}
|
|
|
|
export const watchlistService = new WatchlistService();
|