refactor: 全栈质量硬化
后端: - 权限系统重写: 全局→按 Session 隔离, 新增规则查询 API - 安全加固: 登录 IP 限流, Token 仅存 Cookie, bibcode 白名单校验 - SSE 超时保护, 异步 I/O 迁移, 10+ 处静默 DB 错误改为显式日志 - ar5iv 下标解析修复, parse_paper_row 去重, 优雅关闭 前端: - useSyncScroll 重写: 段落 ID 映射修复中英错位 - 全局竞态修复 (active 标志), libraryRef 闭包过期修复 - ErrorBoundary + vitest 测试基础设施 - Logo 组件提取, CustomSelect 泛型化, TabId 类型统一 - ReaderPanel 自动视图模式, AIAssistantPanel 状态批处理
This commit is contained in:
@@ -129,7 +129,7 @@ pub async fn execute_parallel(
|
||||
app_state: Arc<AppState>,
|
||||
hook_registry: &HookRegistry,
|
||||
permission_checker: Option<&PermissionChecker>,
|
||||
session_permission_checker: Option<&std::sync::RwLock<PermissionChecker>>,
|
||||
session_permission_checker: Option<&PermissionChecker>,
|
||||
denial_tracker: Option<&std::sync::Mutex<DenialTracker>>,
|
||||
checkpoint_manager: Option<&std::sync::Arc<CheckpointManager>>,
|
||||
tx: &mpsc::UnboundedSender<AgentStreamEvent>,
|
||||
@@ -336,25 +336,23 @@ pub async fn execute_parallel(
|
||||
|
||||
// 会话级权限检查(API 动态添加的规则,优先级高于环境变量规则)
|
||||
if let Some(session_checker) = session_permission_checker {
|
||||
if let Ok(checker) = session_checker.read() {
|
||||
let session_result = checker.check(&prep.tool_name, Some(&prep.args));
|
||||
// 会话规则结果覆盖或升级
|
||||
match session_result {
|
||||
PermissionResult::Denied { reason } => {
|
||||
// 会话 Deny 强制覆盖
|
||||
perm_result = PermissionResult::Denied { reason };
|
||||
}
|
||||
PermissionResult::AskUser { message } => {
|
||||
// 会话 Ask 在 Allow 时升级
|
||||
if perm_result.is_allowed() {
|
||||
perm_result = PermissionResult::AskUser { message };
|
||||
}
|
||||
}
|
||||
PermissionResult::Allowed => {
|
||||
// 会话 Allow 仅覆盖 Allowed,保持 Deny/AskUser 不变
|
||||
// 避免覆盖工具级 check_permissions() 升级的 AskUser
|
||||
let session_result = session_checker.check(&prep.tool_name, Some(&prep.args));
|
||||
// 会话规则结果覆盖或升级
|
||||
match session_result {
|
||||
PermissionResult::Denied { reason } => {
|
||||
// 会话 Deny 强制覆盖
|
||||
perm_result = PermissionResult::Denied { reason };
|
||||
}
|
||||
PermissionResult::AskUser { message } => {
|
||||
// 会话 Ask 在 Allow 时升级
|
||||
if perm_result.is_allowed() {
|
||||
perm_result = PermissionResult::AskUser { message };
|
||||
}
|
||||
}
|
||||
PermissionResult::Allowed => {
|
||||
// 会话 Allow 仅覆盖 Allowed,保持 Deny/AskUser 不变
|
||||
// 避免覆盖工具级 check_permissions() 升级的 AskUser
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -130,10 +130,7 @@ impl AgentRuntime {
|
||||
permission_checker.clone(),
|
||||
),
|
||||
));
|
||||
// 初始化会话级权限检查器(与 AgentRuntime 使用相同的环境变量规则)
|
||||
if let Ok(mut session_checker) = app_state.session_permission_checker.write() {
|
||||
*session_checker = (*permission_checker).clone();
|
||||
}
|
||||
// 会话级权限检查器将在 run_react_loop 中按 session_id 注册
|
||||
|
||||
// 视觉模型可用时注册 analyze_image 工具
|
||||
if app_state.vision_llm.is_some() {
|
||||
@@ -202,10 +199,7 @@ impl AgentRuntime {
|
||||
permission_checker.clone(),
|
||||
),
|
||||
));
|
||||
// 初始化会话级权限检查器
|
||||
if let Ok(mut session_checker) = app_state.session_permission_checker.write() {
|
||||
*session_checker = (*permission_checker).clone();
|
||||
}
|
||||
// 会话级权限检查器将在 run_react_loop 中按 session_id 注册
|
||||
|
||||
// 视觉模型可用时注册 analyze_image 工具
|
||||
if app_state.vision_llm.is_some() {
|
||||
@@ -497,6 +491,15 @@ impl AgentRuntime {
|
||||
let sid = &session_info.session_id;
|
||||
let turn_index = session_info.turn_index;
|
||||
|
||||
// 注册当前会话的权限检查器(如不存在则从全局配置初始化)
|
||||
{
|
||||
if let Ok(mut checkers) = self.app_state.session_permission_checkers.write() {
|
||||
checkers
|
||||
.entry(sid.clone())
|
||||
.or_insert_with(|| (*self.permission_checker).clone());
|
||||
}
|
||||
}
|
||||
|
||||
let tool_defs = self.tool_registry.definitions();
|
||||
let mut duplicate_detector = DuplicateDetector::default();
|
||||
let mut metrics = AgentMetrics::default();
|
||||
@@ -867,13 +870,20 @@ impl AgentRuntime {
|
||||
}
|
||||
|
||||
// 并行执行工具(带权限检查、checkpoint 和分区器)
|
||||
let session_checker_snapshot = {
|
||||
self.app_state
|
||||
.session_permission_checkers
|
||||
.read()
|
||||
.ok()
|
||||
.and_then(|checkers| checkers.get(sid).cloned())
|
||||
};
|
||||
let exec_result = executor::execute_parallel(
|
||||
&prepared_calls,
|
||||
&self.tool_registry,
|
||||
self.app_state.clone(),
|
||||
hook_registry,
|
||||
Some(&self.permission_checker),
|
||||
Some(&self.app_state.session_permission_checker),
|
||||
session_checker_snapshot.as_ref(),
|
||||
Some(&self.denial_tracker),
|
||||
Some(&self.checkpoint_manager),
|
||||
tx,
|
||||
|
||||
@@ -76,6 +76,16 @@ impl PermissionChecker {
|
||||
}
|
||||
}
|
||||
|
||||
/// 返回当前规则列表的只读引用
|
||||
pub fn rules(&self) -> &[PermissionRule] {
|
||||
&self.rules
|
||||
}
|
||||
|
||||
/// 返回当前权限模式
|
||||
pub fn mode(&self) -> PermissionMode {
|
||||
self.mode
|
||||
}
|
||||
|
||||
/// 添加规则。先添加的优先级更高。
|
||||
pub fn add_rule(&mut self, rule: PermissionRule) {
|
||||
self.rules.push(rule);
|
||||
|
||||
@@ -600,12 +600,13 @@ mod tests {
|
||||
skill_registry: Arc::new(RwLock::new(SkillRegistry::new(PathBuf::from("/tmp/sk")))),
|
||||
pending_questions: Arc::new(Mutex::new(std::collections::HashMap::new())),
|
||||
pending_permissions: Arc::new(Mutex::new(std::collections::HashMap::new())),
|
||||
session_permission_checker: Arc::new(RwLock::new(PermissionChecker::new())),
|
||||
session_permission_checkers: Arc::new(RwLock::new(std::collections::HashMap::new())),
|
||||
sse_broadcast: None,
|
||||
memory_manager: Arc::new(tokio::sync::Mutex::new(MemoryManager::new(PathBuf::from(
|
||||
"/tmp/test_mem",
|
||||
)))),
|
||||
sessions: Arc::new(Mutex::new(std::collections::HashMap::new())),
|
||||
login_rate_limiter: Arc::new(Mutex::new(std::collections::HashMap::new())),
|
||||
});
|
||||
|
||||
ToolContext::new(app_state)
|
||||
|
||||
Reference in New Issue
Block a user