From b3386bab1dc850c1c416ca38794002ce6dd9a068 Mon Sep 17 00:00:00 2001 From: James Ketrenos Date: Mon, 6 Oct 2025 12:27:26 -0700 Subject: [PATCH] Game import from JSON and listing working --- server/tools/import-games-to-db.ts | 79 ++++++++++++++++++------------ server/tools/list-games.ts | 4 +- 2 files changed, 51 insertions(+), 32 deletions(-) diff --git a/server/tools/import-games-to-db.ts b/server/tools/import-games-to-db.ts index b4c7dbf..23f4af7 100644 --- a/server/tools/import-games-to-db.ts +++ b/server/tools/import-games-to-db.ts @@ -27,8 +27,8 @@ async function main() { } for (const f of files) { - // ignore dotfiles and keep only .json or numeric filenames - if (f.startsWith('.')) continue; + // ignore dotfiles and .bk backup files (we don't want to import backups) + if (f.startsWith('.') || f.endsWith('.bk')) continue; const full = path.join(gamesDir, f); try { const stat = await fs.stat(full); @@ -38,45 +38,64 @@ async function main() { // Derive id from filename (strip .json if present) const idStr = f.endsWith('.json') ? f.slice(0, -5) : f; const id = isNaN(Number(idStr)) ? idStr : Number(idStr); - const payload = JSON.stringify(state); - // Ensure state column exists; attempt a safe ALTER if needed. - try { - await db.sequelize.query('ALTER TABLE games ADD COLUMN state TEXT'); - } catch (e) { - /* ignore: may already exist */ - } + // derive a friendly name from the saved state when present + const nameCandidate = (state && (state.name || state.id)) ? String(state.name || state.id) : undefined; - // If filename is numeric use id column; otherwise use path column so we don't write strings into an INTEGER id. try { if (typeof id === 'number') { - const rows: any[] = await db.sequelize.query('SELECT id FROM games WHERE id=:id', { - replacements: { id }, - type: db.Sequelize.QueryTypes.SELECT - }); - if (rows && rows.length) { - await db.sequelize.query('UPDATE games SET state=:state WHERE id=:id', { replacements: { id, state: payload } }); - console.log(`Updated game id=${id}`); - } else { - await db.sequelize.query('INSERT INTO games (id, state) VALUES(:id, :state)', { replacements: { id, state: payload } }); - console.log(`Inserted game id=${id}`); + // numeric filename: use the typed helper + await db.saveGameState(id, state); + console.log(`Saved game id=${id}`); + if (nameCandidate) { + try { + await db.sequelize.query('UPDATE games SET name=:name WHERE id=:id', { replacements: { id, name: nameCandidate } }); + } catch (_) { + // ignore name update failure + } } } else { - // use path column to record the filename identifier - const rows: any[] = await db.sequelize.query('SELECT id FROM games WHERE path=:path', { - replacements: { path: idStr }, - type: db.Sequelize.QueryTypes.SELECT - }); - if (rows && rows.length) { - await db.sequelize.query('UPDATE games SET state=:state WHERE path=:path', { replacements: { path: idStr, state: payload } }); - console.log(`Updated game path=${idStr}`); + // string filename: try to find an existing row by path and save via id; + // otherwise insert a new row with path and the JSON state. + let found: any[] = []; + try { + found = await db.sequelize.query('SELECT id FROM games WHERE path=:path', { + replacements: { path: idStr }, + type: db.Sequelize.QueryTypes.SELECT + }); + } catch (qe) { + found = []; + } + + if (found && found.length) { + const foundId = found[0].id; + await db.saveGameState(foundId, state); + console.log(`Saved game path=${idStr} -> id=${foundId}`); + if (nameCandidate) { + try { + await db.sequelize.query('UPDATE games SET name=:name WHERE id=:id', { replacements: { id: foundId, name: nameCandidate } }); + } catch (_) { + // ignore + } + } } else { - await db.sequelize.query('INSERT INTO games (path, state) VALUES(:path, :state)', { replacements: { path: idStr, state: payload } }); + // ensure state column exists before inserting a new row + try { + await db.sequelize.query('ALTER TABLE games ADD COLUMN state TEXT'); + } catch (_) { + // ignore + } + const payload = JSON.stringify(state); + if (nameCandidate) { + await db.sequelize.query('INSERT INTO games (path, state, name) VALUES(:path, :state, :name)', { replacements: { path: idStr, state: payload, name: nameCandidate } }); + } else { + await db.sequelize.query('INSERT INTO games (path, state) VALUES(:path, :state)', { replacements: { path: idStr, state: payload } }); + } console.log(`Inserted game path=${idStr}`); } } } catch (e) { - console.error('Failed to insert/update game', idStr, e); + console.error('Failed to save game', idStr, e); } } catch (e) { console.error('Failed to read/parse', full, e); diff --git a/server/tools/list-games.ts b/server/tools/list-games.ts index c1f5139..fdad034 100644 --- a/server/tools/list-games.ts +++ b/server/tools/list-games.ts @@ -37,13 +37,13 @@ async function main() { if (!gameId) { // List all game ids try { - const rows: any[] = await db.sequelize.query('SELECT id FROM games', { type: db.Sequelize.QueryTypes.SELECT }); + const rows: any[] = await db.sequelize.query('SELECT id, name FROM games', { type: db.Sequelize.QueryTypes.SELECT }); if (!rows || rows.length === 0) { console.log('No games found.'); return; } console.log('Games:'); - rows.forEach(r => console.log(` - ${r.id}`)); + rows.forEach(r => console.log(`${r.id} - ${r.name}`)); } catch (e) { console.error('Failed to list games:', e); process.exit(1);