import { boolean, bigint, index, integer, jsonb, pgEnum, pgTable, text, timestamp, uniqueIndex, uuid, } from "drizzle-orm/pg-core"; import { sql } from "drizzle-orm"; export const instanceStatus = pgEnum("instance_status", ["active", "blocked", "disabled"]); export const payloadMode = pgEnum("payload_mode", ["minimal", "rich"]); export const devicePlatform = pgEnum("device_platform", ["web", "ios", "android"]); export const deviceStatus = pgEnum("device_status", ["active", "disabled", "invalid"]); export const deliveryStatus = pgEnum("delivery_status", ["accepted", "processing", "completed", "failed", "partial"]); export const attemptStatus = pgEnum("attempt_status", ["pending", "sent", "failed", "skipped"]); export const attemptProvider = pgEnum("attempt_provider", ["web_push", "apns", "fcm"]); export const centralService = pgEnum("central_service", ["ai", "banking"]); export const usageStatus = pgEnum("usage_status", ["succeeded", "failed"]); export const pushInstances = pgTable( "push_instances", { id: uuid("id").primaryKey().defaultRandom(), instanceId: text("instance_id").notNull().unique(), name: text("name").notNull(), baseUrl: text("base_url").notNull(), status: instanceStatus("status").notNull().default("active"), mode: payloadMode("payload_mode").notNull().default("minimal"), capabilities: jsonb("capabilities").$type().notNull().default(sql`'[]'::jsonb`), rateLimitPerMinute: integer("rate_limit_per_minute").notNull().default(120), dailyQuota: integer("daily_quota").notNull().default(10000), currentSecretEncrypted: text("current_secret_encrypted").notNull(), currentSecretPreview: text("current_secret_preview").notNull(), nextSecretEncrypted: text("next_secret_encrypted"), nextSecretPreview: text("next_secret_preview"), notes: text("notes"), lastHeartbeatAt: timestamp("last_heartbeat_at", { withTimezone: true }), lastHeartbeatVersion: text("last_heartbeat_version"), lastHeartbeatIp: text("last_heartbeat_ip"), createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(), updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(), }, (table) => ({ instanceIdIdx: uniqueIndex("push_instances_instance_id_idx").on(table.instanceId), statusIdx: index("push_instances_status_idx").on(table.status), }), ); export const pushDevices = pgTable( "push_devices", { id: uuid("id").primaryKey().defaultRandom(), centralDeviceId: text("central_device_id").notNull().unique(), instanceId: uuid("instance_id") .notNull() .references(() => pushInstances.id, { onDelete: "cascade" }), localDeviceId: text("local_device_id").notNull(), platform: devicePlatform("platform").notNull(), status: deviceStatus("status").notNull().default("active"), providerTokenEncrypted: text("provider_token_encrypted"), webPushSubscription: jsonb("web_push_subscription").$type>(), meta: jsonb("meta").$type>().notNull().default(sql`'{}'::jsonb`), lastSeenAt: timestamp("last_seen_at", { withTimezone: true }).notNull().defaultNow(), disabledAt: timestamp("disabled_at", { withTimezone: true }), createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(), updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(), }, (table) => ({ centralDeviceIdIdx: uniqueIndex("push_devices_central_device_id_idx").on(table.centralDeviceId), instanceLocalDeviceIdx: uniqueIndex("push_devices_instance_local_device_idx").on(table.instanceId, table.localDeviceId), instanceStatusIdx: index("push_devices_instance_status_idx").on(table.instanceId, table.status), }), ); export const deliveryJobs = pgTable( "delivery_jobs", { id: uuid("id").primaryKey().defaultRandom(), deliveryJobId: text("delivery_job_id").notNull().unique(), instanceId: uuid("instance_id") .notNull() .references(() => pushInstances.id, { onDelete: "cascade" }), idempotencyKey: text("idempotency_key").notNull(), status: deliveryStatus("status").notNull().default("accepted"), priority: text("priority").notNull().default("normal"), collapseKey: text("collapse_key"), ttlSeconds: integer("ttl_seconds").notNull().default(3600), acceptedCount: integer("accepted_count").notNull().default(0), rejectedCount: integer("rejected_count").notNull().default(0), sentCount: integer("sent_count").notNull().default(0), failedCount: integer("failed_count").notNull().default(0), lastErrorCode: text("last_error_code"), lastErrorMessage: text("last_error_message"), completedAt: timestamp("completed_at", { withTimezone: true }), createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(), updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(), }, (table) => ({ deliveryJobIdIdx: uniqueIndex("delivery_jobs_delivery_job_id_idx").on(table.deliveryJobId), instanceIdempotencyIdx: uniqueIndex("delivery_jobs_instance_idempotency_idx").on(table.instanceId, table.idempotencyKey), instanceCreatedIdx: index("delivery_jobs_instance_created_idx").on(table.instanceId, table.createdAt), }), ); export const deliveryAttempts = pgTable( "delivery_attempts", { id: uuid("id").primaryKey().defaultRandom(), deliveryJobId: uuid("delivery_job_id") .notNull() .references(() => deliveryJobs.id, { onDelete: "cascade" }), deviceId: uuid("device_id") .notNull() .references(() => pushDevices.id, { onDelete: "cascade" }), provider: attemptProvider("provider").notNull(), status: attemptStatus("status").notNull().default("pending"), providerMessageId: text("provider_message_id"), errorCode: text("error_code"), errorMessage: text("error_message"), sentAt: timestamp("sent_at", { withTimezone: true }), createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(), }, (table) => ({ jobIdx: index("delivery_attempts_job_idx").on(table.deliveryJobId), deviceIdx: index("delivery_attempts_device_idx").on(table.deviceId), }), ); export const auditLogs = pgTable( "audit_logs", { id: uuid("id").primaryKey().defaultRandom(), actor: text("actor").notNull(), action: text("action").notNull(), instanceId: uuid("instance_id").references(() => pushInstances.id, { onDelete: "set null" }), meta: jsonb("meta").$type>().notNull().default(sql`'{}'::jsonb`), createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(), }, (table) => ({ instanceCreatedIdx: index("audit_logs_instance_created_idx").on(table.instanceId, table.createdAt), }), ); export const serviceEntitlements = pgTable( "service_entitlements", { id: uuid("id").primaryKey().defaultRandom(), instanceId: uuid("instance_id") .notNull() .references(() => pushInstances.id, { onDelete: "cascade" }), service: centralService("service").notNull(), enabled: boolean("enabled").notNull().default(false), monthlyLimit: bigint("monthly_limit", { mode: "number" }), unitPriceMicros: bigint("unit_price_micros", { mode: "number" }).notNull().default(0), metadata: jsonb("metadata").$type>().notNull().default(sql`'{}'::jsonb`), createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(), updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(), }, (table) => ({ instanceServiceIdx: uniqueIndex("service_entitlements_instance_service_idx").on(table.instanceId, table.service), }), ); export const serviceUsageEvents = pgTable( "service_usage_events", { id: uuid("id").primaryKey().defaultRandom(), requestId: text("request_id").notNull().unique(), instanceId: uuid("instance_id") .notNull() .references(() => pushInstances.id, { onDelete: "cascade" }), service: centralService("service").notNull(), operation: text("operation").notNull(), units: bigint("units", { mode: "number" }).notNull().default(1), costMicros: bigint("cost_micros", { mode: "number" }).notNull().default(0), status: usageStatus("status").notNull(), provider: text("provider"), providerRequestId: text("provider_request_id"), errorCode: text("error_code"), metadata: jsonb("metadata").$type>().notNull().default(sql`'{}'::jsonb`), createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(), }, (table) => ({ requestIdIdx: uniqueIndex("service_usage_events_request_id_idx").on(table.requestId), instanceServiceCreatedIdx: index("service_usage_events_instance_service_created_idx").on(table.instanceId, table.service, table.createdAt), }), ); export type PushInstance = typeof pushInstances.$inferSelect; export type NewPushInstance = typeof pushInstances.$inferInsert; export type PushDevice = typeof pushDevices.$inferSelect; export type NewPushDevice = typeof pushDevices.$inferInsert; export type DeliveryJob = typeof deliveryJobs.$inferSelect; export type NewDeliveryJob = typeof deliveryJobs.$inferInsert; export type DeliveryAttempt = typeof deliveryAttempts.$inferSelect; export type NewDeliveryAttempt = typeof deliveryAttempts.$inferInsert; export type ServiceEntitlement = typeof serviceEntitlements.$inferSelect; export type ServiceUsageEvent = typeof serviceUsageEvents.$inferSelect;