import { userTimers } from "@/db/schema"; import { eq, and, lt } from "drizzle-orm"; import { DrizzleClient } from "@/lib/DrizzleClient"; /** * The Janitor responsible for cleaning up expired ACCESS timers * and revoking privileges. */ export const schedulerService = { start: () => { console.log("๐Ÿ•’ Scheduler started: Janitor loop running every 60s"); // Run immediately on start schedulerService.runJanitor(); // Loop every 60 seconds setInterval(() => { schedulerService.runJanitor(); }, 60 * 1000); }, runJanitor: async () => { try { // Find all expired ACCESS timers // We do this in a transaction to ensure we read and delete atomically if possible, // though for this specific case, fetching then deleting is fine as long as we handle race conditions gracefully. const now = new Date(); const expiredAccess = await DrizzleClient.query.userTimers.findMany({ where: and( eq(userTimers.type, 'ACCESS'), lt(userTimers.expiresAt, now) ) }); if (expiredAccess.length === 0) return; console.log(`๐Ÿงน Janitor: Found ${expiredAccess.length} expired access timers.`); for (const timer of expiredAccess) { // TODO: Here we would call Discord API to remove roles/overwrites. const meta = timer.metadata as any; console.log(`๐Ÿšซ Revoking access for User ${timer.userId}: Key=${timer.key} (Channel: ${meta?.channelId || 'N/A'})`); // Delete the timer row await DrizzleClient.delete(userTimers) .where(and( eq(userTimers.userId, timer.userId), eq(userTimers.type, timer.type), eq(userTimers.key, timer.key) )); } } catch (error) { console.error("Janitor Error:", error); } } };