db rearch

This commit is contained in:
2026-05-25 20:50:45 -04:00
parent 3ccaeaa2e3
commit 89822dedb8
19 changed files with 339 additions and 324 deletions

View File

@@ -27,21 +27,21 @@ Tasks
- [x] 20 — Backend Router — Alert Correlation & Normalization Engine → `20-alert-correlation-router.md` - [x] 20 — Backend Router — Alert Correlation & Normalization Engine → `20-alert-correlation-router.md`
- [x] 21 — Backend Router — Security Report Generation → `21-report-generation-router.md` - [x] 21 — Backend Router — Security Report Generation → `21-report-generation-router.md`
- [x] 22 — Background Jobs — Scheduler, Scan Workers, and Reminders → `22-background-jobs.md` - [x] 22 — Background Jobs — Scheduler, Scan Workers, and Reminders → `22-background-jobs.md`
- [ ] 23 — Frontend Integration — Wire All Pages to tRPC APIs → `23-frontend-api-integration.md` - [x] 23 — Frontend Integration — Wire All Pages to tRPC APIs → `23-frontend-api-integration.md`
- [ ] 24 — Dashboard — Unified Widgets for All Services → `24-dashboard-widgets.md` - [x] 24 — Dashboard — Unified Widgets for All Services → `24-dashboard-widgets.md`
- [ ] 25 — Real-Time Alerts — WebSocket Push Notifications → `25-realtime-alerts.md` - [x] 25 — Real-Time Alerts — WebSocket Push Notifications → `25-realtime-alerts.md`
- [ ] 26 — Polish — Error Boundaries, Loading States, Skeletons, and Transitions → `26-error-loading-states.md` - [x] 26 — Polish — Error Boundaries, Loading States, Skeletons, and Transitions → `26-error-loading-states.md`
- [ ] 27 — Browser Extension — Move to browser-ext/ and Update API Client → `27-browser-extension-move.md` - [x] 27 — Browser Extension — Move to browser-ext/ and Update API Client → `27-browser-extension-move.md`
- [ ] 28 — iOS App — SwiftUI Foundation, Navigation, and Shared Theme → `28-ios-app-foundation.md` - [x] 28 — iOS App — SwiftUI Foundation, Navigation, and Shared Theme → `28-ios-app-foundation.md`
- [ ] 29 — iOS App — Design System Components Matching Web Theme → `29-ios-design-system.md` - [x] 29 — iOS App — Design System Components Matching Web Theme → `29-ios-design-system.md`
- [ ] 30 — iOS App — Authentication, Onboarding, and Account Setup → `30-ios-auth-onboarding.md` - [x] 30 — iOS App — Authentication, Onboarding, and Account Setup → `30-ios-auth-onboarding.md`
- [ ] 31 — iOS App — API Client, tRPC Bridge, and Offline Support → `31-ios-api-client.md` - [x] 31 — iOS App — API Client, tRPC Bridge, and Offline Support → `31-ios-api-client.md`
- [ ] 32 — iOS App — Dashboard and Service Screens (DarkWatch, VoicePrint, SpamShield, etc.) → `32-ios-service-screens.md` - [x] 32 — iOS App — Dashboard and Service Screens (DarkWatch, VoicePrint, SpamShield, etc.) → `32-ios-service-screens.md`
- [ ] 33 — iOS App — Push Notifications, Biometrics, Voice Enrollment, Camera → `33-ios-native-features.md` - [x] 33 — iOS App — Push Notifications, Biometrics, Voice Enrollment, Camera → `33-ios-native-features.md`
- [ ] 34 — Android App — Jetpack Compose Foundation, Navigation, and Shared Theme → `34-android-app-foundation.md` - [x] 34 — Android App — Jetpack Compose Foundation, Navigation, and Shared Theme → `34-android-app-foundation.md`
- [ ] 35 — Android App — Design System Components Matching Web Theme → `35-android-design-system.md` - [x] 35 — Android App — Design System Components Matching Web Theme → `35-android-design-system.md`
- [ ] 36 — Android App — Authentication, Onboarding, and Account Setup → `36-android-auth-onboarding.md` - [x] 36 — Android App — Authentication, Onboarding, and Account Setup → `36-android-auth-onboarding.md`
- [ ] 37 — Android App — API Client, tRPC Bridge, and Offline Support → `37-android-api-client.md` - [x] 37 — Android App — API Client, tRPC Bridge, and Offline Support → `37-android-api-client.md`
- [ ] 38 — Android App — Dashboard and Service Screens → `38-android-service-screens.md` - [ ] 38 — Android App — Dashboard and Service Screens → `38-android-service-screens.md`
- [ ] 39 — Android App — Push Notifications, Biometrics, Voice Enrollment, Call Screening → `39-android-native-features.md` - [ ] 39 — Android App — Push Notifications, Biometrics, Voice Enrollment, Call Screening → `39-android-native-features.md`
- [ ] 40 — Shared Mobile Assets — Icons, Colors, Typography, and Brand Guidelines → `40-shared-mobile-assets.md` - [ ] 40 — Shared Mobile Assets — Icons, Colors, Typography, and Brand Guidelines → `40-shared-mobile-assets.md`

View File

@@ -1,10 +1,10 @@
import { describe, it, expect } from "vitest"; import { describe, it, expect } from "vitest";
describe("db module", () => { describe("db module", () => {
it("exports db and pool", async () => { it("exports db and client", async () => {
const mod = await import("./index"); const mod = await import("./index");
expect(mod.db).toBeDefined(); expect(mod.db).toBeDefined();
expect(mod.pool).toBeDefined(); expect(mod.client).toBeDefined();
}); });
}); });

View File

@@ -12,9 +12,9 @@ export const db = drizzle(client, { schema });
export { client }; export { client };
process.on("SIGTERM", () => { process.on("SIGTERM", () => {
client.close().catch(() => process.exit(1)); client.close();
}); });
process.on("SIGINT", () => { process.on("SIGINT", () => {
client.close().catch(() => process.exit(1)); client.close();
}); });

View File

@@ -1,23 +1,22 @@
import { pgTable, text, timestamp, index, uuid, boolean } from "drizzle-orm/pg-core"; import { sqliteTable, text, integer, index } from "drizzle-orm/sqlite-core";
import { users } from "./auth"; import { users } from "./auth";
import { subscriptions } from "./subscription"; import { subscriptions } from "./subscription";
import { exposures } from "./darkwatch"; import { exposures } from "./darkwatch";
import { alertType, alertSeverity, alertChannel } from "./enums";
export const alerts = pgTable("alerts", { export const alerts = sqliteTable("alerts", {
id: uuid("id").defaultRandom().primaryKey(), id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
subscriptionId: uuid("subscription_id").notNull().references(() => subscriptions.id, { onDelete: "cascade" }), subscriptionId: text("subscription_id").notNull().references(() => subscriptions.id, { onDelete: "cascade" }),
userId: uuid("user_id").notNull().references(() => users.id, { onDelete: "cascade" }), userId: text("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
exposureId: uuid("exposure_id").references(() => exposures.id), exposureId: text("exposure_id").references(() => exposures.id),
type: alertType("type").notNull(), type: text("type").notNull(),
title: text("title").notNull(), title: text("title").notNull(),
message: text("message").notNull(), message: text("message").notNull(),
severity: alertSeverity("severity").default("info").notNull(), severity: text("severity").default("info").notNull(),
isRead: boolean("is_read").default(false).notNull(), isRead: integer("is_read", { mode: "boolean" }).default(false).notNull(),
readAt: timestamp("read_at", { withTimezone: true, mode: "date" }), readAt: integer("read_at", { mode: "timestamp_ms" }),
channel: alertChannel("channel").array().notNull(), channel: text("channel", { mode: "json" }).notNull().$defaultFn(() => []),
createdAt: timestamp("created_at", { withTimezone: true, mode: "date" }).defaultNow().notNull(), createdAt: integer("created_at", { mode: "timestamp_ms" }).defaultNow().notNull(),
updatedAt: timestamp("updated_at", { withTimezone: true, mode: "date" }).defaultNow().notNull().$onUpdate(() => new Date()), updatedAt: integer("updated_at", { mode: "timestamp_ms" }).defaultNow().notNull().$onUpdate(() => new Date()),
}, (table) => ({ }, (table) => ({
subscriptionIdIdx: index("alerts_subscription_id_idx").on(table.subscriptionId), subscriptionIdIdx: index("alerts_subscription_id_idx").on(table.subscriptionId),
userIdIdx: index("alerts_user_id_idx").on(table.userId), userIdIdx: index("alerts_user_id_idx").on(table.userId),

View File

@@ -1,16 +1,16 @@
import { pgTable, text, timestamp, index, uuid, jsonb, doublePrecision } from "drizzle-orm/pg-core"; import { sqliteTable, text, integer, real, index } from "drizzle-orm/sqlite-core";
export const auditLogs = pgTable("audit_logs", { export const auditLogs = sqliteTable("audit_logs", {
id: uuid("id").defaultRandom().primaryKey(), id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
userId: uuid("user_id"), userId: text("user_id"),
action: text("action").notNull(), action: text("action").notNull(),
resource: text("resource").notNull(), resource: text("resource").notNull(),
resourceId: text("resource_id"), resourceId: text("resource_id"),
changes: jsonb("changes"), changes: text("changes", { mode: "json" }),
metadata: jsonb("metadata"), metadata: text("metadata", { mode: "json" }),
ipAddress: text("ip_address"), ipAddress: text("ip_address"),
userAgent: text("user_agent"), userAgent: text("user_agent"),
createdAt: timestamp("created_at", { withTimezone: true, mode: "date" }).defaultNow().notNull(), createdAt: integer("created_at", { mode: "timestamp_ms" }).defaultNow().notNull(),
}, (table) => ({ }, (table) => ({
userIdIdx: index("audit_logs_user_id_idx").on(table.userId), userIdIdx: index("audit_logs_user_id_idx").on(table.userId),
actionIdx: index("audit_logs_action_idx").on(table.action), actionIdx: index("audit_logs_action_idx").on(table.action),
@@ -18,13 +18,13 @@ export const auditLogs = pgTable("audit_logs", {
createdAtIdx: index("audit_logs_created_at_idx").on(table.createdAt), createdAtIdx: index("audit_logs_created_at_idx").on(table.createdAt),
})); }));
export const kpiSnapshots = pgTable("kpi_snapshots", { export const kpiSnapshots = sqliteTable("kpi_snapshots", {
id: uuid("id").defaultRandom().primaryKey(), id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
date: timestamp("date", { withTimezone: true, mode: "date" }).notNull().unique(), date: integer("date", { mode: "timestamp_ms" }).notNull().unique(),
metricName: text("metric_name").notNull(), metricName: text("metric_name").notNull(),
metricValue: doublePrecision("metric_value").notNull(), metricValue: real("metric_value").notNull(),
metadata: jsonb("metadata"), metadata: text("metadata", { mode: "json" }),
createdAt: timestamp("created_at", { withTimezone: true, mode: "date" }).defaultNow().notNull(), createdAt: integer("created_at", { mode: "timestamp_ms" }).defaultNow().notNull(),
}, (table) => ({ }, (table) => ({
metricNameIdx: index("kpi_snapshots_metric_name_idx").on(table.metricName), metricNameIdx: index("kpi_snapshots_metric_name_idx").on(table.metricName),
dateIdx: index("kpi_snapshots_date_idx").on(table.date), dateIdx: index("kpi_snapshots_date_idx").on(table.date),

View File

@@ -1,18 +1,17 @@
import { pgTable, text, timestamp, uniqueIndex, index, uuid, jsonb, integer } from "drizzle-orm/pg-core"; import { sqliteTable, text, integer, uniqueIndex, index } from "drizzle-orm/sqlite-core";
import { users } from "./auth"; import { users } from "./auth";
import { alertSource, alertCategory, normalizedAlertSeverity, correlationStatus } from "./enums";
export const correlationGroups = pgTable("correlation_groups", { export const correlationGroups = sqliteTable("correlation_groups", {
id: uuid("id").defaultRandom().primaryKey(), id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
userId: uuid("user_id").notNull().references(() => users.id, { onDelete: "cascade" }), userId: text("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
entities: jsonb("entities").notNull(), entities: text("entities", { mode: "json" }).notNull(),
highestSeverity: normalizedAlertSeverity("highest_severity").notNull(), highestSeverity: text("highest_severity").notNull(),
status: correlationStatus("status").default("ACTIVE").notNull(), status: text("status").default("ACTIVE").notNull(),
alertCount: integer("alert_count").default(0).notNull(), alertCount: integer("alert_count").default(0).notNull(),
summary: text("summary"), summary: text("summary"),
resolvedAt: timestamp("resolved_at", { withTimezone: true, mode: "date" }), resolvedAt: integer("resolved_at", { mode: "timestamp_ms" }),
createdAt: timestamp("created_at", { withTimezone: true, mode: "date" }).defaultNow().notNull(), createdAt: integer("created_at", { mode: "timestamp_ms" }).defaultNow().notNull(),
updatedAt: timestamp("updated_at", { withTimezone: true, mode: "date" }).defaultNow().notNull().$onUpdate(() => new Date()), updatedAt: integer("updated_at", { mode: "timestamp_ms" }).defaultNow().notNull().$onUpdate(() => new Date()),
}, (table) => ({ }, (table) => ({
userIdIdx: index("correlation_groups_user_id_idx").on(table.userId), userIdIdx: index("correlation_groups_user_id_idx").on(table.userId),
statusIdx: index("correlation_groups_status_idx").on(table.status), statusIdx: index("correlation_groups_status_idx").on(table.status),
@@ -20,20 +19,20 @@ export const correlationGroups = pgTable("correlation_groups", {
createdAtIdx: index("correlation_groups_created_at_idx").on(table.createdAt), createdAtIdx: index("correlation_groups_created_at_idx").on(table.createdAt),
})); }));
export const normalizedAlerts = pgTable("normalized_alerts", { export const normalizedAlerts = sqliteTable("normalized_alerts", {
id: uuid("id").defaultRandom().primaryKey(), id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
source: alertSource("source").notNull(), source: text("source").notNull(),
category: alertCategory("category").notNull(), category: text("category").notNull(),
severity: normalizedAlertSeverity("severity").notNull(), severity: text("severity").notNull(),
userId: uuid("user_id").notNull().references(() => users.id, { onDelete: "cascade" }), userId: text("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
title: text("title").notNull(), title: text("title").notNull(),
description: text("description").notNull(), description: text("description").notNull(),
entities: jsonb("entities").notNull(), entities: text("entities", { mode: "json" }).notNull(),
sourceAlertId: text("source_alert_id").notNull(), sourceAlertId: text("source_alert_id").notNull(),
groupId: uuid("group_id").references(() => correlationGroups.id), groupId: text("group_id").references(() => correlationGroups.id),
payload: jsonb("payload"), payload: text("payload", { mode: "json" }),
createdAt: timestamp("created_at", { withTimezone: true, mode: "date" }).notNull(), createdAt: integer("created_at", { mode: "timestamp_ms" }).notNull(),
updatedAt: timestamp("updated_at", { withTimezone: true, mode: "date" }).defaultNow().notNull().$onUpdate(() => new Date()), updatedAt: integer("updated_at", { mode: "timestamp_ms" }).defaultNow().notNull().$onUpdate(() => new Date()),
}, (table) => ({ }, (table) => ({
sourceAlertIdUnique: uniqueIndex("normalized_alerts_source_alert_id_unique").on(table.sourceAlertId), sourceAlertIdUnique: uniqueIndex("normalized_alerts_source_alert_id_unique").on(table.sourceAlertId),
userIdIdx: index("normalized_alerts_user_id_idx").on(table.userId), userIdIdx: index("normalized_alerts_user_id_idx").on(table.userId),

View File

@@ -1,16 +1,15 @@
import { pgTable, text, timestamp, uniqueIndex, index, uuid, boolean, jsonb } from "drizzle-orm/pg-core"; import { sqliteTable, text, integer, uniqueIndex, index } from "drizzle-orm/sqlite-core";
import { subscriptions } from "./subscription"; import { subscriptions } from "./subscription";
import { watchlistType, exposureSource, exposureSeverity } from "./enums";
export const watchlistItems = pgTable("watchlist_items", { export const watchlistItems = sqliteTable("watchlist_items", {
id: uuid("id").defaultRandom().primaryKey(), id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
subscriptionId: uuid("subscription_id").notNull().references(() => subscriptions.id, { onDelete: "cascade" }), subscriptionId: text("subscription_id").notNull().references(() => subscriptions.id, { onDelete: "cascade" }),
type: watchlistType("type").notNull(), type: text("type").notNull(),
value: text("value").notNull(), value: text("value").notNull(),
hash: text("hash").notNull(), hash: text("hash").notNull(),
isActive: boolean("is_active").default(true).notNull(), isActive: integer("is_active", { mode: "boolean" }).default(true).notNull(),
createdAt: timestamp("created_at", { withTimezone: true, mode: "date" }).defaultNow().notNull(), createdAt: integer("created_at", { mode: "timestamp_ms" }).defaultNow().notNull(),
updatedAt: timestamp("updated_at", { withTimezone: true, mode: "date" }).defaultNow().notNull().$onUpdate(() => new Date()), updatedAt: integer("updated_at", { mode: "timestamp_ms" }).defaultNow().notNull().$onUpdate(() => new Date()),
}, (table) => ({ }, (table) => ({
subTypeHashUnique: uniqueIndex("watchlist_items_sub_type_hash_unique").on(table.subscriptionId, table.type, table.hash), subTypeHashUnique: uniqueIndex("watchlist_items_sub_type_hash_unique").on(table.subscriptionId, table.type, table.hash),
subscriptionIdIdx: index("watchlist_items_subscription_id_idx").on(table.subscriptionId), subscriptionIdIdx: index("watchlist_items_subscription_id_idx").on(table.subscriptionId),
@@ -18,20 +17,20 @@ export const watchlistItems = pgTable("watchlist_items", {
hashIdx: index("watchlist_items_hash_idx").on(table.hash), hashIdx: index("watchlist_items_hash_idx").on(table.hash),
})); }));
export const exposures = pgTable("exposures", { export const exposures = sqliteTable("exposures", {
id: uuid("id").defaultRandom().primaryKey(), id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
subscriptionId: uuid("subscription_id").notNull().references(() => subscriptions.id, { onDelete: "cascade" }), subscriptionId: text("subscription_id").notNull().references(() => subscriptions.id, { onDelete: "cascade" }),
watchlistItemId: uuid("watchlist_item_id").references(() => watchlistItems.id), watchlistItemId: text("watchlist_item_id").references(() => watchlistItems.id),
source: exposureSource("source").notNull(), source: text("source").notNull(),
dataType: watchlistType("data_type").notNull(), dataType: text("data_type").notNull(),
identifier: text("identifier").notNull(), identifier: text("identifier").notNull(),
identifierHash: text("identifier_hash").notNull(), identifierHash: text("identifier_hash").notNull(),
severity: exposureSeverity("severity").default("info").notNull(), severity: text("severity").default("info").notNull(),
metadata: jsonb("metadata"), metadata: text("metadata", { mode: "json" }),
isFirstTime: boolean("is_first_time").default(false).notNull(), isFirstTime: integer("is_first_time", { mode: "boolean" }).default(false).notNull(),
detectedAt: timestamp("detected_at", { withTimezone: true, mode: "date" }).notNull(), detectedAt: integer("detected_at", { mode: "timestamp_ms" }).notNull(),
createdAt: timestamp("created_at", { withTimezone: true, mode: "date" }).defaultNow().notNull(), createdAt: integer("created_at", { mode: "timestamp_ms" }).defaultNow().notNull(),
updatedAt: timestamp("updated_at", { withTimezone: true, mode: "date" }).defaultNow().notNull().$onUpdate(() => new Date()), updatedAt: integer("updated_at", { mode: "timestamp_ms" }).defaultNow().notNull().$onUpdate(() => new Date()),
}, (table) => ({ }, (table) => ({
subscriptionIdIdx: index("exposures_subscription_id_idx").on(table.subscriptionId), subscriptionIdIdx: index("exposures_subscription_id_idx").on(table.subscriptionId),
watchlistItemIdIdx: index("exposures_watchlist_item_id_idx").on(table.watchlistItemId), watchlistItemIdIdx: index("exposures_watchlist_item_id_idx").on(table.watchlistItemId),

View File

@@ -1,31 +1,61 @@
import { pgEnum } from "drizzle-orm/pg-core"; export const userRoleValues = ["user", "family_admin", "family_member", "support"] as const;
export const deviceTypeValues = ["mobile", "web", "desktop"] as const;
export const platformValues = ["ios", "android", "web"] as const;
export const familyMemberRoleValues = ["owner", "admin", "member"] as const;
export const subscriptionTierValues = ["basic", "plus", "premium"] as const;
export const subscriptionStatusValues = ["active", "past_due", "canceled", "unpaid", "trialing"] as const;
export const watchlistTypeValues = ["email", "phoneNumber", "ssn", "address", "domain"] as const;
export const exposureSourceValues = ["hibp", "securityTrails", "censys", "darkWebForum", "shodan", "honeypot"] as const;
export const exposureSeverityValues = ["info", "warning", "critical"] as const;
export const alertTypeValues = ["exposure_detected", "exposure_resolved", "scan_complete", "subscription_changed", "system_warning", "property_change"] as const;
export const alertSeverityValues = ["info", "warning", "critical"] as const;
export const alertChannelValues = ["email", "push", "sms"] as const;
export const detectionVerdictValues = ["NATURAL", "SYNTHETIC", "UNCERTAIN"] as const;
export const analysisTypeValues = ["SYNTHETIC_DETECTION", "VOICE_MATCH", "BATCH"] as const;
export const analysisJobStatusValues = ["PENDING", "RUNNING", "COMPLETED", "FAILED"] as const;
export const feedbackTypeValues = ["initial_detection", "user_confirmation", "user_rejection", "auto_learned"] as const;
export const ruleTypeValues = ["phoneNumber", "areaCode", "prefix", "pattern", "reputation"] as const;
export const ruleActionValues = ["block", "flag", "allow", "challenge"] as const;
export const alertSourceValues = ["DARKWATCH", "SPAMSHIELD", "VOICEPRINT", "CALL_ANALYSIS", "HOME_TITLE", "INFO_BROKER"] as const;
export const alertCategoryValues = ["BREACH_EXPOSURE", "SPAM_CALL", "SPAM_SMS", "SYNTHETIC_VOICE", "VOICE_MISMATCH", "CALL_ANOMALY", "CALL_QUALITY", "CALL_EVENT", "HOME_TITLE", "INFO_BROKER_LISTING", "INFO_BROKER_REMOVAL"] as const;
export const normalizedAlertSeverityValues = ["LOW", "INFO", "MEDIUM", "WARNING", "HIGH", "CRITICAL"] as const;
export const correlationStatusValues = ["ACTIVE", "RESOLVED", "FALSE_POSITIVE"] as const;
export const reportTypeValues = ["MONTHLY_PLUS", "ANNUAL_PREMIUM", "WEEKLY_DIGEST"] as const;
export const reportStatusValues = ["PENDING", "GENERATING", "COMPLETED", "FAILED", "DELIVERED"] as const;
export const propertyChangeTypeValues = ["tax_change", "deed_change", "ownership_transfer", "lien_filing", "metadata_change"] as const;
export const propertyChangeSeverityValues = ["info", "warning", "critical"] as const;
export const brokerCategoryValues = ["PEOPLE_SEARCH", "BACKGROUND_CHECK", "PUBLIC_RECORDS", "REVERSE_LOOKUP", "SOCIAL_MEDIA"] as const;
export const removalMethodValues = ["AUTOMATED", "MANUAL_FORM", "EMAIL", "PHONE", "MAIL", "NONE"] as const;
export const removalStatusValues = ["PENDING", "SUBMITTED", "IN_PROGRESS", "COMPLETED", "FAILED", "REJECTED", "CANCELLED"] as const;
export const invitationStatusValues = ["pending", "accepted", "expired", "cancelled"] as const;
export const userRole = pgEnum("user_role", ["user", "family_admin", "family_member", "support"]); export type UserRole = typeof userRoleValues[number];
export const deviceType = pgEnum("device_type", ["mobile", "web", "desktop"]); export type DeviceType = typeof deviceTypeValues[number];
export const platform = pgEnum("platform", ["ios", "android", "web"]); export type Platform = typeof platformValues[number];
export const familyMemberRole = pgEnum("family_member_role", ["owner", "admin", "member"]); export type FamilyMemberRole = typeof familyMemberRoleValues[number];
export const subscriptionTier = pgEnum("subscription_tier", ["basic", "plus", "premium"]); export type SubscriptionTier = typeof subscriptionTierValues[number];
export const subscriptionStatus = pgEnum("subscription_status", ["active", "past_due", "canceled", "unpaid", "trialing"]); export type SubscriptionStatus = typeof subscriptionStatusValues[number];
export const watchlistType = pgEnum("watchlist_type", ["email", "phoneNumber", "ssn", "address", "domain"]); export type WatchlistType = typeof watchlistTypeValues[number];
export const exposureSource = pgEnum("exposure_source", ["hibp", "securityTrails", "censys", "darkWebForum", "shodan", "honeypot"]); export type ExposureSource = typeof exposureSourceValues[number];
export const exposureSeverity = pgEnum("exposure_severity", ["info", "warning", "critical"]); export type ExposureSeverity = typeof exposureSeverityValues[number];
export const alertType = pgEnum("alert_type", ["exposure_detected", "exposure_resolved", "scan_complete", "subscription_changed", "system_warning", "property_change"]); export type AlertType = typeof alertTypeValues[number];
export const alertSeverity = pgEnum("alert_severity", ["info", "warning", "critical"]); export type AlertSeverity = typeof alertSeverityValues[number];
export const alertChannel = pgEnum("alert_channel", ["email", "push", "sms"]); export type AlertChannel = typeof alertChannelValues[number];
export const detectionVerdict = pgEnum("detection_verdict", ["NATURAL", "SYNTHETIC", "UNCERTAIN"]); export type DetectionVerdict = typeof detectionVerdictValues[number];
export const analysisType = pgEnum("analysis_type", ["SYNTHETIC_DETECTION", "VOICE_MATCH", "BATCH"]); export type AnalysisType = typeof analysisTypeValues[number];
export const analysisJobStatus = pgEnum("analysis_job_status", ["PENDING", "RUNNING", "COMPLETED", "FAILED"]); export type AnalysisJobStatus = typeof analysisJobStatusValues[number];
export const feedbackType = pgEnum("feedback_type", ["initial_detection", "user_confirmation", "user_rejection", "auto_learned"]); export type FeedbackType = typeof feedbackTypeValues[number];
export const ruleType = pgEnum("rule_type", ["phoneNumber", "areaCode", "prefix", "pattern", "reputation"]); export type RuleType = typeof ruleTypeValues[number];
export const ruleAction = pgEnum("rule_action", ["block", "flag", "allow", "challenge"]); export type RuleAction = typeof ruleActionValues[number];
export const alertSource = pgEnum("alert_source", ["DARKWATCH", "SPAMSHIELD", "VOICEPRINT", "CALL_ANALYSIS", "HOME_TITLE", "INFO_BROKER"]); export type AlertSource = typeof alertSourceValues[number];
export const alertCategory = pgEnum("alert_category", ["BREACH_EXPOSURE", "SPAM_CALL", "SPAM_SMS", "SYNTHETIC_VOICE", "VOICE_MISMATCH", "CALL_ANOMALY", "CALL_QUALITY", "CALL_EVENT", "HOME_TITLE", "INFO_BROKER_LISTING", "INFO_BROKER_REMOVAL"]); export type AlertCategory = typeof alertCategoryValues[number];
export const normalizedAlertSeverity = pgEnum("normalized_alert_severity", ["LOW", "INFO", "MEDIUM", "WARNING", "HIGH", "CRITICAL"]); export type NormalizedAlertSeverity = typeof normalizedAlertSeverityValues[number];
export const correlationStatus = pgEnum("correlation_status", ["ACTIVE", "RESOLVED", "FALSE_POSITIVE"]); export type CorrelationStatus = typeof correlationStatusValues[number];
export const reportType = pgEnum("report_type", ["MONTHLY_PLUS", "ANNUAL_PREMIUM", "WEEKLY_DIGEST"]); export type ReportType = typeof reportTypeValues[number];
export const reportStatus = pgEnum("report_status", ["PENDING", "GENERATING", "COMPLETED", "FAILED", "DELIVERED"]); export type ReportStatus = typeof reportStatusValues[number];
export const propertyChangeType = pgEnum("property_change_type", ["tax_change", "deed_change", "ownership_transfer", "lien_filing", "metadata_change"]); export type PropertyChangeType = typeof propertyChangeTypeValues[number];
export const propertyChangeSeverity = pgEnum("property_change_severity", ["info", "warning", "critical"]); export type PropertyChangeSeverity = typeof propertyChangeSeverityValues[number];
export const brokerCategory = pgEnum("broker_category", ["PEOPLE_SEARCH", "BACKGROUND_CHECK", "PUBLIC_RECORDS", "REVERSE_LOOKUP", "SOCIAL_MEDIA"]); export type BrokerCategory = typeof brokerCategoryValues[number];
export const removalMethod = pgEnum("removal_method", ["AUTOMATED", "MANUAL_FORM", "EMAIL", "PHONE", "MAIL", "NONE"]); export type RemovalMethod = typeof removalMethodValues[number];
export const removalStatus = pgEnum("removal_status", ["PENDING", "SUBMITTED", "IN_PROGRESS", "COMPLETED", "FAILED", "REJECTED", "CANCELLED"]); export type RemovalStatus = typeof removalStatusValues[number];
export type InvitationStatus = typeof invitationStatusValues[number];

View File

@@ -1,10 +1,9 @@
import { pgTable, text, timestamp, uniqueIndex, index, uuid, boolean, jsonb, doublePrecision, integer } from "drizzle-orm/pg-core"; import { sqliteTable, text, integer, real, uniqueIndex, index } from "drizzle-orm/sqlite-core";
import { subscriptions } from "./subscription"; import { subscriptions } from "./subscription";
import { propertyChangeType, propertyChangeSeverity } from "./enums";
export const propertyWatchlistItems = pgTable("property_watchlist_items", { export const propertyWatchlistItems = sqliteTable("property_watchlist_items", {
id: uuid("id").defaultRandom().primaryKey(), id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
subscriptionId: uuid("subscription_id").notNull().references(() => subscriptions.id, { onDelete: "cascade" }), subscriptionId: text("subscription_id").notNull().references(() => subscriptions.id, { onDelete: "cascade" }),
address: text("address").notNull(), address: text("address").notNull(),
parcelId: text("parcel_id"), parcelId: text("parcel_id"),
ownerName: text("owner_name"), ownerName: text("owner_name"),
@@ -12,11 +11,11 @@ export const propertyWatchlistItems = pgTable("property_watchlist_items", {
city: text("city").default(""), city: text("city").default(""),
state: text("state").default(""), state: text("state").default(""),
zipCode: text("zip_code").default(""), zipCode: text("zip_code").default(""),
latitude: doublePrecision("latitude"), latitude: real("latitude"),
longitude: doublePrecision("longitude"), longitude: real("longitude"),
isActive: boolean("is_active").default(true).notNull(), isActive: integer("is_active", { mode: "boolean" }).default(true).notNull(),
createdAt: timestamp("created_at", { withTimezone: true, mode: "date" }).defaultNow().notNull(), createdAt: integer("created_at", { mode: "timestamp_ms" }).defaultNow().notNull(),
updatedAt: timestamp("updated_at", { withTimezone: true, mode: "date" }).defaultNow().notNull().$onUpdate(() => new Date()), updatedAt: integer("updated_at", { mode: "timestamp_ms" }).defaultNow().notNull().$onUpdate(() => new Date()),
}, (table) => ({ }, (table) => ({
subParcelIdUnique: uniqueIndex("property_watchlist_items_sub_parcel_unique").on(table.subscriptionId, table.parcelId), subParcelIdUnique: uniqueIndex("property_watchlist_items_sub_parcel_unique").on(table.subscriptionId, table.parcelId),
subscriptionIdIdx: index("property_watchlist_items_subscription_id_idx").on(table.subscriptionId), subscriptionIdIdx: index("property_watchlist_items_subscription_id_idx").on(table.subscriptionId),
@@ -24,34 +23,34 @@ export const propertyWatchlistItems = pgTable("property_watchlist_items", {
addressIdx: index("property_watchlist_items_address_idx").on(table.address), addressIdx: index("property_watchlist_items_address_idx").on(table.address),
})); }));
export const propertySnapshots = pgTable("property_snapshots", { export const propertySnapshots = sqliteTable("property_snapshots", {
id: uuid("id").defaultRandom().primaryKey(), id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
propertyWatchlistItemId: uuid("property_watchlist_item_id").notNull().references(() => propertyWatchlistItems.id, { onDelete: "cascade" }), propertyWatchlistItemId: text("property_watchlist_item_id").notNull().references(() => propertyWatchlistItems.id, { onDelete: "cascade" }),
subscriptionId: uuid("subscription_id").notNull(), subscriptionId: text("subscription_id").notNull(),
capturedAt: timestamp("captured_at", { withTimezone: true, mode: "date" }).notNull(), capturedAt: integer("captured_at", { mode: "timestamp_ms" }).notNull(),
ownerName: text("owner_name").notNull(), ownerName: text("owner_name").notNull(),
address: jsonb("address").notNull(), address: text("address", { mode: "json" }).notNull(),
deedDate: text("deed_date"), deedDate: text("deed_date"),
taxId: text("tax_id"), taxId: text("tax_id"),
propertyType: text("property_type").default("residential").notNull(), propertyType: text("property_type").default("residential").notNull(),
taxAmount: doublePrecision("tax_amount"), taxAmount: real("tax_amount"),
lienCount: integer("lien_count").default(0).notNull(), lienCount: integer("lien_count").default(0).notNull(),
createdAt: timestamp("created_at", { withTimezone: true, mode: "date" }).defaultNow().notNull(), createdAt: integer("created_at", { mode: "timestamp_ms" }).defaultNow().notNull(),
updatedAt: timestamp("updated_at", { withTimezone: true, mode: "date" }).defaultNow().notNull().$onUpdate(() => new Date()), updatedAt: integer("updated_at", { mode: "timestamp_ms" }).defaultNow().notNull().$onUpdate(() => new Date()),
}, (table) => ({ }, (table) => ({
propertyWatchlistItemIdIdx: index("property_snapshots_property_watchlist_item_id_idx").on(table.propertyWatchlistItemId), propertyWatchlistItemIdIdx: index("property_snapshots_property_watchlist_item_id_idx").on(table.propertyWatchlistItemId),
subscriptionIdIdx: index("property_snapshots_subscription_id_idx").on(table.subscriptionId), subscriptionIdIdx: index("property_snapshots_subscription_id_idx").on(table.subscriptionId),
capturedAtIdx: index("property_snapshots_captured_at_idx").on(table.capturedAt), capturedAtIdx: index("property_snapshots_captured_at_idx").on(table.capturedAt),
})); }));
export const propertyChanges = pgTable("property_changes", { export const propertyChanges = sqliteTable("property_changes", {
id: uuid("id").defaultRandom().primaryKey(), id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
propertyWatchlistItemId: uuid("property_watchlist_item_id").notNull().references(() => propertyWatchlistItems.id, { onDelete: "cascade" }), propertyWatchlistItemId: text("property_watchlist_item_id").notNull().references(() => propertyWatchlistItems.id, { onDelete: "cascade" }),
snapshotId: uuid("snapshot_id").references(() => propertySnapshots.id), snapshotId: text("snapshot_id").references(() => propertySnapshots.id),
changeType: propertyChangeType("change_type").notNull(), changeType: text("change_type").notNull(),
severity: propertyChangeSeverity("severity").default("info").notNull(), severity: text("severity").default("info").notNull(),
details: jsonb("details"), details: text("details", { mode: "json" }),
detectedAt: timestamp("detected_at", { withTimezone: true, mode: "date" }).defaultNow().notNull(), detectedAt: integer("detected_at", { mode: "timestamp_ms" }).defaultNow().notNull(),
}, (table) => ({ }, (table) => ({
propertyWatchlistItemIdIdx: index("property_changes_property_watchlist_item_id_idx").on(table.propertyWatchlistItemId), propertyWatchlistItemIdIdx: index("property_changes_property_watchlist_item_id_idx").on(table.propertyWatchlistItemId),
snapshotIdIdx: index("property_changes_snapshot_id_idx").on(table.snapshotId), snapshotIdIdx: index("property_changes_snapshot_id_idx").on(table.snapshotId),

View File

@@ -1,18 +1,15 @@
import { pgTable, text, timestamp, uuid, pgEnum } from "drizzle-orm/pg-core"; import { sqliteTable, text, integer } from "drizzle-orm/sqlite-core";
import { users } from "./auth"; import { users } from "./auth";
import { familyGroups } from "./subscription"; import { familyGroups } from "./subscription";
import { familyMemberRole } from "./enums";
export const invitationStatus = pgEnum("invitation_status", ["pending", "accepted", "expired", "cancelled"]); export const invitations = sqliteTable("invitations", {
id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
export const invitations = pgTable("invitations", { groupId: text("group_id").notNull().references(() => familyGroups.id, { onDelete: "cascade" }),
id: uuid("id").defaultRandom().primaryKey(),
groupId: uuid("group_id").notNull().references(() => familyGroups.id, { onDelete: "cascade" }),
email: text("email").notNull(), email: text("email").notNull(),
role: familyMemberRole("role").default("member").notNull(), role: text("role").default("member").notNull(),
invitedBy: uuid("invited_by").notNull().references(() => users.id), invitedBy: text("invited_by").notNull().references(() => users.id),
status: invitationStatus("status").default("pending").notNull(), status: text("status").default("pending").notNull(),
expiresAt: timestamp("expires_at", { withTimezone: true, mode: "date" }).notNull(), expiresAt: integer("expires_at", { mode: "timestamp_ms" }).notNull(),
createdAt: timestamp("created_at", { withTimezone: true, mode: "date" }).defaultNow().notNull(), createdAt: integer("created_at", { mode: "timestamp_ms" }).defaultNow().notNull(),
updatedAt: timestamp("updated_at", { withTimezone: true, mode: "date" }).defaultNow().notNull().$onUpdate(() => new Date()), updatedAt: integer("updated_at", { mode: "timestamp_ms" }).defaultNow().notNull().$onUpdate(() => new Date()),
}); });

View File

@@ -1,41 +1,40 @@
import { pgTable, text, timestamp, index, uuid, boolean, integer, jsonb } from "drizzle-orm/pg-core"; import { sqliteTable, text, integer, index } from "drizzle-orm/sqlite-core";
import { subscriptionTier } from "./enums";
export const waitlistEntries = pgTable("waitlist_entries", { export const waitlistEntries = sqliteTable("waitlist_entries", {
id: uuid("id").defaultRandom().primaryKey(), id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
email: text("email").notNull(), email: text("email").notNull(),
name: text("name"), name: text("name"),
source: text("source"), source: text("source"),
tier: subscriptionTier("tier"), tier: text("tier"),
utmSource: text("utm_source"), utmSource: text("utm_source"),
utmMedium: text("utm_medium"), utmMedium: text("utm_medium"),
utmCampaign: text("utm_campaign"), utmCampaign: text("utm_campaign"),
metadata: jsonb("metadata"), metadata: text("metadata", { mode: "json" }),
convertedAt: timestamp("converted_at", { withTimezone: true, mode: "date" }), convertedAt: integer("converted_at", { mode: "timestamp_ms" }),
convertedToUserId: text("converted_to_user_id"), convertedToUserId: text("converted_to_user_id"),
convertedToSubscriptionId: text("converted_to_subscription_id"), convertedToSubscriptionId: text("converted_to_subscription_id"),
createdAt: timestamp("created_at", { withTimezone: true, mode: "date" }).defaultNow().notNull(), createdAt: integer("created_at", { mode: "timestamp_ms" }).defaultNow().notNull(),
updatedAt: timestamp("updated_at", { withTimezone: true, mode: "date" }).defaultNow().notNull().$onUpdate(() => new Date()), updatedAt: integer("updated_at", { mode: "timestamp_ms" }).defaultNow().notNull().$onUpdate(() => new Date()),
}, (table) => ({ }, (table) => ({
emailIdx: index("waitlist_entries_email_idx").on(table.email), emailIdx: index("waitlist_entries_email_idx").on(table.email),
sourceIdx: index("waitlist_entries_source_idx").on(table.source), sourceIdx: index("waitlist_entries_source_idx").on(table.source),
createdAtIdx: index("waitlist_entries_created_at_idx").on(table.createdAt), createdAtIdx: index("waitlist_entries_created_at_idx").on(table.createdAt),
})); }));
export const blogPosts = pgTable("blog_posts", { export const blogPosts = sqliteTable("blog_posts", {
id: uuid("id").defaultRandom().primaryKey(), id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
slug: text("slug").notNull().unique(), slug: text("slug").notNull().unique(),
title: text("title").notNull(), title: text("title").notNull(),
excerpt: text("excerpt"), excerpt: text("excerpt"),
content: text("content").notNull(), content: text("content").notNull(),
authorName: text("author_name"), authorName: text("author_name"),
coverImageUrl: text("cover_image_url"), coverImageUrl: text("cover_image_url"),
tags: text("tags").array().notNull(), tags: text("tags", { mode: "json" }).notNull().$defaultFn(() => []),
published: boolean("published").default(false).notNull(), published: integer("published", { mode: "boolean" }).default(false).notNull(),
publishedAt: timestamp("published_at", { withTimezone: true, mode: "date" }), publishedAt: integer("published_at", { mode: "timestamp_ms" }),
viewCount: integer("view_count").default(0).notNull(), viewCount: integer("view_count").default(0).notNull(),
createdAt: timestamp("created_at", { withTimezone: true, mode: "date" }).defaultNow().notNull(), createdAt: integer("created_at", { mode: "timestamp_ms" }).defaultNow().notNull(),
updatedAt: timestamp("updated_at", { withTimezone: true, mode: "date" }).defaultNow().notNull().$onUpdate(() => new Date()), updatedAt: integer("updated_at", { mode: "timestamp_ms" }).defaultNow().notNull().$onUpdate(() => new Date()),
}, (table) => ({ }, (table) => ({
slugIdx: index("blog_posts_slug_idx").on(table.slug), slugIdx: index("blog_posts_slug_idx").on(table.slug),
publishedIdx: index("blog_posts_published_idx").on(table.published, table.publishedAt), publishedIdx: index("blog_posts_published_idx").on(table.published, table.publishedAt),

View File

@@ -1,15 +1,15 @@
import { pgTable, uuid, boolean, timestamp } from "drizzle-orm/pg-core"; import { sqliteTable, text, integer } from "drizzle-orm/sqlite-core";
import { users } from "./auth"; import { users } from "./auth";
export const notificationPreferences = pgTable("notification_preferences", { export const notificationPreferences = sqliteTable("notification_preferences", {
id: uuid("id").defaultRandom().primaryKey(), id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
userId: uuid("user_id") userId: text("user_id")
.notNull() .notNull()
.references(() => users.id, { onDelete: "cascade" }) .references(() => users.id, { onDelete: "cascade" })
.unique(), .unique(),
emailEnabled: boolean("email_enabled").default(true).notNull(), emailEnabled: integer("email_enabled", { mode: "boolean" }).default(true).notNull(),
pushEnabled: boolean("push_enabled").default(true).notNull(), pushEnabled: integer("push_enabled", { mode: "boolean" }).default(true).notNull(),
smsEnabled: boolean("sms_enabled").default(true).notNull(), smsEnabled: integer("sms_enabled", { mode: "boolean" }).default(true).notNull(),
createdAt: timestamp("created_at", { withTimezone: true, mode: "date" }).defaultNow().notNull(), createdAt: integer("created_at", { mode: "timestamp_ms" }).defaultNow().notNull(),
updatedAt: timestamp("updated_at", { withTimezone: true, mode: "date" }).defaultNow().notNull().$onUpdate(() => new Date()), updatedAt: integer("updated_at", { mode: "timestamp_ms" }).defaultNow().notNull().$onUpdate(() => new Date()),
}); });

View File

@@ -1,42 +1,41 @@
import { pgTable, text, timestamp, index, uuid, boolean, jsonb, integer } from "drizzle-orm/pg-core"; import { sqliteTable, text, integer, index } from "drizzle-orm/sqlite-core";
import { subscriptions } from "./subscription"; import { subscriptions } from "./subscription";
import { brokerCategory, removalMethod, removalStatus } from "./enums";
export const infoBrokers = pgTable("info_brokers", { export const infoBrokers = sqliteTable("info_brokers", {
id: uuid("id").defaultRandom().primaryKey(), id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
name: text("name").notNull(), name: text("name").notNull(),
domain: text("domain").notNull().unique(), domain: text("domain").notNull().unique(),
category: brokerCategory("category").notNull(), category: text("category").notNull(),
removalMethod: removalMethod("removal_method").notNull(), removalMethod: text("removal_method").notNull(),
removalUrl: text("removal_url"), removalUrl: text("removal_url"),
requiresAccount: boolean("requires_account").default(false).notNull(), requiresAccount: integer("requires_account", { mode: "boolean" }).default(false).notNull(),
requiresVerification: boolean("requires_verification").default(false).notNull(), requiresVerification: integer("requires_verification", { mode: "boolean" }).default(false).notNull(),
estimatedDays: integer("estimated_days").default(14).notNull(), estimatedDays: integer("estimated_days").default(14).notNull(),
isActive: boolean("is_active").default(true).notNull(), isActive: integer("is_active", { mode: "boolean" }).default(true).notNull(),
createdAt: timestamp("created_at", { withTimezone: true, mode: "date" }).defaultNow().notNull(), createdAt: integer("created_at", { mode: "timestamp_ms" }).defaultNow().notNull(),
updatedAt: timestamp("updated_at", { withTimezone: true, mode: "date" }).defaultNow().notNull().$onUpdate(() => new Date()), updatedAt: integer("updated_at", { mode: "timestamp_ms" }).defaultNow().notNull().$onUpdate(() => new Date()),
}, (table) => ({ }, (table) => ({
categoryIdx: index("info_brokers_category_idx").on(table.category), categoryIdx: index("info_brokers_category_idx").on(table.category),
isActiveIdx: index("info_brokers_is_active_idx").on(table.isActive), isActiveIdx: index("info_brokers_is_active_idx").on(table.isActive),
removalMethodIdx: index("info_brokers_removal_method_idx").on(table.removalMethod), removalMethodIdx: index("info_brokers_removal_method_idx").on(table.removalMethod),
})); }));
export const removalRequests = pgTable("removal_requests", { export const removalRequests = sqliteTable("removal_requests", {
id: uuid("id").defaultRandom().primaryKey(), id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
subscriptionId: uuid("subscription_id").notNull().references(() => subscriptions.id, { onDelete: "cascade" }), subscriptionId: text("subscription_id").notNull().references(() => subscriptions.id, { onDelete: "cascade" }),
brokerId: uuid("broker_id").notNull().references(() => infoBrokers.id), brokerId: text("broker_id").notNull().references(() => infoBrokers.id),
status: removalStatus("status").default("PENDING").notNull(), status: text("status").default("PENDING").notNull(),
personalInfo: jsonb("personal_info").notNull(), personalInfo: text("personal_info", { mode: "json" }).notNull(),
method: removalMethod("method").notNull(), method: text("method").notNull(),
attempts: integer("attempts").default(0).notNull(), attempts: integer("attempts").default(0).notNull(),
nextRetryAt: timestamp("next_retry_at", { withTimezone: true, mode: "date" }), nextRetryAt: integer("next_retry_at", { mode: "timestamp_ms" }),
submittedAt: timestamp("submitted_at", { withTimezone: true, mode: "date" }), submittedAt: integer("submitted_at", { mode: "timestamp_ms" }),
completedAt: timestamp("completed_at", { withTimezone: true, mode: "date" }), completedAt: integer("completed_at", { mode: "timestamp_ms" }),
error: text("error"), error: text("error"),
notes: text("notes"), notes: text("notes"),
metadata: jsonb("metadata"), metadata: text("metadata", { mode: "json" }),
createdAt: timestamp("created_at", { withTimezone: true, mode: "date" }).defaultNow().notNull(), createdAt: integer("created_at", { mode: "timestamp_ms" }).defaultNow().notNull(),
updatedAt: timestamp("updated_at", { withTimezone: true, mode: "date" }).defaultNow().notNull().$onUpdate(() => new Date()), updatedAt: integer("updated_at", { mode: "timestamp_ms" }).defaultNow().notNull().$onUpdate(() => new Date()),
}, (table) => ({ }, (table) => ({
subscriptionIdIdx: index("removal_requests_subscription_id_idx").on(table.subscriptionId), subscriptionIdIdx: index("removal_requests_subscription_id_idx").on(table.subscriptionId),
brokerIdIdx: index("removal_requests_broker_id_idx").on(table.brokerId), brokerIdIdx: index("removal_requests_broker_id_idx").on(table.brokerId),
@@ -45,19 +44,19 @@ export const removalRequests = pgTable("removal_requests", {
subscriptionIdStatusIdx: index("removal_requests_sub_id_status_idx").on(table.subscriptionId, table.status), subscriptionIdStatusIdx: index("removal_requests_sub_id_status_idx").on(table.subscriptionId, table.status),
})); }));
export const brokerListings = pgTable("broker_listings", { export const brokerListings = sqliteTable("broker_listings", {
id: uuid("id").defaultRandom().primaryKey(), id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
subscriptionId: uuid("subscription_id").notNull().references(() => subscriptions.id, { onDelete: "cascade" }), subscriptionId: text("subscription_id").notNull().references(() => subscriptions.id, { onDelete: "cascade" }),
brokerId: uuid("broker_id").notNull(), brokerId: text("broker_id").notNull(),
removalRequestId: uuid("removal_request_id").references(() => removalRequests.id), removalRequestId: text("removal_request_id").references(() => removalRequests.id),
url: text("url").notNull(), url: text("url").notNull(),
dataFound: jsonb("data_found").notNull(), dataFound: text("data_found", { mode: "json" }).notNull(),
screenshotUrl: text("screenshot_url"), screenshotUrl: text("screenshot_url"),
isRemoved: boolean("is_removed").default(false).notNull(), isRemoved: integer("is_removed", { mode: "boolean" }).default(false).notNull(),
removedAt: timestamp("removed_at", { withTimezone: true, mode: "date" }), removedAt: integer("removed_at", { mode: "timestamp_ms" }),
scannedAt: timestamp("scanned_at", { withTimezone: true, mode: "date" }).defaultNow().notNull(), scannedAt: integer("scanned_at", { mode: "timestamp_ms" }).defaultNow().notNull(),
createdAt: timestamp("created_at", { withTimezone: true, mode: "date" }).defaultNow().notNull(), createdAt: integer("created_at", { mode: "timestamp_ms" }).defaultNow().notNull(),
updatedAt: timestamp("updated_at", { withTimezone: true, mode: "date" }).defaultNow().notNull().$onUpdate(() => new Date()), updatedAt: integer("updated_at", { mode: "timestamp_ms" }).defaultNow().notNull().$onUpdate(() => new Date()),
}, (table) => ({ }, (table) => ({
subscriptionIdIdx: index("broker_listings_subscription_id_idx").on(table.subscriptionId), subscriptionIdIdx: index("broker_listings_subscription_id_idx").on(table.subscriptionId),
brokerIdIdx: index("broker_listings_broker_id_idx").on(table.brokerId), brokerIdIdx: index("broker_listings_broker_id_idx").on(table.brokerId),

View File

@@ -1,17 +1,16 @@
import { pgTable, text, timestamp, index, uuid, boolean } from "drizzle-orm/pg-core"; import { sqliteTable, text, integer, index } from "drizzle-orm/sqlite-core";
import { users } from "./auth"; import { users } from "./auth";
import { reportType } from "./enums";
export const reportSchedules = pgTable("report_schedules", { export const reportSchedules = sqliteTable("report_schedules", {
id: uuid("id").defaultRandom().primaryKey(), id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
userId: uuid("user_id").notNull().references(() => users.id, { onDelete: "cascade" }), userId: text("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
enabled: boolean("enabled").default(true).notNull(), enabled: integer("enabled", { mode: "boolean" }).default(true).notNull(),
frequency: text("frequency").notNull(), frequency: text("frequency").notNull(),
reportType: reportType("report_type").notNull(), reportType: text("report_type").notNull(),
lastGeneratedAt: timestamp("last_generated_at", { withTimezone: true, mode: "date" }), lastGeneratedAt: integer("last_generated_at", { mode: "timestamp_ms" }),
nextScheduledAt: timestamp("next_scheduled_at", { withTimezone: true, mode: "date" }), nextScheduledAt: integer("next_scheduled_at", { mode: "timestamp_ms" }),
createdAt: timestamp("created_at", { withTimezone: true, mode: "date" }).defaultNow().notNull(), createdAt: integer("created_at", { mode: "timestamp_ms" }).defaultNow().notNull(),
updatedAt: timestamp("updated_at", { withTimezone: true, mode: "date" }).defaultNow().notNull().$onUpdate(() => new Date()), updatedAt: integer("updated_at", { mode: "timestamp_ms" }).defaultNow().notNull().$onUpdate(() => new Date()),
}, (table) => ({ }, (table) => ({
userIdIdx: index("report_schedules_user_id_idx").on(table.userId), userIdIdx: index("report_schedules_user_id_idx").on(table.userId),
enabledIdx: index("report_schedules_enabled_idx").on(table.enabled), enabledIdx: index("report_schedules_enabled_idx").on(table.enabled),

View File

@@ -1,25 +1,24 @@
import { pgTable, text, timestamp, index, uuid, jsonb } from "drizzle-orm/pg-core"; import { sqliteTable, text, integer, index } from "drizzle-orm/sqlite-core";
import { users } from "./auth"; import { users } from "./auth";
import { reportType, reportStatus } from "./enums";
export const securityReports = pgTable("security_reports", { export const securityReports = sqliteTable("security_reports", {
id: uuid("id").defaultRandom().primaryKey(), id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
userId: uuid("user_id").notNull().references(() => users.id, { onDelete: "cascade" }), userId: text("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
subscriptionId: uuid("subscription_id").notNull(), subscriptionId: text("subscription_id").notNull(),
reportType: reportType("report_type").notNull(), reportType: text("report_type").notNull(),
status: reportStatus("status").default("PENDING").notNull(), status: text("status").default("PENDING").notNull(),
periodStart: timestamp("period_start", { withTimezone: true, mode: "date" }).notNull(), periodStart: integer("period_start", { mode: "timestamp_ms" }).notNull(),
periodEnd: timestamp("period_end", { withTimezone: true, mode: "date" }).notNull(), periodEnd: integer("period_end", { mode: "timestamp_ms" }).notNull(),
title: text("title").notNull(), title: text("title").notNull(),
summary: text("summary"), summary: text("summary"),
htmlContent: text("html_content"), htmlContent: text("html_content"),
pdfUrl: text("pdf_url"), pdfUrl: text("pdf_url"),
dataPayload: jsonb("data_payload"), dataPayload: text("data_payload", { mode: "json" }),
error: text("error"), error: text("error"),
scheduledFor: timestamp("scheduled_for", { withTimezone: true, mode: "date" }), scheduledFor: integer("scheduled_for", { mode: "timestamp_ms" }),
deliveredAt: timestamp("delivered_at", { withTimezone: true, mode: "date" }), deliveredAt: integer("delivered_at", { mode: "timestamp_ms" }),
createdAt: timestamp("created_at", { withTimezone: true, mode: "date" }).defaultNow().notNull(), createdAt: integer("created_at", { mode: "timestamp_ms" }).defaultNow().notNull(),
updatedAt: timestamp("updated_at", { withTimezone: true, mode: "date" }).defaultNow().notNull().$onUpdate(() => new Date()), updatedAt: integer("updated_at", { mode: "timestamp_ms" }).defaultNow().notNull().$onUpdate(() => new Date()),
}, (table) => ({ }, (table) => ({
userIdIdx: index("security_reports_user_id_idx").on(table.userId), userIdIdx: index("security_reports_user_id_idx").on(table.userId),
subscriptionIdIdx: index("security_reports_subscription_id_idx").on(table.subscriptionId), subscriptionIdIdx: index("security_reports_subscription_id_idx").on(table.subscriptionId),

View File

@@ -1,35 +1,34 @@
import { pgTable, text, timestamp, index, uuid, boolean, jsonb, doublePrecision, integer } from "drizzle-orm/pg-core"; import { sqliteTable, text, integer, real, index } from "drizzle-orm/sqlite-core";
import { users } from "./auth"; import { users } from "./auth";
import { feedbackType, ruleType, ruleAction } from "./enums";
export const spamFeedback = pgTable("spam_feedback", { export const spamFeedback = sqliteTable("spam_feedback", {
id: uuid("id").defaultRandom().primaryKey(), id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
userId: uuid("user_id").notNull().references(() => users.id, { onDelete: "cascade" }), userId: text("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
phoneNumber: text("phone_number").notNull(), phoneNumber: text("phone_number").notNull(),
phoneNumberHash: text("phone_number_hash").notNull(), phoneNumberHash: text("phone_number_hash").notNull(),
isSpam: boolean("is_spam").notNull(), isSpam: integer("is_spam", { mode: "boolean" }).notNull(),
confidence: doublePrecision("confidence"), confidence: real("confidence"),
feedbackType: feedbackType("feedback_type").notNull(), feedbackType: text("feedback_type").notNull(),
metadata: jsonb("metadata"), metadata: text("metadata", { mode: "json" }),
createdAt: timestamp("created_at", { withTimezone: true, mode: "date" }).defaultNow().notNull(), createdAt: integer("created_at", { mode: "timestamp_ms" }).defaultNow().notNull(),
updatedAt: timestamp("updated_at", { withTimezone: true, mode: "date" }).defaultNow().notNull().$onUpdate(() => new Date()), updatedAt: integer("updated_at", { mode: "timestamp_ms" }).defaultNow().notNull().$onUpdate(() => new Date()),
}, (table) => ({ }, (table) => ({
userIdIdx: index("spam_feedback_user_id_idx").on(table.userId), userIdIdx: index("spam_feedback_user_id_idx").on(table.userId),
phoneNumberHashIdx: index("spam_feedback_phone_number_hash_idx").on(table.phoneNumberHash), phoneNumberHashIdx: index("spam_feedback_phone_number_hash_idx").on(table.phoneNumberHash),
isSpamIdx: index("spam_feedback_is_spam_idx").on(table.isSpam), isSpamIdx: index("spam_feedback_is_spam_idx").on(table.isSpam),
})); }));
export const spamRules = pgTable("spam_rules", { export const spamRules = sqliteTable("spam_rules", {
id: uuid("id").defaultRandom().primaryKey(), id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
userId: uuid("user_id").references(() => users.id, { onDelete: "cascade" }), userId: text("user_id").references(() => users.id, { onDelete: "cascade" }),
isGlobal: boolean("is_global").default(false).notNull(), isGlobal: integer("is_global", { mode: "boolean" }).default(false).notNull(),
ruleType: ruleType("rule_type").notNull(), ruleType: text("rule_type").notNull(),
pattern: text("pattern").notNull(), pattern: text("pattern").notNull(),
action: ruleAction("action").notNull(), action: text("action").notNull(),
priority: integer("priority").default(0).notNull(), priority: integer("priority").default(0).notNull(),
isActive: boolean("is_active").default(true).notNull(), isActive: integer("is_active", { mode: "boolean" }).default(true).notNull(),
createdAt: timestamp("created_at", { withTimezone: true, mode: "date" }).defaultNow().notNull(), createdAt: integer("created_at", { mode: "timestamp_ms" }).defaultNow().notNull(),
updatedAt: timestamp("updated_at", { withTimezone: true, mode: "date" }).defaultNow().notNull().$onUpdate(() => new Date()), updatedAt: integer("updated_at", { mode: "timestamp_ms" }).defaultNow().notNull().$onUpdate(() => new Date()),
}, (table) => ({ }, (table) => ({
userIdIdx: index("spam_rules_user_id_idx").on(table.userId), userIdIdx: index("spam_rules_user_id_idx").on(table.userId),
isGlobalIdx: index("spam_rules_is_global_idx").on(table.isGlobal), isGlobalIdx: index("spam_rules_is_global_idx").on(table.isGlobal),

View File

@@ -1,44 +1,43 @@
import { pgTable, text, timestamp, uniqueIndex, index, uuid, boolean } from "drizzle-orm/pg-core"; import { sqliteTable, text, integer, uniqueIndex, index } from "drizzle-orm/sqlite-core";
import { users } from "./auth"; import { users } from "./auth";
import { familyMemberRole, subscriptionTier, subscriptionStatus } from "./enums";
export const familyGroups = pgTable("family_groups", { export const familyGroups = sqliteTable("family_groups", {
id: uuid("id").defaultRandom().primaryKey(), id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
name: text("name").notNull(), name: text("name").notNull(),
ownerId: uuid("owner_id").notNull().references(() => users.id), ownerId: text("owner_id").notNull().references(() => users.id),
createdAt: timestamp("created_at", { withTimezone: true, mode: "date" }).defaultNow().notNull(), createdAt: integer("created_at", { mode: "timestamp_ms" }).defaultNow().notNull(),
updatedAt: timestamp("updated_at", { withTimezone: true, mode: "date" }).defaultNow().notNull().$onUpdate(() => new Date()), updatedAt: integer("updated_at", { mode: "timestamp_ms" }).defaultNow().notNull().$onUpdate(() => new Date()),
}, (table) => ({ }, (table) => ({
ownerIdIdx: index("family_groups_owner_id_idx").on(table.ownerId), ownerIdIdx: index("family_groups_owner_id_idx").on(table.ownerId),
nameIdx: index("family_groups_name_idx").on(table.name), nameIdx: index("family_groups_name_idx").on(table.name),
})); }));
export const familyGroupMembers = pgTable("family_group_members", { export const familyGroupMembers = sqliteTable("family_group_members", {
id: uuid("id").defaultRandom().primaryKey(), id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
groupId: uuid("group_id").notNull().references(() => familyGroups.id, { onDelete: "cascade" }), groupId: text("group_id").notNull().references(() => familyGroups.id, { onDelete: "cascade" }),
userId: uuid("user_id").notNull().references(() => users.id, { onDelete: "cascade" }), userId: text("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
role: familyMemberRole("role").default("member").notNull(), role: text("role").default("member").notNull(),
joinedAt: timestamp("joined_at", { withTimezone: true, mode: "date" }).defaultNow().notNull(), joinedAt: integer("joined_at", { mode: "timestamp_ms" }).defaultNow().notNull(),
createdAt: timestamp("created_at", { withTimezone: true, mode: "date" }).defaultNow().notNull(), createdAt: integer("created_at", { mode: "timestamp_ms" }).defaultNow().notNull(),
updatedAt: timestamp("updated_at", { withTimezone: true, mode: "date" }).defaultNow().notNull().$onUpdate(() => new Date()), updatedAt: integer("updated_at", { mode: "timestamp_ms" }).defaultNow().notNull().$onUpdate(() => new Date()),
}, (table) => ({ }, (table) => ({
groupUserUnique: uniqueIndex("family_group_members_group_user_unique").on(table.groupId, table.userId), groupUserUnique: uniqueIndex("family_group_members_group_user_unique").on(table.groupId, table.userId),
groupIdIdx: index("family_group_members_group_id_idx").on(table.groupId), groupIdIdx: index("family_group_members_group_id_idx").on(table.groupId),
userIdIdx: index("family_group_members_user_id_idx").on(table.userId), userIdIdx: index("family_group_members_user_id_idx").on(table.userId),
})); }));
export const subscriptions = pgTable("subscriptions", { export const subscriptions = sqliteTable("subscriptions", {
id: uuid("id").defaultRandom().primaryKey(), id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
userId: uuid("user_id").notNull().references(() => users.id, { onDelete: "cascade" }), userId: text("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
familyGroupId: uuid("family_group_id").references(() => familyGroups.id), familyGroupId: text("family_group_id").references(() => familyGroups.id),
stripeId: text("stripe_id").unique(), stripeId: text("stripe_id").unique(),
tier: subscriptionTier("tier").default("basic").notNull(), tier: text("tier").default("basic").notNull(),
status: subscriptionStatus("status").default("active").notNull(), status: text("status").default("active").notNull(),
currentPeriodStart: timestamp("current_period_start", { withTimezone: true, mode: "date" }).notNull(), currentPeriodStart: integer("current_period_start", { mode: "timestamp_ms" }).notNull(),
currentPeriodEnd: timestamp("current_period_end", { withTimezone: true, mode: "date" }).notNull(), currentPeriodEnd: integer("current_period_end", { mode: "timestamp_ms" }).notNull(),
cancelAtPeriodEnd: boolean("cancel_at_period_end").default(false).notNull(), cancelAtPeriodEnd: integer("cancel_at_period_end", { mode: "boolean" }).default(false).notNull(),
createdAt: timestamp("created_at", { withTimezone: true, mode: "date" }).defaultNow().notNull(), createdAt: integer("created_at", { mode: "timestamp_ms" }).defaultNow().notNull(),
updatedAt: timestamp("updated_at", { withTimezone: true, mode: "date" }).defaultNow().notNull().$onUpdate(() => new Date()), updatedAt: integer("updated_at", { mode: "timestamp_ms" }).defaultNow().notNull().$onUpdate(() => new Date()),
}, (table) => ({ }, (table) => ({
userIdIdx: index("subscriptions_user_id_idx").on(table.userId), userIdIdx: index("subscriptions_user_id_idx").on(table.userId),
familyGroupIdIdx: index("subscriptions_family_group_id_idx").on(table.familyGroupId), familyGroupIdIdx: index("subscriptions_family_group_id_idx").on(table.familyGroupId),

View File

@@ -1,63 +1,62 @@
import { pgTable, text, timestamp, index, uuid, boolean, jsonb, doublePrecision, integer } from "drizzle-orm/pg-core"; import { sqliteTable, text, integer, real, index } from "drizzle-orm/sqlite-core";
import { users } from "./auth"; import { users } from "./auth";
import { detectionVerdict, analysisType, analysisJobStatus } from "./enums";
export const voiceEnrollments = pgTable("voice_enrollments", { export const voiceEnrollments = sqliteTable("voice_enrollments", {
id: uuid("id").defaultRandom().primaryKey(), id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
userId: uuid("user_id").notNull().references(() => users.id, { onDelete: "cascade" }), userId: text("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
name: text("name").notNull(), name: text("name").notNull(),
voiceHash: text("voice_hash").notNull(), voiceHash: text("voice_hash").notNull(),
audioMetadata: jsonb("audio_metadata"), audioMetadata: text("audio_metadata", { mode: "json" }),
isActive: boolean("is_active").default(true).notNull(), isActive: integer("is_active", { mode: "boolean" }).default(true).notNull(),
createdAt: timestamp("created_at", { withTimezone: true, mode: "date" }).defaultNow().notNull(), createdAt: integer("created_at", { mode: "timestamp_ms" }).defaultNow().notNull(),
updatedAt: timestamp("updated_at", { withTimezone: true, mode: "date" }).defaultNow().notNull().$onUpdate(() => new Date()), updatedAt: integer("updated_at", { mode: "timestamp_ms" }).defaultNow().notNull().$onUpdate(() => new Date()),
}, (table) => ({ }, (table) => ({
userIdIdx: index("voice_enrollments_user_id_idx").on(table.userId), userIdIdx: index("voice_enrollments_user_id_idx").on(table.userId),
voiceHashIdx: index("voice_enrollments_voice_hash_idx").on(table.voiceHash), voiceHashIdx: index("voice_enrollments_voice_hash_idx").on(table.voiceHash),
})); }));
export const voiceAnalyses = pgTable("voice_analyses", { export const voiceAnalyses = sqliteTable("voice_analyses", {
id: uuid("id").defaultRandom().primaryKey(), id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
enrollmentId: uuid("enrollment_id").references(() => voiceEnrollments.id), enrollmentId: text("enrollment_id").references(() => voiceEnrollments.id),
userId: uuid("user_id").notNull().references(() => users.id), userId: text("user_id").notNull().references(() => users.id),
audioHash: text("audio_hash").notNull(), audioHash: text("audio_hash").notNull(),
isSynthetic: boolean("is_synthetic").notNull(), isSynthetic: integer("is_synthetic", { mode: "boolean" }).notNull(),
confidence: doublePrecision("confidence").notNull(), confidence: real("confidence").notNull(),
analysisResult: jsonb("analysis_result").notNull(), analysisResult: text("analysis_result", { mode: "json" }).notNull(),
audioUrl: text("audio_url").notNull(), audioUrl: text("audio_url").notNull(),
createdAt: timestamp("created_at", { withTimezone: true, mode: "date" }).defaultNow().notNull(), createdAt: integer("created_at", { mode: "timestamp_ms" }).defaultNow().notNull(),
}, (table) => ({ }, (table) => ({
userIdIdx: index("voice_analyses_user_id_idx").on(table.userId), userIdIdx: index("voice_analyses_user_id_idx").on(table.userId),
enrollmentIdIdx: index("voice_analyses_enrollment_id_idx").on(table.enrollmentId), enrollmentIdIdx: index("voice_analyses_enrollment_id_idx").on(table.enrollmentId),
audioHashIdx: index("voice_analyses_audio_hash_idx").on(table.audioHash), audioHashIdx: index("voice_analyses_audio_hash_idx").on(table.audioHash),
})); }));
export const analysisJobs = pgTable("analysis_jobs", { export const analysisJobs = sqliteTable("analysis_jobs", {
id: uuid("id").defaultRandom().primaryKey(), id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
userId: uuid("user_id").notNull().references(() => users.id), userId: text("user_id").notNull().references(() => users.id),
analysisType: analysisType("analysis_type").notNull(), analysisType: text("analysis_type").notNull(),
audioFilePath: text("audio_file_path").notNull(), audioFilePath: text("audio_file_path").notNull(),
status: analysisJobStatus("status").notNull(), status: text("status").notNull(),
errorMessage: text("error_message"), errorMessage: text("error_message"),
completedAt: timestamp("completed_at", { withTimezone: true, mode: "date" }), completedAt: integer("completed_at", { mode: "timestamp_ms" }),
createdAt: timestamp("created_at", { withTimezone: true, mode: "date" }).defaultNow().notNull(), createdAt: integer("created_at", { mode: "timestamp_ms" }).defaultNow().notNull(),
}, (table) => ({ }, (table) => ({
userIdIdx: index("analysis_jobs_user_id_idx").on(table.userId), userIdIdx: index("analysis_jobs_user_id_idx").on(table.userId),
statusIdx: index("analysis_jobs_status_idx").on(table.status), statusIdx: index("analysis_jobs_status_idx").on(table.status),
createdAtIdx: index("analysis_jobs_created_at_idx").on(table.createdAt), createdAtIdx: index("analysis_jobs_created_at_idx").on(table.createdAt),
})); }));
export const analysisResults = pgTable("analysis_results", { export const analysisResults = sqliteTable("analysis_results", {
id: uuid("id").defaultRandom().primaryKey(), id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
analysisJobId: uuid("analysis_job_id").notNull().unique().references(() => analysisJobs.id), analysisJobId: text("analysis_job_id").notNull().unique().references(() => analysisJobs.id),
syntheticScore: doublePrecision("synthetic_score").notNull(), syntheticScore: real("synthetic_score").notNull(),
verdict: detectionVerdict("verdict").notNull(), verdict: text("verdict").notNull(),
confidence: doublePrecision("confidence").notNull(), confidence: real("confidence").notNull(),
processingTimeMs: integer("processing_time_ms").notNull(), processingTimeMs: integer("processing_time_ms").notNull(),
matchedEnrollmentId: text("matched_enrollment_id"), matchedEnrollmentId: text("matched_enrollment_id"),
matchedSimilarity: doublePrecision("matched_similarity"), matchedSimilarity: real("matched_similarity"),
modelVersion: text("model_version"), modelVersion: text("model_version"),
createdAt: timestamp("created_at", { withTimezone: true, mode: "date" }).defaultNow().notNull(), createdAt: integer("created_at", { mode: "timestamp_ms" }).defaultNow().notNull(),
}, (table) => ({ }, (table) => ({
analysisJobIdIdx: index("analysis_results_analysis_job_id_idx").on(table.analysisJobId), analysisJobIdIdx: index("analysis_results_analysis_job_id_idx").on(table.analysisJobId),
syntheticScoreIdx: index("analysis_results_synthetic_score_idx").on(table.syntheticScore), syntheticScoreIdx: index("analysis_results_synthetic_score_idx").on(table.syntheticScore),

View File

@@ -1,5 +1,4 @@
import { and, eq, gte, inArray, not, sql } from "drizzle-orm"; import { and, eq, gte, inArray, not, sql } from "drizzle-orm";
import type { NodePgDatabase } from "drizzle-orm/node-postgres";
import { db } from "~/server/db"; import { db } from "~/server/db";
import { normalizedAlerts, correlationGroups } from "~/server/db/schema"; import { normalizedAlerts, correlationGroups } from "~/server/db/schema";
import type { NormalizedAlertInput, EntitySet } from "./normalizer"; import type { NormalizedAlertInput, EntitySet } from "./normalizer";