import { connect } from '@tursodatabase/database'; import { mkdir, readFile, writeFile } from 'node:fs/promises'; import { basename, dirname, join, resolve } from 'node:path'; import process from 'node:process'; import { ISAC_DATA_TABLES, quoteIdentifier } from './isac-data-tables.mjs'; const databasePath = resolve( process.env.TDR_DATABASE_PATH || './data/shd-build-development.db', ); const iconDirectory = resolve( process.env.TDR_GAME_ICON_STORAGE_PATH || './data/game-icons', ); const timestamp = new Date() .toISOString() .replaceAll(':', '-') .replace(/\.\d{3}Z$/, 'Z'); const outputPath = resolve( process.argv[2] || `./data/backups/isac-data-${timestamp}.json`, ); const database = await connect(databasePath); const tables = []; let totalRows = 0; for (const name of ISAC_DATA_TABLES) { const exists = await ( await database.prepare( "SELECT 1 AS present FROM sqlite_master WHERE type='table' AND name=?", ) ).get(name); if (!exists) throw new Error( `ISAC export requires missing table "${name}". Run the schema migration first.`, ); const rows = await ( await database.prepare(`SELECT * FROM ${quoteIdentifier(name)}`) ).all(); tables.push({ name, rowCount: rows.length, rows }); totalRows += rows.length; } const icons = []; const exportedStorageKeys = new Set(); const iconTable = tables.find((table) => table.name === 'game_data_icons'); const managedImageTable = tables.find( (table) => table.name === 'managed_images', ); for (const row of [ ...(iconTable?.rows ?? []), ...(managedImageTable?.rows ?? []), ]) { if ( typeof row.storage_key !== 'string' || !/^[a-z_]+-[0-9a-f-]{36}\.webp$/i.test(row.storage_key) ) { throw new Error( `Invalid managed asset storage key "${String(row.storage_key)}".`, ); } if (exportedStorageKeys.has(row.storage_key)) continue; exportedStorageKeys.add(row.storage_key); const content = await readFile(join(iconDirectory, row.storage_key)); icons.push({ storageKey: row.storage_key, contentBase64: content.toString('base64'), }); } const snapshot = { format: 'shd-build-isac-data', version: 4, createdAt: new Date().toISOString(), sourceDatabase: basename(databasePath), totalRows, tables, icons, }; await mkdir(dirname(outputPath), { recursive: true }); await writeFile(outputPath, `${JSON.stringify(snapshot, null, 2)}\n`, { encoding: 'utf8', flag: 'wx', }); console.log( `Exported ${totalRows} ISAC rows and ${icons.length} uploaded image assets.`, ); console.log(`ISAC snapshot: ${outputPath}`);