import { sqlite } from './index.js' import { logger } from '../lib/logger.js' export function runMigrations() { sqlite.exec(` CREATE TABLE IF NOT EXISTS bots ( id TEXT PRIMARY KEY, name TEXT NOT NULL UNIQUE, webhook_url TEXT NOT NULL, avatar_seed TEXT NOT NULL, archetype TEXT NOT NULL DEFAULT 'standard', secret_hash TEXT NOT NULL, public_key TEXT, profile_pic_url TEXT, elo_rating REAL NOT NULL DEFAULT 1200, wins INTEGER NOT NULL DEFAULT 0, losses INTEGER NOT NULL DEFAULT 0, win_streak INTEGER NOT NULL DEFAULT 0, best_streak INTEGER NOT NULL DEFAULT 0, tier INTEGER NOT NULL DEFAULT 0, is_active INTEGER NOT NULL DEFAULT 1, last_fight_at TEXT, consecutive_errors INTEGER NOT NULL DEFAULT 0, last_error_at TEXT, created_at TEXT NOT NULL ); CREATE TABLE IF NOT EXISTS fights ( id TEXT PRIMARY KEY, bot_a_id TEXT NOT NULL REFERENCES bots(id), bot_b_id TEXT NOT NULL REFERENCES bots(id), arena TEXT NOT NULL, status TEXT NOT NULL DEFAULT 'scheduled', winner_id TEXT REFERENCES bots(id), bot_a_hp INTEGER NOT NULL DEFAULT 200, bot_b_hp INTEGER NOT NULL DEFAULT 200, total_rounds INTEGER NOT NULL DEFAULT 0, scheduled_at TEXT, started_at TEXT, ended_at TEXT, created_at TEXT NOT NULL ); CREATE TABLE IF NOT EXISTS rounds ( id TEXT PRIMARY KEY, fight_id TEXT NOT NULL REFERENCES fights(id), round_number INTEGER NOT NULL, challenge_type TEXT NOT NULL, challenge_data TEXT NOT NULL, bot_a_response TEXT, bot_a_time_ms INTEGER, bot_a_score REAL, bot_b_response TEXT, bot_b_time_ms INTEGER, bot_b_score REAL, winner_id TEXT REFERENCES bots(id), narration TEXT, created_at TEXT NOT NULL ); `) sqlite.exec(` CREATE TABLE IF NOT EXISTS payments ( id TEXT PRIMARY KEY, fight_id TEXT REFERENCES fights(id), bot_id TEXT NOT NULL REFERENCES bots(id), direction TEXT NOT NULL, amount_sats INTEGER NOT NULL, method TEXT NOT NULL, status TEXT NOT NULL DEFAULT 'pending', invoice TEXT, preimage TEXT, cashu_token TEXT, error_reason TEXT, created_at TEXT NOT NULL, confirmed_at TEXT, refunded_at TEXT ); CREATE TABLE IF NOT EXISTS wallet_connections ( id TEXT PRIMARY KEY, bot_id TEXT NOT NULL UNIQUE REFERENCES bots(id), method TEXT NOT NULL, connection_data TEXT NOT NULL, created_at TEXT NOT NULL, last_used_at TEXT ); `) sqlite.exec(` CREATE TABLE IF NOT EXISTS tournaments ( id TEXT PRIMARY KEY, name TEXT NOT NULL, format TEXT NOT NULL DEFAULT 'single_elim', size INTEGER NOT NULL, entry_sats INTEGER NOT NULL DEFAULT 0, prize_sats INTEGER NOT NULL DEFAULT 0, status TEXT NOT NULL DEFAULT 'open', current_round INTEGER NOT NULL DEFAULT 0, season_id TEXT, created_at TEXT NOT NULL, started_at TEXT, finished_at TEXT ); CREATE TABLE IF NOT EXISTS tournament_entries ( id TEXT PRIMARY KEY, tournament_id TEXT NOT NULL REFERENCES tournaments(id), bot_id TEXT NOT NULL REFERENCES bots(id), seed INTEGER NOT NULL DEFAULT 0, eliminated INTEGER NOT NULL DEFAULT 0, created_at TEXT NOT NULL ); CREATE TABLE IF NOT EXISTS tournament_matches ( id TEXT PRIMARY KEY, tournament_id TEXT NOT NULL REFERENCES tournaments(id), round INTEGER NOT NULL, match_index INTEGER NOT NULL, bot_a_id TEXT REFERENCES bots(id), bot_b_id TEXT REFERENCES bots(id), fight_id TEXT REFERENCES fights(id), winner_id TEXT REFERENCES bots(id), status TEXT NOT NULL DEFAULT 'pending' ); `) sqlite.exec(` CREATE TABLE IF NOT EXISTS analytics ( date TEXT NOT NULL, metric TEXT NOT NULL, value INTEGER NOT NULL DEFAULT 0 ); CREATE UNIQUE INDEX IF NOT EXISTS idx_analytics_date_metric ON analytics(date, metric); `) // Column migrations for existing databases const migrations = [ "ALTER TABLE bots ADD COLUMN archetype TEXT NOT NULL DEFAULT 'standard'", "ALTER TABLE bots ADD COLUMN profile_pic_url TEXT", "ALTER TABLE bots ADD COLUMN last_fight_at TEXT", "ALTER TABLE bots ADD COLUMN consecutive_errors INTEGER NOT NULL DEFAULT 0", "ALTER TABLE bots ADD COLUMN last_error_at TEXT", "ALTER TABLE bots ADD COLUMN customization TEXT", // Ranked fights columns "ALTER TABLE fights ADD COLUMN mode TEXT NOT NULL DEFAULT 'free'", "ALTER TABLE fights ADD COLUMN pot_sats INTEGER NOT NULL DEFAULT 0", "ALTER TABLE fights ADD COLUMN payout_status TEXT", // Bot sats tracking "ALTER TABLE bots ADD COLUMN sats_won INTEGER NOT NULL DEFAULT 0", "ALTER TABLE bots ADD COLUMN sats_wagered INTEGER NOT NULL DEFAULT 0", "ALTER TABLE bots ADD COLUMN has_wallet INTEGER NOT NULL DEFAULT 0", // Zap tracking "ALTER TABLE bots ADD COLUMN zaps_received INTEGER NOT NULL DEFAULT 0", // Seasons "ALTER TABLE fights ADD COLUMN current_season TEXT", // Classic bots "ALTER TABLE bots ADD COLUMN bot_type TEXT NOT NULL DEFAULT 'regular'", ] for (const sql of migrations) { try { sqlite.exec(sql) } catch { /* column already exists */ } } // Backfill bot_type for existing mock bots try { sqlite.exec("UPDATE bots SET bot_type = 'mock' WHERE webhook_url LIKE 'http://mock.local%' AND bot_type = 'regular'") sqlite.exec("UPDATE bots SET bot_type = 'classic' WHERE webhook_url LIKE 'http://classic.local%' AND bot_type = 'regular'") } catch { /* ok */ } // Indexes for common query patterns sqlite.exec(` CREATE INDEX IF NOT EXISTS idx_fights_status ON fights(status); CREATE INDEX IF NOT EXISTS idx_fights_created ON fights(created_at); CREATE INDEX IF NOT EXISTS idx_fights_bot_a ON fights(bot_a_id); CREATE INDEX IF NOT EXISTS idx_fights_bot_b ON fights(bot_b_id); CREATE INDEX IF NOT EXISTS idx_bots_elo ON bots(elo_rating); CREATE INDEX IF NOT EXISTS idx_bots_tier ON bots(tier); CREATE INDEX IF NOT EXISTS idx_bots_active ON bots(is_active); CREATE INDEX IF NOT EXISTS idx_rounds_fight ON rounds(fight_id); CREATE INDEX IF NOT EXISTS idx_payments_status ON payments(status); CREATE INDEX IF NOT EXISTS idx_payments_bot ON payments(bot_id); CREATE INDEX IF NOT EXISTS idx_tournament_matches_tournament ON tournament_matches(tournament_id); CREATE INDEX IF NOT EXISTS idx_bets_fight ON bets(fight_id); CREATE INDEX IF NOT EXISTS idx_bets_bettor ON bets(bettor_pubkey, created_at); CREATE INDEX IF NOT EXISTS idx_bots_type_active ON bots(bot_type, is_active); CREATE INDEX IF NOT EXISTS idx_payments_status_created ON payments(status, created_at); CREATE INDEX IF NOT EXISTS idx_tournament_entries_tournament ON tournament_entries(tournament_id); CREATE UNIQUE INDEX IF NOT EXISTS idx_analytics_date_metric ON analytics(date, metric); `) // Run PRAGMA optimize on startup for query planner stats sqlite.pragma('optimize') logger.info('db', 'database migrated') }