import { userTimers } from "@/db/schema"; import { eq, and, lt } from "drizzle-orm"; import { DrizzleClient } from "@/lib/DrizzleClient"; export type TimerType = 'COOLDOWN' | 'EFFECT' | 'ACCESS'; export const userTimerService = { /** * Set a timer for a user. * Upserts the timer (updates expiration if exists). */ setTimer: async (userId: string, type: TimerType, key: string, durationMs: number, metadata: any = {}, tx?: any) => { const execute = async (txFn: any) => { const expiresAt = new Date(Date.now() + durationMs); await txFn.insert(userTimers) .values({ userId: BigInt(userId), type, key, expiresAt, metadata, }) .onConflictDoUpdate({ target: [userTimers.userId, userTimers.type, userTimers.key], set: { expiresAt, metadata }, // Update metadata too on re-set }); return expiresAt; }; if (tx) { return await execute(tx); } else { return await DrizzleClient.transaction(async (t) => { return await execute(t); }); } }, /** * Check if a timer is active (not expired). * Returns true if ACTIVE. */ checkTimer: async (userId: string, type: TimerType, key: string, tx?: any): Promise => { const uniqueTx = tx || DrizzleClient; // Optimization: Read-only doesn't strictly need transaction wrapper overhead if single query const timer = await uniqueTx.query.userTimers.findFirst({ where: and( eq(userTimers.userId, BigInt(userId)), eq(userTimers.type, type), eq(userTimers.key, key) ), }); if (!timer) return false; return timer.expiresAt > new Date(); }, /** * Get timer details including metadata and expiry. */ getTimer: async (userId: string, type: TimerType, key: string, tx?: any) => { const uniqueTx = tx || DrizzleClient; return await uniqueTx.query.userTimers.findFirst({ where: and( eq(userTimers.userId, BigInt(userId)), eq(userTimers.type, type), eq(userTimers.key, key) ), }); }, /** * Manually clear a timer. */ clearTimer: async (userId: string, type: TimerType, key: string, tx?: any) => { const execute = async (txFn: any) => { await txFn.delete(userTimers) .where(and( eq(userTimers.userId, BigInt(userId)), eq(userTimers.type, type), eq(userTimers.key, key) )); }; if (tx) { return await execute(tx); } else { return await DrizzleClient.transaction(async (t) => { return await execute(t); }); } } };