import { pgTable, text, integer, timestamp, index, uniqueIndex } from 'drizzle-orm/pg-core'; import { ports } from './ports'; import { user } from './users'; import { documentSends } from './brochures'; /** * Phase 4c — tracked redirect links. A short URL `/q/` records a * click and 302s the recipient on to `targetUrl`. The matching click * row is fire-and-forget so the redirect stays snappy; an aggregate * `clickCount` on the parent row keeps "was clicked at all" queries * cheap. * * `sendId` is the optional link back to the originating outbound email * — set when the link is minted via the email-composer flow so reps can * see per-email click-throughs. Manual one-off short links leave it null. */ export const trackedLinks = pgTable( 'tracked_links', { id: text('id') .primaryKey() .$defaultFn(() => crypto.randomUUID()), portId: text('port_id') .notNull() .references(() => ports.id), slug: text('slug').notNull(), targetUrl: text('target_url').notNull(), sendId: text('send_id').references(() => documentSends.id, { onDelete: 'set null' }), clickCount: integer('click_count').notNull().default(0), firstClickedAt: timestamp('first_clicked_at', { withTimezone: true }), lastClickedAt: timestamp('last_clicked_at', { withTimezone: true }), createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(), createdByUserId: text('created_by_user_id').references(() => user.id, { onDelete: 'set null' }), }, (t) => [ uniqueIndex('uniq_tracked_links_slug').on(t.slug), index('idx_tracked_links_send').on(t.sendId), index('idx_tracked_links_port').on(t.portId, t.createdAt), ], ); /** Per-click log. Apple Mail privacy proxy will pre-fetch tracked link * URLs the same way it does pixels — clicks from iOS users are * over-counted. Standard email-tracking caveats apply. */ export const trackedLinkClicks = pgTable( 'tracked_link_clicks', { id: text('id') .primaryKey() .$defaultFn(() => crypto.randomUUID()), trackedLinkId: text('tracked_link_id') .notNull() .references(() => trackedLinks.id, { onDelete: 'cascade' }), portId: text('port_id') .notNull() .references(() => ports.id), clickedAt: timestamp('clicked_at', { withTimezone: true }).notNull().defaultNow(), userAgent: text('user_agent'), referer: text('referer'), }, (t) => [ index('idx_tlc_link').on(t.trackedLinkId, t.clickedAt), index('idx_tlc_port').on(t.portId, t.clickedAt), ], ); export type TrackedLink = typeof trackedLinks.$inferSelect; export type NewTrackedLink = typeof trackedLinks.$inferInsert; export type TrackedLinkClick = typeof trackedLinkClicks.$inferSelect; export type NewTrackedLinkClick = typeof trackedLinkClicks.$inferInsert;