168 lines
10 KiB
TypeScript
168 lines
10 KiB
TypeScript
import { sqliteTable, text, integer, index } from "drizzle-orm/sqlite-core";
|
|
import { subscriptions } from "./subscription";
|
|
|
|
export const infoBrokers = sqliteTable("info_brokers", {
|
|
id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
|
|
name: text("name").notNull(),
|
|
domain: text("domain").notNull().unique(),
|
|
category: text("category").notNull(),
|
|
removalMethod: text("removal_method").notNull(),
|
|
removalUrl: text("removal_url"),
|
|
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: 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 = 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: 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: 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),
|
|
statusIdx: index("removal_requests_status_idx").on(table.status),
|
|
submittedAtIdx: index("removal_requests_submitted_at_idx").on(table.submittedAt),
|
|
subscriptionIdStatusIdx: index("removal_requests_sub_id_status_idx").on(table.subscriptionId, table.status),
|
|
}));
|
|
|
|
export const captchaEvents = sqliteTable("captcha_events", {
|
|
id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
|
|
removalRequestId: text("removal_request_id").references(() => removalRequests.id, { onDelete: "set null" }),
|
|
brokerId: text("broker_id").notNull().references(() => infoBrokers.id, { onDelete: "cascade" }),
|
|
captchaType: text("captcha_type").notNull(), // recaptcha_v2, recaptcha_v3, hcaptcha, image_challenge, turnstile
|
|
status: text("status").notNull().default("detected"), // detected, solving, solved, failed, escalated
|
|
solverProvider: text("solver_provider"), // 2captcha, anticaptcha, manual
|
|
solveAttempts: integer("solve_attempts").default(0).notNull(),
|
|
costInCents: integer("cost_in_cents"),
|
|
error: text("error"),
|
|
solvedAt: integer("solved_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) => ({
|
|
removalRequestIdIdx: index("captcha_events_removal_request_id_idx").on(table.removalRequestId),
|
|
brokerIdIdx: index("captcha_events_broker_id_idx").on(table.brokerId),
|
|
statusIdx: index("captcha_events_status_idx").on(table.status),
|
|
createdAtIdx: index("captcha_events_created_at_idx").on(table.createdAt),
|
|
}));
|
|
|
|
export const emailVerifications = sqliteTable("email_verifications", {
|
|
id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
|
|
removalRequestId: text("removal_request_id").references(() => removalRequests.id, { onDelete: "set null" }),
|
|
brokerId: text("broker_id").notNull().references(() => infoBrokers.id, { onDelete: "cascade" }),
|
|
emailTo: text("email_to").notNull(),
|
|
emailFrom: text("email_from"),
|
|
emailSubject: text("email_subject"),
|
|
confirmationUrl: text("confirmation_url"),
|
|
status: text("status").notNull().default("pending"), // pending, confirmed, expired, failed
|
|
clickedAt: integer("clicked_at", { mode: "timestamp_ms" }),
|
|
confirmedAt: integer("confirmed_at", { mode: "timestamp_ms" }),
|
|
expiresAt: integer("expires_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) => ({
|
|
removalRequestIdIdx: index("email_verifications_removal_request_id_idx").on(table.removalRequestId),
|
|
brokerIdIdx: index("email_verifications_broker_id_idx").on(table.brokerId),
|
|
statusIdx: index("email_verifications_status_idx").on(table.status),
|
|
createdAtIdx: index("email_verifications_created_at_idx").on(table.createdAt),
|
|
emailToIdx: index("email_verifications_email_to_idx").on(table.emailTo),
|
|
}));
|
|
|
|
export const reScanResults = sqliteTable("re_scan_results", {
|
|
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, { onDelete: "set null" }),
|
|
wasRemoved: integer("was_removed", { mode: "boolean" }).notNull(),
|
|
isReListed: integer("is_re_listed", { mode: "boolean" }).default(false).notNull(),
|
|
profileUrl: text("profile_url"),
|
|
scanType: text("scan_type").notNull(), // initial_scan, status_check, weekly_rescan, re_listing_detected
|
|
createdAt: integer("created_at", { mode: "timestamp_ms" }).defaultNow().notNull(),
|
|
}, (table) => ({
|
|
subscriptionIdIdx: index("re_scan_results_subscription_id_idx").on(table.subscriptionId),
|
|
brokerIdIdx: index("re_scan_results_broker_id_idx").on(table.brokerId),
|
|
isReListedIdx: index("re_scan_results_is_re_listed_idx").on(table.isReListed),
|
|
scanTypeIdx: index("re_scan_results_scan_type_idx").on(table.scanType),
|
|
createdAtIdx: index("re_scan_results_created_at_idx").on(table.createdAt),
|
|
}));
|
|
|
|
export const adapterHealth = sqliteTable("adapter_health", {
|
|
id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
|
|
brokerId: text("broker_id").notNull().references(() => infoBrokers.id, { onDelete: "cascade" }),
|
|
brokerName: text("broker_name").notNull(),
|
|
status: text("status").notNull().default("healthy"), // healthy, degraded, broken, disabled
|
|
successCount: integer("success_count").default(0).notNull(),
|
|
failureCount: integer("failure_count").default(0).notNull(),
|
|
lastSuccessAt: integer("last_success_at", { mode: "timestamp_ms" }),
|
|
lastFailureAt: integer("last_failure_at", { mode: "timestamp_ms" }),
|
|
lastError: text("last_error"),
|
|
failureRate24h: integer("failure_rate_24h"),
|
|
totalOps24h: integer("total_ops_24h").default(0),
|
|
isAutoDisabled: integer("is_auto_disabled", { 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) => ({
|
|
brokerIdIdx: index("adapter_health_broker_id_idx").on(table.brokerId),
|
|
statusIdx: index("adapter_health_status_idx").on(table.status),
|
|
isAutoDisabledIdx: index("adapter_health_is_auto_disabled_idx").on(table.isAutoDisabled),
|
|
failureRate24hIdx: index("adapter_health_failure_rate_24h_idx").on(table.failureRate24h),
|
|
}));
|
|
|
|
export const costTracking = sqliteTable("cost_tracking", {
|
|
id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
|
|
subscriptionId: text("subscription_id").references(() => subscriptions.id, { onDelete: "set null" }),
|
|
proxyProvider: text("proxy_provider"),
|
|
captchaSolver: text("captcha_solver"),
|
|
proxyRequests: integer("proxy_requests").default(0).notNull(),
|
|
captchaSolves: integer("captcha_solves").default(0).notNull(),
|
|
captchaCostCents: integer("captcha_cost_cents").default(0).notNull(),
|
|
proxyCostCents: integer("proxy_cost_cents").default(0).notNull(),
|
|
totalCostCents: integer("total_cost_cents").default(0).notNull(),
|
|
periodStart: integer("period_start", { mode: "timestamp_ms" }).notNull(),
|
|
periodEnd: integer("period_end", { mode: "timestamp_ms" }).notNull(),
|
|
createdAt: integer("created_at", { mode: "timestamp_ms" }).defaultNow().notNull(),
|
|
}, (table) => ({
|
|
subscriptionIdIdx: index("cost_tracking_subscription_id_idx").on(table.subscriptionId),
|
|
periodStartIdx: index("cost_tracking_period_start_idx").on(table.periodStart),
|
|
periodEndIdx: index("cost_tracking_period_end_idx").on(table.periodEnd),
|
|
}));
|
|
|
|
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: text("data_found", { mode: "json" }).notNull(),
|
|
screenshotUrl: text("screenshot_url"),
|
|
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),
|
|
removalRequestIdIdx: index("broker_listings_removal_request_id_idx").on(table.removalRequestId),
|
|
isRemovedIdx: index("broker_listings_is_removed_idx").on(table.isRemoved),
|
|
subscriptionIdIsRemovedIdx: index("broker_listings_sub_id_is_removed_idx").on(table.subscriptionId, table.isRemoved),
|
|
}));
|