99 lines
3.3 KiB
JavaScript
99 lines
3.3 KiB
JavaScript
import 'reflect-metadata';
|
|
import { createHash, randomUUID } from 'node:crypto';
|
|
import {
|
|
closeSync,
|
|
openSync,
|
|
readFileSync,
|
|
statSync,
|
|
unlinkSync,
|
|
writeFileSync,
|
|
} from 'node:fs';
|
|
import { basename, resolve } from 'node:path';
|
|
import { createRequire } from 'node:module';
|
|
import { parseArgs, parseEnv } from 'node:util';
|
|
|
|
const require = createRequire(import.meta.url);
|
|
const { PrismaService } = require('../dist/database/prisma.service.js');
|
|
const { WorksheetImporter } = require('../dist/imports/worksheet-import.js');
|
|
const { HttpException } = require('@nestjs/common');
|
|
let database;
|
|
let reportFd;
|
|
let reportPath;
|
|
let reportWritten = false;
|
|
let committed = false;
|
|
|
|
try {
|
|
const { values } = parseArgs({
|
|
options: {
|
|
file: { type: 'string' },
|
|
apply: { type: 'boolean', default: false },
|
|
'metadata-only': { type: 'boolean', default: false },
|
|
report: { type: 'string' },
|
|
},
|
|
});
|
|
if (!values.file)
|
|
throw new Error('请用 --file 指定题型导出;默认预演,--apply 才写库');
|
|
const file = resolve(values.file);
|
|
if (statSync(file).size > 10 * 1024 * 1024)
|
|
throw new Error('题型文件不能超过 10 MiB');
|
|
const source = readFileSync(file, 'utf8');
|
|
const sourceSha256 = createHash('sha256').update(source).digest('hex');
|
|
const local = parseEnv(
|
|
readFileSync(new URL('../.env', import.meta.url), 'utf8'),
|
|
);
|
|
const url = new URL(process.env.DATABASE_URL ?? local.DATABASE_URL);
|
|
// 元数据暂存仅限本地开发库,不能带着未就绪的预览图导入到生产。
|
|
if (
|
|
!['127.0.0.1', 'localhost'].includes(url.hostname) ||
|
|
url.pathname !== '/doodle_dev'
|
|
)
|
|
throw new Error('题型导入工具只允许本机 doodle_dev');
|
|
if (values.report) {
|
|
reportPath = resolve(values.report);
|
|
reportFd = openSync(reportPath, 'wx');
|
|
}
|
|
database = new PrismaService({ databaseUrl: url.toString() });
|
|
const requestId = `worksheets-import-${randomUUID()}`;
|
|
const result = await new WorksheetImporter(database).importExport(source, {
|
|
dryRun: !values.apply,
|
|
metadataOnly: values['metadata-only'],
|
|
requestId,
|
|
sourceSha256,
|
|
});
|
|
committed = !result.dryRun;
|
|
const report = {
|
|
source: basename(file),
|
|
sourceSha256,
|
|
requestId,
|
|
...result,
|
|
};
|
|
const output = `${JSON.stringify(report, null, 4)}\n`;
|
|
process.stdout.write(output);
|
|
if (reportFd !== undefined) {
|
|
writeFileSync(reportFd, output);
|
|
reportWritten = true;
|
|
}
|
|
} catch (error) {
|
|
const message =
|
|
error instanceof HttpException
|
|
? error.message
|
|
: error instanceof Error &&
|
|
!/(postgres|password|connection|prisma|\/\/)/i.test(
|
|
error.message,
|
|
)
|
|
? error.message
|
|
: '题型导入失败,请检查本地数据库配置和文件';
|
|
process.stderr.write(`${message}\n`);
|
|
if (committed)
|
|
process.stderr.write(
|
|
'数据库事务已提交;报告保存失败,批次见上方输出及审计。\n',
|
|
);
|
|
process.exitCode = 1;
|
|
} finally {
|
|
if (reportFd !== undefined) {
|
|
closeSync(reportFd);
|
|
if (!reportWritten) unlinkSync(reportPath);
|
|
}
|
|
await database?.onModuleDestroy();
|
|
}
|