db rearch
This commit is contained in:
@@ -27,21 +27,21 @@ Tasks
|
||||
- [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] 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`
|
||||
- [ ] 24 — Dashboard — Unified Widgets for All Services → `24-dashboard-widgets.md`
|
||||
- [ ] 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`
|
||||
- [ ] 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`
|
||||
- [ ] 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`
|
||||
- [ ] 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`
|
||||
- [ ] 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`
|
||||
- [ ] 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`
|
||||
- [ ] 37 — Android App — API Client, tRPC Bridge, and Offline Support → `37-android-api-client.md`
|
||||
- [x] 23 — Frontend Integration — Wire All Pages to tRPC APIs → `23-frontend-api-integration.md`
|
||||
- [x] 24 — Dashboard — Unified Widgets for All Services → `24-dashboard-widgets.md`
|
||||
- [x] 25 — Real-Time Alerts — WebSocket Push Notifications → `25-realtime-alerts.md`
|
||||
- [x] 26 — Polish — Error Boundaries, Loading States, Skeletons, and Transitions → `26-error-loading-states.md`
|
||||
- [x] 27 — Browser Extension — Move to browser-ext/ and Update API Client → `27-browser-extension-move.md`
|
||||
- [x] 28 — iOS App — SwiftUI Foundation, Navigation, and Shared Theme → `28-ios-app-foundation.md`
|
||||
- [x] 29 — iOS App — Design System Components Matching Web Theme → `29-ios-design-system.md`
|
||||
- [x] 30 — iOS App — Authentication, Onboarding, and Account Setup → `30-ios-auth-onboarding.md`
|
||||
- [x] 31 — iOS App — API Client, tRPC Bridge, and Offline Support → `31-ios-api-client.md`
|
||||
- [x] 32 — iOS App — Dashboard and Service Screens (DarkWatch, VoicePrint, SpamShield, etc.) → `32-ios-service-screens.md`
|
||||
- [x] 33 — iOS App — Push Notifications, Biometrics, Voice Enrollment, Camera → `33-ios-native-features.md`
|
||||
- [x] 34 — Android App — Jetpack Compose Foundation, Navigation, and Shared Theme → `34-android-app-foundation.md`
|
||||
- [x] 35 — Android App — Design System Components Matching Web Theme → `35-android-design-system.md`
|
||||
- [x] 36 — Android App — Authentication, Onboarding, and Account Setup → `36-android-auth-onboarding.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`
|
||||
- [ ] 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`
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { describe, it, expect } from "vitest";
|
||||
|
||||
describe("db module", () => {
|
||||
it("exports db and pool", async () => {
|
||||
it("exports db and client", async () => {
|
||||
const mod = await import("./index");
|
||||
expect(mod.db).toBeDefined();
|
||||
expect(mod.pool).toBeDefined();
|
||||
expect(mod.client).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -12,9 +12,9 @@ export const db = drizzle(client, { schema });
|
||||
export { client };
|
||||
|
||||
process.on("SIGTERM", () => {
|
||||
client.close().catch(() => process.exit(1));
|
||||
client.close();
|
||||
});
|
||||
|
||||
process.on("SIGINT", () => {
|
||||
client.close().catch(() => process.exit(1));
|
||||
client.close();
|
||||
});
|
||||
|
||||
@@ -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 { subscriptions } from "./subscription";
|
||||
import { exposures } from "./darkwatch";
|
||||
import { alertType, alertSeverity, alertChannel } from "./enums";
|
||||
|
||||
export const alerts = pgTable("alerts", {
|
||||
id: uuid("id").defaultRandom().primaryKey(),
|
||||
subscriptionId: uuid("subscription_id").notNull().references(() => subscriptions.id, { onDelete: "cascade" }),
|
||||
userId: uuid("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
|
||||
exposureId: uuid("exposure_id").references(() => exposures.id),
|
||||
type: alertType("type").notNull(),
|
||||
export const alerts = sqliteTable("alerts", {
|
||||
id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
|
||||
subscriptionId: text("subscription_id").notNull().references(() => subscriptions.id, { onDelete: "cascade" }),
|
||||
userId: text("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
|
||||
exposureId: text("exposure_id").references(() => exposures.id),
|
||||
type: text("type").notNull(),
|
||||
title: text("title").notNull(),
|
||||
message: text("message").notNull(),
|
||||
severity: alertSeverity("severity").default("info").notNull(),
|
||||
isRead: boolean("is_read").default(false).notNull(),
|
||||
readAt: timestamp("read_at", { withTimezone: true, mode: "date" }),
|
||||
channel: alertChannel("channel").array().notNull(),
|
||||
createdAt: timestamp("created_at", { withTimezone: true, mode: "date" }).defaultNow().notNull(),
|
||||
updatedAt: timestamp("updated_at", { withTimezone: true, mode: "date" }).defaultNow().notNull().$onUpdate(() => new Date()),
|
||||
severity: text("severity").default("info").notNull(),
|
||||
isRead: integer("is_read", { mode: "boolean" }).default(false).notNull(),
|
||||
readAt: integer("read_at", { mode: "timestamp_ms" }),
|
||||
channel: text("channel", { mode: "json" }).notNull().$defaultFn(() => []),
|
||||
createdAt: integer("created_at", { mode: "timestamp_ms" }).defaultNow().notNull(),
|
||||
updatedAt: integer("updated_at", { mode: "timestamp_ms" }).defaultNow().notNull().$onUpdate(() => new Date()),
|
||||
}, (table) => ({
|
||||
subscriptionIdIdx: index("alerts_subscription_id_idx").on(table.subscriptionId),
|
||||
userIdIdx: index("alerts_user_id_idx").on(table.userId),
|
||||
|
||||
@@ -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", {
|
||||
id: uuid("id").defaultRandom().primaryKey(),
|
||||
userId: uuid("user_id"),
|
||||
export const auditLogs = sqliteTable("audit_logs", {
|
||||
id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
|
||||
userId: text("user_id"),
|
||||
action: text("action").notNull(),
|
||||
resource: text("resource").notNull(),
|
||||
resourceId: text("resource_id"),
|
||||
changes: jsonb("changes"),
|
||||
metadata: jsonb("metadata"),
|
||||
changes: text("changes", { mode: "json" }),
|
||||
metadata: text("metadata", { mode: "json" }),
|
||||
ipAddress: text("ip_address"),
|
||||
userAgent: text("user_agent"),
|
||||
createdAt: timestamp("created_at", { withTimezone: true, mode: "date" }).defaultNow().notNull(),
|
||||
createdAt: integer("created_at", { mode: "timestamp_ms" }).defaultNow().notNull(),
|
||||
}, (table) => ({
|
||||
userIdIdx: index("audit_logs_user_id_idx").on(table.userId),
|
||||
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),
|
||||
}));
|
||||
|
||||
export const kpiSnapshots = pgTable("kpi_snapshots", {
|
||||
id: uuid("id").defaultRandom().primaryKey(),
|
||||
date: timestamp("date", { withTimezone: true, mode: "date" }).notNull().unique(),
|
||||
export const kpiSnapshots = sqliteTable("kpi_snapshots", {
|
||||
id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
|
||||
date: integer("date", { mode: "timestamp_ms" }).notNull().unique(),
|
||||
metricName: text("metric_name").notNull(),
|
||||
metricValue: doublePrecision("metric_value").notNull(),
|
||||
metadata: jsonb("metadata"),
|
||||
createdAt: timestamp("created_at", { withTimezone: true, mode: "date" }).defaultNow().notNull(),
|
||||
metricValue: real("metric_value").notNull(),
|
||||
metadata: text("metadata", { mode: "json" }),
|
||||
createdAt: integer("created_at", { mode: "timestamp_ms" }).defaultNow().notNull(),
|
||||
}, (table) => ({
|
||||
metricNameIdx: index("kpi_snapshots_metric_name_idx").on(table.metricName),
|
||||
dateIdx: index("kpi_snapshots_date_idx").on(table.date),
|
||||
|
||||
@@ -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 { alertSource, alertCategory, normalizedAlertSeverity, correlationStatus } from "./enums";
|
||||
|
||||
export const correlationGroups = pgTable("correlation_groups", {
|
||||
id: uuid("id").defaultRandom().primaryKey(),
|
||||
userId: uuid("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
|
||||
entities: jsonb("entities").notNull(),
|
||||
highestSeverity: normalizedAlertSeverity("highest_severity").notNull(),
|
||||
status: correlationStatus("status").default("ACTIVE").notNull(),
|
||||
export const correlationGroups = sqliteTable("correlation_groups", {
|
||||
id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
|
||||
userId: text("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
|
||||
entities: text("entities", { mode: "json" }).notNull(),
|
||||
highestSeverity: text("highest_severity").notNull(),
|
||||
status: text("status").default("ACTIVE").notNull(),
|
||||
alertCount: integer("alert_count").default(0).notNull(),
|
||||
summary: text("summary"),
|
||||
resolvedAt: timestamp("resolved_at", { withTimezone: true, mode: "date" }),
|
||||
createdAt: timestamp("created_at", { withTimezone: true, mode: "date" }).defaultNow().notNull(),
|
||||
updatedAt: timestamp("updated_at", { withTimezone: true, mode: "date" }).defaultNow().notNull().$onUpdate(() => new Date()),
|
||||
resolvedAt: integer("resolved_at", { mode: "timestamp_ms" }),
|
||||
createdAt: integer("created_at", { mode: "timestamp_ms" }).defaultNow().notNull(),
|
||||
updatedAt: integer("updated_at", { mode: "timestamp_ms" }).defaultNow().notNull().$onUpdate(() => new Date()),
|
||||
}, (table) => ({
|
||||
userIdIdx: index("correlation_groups_user_id_idx").on(table.userId),
|
||||
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),
|
||||
}));
|
||||
|
||||
export const normalizedAlerts = pgTable("normalized_alerts", {
|
||||
id: uuid("id").defaultRandom().primaryKey(),
|
||||
source: alertSource("source").notNull(),
|
||||
category: alertCategory("category").notNull(),
|
||||
severity: normalizedAlertSeverity("severity").notNull(),
|
||||
userId: uuid("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
|
||||
export const normalizedAlerts = sqliteTable("normalized_alerts", {
|
||||
id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
|
||||
source: text("source").notNull(),
|
||||
category: text("category").notNull(),
|
||||
severity: text("severity").notNull(),
|
||||
userId: text("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
|
||||
title: text("title").notNull(),
|
||||
description: text("description").notNull(),
|
||||
entities: jsonb("entities").notNull(),
|
||||
entities: text("entities", { mode: "json" }).notNull(),
|
||||
sourceAlertId: text("source_alert_id").notNull(),
|
||||
groupId: uuid("group_id").references(() => correlationGroups.id),
|
||||
payload: jsonb("payload"),
|
||||
createdAt: timestamp("created_at", { withTimezone: true, mode: "date" }).notNull(),
|
||||
updatedAt: timestamp("updated_at", { withTimezone: true, mode: "date" }).defaultNow().notNull().$onUpdate(() => new Date()),
|
||||
groupId: text("group_id").references(() => correlationGroups.id),
|
||||
payload: text("payload", { mode: "json" }),
|
||||
createdAt: integer("created_at", { mode: "timestamp_ms" }).notNull(),
|
||||
updatedAt: integer("updated_at", { mode: "timestamp_ms" }).defaultNow().notNull().$onUpdate(() => new Date()),
|
||||
}, (table) => ({
|
||||
sourceAlertIdUnique: uniqueIndex("normalized_alerts_source_alert_id_unique").on(table.sourceAlertId),
|
||||
userIdIdx: index("normalized_alerts_user_id_idx").on(table.userId),
|
||||
|
||||
@@ -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 { watchlistType, exposureSource, exposureSeverity } from "./enums";
|
||||
|
||||
export const watchlistItems = pgTable("watchlist_items", {
|
||||
id: uuid("id").defaultRandom().primaryKey(),
|
||||
subscriptionId: uuid("subscription_id").notNull().references(() => subscriptions.id, { onDelete: "cascade" }),
|
||||
type: watchlistType("type").notNull(),
|
||||
export const watchlistItems = sqliteTable("watchlist_items", {
|
||||
id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
|
||||
subscriptionId: text("subscription_id").notNull().references(() => subscriptions.id, { onDelete: "cascade" }),
|
||||
type: text("type").notNull(),
|
||||
value: text("value").notNull(),
|
||||
hash: text("hash").notNull(),
|
||||
isActive: boolean("is_active").default(true).notNull(),
|
||||
createdAt: timestamp("created_at", { withTimezone: true, mode: "date" }).defaultNow().notNull(),
|
||||
updatedAt: timestamp("updated_at", { withTimezone: true, mode: "date" }).defaultNow().notNull().$onUpdate(() => new Date()),
|
||||
isActive: integer("is_active", { mode: "boolean" }).default(true).notNull(),
|
||||
createdAt: integer("created_at", { mode: "timestamp_ms" }).defaultNow().notNull(),
|
||||
updatedAt: integer("updated_at", { mode: "timestamp_ms" }).defaultNow().notNull().$onUpdate(() => new Date()),
|
||||
}, (table) => ({
|
||||
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),
|
||||
@@ -18,20 +17,20 @@ export const watchlistItems = pgTable("watchlist_items", {
|
||||
hashIdx: index("watchlist_items_hash_idx").on(table.hash),
|
||||
}));
|
||||
|
||||
export const exposures = pgTable("exposures", {
|
||||
id: uuid("id").defaultRandom().primaryKey(),
|
||||
subscriptionId: uuid("subscription_id").notNull().references(() => subscriptions.id, { onDelete: "cascade" }),
|
||||
watchlistItemId: uuid("watchlist_item_id").references(() => watchlistItems.id),
|
||||
source: exposureSource("source").notNull(),
|
||||
dataType: watchlistType("data_type").notNull(),
|
||||
export const exposures = sqliteTable("exposures", {
|
||||
id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
|
||||
subscriptionId: text("subscription_id").notNull().references(() => subscriptions.id, { onDelete: "cascade" }),
|
||||
watchlistItemId: text("watchlist_item_id").references(() => watchlistItems.id),
|
||||
source: text("source").notNull(),
|
||||
dataType: text("data_type").notNull(),
|
||||
identifier: text("identifier").notNull(),
|
||||
identifierHash: text("identifier_hash").notNull(),
|
||||
severity: exposureSeverity("severity").default("info").notNull(),
|
||||
metadata: jsonb("metadata"),
|
||||
isFirstTime: boolean("is_first_time").default(false).notNull(),
|
||||
detectedAt: timestamp("detected_at", { withTimezone: true, mode: "date" }).notNull(),
|
||||
createdAt: timestamp("created_at", { withTimezone: true, mode: "date" }).defaultNow().notNull(),
|
||||
updatedAt: timestamp("updated_at", { withTimezone: true, mode: "date" }).defaultNow().notNull().$onUpdate(() => new Date()),
|
||||
severity: text("severity").default("info").notNull(),
|
||||
metadata: text("metadata", { mode: "json" }),
|
||||
isFirstTime: integer("is_first_time", { mode: "boolean" }).default(false).notNull(),
|
||||
detectedAt: integer("detected_at", { mode: "timestamp_ms" }).notNull(),
|
||||
createdAt: integer("created_at", { mode: "timestamp_ms" }).defaultNow().notNull(),
|
||||
updatedAt: integer("updated_at", { mode: "timestamp_ms" }).defaultNow().notNull().$onUpdate(() => new Date()),
|
||||
}, (table) => ({
|
||||
subscriptionIdIdx: index("exposures_subscription_id_idx").on(table.subscriptionId),
|
||||
watchlistItemIdIdx: index("exposures_watchlist_item_id_idx").on(table.watchlistItemId),
|
||||
|
||||
@@ -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 const deviceType = pgEnum("device_type", ["mobile", "web", "desktop"]);
|
||||
export const platform = pgEnum("platform", ["ios", "android", "web"]);
|
||||
export const familyMemberRole = pgEnum("family_member_role", ["owner", "admin", "member"]);
|
||||
export const subscriptionTier = pgEnum("subscription_tier", ["basic", "plus", "premium"]);
|
||||
export const subscriptionStatus = pgEnum("subscription_status", ["active", "past_due", "canceled", "unpaid", "trialing"]);
|
||||
export const watchlistType = pgEnum("watchlist_type", ["email", "phoneNumber", "ssn", "address", "domain"]);
|
||||
export const exposureSource = pgEnum("exposure_source", ["hibp", "securityTrails", "censys", "darkWebForum", "shodan", "honeypot"]);
|
||||
export const exposureSeverity = pgEnum("exposure_severity", ["info", "warning", "critical"]);
|
||||
export const alertType = pgEnum("alert_type", ["exposure_detected", "exposure_resolved", "scan_complete", "subscription_changed", "system_warning", "property_change"]);
|
||||
export const alertSeverity = pgEnum("alert_severity", ["info", "warning", "critical"]);
|
||||
export const alertChannel = pgEnum("alert_channel", ["email", "push", "sms"]);
|
||||
export const detectionVerdict = pgEnum("detection_verdict", ["NATURAL", "SYNTHETIC", "UNCERTAIN"]);
|
||||
export const analysisType = pgEnum("analysis_type", ["SYNTHETIC_DETECTION", "VOICE_MATCH", "BATCH"]);
|
||||
export const analysisJobStatus = pgEnum("analysis_job_status", ["PENDING", "RUNNING", "COMPLETED", "FAILED"]);
|
||||
export const feedbackType = pgEnum("feedback_type", ["initial_detection", "user_confirmation", "user_rejection", "auto_learned"]);
|
||||
export const ruleType = pgEnum("rule_type", ["phoneNumber", "areaCode", "prefix", "pattern", "reputation"]);
|
||||
export const ruleAction = pgEnum("rule_action", ["block", "flag", "allow", "challenge"]);
|
||||
export const alertSource = pgEnum("alert_source", ["DARKWATCH", "SPAMSHIELD", "VOICEPRINT", "CALL_ANALYSIS", "HOME_TITLE", "INFO_BROKER"]);
|
||||
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 const normalizedAlertSeverity = pgEnum("normalized_alert_severity", ["LOW", "INFO", "MEDIUM", "WARNING", "HIGH", "CRITICAL"]);
|
||||
export const correlationStatus = pgEnum("correlation_status", ["ACTIVE", "RESOLVED", "FALSE_POSITIVE"]);
|
||||
export const reportType = pgEnum("report_type", ["MONTHLY_PLUS", "ANNUAL_PREMIUM", "WEEKLY_DIGEST"]);
|
||||
export const reportStatus = pgEnum("report_status", ["PENDING", "GENERATING", "COMPLETED", "FAILED", "DELIVERED"]);
|
||||
export const propertyChangeType = pgEnum("property_change_type", ["tax_change", "deed_change", "ownership_transfer", "lien_filing", "metadata_change"]);
|
||||
export const propertyChangeSeverity = pgEnum("property_change_severity", ["info", "warning", "critical"]);
|
||||
export const brokerCategory = pgEnum("broker_category", ["PEOPLE_SEARCH", "BACKGROUND_CHECK", "PUBLIC_RECORDS", "REVERSE_LOOKUP", "SOCIAL_MEDIA"]);
|
||||
export const removalMethod = pgEnum("removal_method", ["AUTOMATED", "MANUAL_FORM", "EMAIL", "PHONE", "MAIL", "NONE"]);
|
||||
export const removalStatus = pgEnum("removal_status", ["PENDING", "SUBMITTED", "IN_PROGRESS", "COMPLETED", "FAILED", "REJECTED", "CANCELLED"]);
|
||||
export type UserRole = typeof userRoleValues[number];
|
||||
export type DeviceType = typeof deviceTypeValues[number];
|
||||
export type Platform = typeof platformValues[number];
|
||||
export type FamilyMemberRole = typeof familyMemberRoleValues[number];
|
||||
export type SubscriptionTier = typeof subscriptionTierValues[number];
|
||||
export type SubscriptionStatus = typeof subscriptionStatusValues[number];
|
||||
export type WatchlistType = typeof watchlistTypeValues[number];
|
||||
export type ExposureSource = typeof exposureSourceValues[number];
|
||||
export type ExposureSeverity = typeof exposureSeverityValues[number];
|
||||
export type AlertType = typeof alertTypeValues[number];
|
||||
export type AlertSeverity = typeof alertSeverityValues[number];
|
||||
export type AlertChannel = typeof alertChannelValues[number];
|
||||
export type DetectionVerdict = typeof detectionVerdictValues[number];
|
||||
export type AnalysisType = typeof analysisTypeValues[number];
|
||||
export type AnalysisJobStatus = typeof analysisJobStatusValues[number];
|
||||
export type FeedbackType = typeof feedbackTypeValues[number];
|
||||
export type RuleType = typeof ruleTypeValues[number];
|
||||
export type RuleAction = typeof ruleActionValues[number];
|
||||
export type AlertSource = typeof alertSourceValues[number];
|
||||
export type AlertCategory = typeof alertCategoryValues[number];
|
||||
export type NormalizedAlertSeverity = typeof normalizedAlertSeverityValues[number];
|
||||
export type CorrelationStatus = typeof correlationStatusValues[number];
|
||||
export type ReportType = typeof reportTypeValues[number];
|
||||
export type ReportStatus = typeof reportStatusValues[number];
|
||||
export type PropertyChangeType = typeof propertyChangeTypeValues[number];
|
||||
export type PropertyChangeSeverity = typeof propertyChangeSeverityValues[number];
|
||||
export type BrokerCategory = typeof brokerCategoryValues[number];
|
||||
export type RemovalMethod = typeof removalMethodValues[number];
|
||||
export type RemovalStatus = typeof removalStatusValues[number];
|
||||
export type InvitationStatus = typeof invitationStatusValues[number];
|
||||
|
||||
@@ -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 { propertyChangeType, propertyChangeSeverity } from "./enums";
|
||||
|
||||
export const propertyWatchlistItems = pgTable("property_watchlist_items", {
|
||||
id: uuid("id").defaultRandom().primaryKey(),
|
||||
subscriptionId: uuid("subscription_id").notNull().references(() => subscriptions.id, { onDelete: "cascade" }),
|
||||
export const propertyWatchlistItems = sqliteTable("property_watchlist_items", {
|
||||
id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
|
||||
subscriptionId: text("subscription_id").notNull().references(() => subscriptions.id, { onDelete: "cascade" }),
|
||||
address: text("address").notNull(),
|
||||
parcelId: text("parcel_id"),
|
||||
ownerName: text("owner_name"),
|
||||
@@ -12,11 +11,11 @@ export const propertyWatchlistItems = pgTable("property_watchlist_items", {
|
||||
city: text("city").default(""),
|
||||
state: text("state").default(""),
|
||||
zipCode: text("zip_code").default(""),
|
||||
latitude: doublePrecision("latitude"),
|
||||
longitude: doublePrecision("longitude"),
|
||||
isActive: boolean("is_active").default(true).notNull(),
|
||||
createdAt: timestamp("created_at", { withTimezone: true, mode: "date" }).defaultNow().notNull(),
|
||||
updatedAt: timestamp("updated_at", { withTimezone: true, mode: "date" }).defaultNow().notNull().$onUpdate(() => new Date()),
|
||||
latitude: real("latitude"),
|
||||
longitude: real("longitude"),
|
||||
isActive: integer("is_active", { mode: "boolean" }).default(true).notNull(),
|
||||
createdAt: integer("created_at", { mode: "timestamp_ms" }).defaultNow().notNull(),
|
||||
updatedAt: integer("updated_at", { mode: "timestamp_ms" }).defaultNow().notNull().$onUpdate(() => new Date()),
|
||||
}, (table) => ({
|
||||
subParcelIdUnique: uniqueIndex("property_watchlist_items_sub_parcel_unique").on(table.subscriptionId, table.parcelId),
|
||||
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),
|
||||
}));
|
||||
|
||||
export const propertySnapshots = pgTable("property_snapshots", {
|
||||
id: uuid("id").defaultRandom().primaryKey(),
|
||||
propertyWatchlistItemId: uuid("property_watchlist_item_id").notNull().references(() => propertyWatchlistItems.id, { onDelete: "cascade" }),
|
||||
subscriptionId: uuid("subscription_id").notNull(),
|
||||
capturedAt: timestamp("captured_at", { withTimezone: true, mode: "date" }).notNull(),
|
||||
export const propertySnapshots = sqliteTable("property_snapshots", {
|
||||
id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
|
||||
propertyWatchlistItemId: text("property_watchlist_item_id").notNull().references(() => propertyWatchlistItems.id, { onDelete: "cascade" }),
|
||||
subscriptionId: text("subscription_id").notNull(),
|
||||
capturedAt: integer("captured_at", { mode: "timestamp_ms" }).notNull(),
|
||||
ownerName: text("owner_name").notNull(),
|
||||
address: jsonb("address").notNull(),
|
||||
address: text("address", { mode: "json" }).notNull(),
|
||||
deedDate: text("deed_date"),
|
||||
taxId: text("tax_id"),
|
||||
propertyType: text("property_type").default("residential").notNull(),
|
||||
taxAmount: doublePrecision("tax_amount"),
|
||||
taxAmount: real("tax_amount"),
|
||||
lienCount: integer("lien_count").default(0).notNull(),
|
||||
createdAt: timestamp("created_at", { withTimezone: true, mode: "date" }).defaultNow().notNull(),
|
||||
updatedAt: timestamp("updated_at", { withTimezone: true, mode: "date" }).defaultNow().notNull().$onUpdate(() => new Date()),
|
||||
createdAt: integer("created_at", { mode: "timestamp_ms" }).defaultNow().notNull(),
|
||||
updatedAt: integer("updated_at", { mode: "timestamp_ms" }).defaultNow().notNull().$onUpdate(() => new Date()),
|
||||
}, (table) => ({
|
||||
propertyWatchlistItemIdIdx: index("property_snapshots_property_watchlist_item_id_idx").on(table.propertyWatchlistItemId),
|
||||
subscriptionIdIdx: index("property_snapshots_subscription_id_idx").on(table.subscriptionId),
|
||||
capturedAtIdx: index("property_snapshots_captured_at_idx").on(table.capturedAt),
|
||||
}));
|
||||
|
||||
export const propertyChanges = pgTable("property_changes", {
|
||||
id: uuid("id").defaultRandom().primaryKey(),
|
||||
propertyWatchlistItemId: uuid("property_watchlist_item_id").notNull().references(() => propertyWatchlistItems.id, { onDelete: "cascade" }),
|
||||
snapshotId: uuid("snapshot_id").references(() => propertySnapshots.id),
|
||||
changeType: propertyChangeType("change_type").notNull(),
|
||||
severity: propertyChangeSeverity("severity").default("info").notNull(),
|
||||
details: jsonb("details"),
|
||||
detectedAt: timestamp("detected_at", { withTimezone: true, mode: "date" }).defaultNow().notNull(),
|
||||
export const propertyChanges = sqliteTable("property_changes", {
|
||||
id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
|
||||
propertyWatchlistItemId: text("property_watchlist_item_id").notNull().references(() => propertyWatchlistItems.id, { onDelete: "cascade" }),
|
||||
snapshotId: text("snapshot_id").references(() => propertySnapshots.id),
|
||||
changeType: text("change_type").notNull(),
|
||||
severity: text("severity").default("info").notNull(),
|
||||
details: text("details", { mode: "json" }),
|
||||
detectedAt: integer("detected_at", { mode: "timestamp_ms" }).defaultNow().notNull(),
|
||||
}, (table) => ({
|
||||
propertyWatchlistItemIdIdx: index("property_changes_property_watchlist_item_id_idx").on(table.propertyWatchlistItemId),
|
||||
snapshotIdIdx: index("property_changes_snapshot_id_idx").on(table.snapshotId),
|
||||
|
||||
@@ -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 { familyGroups } from "./subscription";
|
||||
import { familyMemberRole } from "./enums";
|
||||
|
||||
export const invitationStatus = pgEnum("invitation_status", ["pending", "accepted", "expired", "cancelled"]);
|
||||
|
||||
export const invitations = pgTable("invitations", {
|
||||
id: uuid("id").defaultRandom().primaryKey(),
|
||||
groupId: uuid("group_id").notNull().references(() => familyGroups.id, { onDelete: "cascade" }),
|
||||
export const invitations = sqliteTable("invitations", {
|
||||
id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
|
||||
groupId: text("group_id").notNull().references(() => familyGroups.id, { onDelete: "cascade" }),
|
||||
email: text("email").notNull(),
|
||||
role: familyMemberRole("role").default("member").notNull(),
|
||||
invitedBy: uuid("invited_by").notNull().references(() => users.id),
|
||||
status: invitationStatus("status").default("pending").notNull(),
|
||||
expiresAt: timestamp("expires_at", { withTimezone: true, mode: "date" }).notNull(),
|
||||
createdAt: timestamp("created_at", { withTimezone: true, mode: "date" }).defaultNow().notNull(),
|
||||
updatedAt: timestamp("updated_at", { withTimezone: true, mode: "date" }).defaultNow().notNull().$onUpdate(() => new Date()),
|
||||
role: text("role").default("member").notNull(),
|
||||
invitedBy: text("invited_by").notNull().references(() => users.id),
|
||||
status: text("status").default("pending").notNull(),
|
||||
expiresAt: integer("expires_at", { mode: "timestamp_ms" }).notNull(),
|
||||
createdAt: integer("created_at", { mode: "timestamp_ms" }).defaultNow().notNull(),
|
||||
updatedAt: integer("updated_at", { mode: "timestamp_ms" }).defaultNow().notNull().$onUpdate(() => new Date()),
|
||||
});
|
||||
|
||||
@@ -1,41 +1,40 @@
|
||||
import { pgTable, text, timestamp, index, uuid, boolean, integer, jsonb } from "drizzle-orm/pg-core";
|
||||
import { subscriptionTier } from "./enums";
|
||||
import { sqliteTable, text, integer, index } from "drizzle-orm/sqlite-core";
|
||||
|
||||
export const waitlistEntries = pgTable("waitlist_entries", {
|
||||
id: uuid("id").defaultRandom().primaryKey(),
|
||||
export const waitlistEntries = sqliteTable("waitlist_entries", {
|
||||
id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
|
||||
email: text("email").notNull(),
|
||||
name: text("name"),
|
||||
source: text("source"),
|
||||
tier: subscriptionTier("tier"),
|
||||
tier: text("tier"),
|
||||
utmSource: text("utm_source"),
|
||||
utmMedium: text("utm_medium"),
|
||||
utmCampaign: text("utm_campaign"),
|
||||
metadata: jsonb("metadata"),
|
||||
convertedAt: timestamp("converted_at", { withTimezone: true, mode: "date" }),
|
||||
metadata: text("metadata", { mode: "json" }),
|
||||
convertedAt: integer("converted_at", { mode: "timestamp_ms" }),
|
||||
convertedToUserId: text("converted_to_user_id"),
|
||||
convertedToSubscriptionId: text("converted_to_subscription_id"),
|
||||
createdAt: timestamp("created_at", { withTimezone: true, mode: "date" }).defaultNow().notNull(),
|
||||
updatedAt: timestamp("updated_at", { withTimezone: true, mode: "date" }).defaultNow().notNull().$onUpdate(() => new Date()),
|
||||
createdAt: integer("created_at", { mode: "timestamp_ms" }).defaultNow().notNull(),
|
||||
updatedAt: integer("updated_at", { mode: "timestamp_ms" }).defaultNow().notNull().$onUpdate(() => new Date()),
|
||||
}, (table) => ({
|
||||
emailIdx: index("waitlist_entries_email_idx").on(table.email),
|
||||
sourceIdx: index("waitlist_entries_source_idx").on(table.source),
|
||||
createdAtIdx: index("waitlist_entries_created_at_idx").on(table.createdAt),
|
||||
}));
|
||||
|
||||
export const blogPosts = pgTable("blog_posts", {
|
||||
id: uuid("id").defaultRandom().primaryKey(),
|
||||
export const blogPosts = sqliteTable("blog_posts", {
|
||||
id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
|
||||
slug: text("slug").notNull().unique(),
|
||||
title: text("title").notNull(),
|
||||
excerpt: text("excerpt"),
|
||||
content: text("content").notNull(),
|
||||
authorName: text("author_name"),
|
||||
coverImageUrl: text("cover_image_url"),
|
||||
tags: text("tags").array().notNull(),
|
||||
published: boolean("published").default(false).notNull(),
|
||||
publishedAt: timestamp("published_at", { withTimezone: true, mode: "date" }),
|
||||
tags: text("tags", { mode: "json" }).notNull().$defaultFn(() => []),
|
||||
published: integer("published", { mode: "boolean" }).default(false).notNull(),
|
||||
publishedAt: integer("published_at", { mode: "timestamp_ms" }),
|
||||
viewCount: integer("view_count").default(0).notNull(),
|
||||
createdAt: timestamp("created_at", { withTimezone: true, mode: "date" }).defaultNow().notNull(),
|
||||
updatedAt: timestamp("updated_at", { withTimezone: true, mode: "date" }).defaultNow().notNull().$onUpdate(() => new Date()),
|
||||
createdAt: integer("created_at", { mode: "timestamp_ms" }).defaultNow().notNull(),
|
||||
updatedAt: integer("updated_at", { mode: "timestamp_ms" }).defaultNow().notNull().$onUpdate(() => new Date()),
|
||||
}, (table) => ({
|
||||
slugIdx: index("blog_posts_slug_idx").on(table.slug),
|
||||
publishedIdx: index("blog_posts_published_idx").on(table.published, table.publishedAt),
|
||||
|
||||
@@ -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";
|
||||
|
||||
export const notificationPreferences = pgTable("notification_preferences", {
|
||||
id: uuid("id").defaultRandom().primaryKey(),
|
||||
userId: uuid("user_id")
|
||||
export const notificationPreferences = sqliteTable("notification_preferences", {
|
||||
id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
|
||||
userId: text("user_id")
|
||||
.notNull()
|
||||
.references(() => users.id, { onDelete: "cascade" })
|
||||
.unique(),
|
||||
emailEnabled: boolean("email_enabled").default(true).notNull(),
|
||||
pushEnabled: boolean("push_enabled").default(true).notNull(),
|
||||
smsEnabled: boolean("sms_enabled").default(true).notNull(),
|
||||
createdAt: timestamp("created_at", { withTimezone: true, mode: "date" }).defaultNow().notNull(),
|
||||
updatedAt: timestamp("updated_at", { withTimezone: true, mode: "date" }).defaultNow().notNull().$onUpdate(() => new Date()),
|
||||
emailEnabled: integer("email_enabled", { mode: "boolean" }).default(true).notNull(),
|
||||
pushEnabled: integer("push_enabled", { mode: "boolean" }).default(true).notNull(),
|
||||
smsEnabled: integer("sms_enabled", { mode: "boolean" }).default(true).notNull(),
|
||||
createdAt: integer("created_at", { mode: "timestamp_ms" }).defaultNow().notNull(),
|
||||
updatedAt: integer("updated_at", { mode: "timestamp_ms" }).defaultNow().notNull().$onUpdate(() => new Date()),
|
||||
});
|
||||
|
||||
@@ -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 { brokerCategory, removalMethod, removalStatus } from "./enums";
|
||||
|
||||
export const infoBrokers = pgTable("info_brokers", {
|
||||
id: uuid("id").defaultRandom().primaryKey(),
|
||||
export const infoBrokers = sqliteTable("info_brokers", {
|
||||
id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
|
||||
name: text("name").notNull(),
|
||||
domain: text("domain").notNull().unique(),
|
||||
category: brokerCategory("category").notNull(),
|
||||
removalMethod: removalMethod("removal_method").notNull(),
|
||||
category: text("category").notNull(),
|
||||
removalMethod: text("removal_method").notNull(),
|
||||
removalUrl: text("removal_url"),
|
||||
requiresAccount: boolean("requires_account").default(false).notNull(),
|
||||
requiresVerification: boolean("requires_verification").default(false).notNull(),
|
||||
requiresAccount: integer("requires_account", { mode: "boolean" }).default(false).notNull(),
|
||||
requiresVerification: integer("requires_verification", { mode: "boolean" }).default(false).notNull(),
|
||||
estimatedDays: integer("estimated_days").default(14).notNull(),
|
||||
isActive: boolean("is_active").default(true).notNull(),
|
||||
createdAt: timestamp("created_at", { withTimezone: true, mode: "date" }).defaultNow().notNull(),
|
||||
updatedAt: timestamp("updated_at", { withTimezone: true, mode: "date" }).defaultNow().notNull().$onUpdate(() => new Date()),
|
||||
isActive: integer("is_active", { mode: "boolean" }).default(true).notNull(),
|
||||
createdAt: integer("created_at", { mode: "timestamp_ms" }).defaultNow().notNull(),
|
||||
updatedAt: integer("updated_at", { mode: "timestamp_ms" }).defaultNow().notNull().$onUpdate(() => new Date()),
|
||||
}, (table) => ({
|
||||
categoryIdx: index("info_brokers_category_idx").on(table.category),
|
||||
isActiveIdx: index("info_brokers_is_active_idx").on(table.isActive),
|
||||
removalMethodIdx: index("info_brokers_removal_method_idx").on(table.removalMethod),
|
||||
}));
|
||||
|
||||
export const removalRequests = pgTable("removal_requests", {
|
||||
id: uuid("id").defaultRandom().primaryKey(),
|
||||
subscriptionId: uuid("subscription_id").notNull().references(() => subscriptions.id, { onDelete: "cascade" }),
|
||||
brokerId: uuid("broker_id").notNull().references(() => infoBrokers.id),
|
||||
status: removalStatus("status").default("PENDING").notNull(),
|
||||
personalInfo: jsonb("personal_info").notNull(),
|
||||
method: removalMethod("method").notNull(),
|
||||
export const removalRequests = sqliteTable("removal_requests", {
|
||||
id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
|
||||
subscriptionId: text("subscription_id").notNull().references(() => subscriptions.id, { onDelete: "cascade" }),
|
||||
brokerId: text("broker_id").notNull().references(() => infoBrokers.id),
|
||||
status: text("status").default("PENDING").notNull(),
|
||||
personalInfo: text("personal_info", { mode: "json" }).notNull(),
|
||||
method: text("method").notNull(),
|
||||
attempts: integer("attempts").default(0).notNull(),
|
||||
nextRetryAt: timestamp("next_retry_at", { withTimezone: true, mode: "date" }),
|
||||
submittedAt: timestamp("submitted_at", { withTimezone: true, mode: "date" }),
|
||||
completedAt: timestamp("completed_at", { withTimezone: true, mode: "date" }),
|
||||
nextRetryAt: integer("next_retry_at", { mode: "timestamp_ms" }),
|
||||
submittedAt: integer("submitted_at", { mode: "timestamp_ms" }),
|
||||
completedAt: integer("completed_at", { mode: "timestamp_ms" }),
|
||||
error: text("error"),
|
||||
notes: text("notes"),
|
||||
metadata: jsonb("metadata"),
|
||||
createdAt: timestamp("created_at", { withTimezone: true, mode: "date" }).defaultNow().notNull(),
|
||||
updatedAt: timestamp("updated_at", { withTimezone: true, mode: "date" }).defaultNow().notNull().$onUpdate(() => new Date()),
|
||||
metadata: text("metadata", { mode: "json" }),
|
||||
createdAt: integer("created_at", { mode: "timestamp_ms" }).defaultNow().notNull(),
|
||||
updatedAt: integer("updated_at", { mode: "timestamp_ms" }).defaultNow().notNull().$onUpdate(() => new Date()),
|
||||
}, (table) => ({
|
||||
subscriptionIdIdx: index("removal_requests_subscription_id_idx").on(table.subscriptionId),
|
||||
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),
|
||||
}));
|
||||
|
||||
export const brokerListings = pgTable("broker_listings", {
|
||||
id: uuid("id").defaultRandom().primaryKey(),
|
||||
subscriptionId: uuid("subscription_id").notNull().references(() => subscriptions.id, { onDelete: "cascade" }),
|
||||
brokerId: uuid("broker_id").notNull(),
|
||||
removalRequestId: uuid("removal_request_id").references(() => removalRequests.id),
|
||||
export const brokerListings = sqliteTable("broker_listings", {
|
||||
id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
|
||||
subscriptionId: text("subscription_id").notNull().references(() => subscriptions.id, { onDelete: "cascade" }),
|
||||
brokerId: text("broker_id").notNull(),
|
||||
removalRequestId: text("removal_request_id").references(() => removalRequests.id),
|
||||
url: text("url").notNull(),
|
||||
dataFound: jsonb("data_found").notNull(),
|
||||
dataFound: text("data_found", { mode: "json" }).notNull(),
|
||||
screenshotUrl: text("screenshot_url"),
|
||||
isRemoved: boolean("is_removed").default(false).notNull(),
|
||||
removedAt: timestamp("removed_at", { withTimezone: true, mode: "date" }),
|
||||
scannedAt: timestamp("scanned_at", { withTimezone: true, mode: "date" }).defaultNow().notNull(),
|
||||
createdAt: timestamp("created_at", { withTimezone: true, mode: "date" }).defaultNow().notNull(),
|
||||
updatedAt: timestamp("updated_at", { withTimezone: true, mode: "date" }).defaultNow().notNull().$onUpdate(() => new Date()),
|
||||
isRemoved: integer("is_removed", { mode: "boolean" }).default(false).notNull(),
|
||||
removedAt: integer("removed_at", { mode: "timestamp_ms" }),
|
||||
scannedAt: integer("scanned_at", { mode: "timestamp_ms" }).defaultNow().notNull(),
|
||||
createdAt: integer("created_at", { mode: "timestamp_ms" }).defaultNow().notNull(),
|
||||
updatedAt: integer("updated_at", { mode: "timestamp_ms" }).defaultNow().notNull().$onUpdate(() => new Date()),
|
||||
}, (table) => ({
|
||||
subscriptionIdIdx: index("broker_listings_subscription_id_idx").on(table.subscriptionId),
|
||||
brokerIdIdx: index("broker_listings_broker_id_idx").on(table.brokerId),
|
||||
|
||||
@@ -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 { reportType } from "./enums";
|
||||
|
||||
export const reportSchedules = pgTable("report_schedules", {
|
||||
id: uuid("id").defaultRandom().primaryKey(),
|
||||
userId: uuid("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
|
||||
enabled: boolean("enabled").default(true).notNull(),
|
||||
export const reportSchedules = sqliteTable("report_schedules", {
|
||||
id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
|
||||
userId: text("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
|
||||
enabled: integer("enabled", { mode: "boolean" }).default(true).notNull(),
|
||||
frequency: text("frequency").notNull(),
|
||||
reportType: reportType("report_type").notNull(),
|
||||
lastGeneratedAt: timestamp("last_generated_at", { withTimezone: true, mode: "date" }),
|
||||
nextScheduledAt: timestamp("next_scheduled_at", { withTimezone: true, mode: "date" }),
|
||||
createdAt: timestamp("created_at", { withTimezone: true, mode: "date" }).defaultNow().notNull(),
|
||||
updatedAt: timestamp("updated_at", { withTimezone: true, mode: "date" }).defaultNow().notNull().$onUpdate(() => new Date()),
|
||||
reportType: text("report_type").notNull(),
|
||||
lastGeneratedAt: integer("last_generated_at", { mode: "timestamp_ms" }),
|
||||
nextScheduledAt: integer("next_scheduled_at", { mode: "timestamp_ms" }),
|
||||
createdAt: integer("created_at", { mode: "timestamp_ms" }).defaultNow().notNull(),
|
||||
updatedAt: integer("updated_at", { mode: "timestamp_ms" }).defaultNow().notNull().$onUpdate(() => new Date()),
|
||||
}, (table) => ({
|
||||
userIdIdx: index("report_schedules_user_id_idx").on(table.userId),
|
||||
enabledIdx: index("report_schedules_enabled_idx").on(table.enabled),
|
||||
|
||||
@@ -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 { reportType, reportStatus } from "./enums";
|
||||
|
||||
export const securityReports = pgTable("security_reports", {
|
||||
id: uuid("id").defaultRandom().primaryKey(),
|
||||
userId: uuid("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
|
||||
subscriptionId: uuid("subscription_id").notNull(),
|
||||
reportType: reportType("report_type").notNull(),
|
||||
status: reportStatus("status").default("PENDING").notNull(),
|
||||
periodStart: timestamp("period_start", { withTimezone: true, mode: "date" }).notNull(),
|
||||
periodEnd: timestamp("period_end", { withTimezone: true, mode: "date" }).notNull(),
|
||||
export const securityReports = sqliteTable("security_reports", {
|
||||
id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
|
||||
userId: text("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
|
||||
subscriptionId: text("subscription_id").notNull(),
|
||||
reportType: text("report_type").notNull(),
|
||||
status: text("status").default("PENDING").notNull(),
|
||||
periodStart: integer("period_start", { mode: "timestamp_ms" }).notNull(),
|
||||
periodEnd: integer("period_end", { mode: "timestamp_ms" }).notNull(),
|
||||
title: text("title").notNull(),
|
||||
summary: text("summary"),
|
||||
htmlContent: text("html_content"),
|
||||
pdfUrl: text("pdf_url"),
|
||||
dataPayload: jsonb("data_payload"),
|
||||
dataPayload: text("data_payload", { mode: "json" }),
|
||||
error: text("error"),
|
||||
scheduledFor: timestamp("scheduled_for", { withTimezone: true, mode: "date" }),
|
||||
deliveredAt: timestamp("delivered_at", { withTimezone: true, mode: "date" }),
|
||||
createdAt: timestamp("created_at", { withTimezone: true, mode: "date" }).defaultNow().notNull(),
|
||||
updatedAt: timestamp("updated_at", { withTimezone: true, mode: "date" }).defaultNow().notNull().$onUpdate(() => new Date()),
|
||||
scheduledFor: integer("scheduled_for", { mode: "timestamp_ms" }),
|
||||
deliveredAt: integer("delivered_at", { mode: "timestamp_ms" }),
|
||||
createdAt: integer("created_at", { mode: "timestamp_ms" }).defaultNow().notNull(),
|
||||
updatedAt: integer("updated_at", { mode: "timestamp_ms" }).defaultNow().notNull().$onUpdate(() => new Date()),
|
||||
}, (table) => ({
|
||||
userIdIdx: index("security_reports_user_id_idx").on(table.userId),
|
||||
subscriptionIdIdx: index("security_reports_subscription_id_idx").on(table.subscriptionId),
|
||||
|
||||
@@ -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 { feedbackType, ruleType, ruleAction } from "./enums";
|
||||
|
||||
export const spamFeedback = pgTable("spam_feedback", {
|
||||
id: uuid("id").defaultRandom().primaryKey(),
|
||||
userId: uuid("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
|
||||
export const spamFeedback = sqliteTable("spam_feedback", {
|
||||
id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
|
||||
userId: text("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
|
||||
phoneNumber: text("phone_number").notNull(),
|
||||
phoneNumberHash: text("phone_number_hash").notNull(),
|
||||
isSpam: boolean("is_spam").notNull(),
|
||||
confidence: doublePrecision("confidence"),
|
||||
feedbackType: feedbackType("feedback_type").notNull(),
|
||||
metadata: jsonb("metadata"),
|
||||
createdAt: timestamp("created_at", { withTimezone: true, mode: "date" }).defaultNow().notNull(),
|
||||
updatedAt: timestamp("updated_at", { withTimezone: true, mode: "date" }).defaultNow().notNull().$onUpdate(() => new Date()),
|
||||
isSpam: integer("is_spam", { mode: "boolean" }).notNull(),
|
||||
confidence: real("confidence"),
|
||||
feedbackType: text("feedback_type").notNull(),
|
||||
metadata: text("metadata", { mode: "json" }),
|
||||
createdAt: integer("created_at", { mode: "timestamp_ms" }).defaultNow().notNull(),
|
||||
updatedAt: integer("updated_at", { mode: "timestamp_ms" }).defaultNow().notNull().$onUpdate(() => new Date()),
|
||||
}, (table) => ({
|
||||
userIdIdx: index("spam_feedback_user_id_idx").on(table.userId),
|
||||
phoneNumberHashIdx: index("spam_feedback_phone_number_hash_idx").on(table.phoneNumberHash),
|
||||
isSpamIdx: index("spam_feedback_is_spam_idx").on(table.isSpam),
|
||||
}));
|
||||
|
||||
export const spamRules = pgTable("spam_rules", {
|
||||
id: uuid("id").defaultRandom().primaryKey(),
|
||||
userId: uuid("user_id").references(() => users.id, { onDelete: "cascade" }),
|
||||
isGlobal: boolean("is_global").default(false).notNull(),
|
||||
ruleType: ruleType("rule_type").notNull(),
|
||||
export const spamRules = sqliteTable("spam_rules", {
|
||||
id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
|
||||
userId: text("user_id").references(() => users.id, { onDelete: "cascade" }),
|
||||
isGlobal: integer("is_global", { mode: "boolean" }).default(false).notNull(),
|
||||
ruleType: text("rule_type").notNull(),
|
||||
pattern: text("pattern").notNull(),
|
||||
action: ruleAction("action").notNull(),
|
||||
action: text("action").notNull(),
|
||||
priority: integer("priority").default(0).notNull(),
|
||||
isActive: boolean("is_active").default(true).notNull(),
|
||||
createdAt: timestamp("created_at", { withTimezone: true, mode: "date" }).defaultNow().notNull(),
|
||||
updatedAt: timestamp("updated_at", { withTimezone: true, mode: "date" }).defaultNow().notNull().$onUpdate(() => new Date()),
|
||||
isActive: integer("is_active", { mode: "boolean" }).default(true).notNull(),
|
||||
createdAt: integer("created_at", { mode: "timestamp_ms" }).defaultNow().notNull(),
|
||||
updatedAt: integer("updated_at", { mode: "timestamp_ms" }).defaultNow().notNull().$onUpdate(() => new Date()),
|
||||
}, (table) => ({
|
||||
userIdIdx: index("spam_rules_user_id_idx").on(table.userId),
|
||||
isGlobalIdx: index("spam_rules_is_global_idx").on(table.isGlobal),
|
||||
|
||||
@@ -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 { familyMemberRole, subscriptionTier, subscriptionStatus } from "./enums";
|
||||
|
||||
export const familyGroups = pgTable("family_groups", {
|
||||
id: uuid("id").defaultRandom().primaryKey(),
|
||||
export const familyGroups = sqliteTable("family_groups", {
|
||||
id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
|
||||
name: text("name").notNull(),
|
||||
ownerId: uuid("owner_id").notNull().references(() => users.id),
|
||||
createdAt: timestamp("created_at", { withTimezone: true, mode: "date" }).defaultNow().notNull(),
|
||||
updatedAt: timestamp("updated_at", { withTimezone: true, mode: "date" }).defaultNow().notNull().$onUpdate(() => new Date()),
|
||||
ownerId: text("owner_id").notNull().references(() => users.id),
|
||||
createdAt: integer("created_at", { mode: "timestamp_ms" }).defaultNow().notNull(),
|
||||
updatedAt: integer("updated_at", { mode: "timestamp_ms" }).defaultNow().notNull().$onUpdate(() => new Date()),
|
||||
}, (table) => ({
|
||||
ownerIdIdx: index("family_groups_owner_id_idx").on(table.ownerId),
|
||||
nameIdx: index("family_groups_name_idx").on(table.name),
|
||||
}));
|
||||
|
||||
export const familyGroupMembers = pgTable("family_group_members", {
|
||||
id: uuid("id").defaultRandom().primaryKey(),
|
||||
groupId: uuid("group_id").notNull().references(() => familyGroups.id, { onDelete: "cascade" }),
|
||||
userId: uuid("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
|
||||
role: familyMemberRole("role").default("member").notNull(),
|
||||
joinedAt: timestamp("joined_at", { withTimezone: true, mode: "date" }).defaultNow().notNull(),
|
||||
createdAt: timestamp("created_at", { withTimezone: true, mode: "date" }).defaultNow().notNull(),
|
||||
updatedAt: timestamp("updated_at", { withTimezone: true, mode: "date" }).defaultNow().notNull().$onUpdate(() => new Date()),
|
||||
export const familyGroupMembers = sqliteTable("family_group_members", {
|
||||
id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
|
||||
groupId: text("group_id").notNull().references(() => familyGroups.id, { onDelete: "cascade" }),
|
||||
userId: text("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
|
||||
role: text("role").default("member").notNull(),
|
||||
joinedAt: integer("joined_at", { mode: "timestamp_ms" }).defaultNow().notNull(),
|
||||
createdAt: integer("created_at", { mode: "timestamp_ms" }).defaultNow().notNull(),
|
||||
updatedAt: integer("updated_at", { mode: "timestamp_ms" }).defaultNow().notNull().$onUpdate(() => new Date()),
|
||||
}, (table) => ({
|
||||
groupUserUnique: uniqueIndex("family_group_members_group_user_unique").on(table.groupId, table.userId),
|
||||
groupIdIdx: index("family_group_members_group_id_idx").on(table.groupId),
|
||||
userIdIdx: index("family_group_members_user_id_idx").on(table.userId),
|
||||
}));
|
||||
|
||||
export const subscriptions = pgTable("subscriptions", {
|
||||
id: uuid("id").defaultRandom().primaryKey(),
|
||||
userId: uuid("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
|
||||
familyGroupId: uuid("family_group_id").references(() => familyGroups.id),
|
||||
export const subscriptions = sqliteTable("subscriptions", {
|
||||
id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
|
||||
userId: text("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
|
||||
familyGroupId: text("family_group_id").references(() => familyGroups.id),
|
||||
stripeId: text("stripe_id").unique(),
|
||||
tier: subscriptionTier("tier").default("basic").notNull(),
|
||||
status: subscriptionStatus("status").default("active").notNull(),
|
||||
currentPeriodStart: timestamp("current_period_start", { withTimezone: true, mode: "date" }).notNull(),
|
||||
currentPeriodEnd: timestamp("current_period_end", { withTimezone: true, mode: "date" }).notNull(),
|
||||
cancelAtPeriodEnd: boolean("cancel_at_period_end").default(false).notNull(),
|
||||
createdAt: timestamp("created_at", { withTimezone: true, mode: "date" }).defaultNow().notNull(),
|
||||
updatedAt: timestamp("updated_at", { withTimezone: true, mode: "date" }).defaultNow().notNull().$onUpdate(() => new Date()),
|
||||
tier: text("tier").default("basic").notNull(),
|
||||
status: text("status").default("active").notNull(),
|
||||
currentPeriodStart: integer("current_period_start", { mode: "timestamp_ms" }).notNull(),
|
||||
currentPeriodEnd: integer("current_period_end", { mode: "timestamp_ms" }).notNull(),
|
||||
cancelAtPeriodEnd: integer("cancel_at_period_end", { mode: "boolean" }).default(false).notNull(),
|
||||
createdAt: integer("created_at", { mode: "timestamp_ms" }).defaultNow().notNull(),
|
||||
updatedAt: integer("updated_at", { mode: "timestamp_ms" }).defaultNow().notNull().$onUpdate(() => new Date()),
|
||||
}, (table) => ({
|
||||
userIdIdx: index("subscriptions_user_id_idx").on(table.userId),
|
||||
familyGroupIdIdx: index("subscriptions_family_group_id_idx").on(table.familyGroupId),
|
||||
|
||||
@@ -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 { detectionVerdict, analysisType, analysisJobStatus } from "./enums";
|
||||
|
||||
export const voiceEnrollments = pgTable("voice_enrollments", {
|
||||
id: uuid("id").defaultRandom().primaryKey(),
|
||||
userId: uuid("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
|
||||
export const voiceEnrollments = sqliteTable("voice_enrollments", {
|
||||
id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
|
||||
userId: text("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
|
||||
name: text("name").notNull(),
|
||||
voiceHash: text("voice_hash").notNull(),
|
||||
audioMetadata: jsonb("audio_metadata"),
|
||||
isActive: boolean("is_active").default(true).notNull(),
|
||||
createdAt: timestamp("created_at", { withTimezone: true, mode: "date" }).defaultNow().notNull(),
|
||||
updatedAt: timestamp("updated_at", { withTimezone: true, mode: "date" }).defaultNow().notNull().$onUpdate(() => new Date()),
|
||||
audioMetadata: text("audio_metadata", { mode: "json" }),
|
||||
isActive: integer("is_active", { mode: "boolean" }).default(true).notNull(),
|
||||
createdAt: integer("created_at", { mode: "timestamp_ms" }).defaultNow().notNull(),
|
||||
updatedAt: integer("updated_at", { mode: "timestamp_ms" }).defaultNow().notNull().$onUpdate(() => new Date()),
|
||||
}, (table) => ({
|
||||
userIdIdx: index("voice_enrollments_user_id_idx").on(table.userId),
|
||||
voiceHashIdx: index("voice_enrollments_voice_hash_idx").on(table.voiceHash),
|
||||
}));
|
||||
|
||||
export const voiceAnalyses = pgTable("voice_analyses", {
|
||||
id: uuid("id").defaultRandom().primaryKey(),
|
||||
enrollmentId: uuid("enrollment_id").references(() => voiceEnrollments.id),
|
||||
userId: uuid("user_id").notNull().references(() => users.id),
|
||||
export const voiceAnalyses = sqliteTable("voice_analyses", {
|
||||
id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
|
||||
enrollmentId: text("enrollment_id").references(() => voiceEnrollments.id),
|
||||
userId: text("user_id").notNull().references(() => users.id),
|
||||
audioHash: text("audio_hash").notNull(),
|
||||
isSynthetic: boolean("is_synthetic").notNull(),
|
||||
confidence: doublePrecision("confidence").notNull(),
|
||||
analysisResult: jsonb("analysis_result").notNull(),
|
||||
isSynthetic: integer("is_synthetic", { mode: "boolean" }).notNull(),
|
||||
confidence: real("confidence").notNull(),
|
||||
analysisResult: text("analysis_result", { mode: "json" }).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) => ({
|
||||
userIdIdx: index("voice_analyses_user_id_idx").on(table.userId),
|
||||
enrollmentIdIdx: index("voice_analyses_enrollment_id_idx").on(table.enrollmentId),
|
||||
audioHashIdx: index("voice_analyses_audio_hash_idx").on(table.audioHash),
|
||||
}));
|
||||
|
||||
export const analysisJobs = pgTable("analysis_jobs", {
|
||||
id: uuid("id").defaultRandom().primaryKey(),
|
||||
userId: uuid("user_id").notNull().references(() => users.id),
|
||||
analysisType: analysisType("analysis_type").notNull(),
|
||||
export const analysisJobs = sqliteTable("analysis_jobs", {
|
||||
id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
|
||||
userId: text("user_id").notNull().references(() => users.id),
|
||||
analysisType: text("analysis_type").notNull(),
|
||||
audioFilePath: text("audio_file_path").notNull(),
|
||||
status: analysisJobStatus("status").notNull(),
|
||||
status: text("status").notNull(),
|
||||
errorMessage: text("error_message"),
|
||||
completedAt: timestamp("completed_at", { withTimezone: true, mode: "date" }),
|
||||
createdAt: timestamp("created_at", { withTimezone: true, mode: "date" }).defaultNow().notNull(),
|
||||
completedAt: integer("completed_at", { mode: "timestamp_ms" }),
|
||||
createdAt: integer("created_at", { mode: "timestamp_ms" }).defaultNow().notNull(),
|
||||
}, (table) => ({
|
||||
userIdIdx: index("analysis_jobs_user_id_idx").on(table.userId),
|
||||
statusIdx: index("analysis_jobs_status_idx").on(table.status),
|
||||
createdAtIdx: index("analysis_jobs_created_at_idx").on(table.createdAt),
|
||||
}));
|
||||
|
||||
export const analysisResults = pgTable("analysis_results", {
|
||||
id: uuid("id").defaultRandom().primaryKey(),
|
||||
analysisJobId: uuid("analysis_job_id").notNull().unique().references(() => analysisJobs.id),
|
||||
syntheticScore: doublePrecision("synthetic_score").notNull(),
|
||||
verdict: detectionVerdict("verdict").notNull(),
|
||||
confidence: doublePrecision("confidence").notNull(),
|
||||
export const analysisResults = sqliteTable("analysis_results", {
|
||||
id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
|
||||
analysisJobId: text("analysis_job_id").notNull().unique().references(() => analysisJobs.id),
|
||||
syntheticScore: real("synthetic_score").notNull(),
|
||||
verdict: text("verdict").notNull(),
|
||||
confidence: real("confidence").notNull(),
|
||||
processingTimeMs: integer("processing_time_ms").notNull(),
|
||||
matchedEnrollmentId: text("matched_enrollment_id"),
|
||||
matchedSimilarity: doublePrecision("matched_similarity"),
|
||||
matchedSimilarity: real("matched_similarity"),
|
||||
modelVersion: text("model_version"),
|
||||
createdAt: timestamp("created_at", { withTimezone: true, mode: "date" }).defaultNow().notNull(),
|
||||
createdAt: integer("created_at", { mode: "timestamp_ms" }).defaultNow().notNull(),
|
||||
}, (table) => ({
|
||||
analysisJobIdIdx: index("analysis_results_analysis_job_id_idx").on(table.analysisJobId),
|
||||
syntheticScoreIdx: index("analysis_results_synthetic_score_idx").on(table.syntheticScore),
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { and, eq, gte, inArray, not, sql } from "drizzle-orm";
|
||||
import type { NodePgDatabase } from "drizzle-orm/node-postgres";
|
||||
import { db } from "~/server/db";
|
||||
import { normalizedAlerts, correlationGroups } from "~/server/db/schema";
|
||||
import type { NormalizedAlertInput, EntitySet } from "./normalizer";
|
||||
|
||||
Reference in New Issue
Block a user