2025-11-14 09:15:58 -08:00
|
|
|
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<void> {
|
2026-03-18 22:15:28 -07:00
|
|
|
const now = new Date();
|
|
|
|
|
const today = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, "0")}-${String(now.getDate()).padStart(2, "0")}`;
|
2025-11-14 09:15:58 -08:00
|
|
|
|
|
|
|
|
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);
|
2026-03-18 22:15:28 -07:00
|
|
|
console.log(`[Snapshots] ✅ Upserted snapshot for season ${season.id}`);
|
2025-11-14 09:15:58 -08:00
|
|
|
} 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<void> {
|
|
|
|
|
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<void> {
|
|
|
|
|
console.log("[Snapshots] Manual trigger for all active seasons");
|
|
|
|
|
await createDailySnapshots();
|
|
|
|
|
}
|