import postgres from 'postgres'; const expectedOrigin = process.argv[2]; if ( !expectedOrigin || !['https://stanrunge.dev', 'https://staging.stanrunge.dev'].includes(expectedOrigin) ) { throw new Error('Expected a known production or staging origin'); } if (!process.env.DB_URL) throw new Error('DB_URL is missing'); if (!process.env.BETTER_AUTH_SECRET || process.env.BETTER_AUTH_SECRET.length < 32) { throw new Error('BETTER_AUTH_SECRET must be at least 32 characters'); } if (process.env.ORIGIN !== expectedOrigin || process.env.BETTER_AUTH_URL !== expectedOrigin) { throw new Error('ORIGIN and BETTER_AUTH_URL must both match the public site origin'); } const client = postgres(process.env.DB_URL, { max: 1, connect_timeout: 10 }); try { await client`select 1`; const [schema] = await client` select (select count(*) from information_schema.columns where table_name = 'user' and column_name = 'role') as user_role, (select count(*) from information_schema.columns where table_name = 'account' and column_name = 'issuer') as account_issuer, (select count(*) from information_schema.columns where table_name = 'blog_articles' and column_name = 'tags') as blog_tags, to_regclass('public.catch_em_all_settings') is not null as settings_ready, (select count(*) from information_schema.columns where table_name = 'lists' and column_name = 'slug') as list_slug, to_regclass('public.list_item_completions') is not null as list_completions, (select count(*) from information_schema.columns where table_name = 'tasks_to_topics' and column_name = 'id') as topic_link_id, (select count(*) from information_schema.columns where table_name in ('animals', 'sightings', 'photos') and column_name = 'id' and data_type = 'integer') as animal_serial_ids, (select count(*) from information_schema.tables where table_schema = 'public' and table_name in ( 'money_accounts', 'money_transactions', 'money_recurring', 'money_forecasts', 'money_utility_settings', 'money_utility_readings', 'money_wishlist', 'money_insurances', 'money_projection_settings' )) as money_tables, (select count(*) from information_schema.columns where table_name = 'money_utility_settings' and column_name = 'billing_start') as money_billing_start, (select count(*) from information_schema.tables where table_schema = 'public' and table_name in ( 'pokemon_locations', 'pokemon_encounters', 'pokemon_evolutions', 'tracker_games', 'tracker_items', 'tracker_progress' )) as game_tracker_tables, (select count(*) from information_schema.tables where table_schema='public' and table_name in ( 'pokemon_research','pokemon_dex_numbers','pokemon_moves','pokemon_learnsets','pokemon_game_research', 'pokemon_journeys','pokemon_catches','pokemon_milestones','tracker_ranks','tracker_weapon_ranks', 'tracker_challenges','tracker_challenge_progress','osu_entries','pokemon_seen' )) as tracker_journal_tables, (select count(*) from information_schema.columns where table_name='pokemon_journeys' and column_name='completed') as tracker_completion_column, (select count(*) from information_schema.columns where table_name = 'pokemon_game_entries' and column_name = 'source') as entry_source, (select count(*) from information_schema.columns where (table_name = 'money_accounts' and column_name = 'kind') or (table_name = 'money_recurring' and column_name in ('starts_on', 'ends_on')) or (table_name = 'money_wishlist' and column_name = 'priority') or (table_name = 'money_projection_settings' and column_name = 'include_recurring')) as money_projection_columns `; if ( !Number(schema.user_role) || !Number(schema.account_issuer) || !Number(schema.blog_tags) || !schema.settings_ready || !Number(schema.list_slug) || !schema.list_completions || !Number(schema.topic_link_id) || Number(schema.animal_serial_ids) !== 3 || Number(schema.money_tables) !== 9 || !Number(schema.money_billing_start) || Number(schema.money_projection_columns) !== 5 || Number(schema.game_tracker_tables) !== 6 || Number(schema.tracker_journal_tables) !== 14 || !Number(schema.tracker_completion_column) || !Number(schema.entry_source) ) { throw new Error( 'Required migrations or Animaldex ID conversion have not been applied to this database' ); } const [catalog] = await client` select (select count(*) from pokemon) as pokemon, (select count(*) from games where source_id is not null) as games, (select count(*) from pokemon_game_entries) as entries, (select count(*) from pokemon_encounters) as encounters, (select count(*) from tracker_games where franchise = 'call-of-duty') as cod_games, (select count(*) from pokemon_research) as pokemon_research, (select count(*) from pokemon_moves) as pokemon_moves, (select count(*) from pokemon_learnsets) as pokemon_learnsets, (select count(*) from pokemon_dex_numbers) as pokemon_dex_numbers, (select count(*) from tracker_challenges) as tracker_challenges `; if ( Number(catalog.pokemon) < 1025 || Number(catalog.games) < 53 || Number(catalog.entries) < 27267 || Number(catalog.encounters) < 60000 ) { throw new Error('Pokémon catalog has not been seeded in this database'); } if ( Number(catalog.pokemon_research) < 1025 || Number(catalog.pokemon_moves) < 937 || Number(catalog.pokemon_learnsets) < 11000 || Number(catalog.pokemon_dex_numbers) < 16000 || Number(catalog.tracker_challenges) < 3600 ) throw new Error( 'Run db:migrate:tracker-journal, db:seed:research and db:seed:challenges before deploying the tracker expansion' ); if (Number(catalog.cod_games) < 7) { throw new Error('Call of Duty catalog has not been seeded in this database'); } const [bo2] = await client`select rules, (select count(*)::int from tracker_items i where i.game_id=g.id and i.kind='weapon') as weapons, (select count(*)::int from tracker_items i where i.game_id=g.id and i.kind='weapon' and i.icon is not null) as pictures from tracker_games g where slug='black-ops-2'`; if ( !bo2 || bo2.rules.rankCaps?.weaponPrestige !== 2 || bo2.rules.rankCaps?.weaponLevelsByClass?.['Assault Rifles'] !== 17 || bo2.weapons < 40 || bo2.pictures < 39 ) { throw new Error('Seed the updated BO2 catalog and artwork before deploying'); } } finally { await client.end(); } const google = !!(process.env.GOOGLE_CLIENT_ID && process.env.GOOGLE_CLIENT_SECRET); const osu = !!(process.env.OSU_CLIENT_ID && process.env.OSU_CLIENT_SECRET); console.log( `Preflight passed for ${expectedOrigin}; Google ${google ? 'configured' : 'disabled'}, osu! ${osu ? 'configured' : 'disabled'}` );