diff --git a/.forgejo/workflows/daily-snapshots.yml b/.forgejo/workflows/daily-snapshots.yml index bdf816f..b099aa8 100644 --- a/.forgejo/workflows/daily-snapshots.yml +++ b/.forgejo/workflows/daily-snapshots.yml @@ -11,5 +11,4 @@ jobs: run: | curl -sf -X POST https://brackt.com/admin/jobs/run-daily-snapshots \ -H "X-Cron-Secret: ${{ secrets.CRON_SECRET }}" \ - -H "Content-Type: application/json" \ - | cat + -H "Content-Type: application/json" diff --git a/.forgejo/workflows/sync-and-simulate.yml b/.forgejo/workflows/sync-and-simulate.yml index 6dfb844..bd2f614 100644 --- a/.forgejo/workflows/sync-and-simulate.yml +++ b/.forgejo/workflows/sync-and-simulate.yml @@ -11,5 +11,4 @@ jobs: run: | curl -sf -X POST https://brackt.com/admin/jobs/sync-and-simulate \ -H "X-Cron-Secret: ${{ secrets.CRON_SECRET }}" \ - -H "Content-Type: application/json" \ - | cat + -H "Content-Type: application/json" diff --git a/app/routes/admin/jobs.sync-and-simulate.ts b/app/routes/admin/jobs.sync-and-simulate.ts index fab3459..daac871 100644 --- a/app/routes/admin/jobs.sync-and-simulate.ts +++ b/app/routes/admin/jobs.sync-and-simulate.ts @@ -35,19 +35,8 @@ export async function action({ request }: { request: Request }) { if (!changed) continue; - // Only simulate if standings changed since last simulation - const current = await database() - .select({ standingsLastChangedAt: schema.sportsSeasons.standingsLastChangedAt, lastSimulatedAt: schema.sportsSeasons.lastSimulatedAt }) - .from(schema.sportsSeasons) - .where(eq(schema.sportsSeasons.id, season.id)) - .then((rows) => rows[0]); - - const needsSim = - current?.standingsLastChangedAt && - (!current.lastSimulatedAt || current.standingsLastChangedAt > current.lastSimulatedAt); - - if (!needsSim) continue; - + // changed === true means syncStandings() detected new data this run, so simulate. + // Overlap is guarded inside runSportsSeasonSimulation (throws if simulationStatus === "running"). try { await runSportsSeasonSimulation(season.id); await database() diff --git a/server/snapshots.ts b/server/snapshots.ts deleted file mode 100644 index 848206a..0000000 --- a/server/snapshots.ts +++ /dev/null @@ -1,108 +0,0 @@ -import * as schema from "~/database/schema"; -import { eq, or } from "drizzle-orm"; -import { createDailySnapshot } from "~/models/standings"; -import { logger } from "./logger"; -import { db } from "./db"; - -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) { - logger.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) { - logger.error("[Snapshots] Error creating daily snapshots:", error); - } - }, CHECK_INTERVAL); - - logger.log("[Snapshots] Daily snapshot system started (runs once per day)"); -} - -/** - * Stop the snapshot system - */ -export function stopSnapshotSystem(): void { - if (snapshotInterval) { - clearInterval(snapshotInterval); - snapshotInterval = null; - logger.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")}`; - - logger.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) { - logger.log("[Snapshots] No active seasons found"); - return; - } - - logger.log(`[Snapshots] Found ${activeSeasons.length} active season(s)`); - - for (const season of activeSeasons) { - try { - await createDailySnapshot(season.id, db); - logger.log(`[Snapshots] ✅ Upserted snapshot for season ${season.id}`); - } catch (error) { - logger.error(`[Snapshots] Error creating snapshot for season ${season.id}:`, error); - } - } - - logger.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 { - logger.log(`[Snapshots] Manual trigger for ${seasonIds.length} season(s)`); - - for (const seasonId of seasonIds) { - try { - await createDailySnapshot(seasonId, db); - logger.log(`[Snapshots] ✅ Created snapshot for season ${seasonId}`); - } catch (error) { - logger.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 { - logger.log("[Snapshots] Manual trigger for all active seasons"); - await createDailySnapshots(); -}