91 lines
2.6 KiB
JavaScript
91 lines
2.6 KiB
JavaScript
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_FORMAT_VERSION,
|
|
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: ISAC_DATA_FORMAT_VERSION,
|
|
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}`);
|