feat: 完善/优化/新增大批次 + 安全加固 + 性能优化
- 修复再次提交不置顶(排序键改用 last_submit_ts) - 后端结果缓存 + 并发锁(DataCache / ny_build_data / Roster 指纹失效) - 导出 xlsx 多表:按最新成绩 / 按班级分表 / 可选列 - 名单管理:新增改弹窗、删除多选+二次确认、登陆账号列 - 安全:CSRF token + 登录失败限流(5次锁10分钟) - 未匹配答卷一键加入名单(unmatched_map) - 缺考名单增强:复制学号/姓名、按班统计、打印 - 成绩快照 + 跨卷趋势(Snapshot) - 异常检测线索(短时长高分/高频重考/雷同成绩) - 前端性能:虚拟滚动、搜索防抖、后台暂停、失败退避 - 测试沉淀:test-new-features.php + run-all.sh
This commit is contained in:
@@ -77,6 +77,8 @@ function chk(name, cond, extra) {
|
||||
|
||||
function req(method, path, body, headers) {
|
||||
return new Promise((resolve, reject) => {
|
||||
// 写操作自动附带 CSRF token(后端对所有写 action 强制校验)
|
||||
if (body && CSRF && !body._csrf) body = Object.assign({}, body, { _csrf: CSRF });
|
||||
const data = body ? JSON.stringify(body) : '';
|
||||
const h = Object.assign({
|
||||
'Content-Type': 'application/json',
|
||||
@@ -114,6 +116,7 @@ function req(method, path, body, headers) {
|
||||
});
|
||||
}
|
||||
const cookieJar = [];
|
||||
let CSRF = ''; // 从 config_get 取到的会话 CSRF token,POST 写请求自动附带
|
||||
|
||||
function formPost(path, fields) {
|
||||
return new Promise((resolve, reject) => {
|
||||
@@ -204,8 +207,7 @@ async function login() {
|
||||
process.exit(1);
|
||||
}
|
||||
// 之后用 ping 触发 SurveyKing 登录
|
||||
const p1 = await req('GET', '/api.php?action=ping&_t=' + Date.now());
|
||||
console.log(' [debug] ping raw:', p1.status, 'loc=' + p1.loc, (p1.raw||'').slice(0, 200));
|
||||
const p1 = await req('GET', '/api.php?action=ping&_t=' + Date.now()); console.log(' [debug] ping raw:', p1.status, 'loc=' + p1.loc, (p1.raw||'').slice(0, 200));
|
||||
chk('ping 200', p1.status === 200, 'status=' + p1.status + ' body=' + (p1.raw||'').slice(0, 200));
|
||||
chk('ping.ok', p1.body && p1.body.ok === true, JSON.stringify(p1.body));
|
||||
await delay(100);
|
||||
@@ -218,6 +220,9 @@ async function login() {
|
||||
'storage=' + (cg1.body && cg1.body.data && cg1.body.data.storage));
|
||||
chk('不返回明文密码', cg1.body && cg1.body.data && cg1.body.data.password === undefined);
|
||||
chk('has_password=true', cg1.body && cg1.body.data && cg1.body.data.has_password === true);
|
||||
// 取 CSRF token,后续写请求自动附带
|
||||
CSRF = (cg1.body && cg1.body.data && cg1.body.data.csrf_token) || '';
|
||||
chk('config_get 返回 csrf_token', CSRF !== '', 'csrf=' + CSRF);
|
||||
|
||||
console.log('\n--- 3) config_save 不改连接信息:不应清 token / 不应触发 ping ---');
|
||||
const before = fs.existsSync(tokenFile) ? fs.readFileSync(tokenFile, 'utf8') : '';
|
||||
|
||||
Reference in New Issue
Block a user