import { drizzle } from "drizzle-orm/postgres-js"; import postgres from "postgres"; import * as schema from "~/database/schema"; import { eq, inArray, or } from "drizzle-orm"; import { createDailySnapshot } from "~/models/standings"; // Create a dedicated database connection for the snapshot system const connectionString = process.env.DATABASE_URL; if (!connectionString) { throw new Error("DATABASE_URL is required for snapshot system"); } const client = postgres(connectionString); const db = drizzle(client, { schema }); let snapshotInterval: NodeJS.Timeout | null = null; const CHECK_INTERVAL = 24 * 60 * 60 * 1000; // Check once per day (in milliseconds) /** * Start the daily snapshot system * Runs once per day to create snapshots for all active seasons */ export function startSnapshotSystem(): void { if (snapshotInterval) { console.log("[Snapshots] Snapshot system already running"); return; } // Run immediately on startup void createDailySnapshots(); // Then run once per day snapshotInterval = setInterval(async () => { try { await createDailySnapshots(); } catch (error) { console.error("[Snapshots] Error creating daily snapshots:", error); } }, CHECK_INTERVAL); console.log("[Snapshots] Daily snapshot system started (runs once per day)"); } /** * Stop the snapshot system */ export function stopSnapshotSystem(): void { if (snapshotInterval) { clearInterval(snapshotInterval); snapshotInterval = null; console.log("[Snapshots] Snapshot system stopped"); } } /** * Create daily snapshots for all active seasons * Only creates snapshots if they don't already exist for today */ async function createDailySnapshots(): Promise { const now = new Date(); const today = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, "0")}-${String(now.getDate()).padStart(2, "0")}`; console.log(`[Snapshots] Checking for snapshots to create (${today})`); // Get all seasons that are active or in draft (we want to track standings for these) const activeSeasons = await db.query.seasons.findMany({ where: or( eq(schema.seasons.status, "active"), eq(schema.seasons.status, "draft") ), }); if (activeSeasons.length === 0) { console.log("[Snapshots] No active seasons found"); return; } console.log(`[Snapshots] Found ${activeSeasons.length} active season(s)`); for (const season of activeSeasons) { try { await createDailySnapshot(season.id, db); console.log(`[Snapshots] ✅ Upserted snapshot for season ${season.id}`); } catch (error) { console.error(`[Snapshots] Error creating snapshot for season ${season.id}:`, error); } } console.log("[Snapshots] Daily snapshot check complete"); } /** * Manually trigger snapshot creation for specific seasons * Useful for admin tools or manual triggers */ export async function createSnapshotsForSeasons(seasonIds: string[]): Promise { console.log(`[Snapshots] Manual trigger for ${seasonIds.length} season(s)`); for (const seasonId of seasonIds) { try { await createDailySnapshot(seasonId, db); console.log(`[Snapshots] ✅ Created snapshot for season ${seasonId}`); } catch (error) { console.error(`[Snapshots] Error creating snapshot for season ${seasonId}:`, error); throw error; } } } /** * Manually trigger snapshot creation for all active seasons * Useful for testing or manual refreshes */ export async function createSnapshotsForAllSeasons(): Promise { console.log("[Snapshots] Manual trigger for all active seasons"); await createDailySnapshots(); }