FRE-4471: Scaffold DarkWatch MVP — monorepo, schema, services, API routes, tests
- Turborepo monorepo structure (packages: api, db, types, jobs; services: darkwatch) - Prisma schema: User, WatchListItem, Exposure, Alert, ScanJob models - WatchListService: CRUD with normalization, dedup, tier-based limits - HIBPService: API integration with severity scoring - MatchingEngine: exact-match with content hash dedup - AlertPipeline: dedup window, email notifications - ScanService: orchestrates watch list -> HIBP -> match -> alert flow - BullMQ job workers for scan and alert processing - Fastify API routes: watchlist, exposures, alerts, scan - Docker Compose: PostgreSQL 16 + Redis 7 - 15 unit tests passing - Implementation plan document uploaded
This commit is contained in:
152
packages/db/prisma/migrations/20260429132230_init/migration.sql
Normal file
152
packages/db/prisma/migrations/20260429132230_init/migration.sql
Normal file
@@ -0,0 +1,152 @@
|
||||
-- CreateEnum
|
||||
CREATE TYPE "SubscriptionTier" AS ENUM ('BASIC', 'PLUS', 'PREMIUM');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "IdentifierType" AS ENUM ('EMAIL', 'PHONE', 'SSN');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "WatchListStatus" AS ENUM ('ACTIVE', 'PAUSED');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "Severity" AS ENUM ('INFO', 'WARNING', 'CRITICAL');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "AlertChannel" AS ENUM ('EMAIL', 'PUSH', 'SMS');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "AlertStatus" AS ENUM ('PENDING', 'SENT', 'READ');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "ScanJobStatus" AS ENUM ('PENDING', 'RUNNING', 'COMPLETED', 'FAILED');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "DataSource" AS ENUM ('HIBP', 'SECURITY_TRAILS', 'CENSYS', 'SHODAN', 'HONEYPOT');
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "User" (
|
||||
"id" TEXT NOT NULL,
|
||||
"email" TEXT NOT NULL,
|
||||
"name" TEXT,
|
||||
"subscriptionTier" "SubscriptionTier" NOT NULL DEFAULT 'BASIC',
|
||||
"familyGroupId" TEXT,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "User_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "WatchListItem" (
|
||||
"id" TEXT NOT NULL,
|
||||
"userId" TEXT NOT NULL,
|
||||
"identifierType" "IdentifierType" NOT NULL,
|
||||
"identifierValue" TEXT NOT NULL,
|
||||
"identifierHash" TEXT NOT NULL,
|
||||
"status" "WatchListStatus" NOT NULL DEFAULT 'ACTIVE',
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "WatchListItem_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Exposure" (
|
||||
"id" TEXT NOT NULL,
|
||||
"watchListItemId" TEXT NOT NULL,
|
||||
"dataSource" "DataSource" NOT NULL,
|
||||
"breachName" TEXT NOT NULL,
|
||||
"exposedAt" TIMESTAMP(3) NOT NULL,
|
||||
"dataType" TEXT[],
|
||||
"severity" "Severity" NOT NULL,
|
||||
"details" TEXT,
|
||||
"contentHash" TEXT NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "Exposure_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Alert" (
|
||||
"id" TEXT NOT NULL,
|
||||
"userId" TEXT NOT NULL,
|
||||
"exposureId" TEXT NOT NULL,
|
||||
"severity" "Severity" NOT NULL,
|
||||
"channel" "AlertChannel" NOT NULL,
|
||||
"status" "AlertStatus" NOT NULL DEFAULT 'PENDING',
|
||||
"dedupKey" TEXT NOT NULL,
|
||||
"sentAt" TIMESTAMP(3),
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "Alert_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "ScanJob" (
|
||||
"id" TEXT NOT NULL,
|
||||
"userId" TEXT NOT NULL,
|
||||
"status" "ScanJobStatus" NOT NULL DEFAULT 'PENDING',
|
||||
"source" "DataSource",
|
||||
"resultCount" INTEGER NOT NULL DEFAULT 0,
|
||||
"errorMessage" TEXT,
|
||||
"completedAt" TIMESTAMP(3),
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "ScanJob_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "User_email_key" ON "User"("email");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "User_email_idx" ON "User"("email");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "WatchListItem_identifierHash_key" ON "WatchListItem"("identifierHash");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "WatchListItem_userId_idx" ON "WatchListItem"("userId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "WatchListItem_identifierHash_idx" ON "WatchListItem"("identifierHash");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "Exposure_contentHash_key" ON "Exposure"("contentHash");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "Exposure_watchListItemId_idx" ON "Exposure"("watchListItemId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "Exposure_contentHash_idx" ON "Exposure"("contentHash");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "Exposure_dataSource_idx" ON "Exposure"("dataSource");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "Alert_exposureId_key" ON "Alert"("exposureId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "Alert_userId_status_idx" ON "Alert"("userId", "status");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "Alert_dedupKey_idx" ON "Alert"("dedupKey");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "ScanJob_userId_status_idx" ON "ScanJob"("userId", "status");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "ScanJob_createdAt_idx" ON "ScanJob"("createdAt");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "WatchListItem" ADD CONSTRAINT "WatchListItem_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Exposure" ADD CONSTRAINT "Exposure_watchListItemId_fkey" FOREIGN KEY ("watchListItemId") REFERENCES "WatchListItem"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Alert" ADD CONSTRAINT "Alert_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Alert" ADD CONSTRAINT "Alert_exposureId_fkey" FOREIGN KEY ("exposureId") REFERENCES "Exposure"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "ScanJob" ADD CONSTRAINT "ScanJob_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
3
packages/db/prisma/migrations/migration_lock.toml
Normal file
3
packages/db/prisma/migrations/migration_lock.toml
Normal file
@@ -0,0 +1,3 @@
|
||||
# Please do not edit this file manually
|
||||
# It should be added in your version-control system (e.g., Git)
|
||||
provider = "postgresql"
|
||||
Reference in New Issue
Block a user