From 5e8bb969a4ca7c34352609617d1a6cca862318f9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 25 Mar 2026 08:27:00 +0000 Subject: [PATCH] fix: address code review feedback for LANGDRIVE implementation - Normalize module paths in deploy permission checks (trailing slash) - Add devId format validation in PATCH /dev/:devId/status - Fix onboarding resume logic (allow resuming incomplete sessions) - Switch audit logging from sync to async file I/O - Add hasModuleAccess helper for consistent module permission checks Co-authored-by: qinfendebingshuo <207279273+qinfendebingshuo@users.noreply.github.com> Agent-Logs-Url: https://github.com/qinfendebingshuo/guanghulab/sessions/a260ce18-279a-4490-af90-a90a7b1f46cd --- backend/api-server/middleware/audit.js | 15 ++++++++------ backend/api-server/routes/write.js | 28 ++++++++++++++++++++++++-- docs/js/onboarding.js | 3 ++- 3 files changed, 37 insertions(+), 9 deletions(-) diff --git a/backend/api-server/middleware/audit.js b/backend/api-server/middleware/audit.js index 5c92e9ac..052d09d5 100644 --- a/backend/api-server/middleware/audit.js +++ b/backend/api-server/middleware/audit.js @@ -45,14 +45,17 @@ function auditLog(req, res, next) { logEntry.statusCode = res.statusCode; // 异步写入日志,不阻塞响应 - try { - fs.mkdirSync(AUDIT_LOG_DIR, { recursive: true }); + fs.mkdir(AUDIT_LOG_DIR, { recursive: true }, function(mkdirErr) { + if (mkdirErr) { + console.error('审计日志目录创建失败:', mkdirErr.message); + return; + } var today = new Date().toISOString().split('T')[0]; var logFile = path.join(AUDIT_LOG_DIR, 'audit-' + today + '.jsonl'); - fs.appendFileSync(logFile, JSON.stringify(logEntry) + '\n'); - } catch (e) { - console.error('审计日志写入失败:', e.message); - } + fs.appendFile(logFile, JSON.stringify(logEntry) + '\n', function(writeErr) { + if (writeErr) console.error('审计日志写入失败:', writeErr.message); + }); + }); return originalSend.call(this, body); }; diff --git a/backend/api-server/routes/write.js b/backend/api-server/routes/write.js index ba1efc63..d2cad45b 100644 --- a/backend/api-server/routes/write.js +++ b/backend/api-server/routes/write.js @@ -62,6 +62,25 @@ function rateLimit(req, res, next) { router.use(rateLimit); +// 模块路径归一化(确保带末尾斜杠) +function normalizeModule(mod) { + if (!mod) return ''; + mod = mod.trim(); + if (mod !== '*' && !mod.endsWith('/')) mod += '/'; + return mod; +} + +// 检查开发者是否拥有指定模块 +function hasModuleAccess(userModules, module) { + if (!userModules || !module) return false; + if (userModules.includes('*')) return true; + var normalized = normalizeModule(module); + for (var i = 0; i < userModules.length; i++) { + if (normalizeModule(userModules[i]) === normalized) return true; + } + return false; +} + // 获取实际数据库 ID(沙箱环境使用沙箱 DB) function getDbId(req, configKey) { if (req.sandbox && req.sandboxDbIds && req.sandboxDbIds[configKey]) { @@ -251,7 +270,7 @@ router.post('/deploy/preview', } // 检查开发者是否拥有该模块 - if (!req.user.modules.includes('*') && !req.user.modules.includes(module)) { + if (!hasModuleAccess(req.user.modules, module)) { return res.status(403).json({ error: true, code: 'MODULE_DENIED', @@ -290,7 +309,7 @@ router.post('/deploy/production', } // 检查模块权限 - if (!req.user.modules.includes('*') && !req.user.modules.includes(module)) { + if (!hasModuleAccess(req.user.modules, module)) { return res.status(403).json({ error: true, code: 'MODULE_DENIED', @@ -432,6 +451,11 @@ router.patch('/dev/:devId/status', var devId = req.params.devId; var status = req.body.status; + // 验证 devId 格式 + if (!/^DEV-\d{3}$/.test(devId) && !/^TCS-\d{4}$/.test(devId)) { + return res.status(400).json({ error: true, code: 'INVALID_DEV_ID', message: '无效的开发者编号' }); + } + // 开发者只能更新自己的状态(管理者除外) if (req.user.devId !== devId && !req.user.permissions.includes('dev:update_all')) { return res.status(403).json({ diff --git a/docs/js/onboarding.js b/docs/js/onboarding.js index c15aa59f..9804098a 100644 --- a/docs/js/onboarding.js +++ b/docs/js/onboarding.js @@ -25,7 +25,8 @@ }); if (res.ok) { var data = await res.json(); - return !data.started && !data.completed && data.permissionLevel === 0; + // Show onboarding if not completed (handles both new and resumed sessions) + return !data.completed && data.permissionLevel === 0; } } catch (e) { console.debug('[Onboarding] check failed:', e.message);