import { index, integer, jsonb, pgTable, text, timestamp, uniqueIndex, uuid } from 'drizzle-orm/pg-core'; import { channelLinks } from './crm'; import { users } from './identity'; /** * External delivery is a separate durable concern from the CRM transaction. * A deal write only adds a row here; a provider-specific worker owns network * failure, retry and completion without holding the request thread open. */ export const notificationOutbox = pgTable( 'notification_outbox', { id: uuid('id').primaryKey().defaultRandom(), provider: text('provider').notNull(), kind: text('kind').notNull(), linkId: uuid('link_id').references(() => channelLinks.id, { onDelete: 'set null' }), workspaceId: text('workspace_id'), destination: text('destination').notNull(), payload: jsonb('payload').$type>().notNull(), idempotencyKey: text('idempotency_key').notNull(), status: text('status').notNull().default('pending'), attempts: integer('attempts').notNull().default(0), maxAttempts: integer('max_attempts').notNull().default(5), dueAt: timestamp('due_at', { withTimezone: true }).notNull().defaultNow(), leasedUntil: timestamp('leased_until', { withTimezone: true }), leasedBy: text('leased_by'), deliveredAt: timestamp('delivered_at', { withTimezone: true }), externalId: text('external_id'), error: text('error'), requestedByUserId: uuid('requested_by_user_id').references(() => users.id, { onDelete: 'set null', }), createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(), }, (t) => [ uniqueIndex('notification_outbox_idempotency_key').on(t.idempotencyKey), index('notification_outbox_claimable_idx').on(t.provider, t.status, t.dueAt), index('notification_outbox_link_idx').on(t.linkId), ], ); export type NotificationOutboxItem = typeof notificationOutbox.$inferSelect;