feat: add community publishing and news syndication
Validate and deploy production / validate (push) Successful in 23s
Validate and deploy staging / validate (push) Successful in 50s
Validate and deploy production / deploy (push) Failing after 40s
Validate and deploy staging / deploy (push) Failing after 7s

This commit is contained in:
jb
2026-08-01 21:42:09 -04:00
parent b47e2588e9
commit dd84dff055
288 changed files with 83254 additions and 38351 deletions
+63 -29
View File
@@ -4,49 +4,83 @@ 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 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 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 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: 2,
createdAt: new Date().toISOString(),
sourceDatabase: basename(databasePath),
totalRows,
tables,
icons
format: 'shd-build-isac-data',
version: 2,
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' });
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(
`Exported ${totalRows} ISAC rows and ${icons.length} uploaded image assets.`,
);
console.log(`ISAC snapshot: ${outputPath}`);