Files
ftb-project-management/scripts/export-appdata-archive.mjs

88 lines
2.8 KiB
JavaScript

#!/usr/bin/env node
import { execFileSync } from 'node:child_process';
import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
import { createRequire } from 'node:module';
import { dirname, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import { buildAppDataArchive } from './lib/appdata-archive.mjs';
const root = dirname(fileURLToPath(new URL('../package.json', import.meta.url)));
const args = parseArgs(process.argv.slice(2));
if (args.help) {
printHelp();
process.exit(0);
}
const outPath = resolve(root, args.out ?? defaultArchiveName());
const serverRequire = createRequire(resolve(root, 'apps/server/package.json'));
const { PrismaClient } = serverRequire('@prisma/client');
const prisma = new PrismaClient();
try {
const rows = await prisma.appData.findMany({
orderBy: { key: 'asc' },
select: { key: true, value: true, updatedAt: true },
});
const archive = buildAppDataArchive(rows, {
appVersion: process.env.APP_VERSION || readPackageVersion(),
appBuildTime: process.env.APP_BUILD_TIME || null,
sourceCommit: process.env.GITHUB_SHA || readGitCommit(),
});
mkdirSync(dirname(outPath), { recursive: true });
writeFileSync(outPath, `${JSON.stringify(archive, null, 2)}\n`);
console.log(`AppData archive exported: ${outPath}`);
console.log(`Rows: ${archive.metadata.rowCount}; keys: ${archive.keys.join(', ') || '(none)'}`);
console.log(`Payload checksum: ${archive.metadata.payloadChecksum}`);
} finally {
await prisma.$disconnect();
}
function parseArgs(argv) {
const parsed = { help: false, out: null };
for (let index = 0; index < argv.length; index += 1) {
const arg = argv[index];
if (arg === '--') continue;
else if (arg === '--help' || arg === '-h') parsed.help = true;
else if (arg === '--out') parsed.out = argv[++index];
else if (arg.startsWith('--out=')) parsed.out = arg.slice('--out='.length);
else throw new Error(`Unknown argument: ${arg}`);
}
return parsed;
}
function defaultArchiveName() {
const stamp = new Date().toISOString().replace(/[:.]/g, '-');
return `appdata-archive-${stamp}.json`;
}
function readPackageVersion() {
try {
const pkg = JSON.parse(readFileSync(resolve(root, 'apps/server/package.json'), 'utf8'));
return pkg.version || 'unknown';
} catch {
return 'unknown';
}
}
function readGitCommit() {
try {
return execFileSync('git', ['rev-parse', 'HEAD'], { cwd: root, encoding: 'utf8' }).trim();
} catch {
return 'unknown';
}
}
function printHelp() {
console.log(`Usage: pnpm appdata:archive:export -- --out backups/appdata-archive.json
Exports every row from app_data to a JSON archive with key list, app version metadata,
per-row SHA-256 checksums, and a payload checksum.
Options:
--out <path> Output archive path. Defaults to appdata-archive-<timestamp>.json.
-h, --help Show this help.
`);
}