import { and, eq } from "drizzle-orm"; import { trustedDevices } from "../db/schema.js"; import type { DatabaseContext } from "./database-context.js"; export type TrustedDeviceRecord = typeof trustedDevices.$inferSelect; export type NewTrustedDeviceRecord = typeof trustedDevices.$inferInsert; export class TrustedDeviceRepository { constructor( private readonly context: DatabaseContext, private readonly onWrite?: () => void | Promise, ) {} async findByUserAndFingerprint( userId: string, deviceFingerprint: string, ): Promise { const rows = await this.context.drizzle .select() .from(trustedDevices) .where( and( eq(trustedDevices.userId, userId), eq(trustedDevices.deviceFingerprint, deviceFingerprint), ), ) .limit(1); return rows[0] ?? null; } async touch( userId: string, deviceFingerprint: string, lastUsedAt = new Date().toISOString(), ): Promise { await this.context.drizzle .update(trustedDevices) .set({ lastUsedAt }) .where( and( eq(trustedDevices.userId, userId), eq(trustedDevices.deviceFingerprint, deviceFingerprint), ), ); await this.afterWrite(); } async upsert(device: NewTrustedDeviceRecord): Promise { const existing = await this.findByUserAndFingerprint( device.userId, device.deviceFingerprint, ); if (existing) { await this.context.drizzle .update(trustedDevices) .set({ expiresAt: device.expiresAt, lastUsedAt: device.lastUsedAt, }) .where( and( eq(trustedDevices.userId, device.userId), eq(trustedDevices.deviceFingerprint, device.deviceFingerprint), ), ); } else { await this.context.drizzle.insert(trustedDevices).values(device); } await this.afterWrite(); } async deleteByUserAndFingerprint( userId: string, deviceFingerprint: string, ): Promise { await this.context.drizzle .delete(trustedDevices) .where( and( eq(trustedDevices.userId, userId), eq(trustedDevices.deviceFingerprint, deviceFingerprint), ), ); await this.afterWrite(); } async deleteByUserId(userId: string): Promise { await this.context.drizzle .delete(trustedDevices) .where(eq(trustedDevices.userId, userId)); await this.afterWrite(); } private async afterWrite(): Promise { await this.onWrite?.(); } }