fix: 修复14个中等问题(安全+稳定性)

后端安全修复:
- devices.js: page/pageSize parseInt+范围限制、CSV导入path.basename防路径遍历
- tickets.js: CSV导出收集所有metadata key、评价限制completed状态
- users.js: 重置密码加权限检查(仅管理员或本人)
- systemSettings.js: Boolean改显式判断(修复false存储为true)
- inventory.js: 空数组Array.isArray检查(修复无法清空目标机房)
- statistics.js: 在线率改用runningDevices计算
- operationLogs.js: DATE()改strftime兼容SQLite
- devicePorts.js: 端口更新白名单过滤(防修改portId/deviceId)
- server.js: 生产环境禁用sync alter

前端修复:
- api/index.js: 401优先React Router导航(保留SPA状态)
- Scene.jsx: useFrame仅target变化时更新(减少不必要矩阵计算)
- vite.config.mjs: 移除Vue插件残留(unplugin-vue-components)
This commit is contained in:
zhang96110
2026-04-02 03:10:06 +00:00
parent 7f1df28858
commit dc20bdba1f
12 changed files with 82 additions and 36 deletions
+17 -6
View File
@@ -390,6 +390,14 @@ router.get('/export', async (req, res) => {
order: [['createdAt', 'DESC']],
});
// 收集所有工单的自定义字段 key,确保导出列完整
const allCustomKeys = new Set();
tickets.forEach(ticket => {
if (ticket.metadata && typeof ticket.metadata === 'object') {
Object.keys(ticket.metadata).forEach(key => allCustomKeys.add(key));
}
});
const exportData = tickets.map(ticket => {
const item = {};
TICKET_EXPORT_FIELDS.forEach(({ fieldName, displayName }) => {
@@ -418,12 +426,11 @@ router.get('/export', async (req, res) => {
item[displayName] = value !== null && value !== undefined ? String(value) : '';
});
if (ticket.metadata && typeof ticket.metadata === 'object') {
Object.entries(ticket.metadata).forEach(([key, val]) => {
const customDisplayName = key;
item[customDisplayName] = val !== null && val !== undefined ? String(val) : '';
});
}
// 填充所有自定义字段(缺失的填空字符串,保证每行列数一致)
allCustomKeys.forEach(key => {
const val = ticket.metadata && ticket.metadata[key];
item[key] = val !== null && val !== undefined ? String(val) : '';
});
return item;
});
@@ -857,6 +864,10 @@ router.post('/:ticketId/evaluate', async (req, res) => {
return res.status(404).json({ error: '工单不存在' });
}
if (ticket.status !== 'completed') {
return res.status(400).json({ error: '只有已完成的工单才能评价' });
}
await ticket.update({ evaluation, evaluationRating });
await TicketOperationRecord.create({