feat: 修改部分导入文本的逻辑,添加部署脚本和样式文件,更新数据库迁移逻辑
- 新增部署脚本 `build-deploy-bundle.mjs`,用于构建和部署 web 和 server 目录。 - 新增样式文件 `index-acd65452.css`,包含基础样式和响应式设计。 - 新增脚本 `repro-import-text.mjs`,用于测试文本导入 API。 - 新增测试文件 `db-migration-score-zero.test.ts`,验证历史数据库中 questions.score 约束的迁移逻辑。 - 更新数据库初始化逻辑,允许插入 score=0 的问题。
This commit is contained in:
88
scripts/build-deploy-bundle.mjs
Normal file
88
scripts/build-deploy-bundle.mjs
Normal file
@@ -0,0 +1,88 @@
|
||||
import fs from 'node:fs/promises';
|
||||
import path from 'node:path';
|
||||
|
||||
const projectRoot = process.cwd();
|
||||
|
||||
const distDir = path.join(projectRoot, 'dist');
|
||||
const deployDir = path.join(projectRoot, 'deploy_bundle');
|
||||
const deployWebDir = path.join(deployDir, 'web');
|
||||
const deployServerDir = path.join(deployDir, 'server');
|
||||
|
||||
const exists = async (p) => {
|
||||
try {
|
||||
await fs.access(p);
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
const ensureDir = async (p) => {
|
||||
await fs.mkdir(p, { recursive: true });
|
||||
};
|
||||
|
||||
const emptyDir = async (p) => {
|
||||
await fs.rm(p, { recursive: true, force: true });
|
||||
await fs.mkdir(p, { recursive: true });
|
||||
};
|
||||
|
||||
const copyDir = async (src, dest) => {
|
||||
await fs.cp(src, dest, { recursive: true });
|
||||
};
|
||||
|
||||
const copyFile = async (src, dest) => {
|
||||
await ensureDir(path.dirname(dest));
|
||||
await fs.copyFile(src, dest);
|
||||
};
|
||||
|
||||
if (!(await exists(distDir))) {
|
||||
throw new Error('未找到 dist/。请先执行:npm run build');
|
||||
}
|
||||
|
||||
// 1) web: dist/ 下除 api/ 之外所有内容
|
||||
await emptyDir(deployWebDir);
|
||||
|
||||
const distEntries = await fs.readdir(distDir, { withFileTypes: true });
|
||||
for (const entry of distEntries) {
|
||||
if (entry.name === 'api') continue;
|
||||
const src = path.join(distDir, entry.name);
|
||||
const dest = path.join(deployWebDir, entry.name);
|
||||
if (entry.isDirectory()) {
|
||||
await copyDir(src, dest);
|
||||
} else {
|
||||
await copyFile(src, dest);
|
||||
}
|
||||
}
|
||||
|
||||
// 2) server: dist/api -> deploy_bundle/server/dist/api
|
||||
await ensureDir(deployServerDir);
|
||||
await ensureDir(path.join(deployServerDir, 'dist'));
|
||||
|
||||
const distApiDir = path.join(distDir, 'api');
|
||||
if (!(await exists(distApiDir))) {
|
||||
throw new Error('未找到 dist/api/。请确认 npm run build 已成功执行(包含 scripts/build-api.mjs)。');
|
||||
}
|
||||
|
||||
await emptyDir(path.join(deployServerDir, 'dist', 'api'));
|
||||
await copyDir(distApiDir, path.join(deployServerDir, 'dist', 'api'));
|
||||
|
||||
// 3) 同步 server 运行所需文件
|
||||
await copyFile(path.join(projectRoot, 'package.json'), path.join(deployServerDir, 'package.json'));
|
||||
if (await exists(path.join(projectRoot, 'package-lock.json'))) {
|
||||
await copyFile(path.join(projectRoot, 'package-lock.json'), path.join(deployServerDir, 'package-lock.json'));
|
||||
}
|
||||
|
||||
// ecosystem.config.cjs:以仓库根目录为准同步(如需环境差异,可在服务器上覆盖 env/cwd)
|
||||
if (await exists(path.join(projectRoot, 'ecosystem.config.cjs'))) {
|
||||
await copyFile(path.join(projectRoot, 'ecosystem.config.cjs'), path.join(deployServerDir, 'ecosystem.config.cjs'));
|
||||
}
|
||||
|
||||
// sanity check
|
||||
const initSql = path.join(deployServerDir, 'dist', 'api', 'database', 'init.sql');
|
||||
if (!(await exists(initSql))) {
|
||||
throw new Error('缺少 deploy_bundle/server/dist/api/database/init.sql(postbuild 应该会复制)。');
|
||||
}
|
||||
|
||||
console.log('deploy_bundle 已生成:');
|
||||
console.log('- deploy_bundle/web');
|
||||
console.log('- deploy_bundle/server');
|
||||
40
scripts/repro-import-text.mjs
Normal file
40
scripts/repro-import-text.mjs
Normal file
@@ -0,0 +1,40 @@
|
||||
process.env.NODE_ENV = 'test';
|
||||
process.env.DB_PATH = ':memory:';
|
||||
|
||||
const { initDatabase } = await import('../api/database');
|
||||
await initDatabase();
|
||||
|
||||
const { app } = await import('../api/server');
|
||||
|
||||
const server = app.listen(0);
|
||||
const addr = server.address();
|
||||
const baseUrl = `http://127.0.0.1:${addr.port}`;
|
||||
|
||||
try {
|
||||
const body = {
|
||||
mode: 'incremental',
|
||||
questions: [
|
||||
{
|
||||
content:
|
||||
'【文字描述题】请简述你对公司“只服务渠道客户,不直接做甲方项目(除深圳周边近的)”这一政策的理解。',
|
||||
type: 'text',
|
||||
category: '通用',
|
||||
score: 0,
|
||||
answer: '',
|
||||
analysis: '用于人工评阅,关注对渠道保护、资源倾斜、合作共赢等理念的理解。',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const res = await fetch(`${baseUrl}/api/questions/import-text`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
|
||||
const text = await res.text();
|
||||
console.log('status:', res.status);
|
||||
console.log(text);
|
||||
} finally {
|
||||
await new Promise((resolve) => server.close(resolve));
|
||||
}
|
||||
Reference in New Issue
Block a user