import { readFile } from 'node:fs/promises'; import postgres from 'postgres'; if (!process.env.DB_URL) throw new Error('DB_URL is required'); const catalog = JSON.parse( await readFile(new URL('../data/cod-challenges.json', import.meta.url), 'utf8') ); const client = postgres(process.env.DB_URL, { max: 1 }); try { await client.begin(async (tx) => { for (const [slug, rows] of Object.entries(catalog.games) as [ string, { key: string; name: string; category: string; requirement: string; reward: string | null; icon: string | null; target: number | null; sourceUrl: string; }[] ][]) { const [game] = await tx`select id from tracker_games where slug=${slug} and franchise='call-of-duty'`; if (!game) throw new Error(`Seed Call of Duty catalog first: ${slug}`); for (let offset = 0; offset < rows.length; offset += 500) { const batch = rows .slice(offset, offset + 500) .map(({ sourceUrl, ...r }) => ({ ...r, game_id: game.id, source_url: sourceUrl })); await tx`insert into tracker_challenges ${tx(batch)} on conflict (game_id,key) do update set name=excluded.name,category=excluded.category,requirement=excluded.requirement,reward=excluded.reward,icon=excluded.icon,target=excluded.target,source_url=excluded.source_url`; } // Retire removed source definitions only when nobody has saved progress. if (rows.length) await tx`delete from tracker_challenges c where c.game_id=${game.id} and c.key not in ${tx(rows.map((r) => r.key))} and not exists (select 1 from tracker_challenge_progress p where p.challenge_id=c.id)`; console.log(`${slug}: ${rows.length} challenge rows`); } }); } finally { await client.end(); }