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
This commit is contained in:
parent
38d8afef55
commit
5e8bb969a4
|
|
@ -45,14 +45,17 @@ function auditLog(req, res, next) {
|
||||||
logEntry.statusCode = res.statusCode;
|
logEntry.statusCode = res.statusCode;
|
||||||
|
|
||||||
// 异步写入日志,不阻塞响应
|
// 异步写入日志,不阻塞响应
|
||||||
try {
|
fs.mkdir(AUDIT_LOG_DIR, { recursive: true }, function(mkdirErr) {
|
||||||
fs.mkdirSync(AUDIT_LOG_DIR, { recursive: true });
|
if (mkdirErr) {
|
||||||
|
console.error('审计日志目录创建失败:', mkdirErr.message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
var today = new Date().toISOString().split('T')[0];
|
var today = new Date().toISOString().split('T')[0];
|
||||||
var logFile = path.join(AUDIT_LOG_DIR, 'audit-' + today + '.jsonl');
|
var logFile = path.join(AUDIT_LOG_DIR, 'audit-' + today + '.jsonl');
|
||||||
fs.appendFileSync(logFile, JSON.stringify(logEntry) + '\n');
|
fs.appendFile(logFile, JSON.stringify(logEntry) + '\n', function(writeErr) {
|
||||||
} catch (e) {
|
if (writeErr) console.error('审计日志写入失败:', writeErr.message);
|
||||||
console.error('审计日志写入失败:', e.message);
|
});
|
||||||
}
|
});
|
||||||
|
|
||||||
return originalSend.call(this, body);
|
return originalSend.call(this, body);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -62,6 +62,25 @@ function rateLimit(req, res, next) {
|
||||||
|
|
||||||
router.use(rateLimit);
|
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)
|
// 获取实际数据库 ID(沙箱环境使用沙箱 DB)
|
||||||
function getDbId(req, configKey) {
|
function getDbId(req, configKey) {
|
||||||
if (req.sandbox && req.sandboxDbIds && req.sandboxDbIds[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({
|
return res.status(403).json({
|
||||||
error: true,
|
error: true,
|
||||||
code: 'MODULE_DENIED',
|
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({
|
return res.status(403).json({
|
||||||
error: true,
|
error: true,
|
||||||
code: 'MODULE_DENIED',
|
code: 'MODULE_DENIED',
|
||||||
|
|
@ -432,6 +451,11 @@ router.patch('/dev/:devId/status',
|
||||||
var devId = req.params.devId;
|
var devId = req.params.devId;
|
||||||
var status = req.body.status;
|
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')) {
|
if (req.user.devId !== devId && !req.user.permissions.includes('dev:update_all')) {
|
||||||
return res.status(403).json({
|
return res.status(403).json({
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,8 @@
|
||||||
});
|
});
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
var data = await res.json();
|
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) {
|
} catch (e) {
|
||||||
console.debug('[Onboarding] check failed:', e.message);
|
console.debug('[Onboarding] check failed:', e.message);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue