Skip to content

Conversation

@jsrcode
Copy link
Collaborator

@jsrcode jsrcode commented Jan 8, 2026

概述

本 PR 实现了 v1.5.1 版本的配置守护系统,包括配置变更监听、快照管理、变更历史和通知队列机制。

🎯 核心功能

配置守护系统(Config Guard)

动机

  • 用户在外部修改工具配置时,应用无法感知变更
  • 需要保护敏感配置(API Key、Base URL)不被意外修改
  • 需要提供变更历史追踪和快照恢复功能
  • 多个工具连续修改时,变更通知会丢失

核心特性

  • 双模式监听:默认模式(仅敏感字段)+ 全量模式(所有变更)
  • 完整差异分析:递归比较 JSON、支持通配符模式匹配、黑名单过滤
  • 变更通知队列:解决多工具连续修改时弹窗丢失问题
  • 快照管理:支持 Block(恢复快照)和 Allow(更新快照)操作
  • 变更历史:分页查看所有配置变更记录(最多保留 100 条)
  • 敏感字段/黑名单管理:可视化配置管理界面

技术实现

后端(Rust)

  1. 数据模块 (src-tauri/src/data/)

    • changelogs.rs:配置变更日志管理(ChangeLogStore,支持分页、过期标记)
    • snapshots.rs:配置快照管理(SnapshotStore,保存工具配置文件快照)
  2. 配置监听 (src-tauri/src/services/config/watcher.rs)

    • 差异分析引擎:compute_diff() 递归比较 JSON 对象
    • 模式匹配:matches_pattern() 支持通配符(如 model_providers.*.base_url
    • 敏感字段检测:is_sensitive_change() 判断是否包含敏感字段
    • 黑名单过滤:filter_blacklisted() 过滤自动修改字段
    • 双模式支持:Default(仅敏感字段)/ Full(全量变更)
  3. Tauri 命令 (src-tauri/src/commands/config_commands.rs)

    • block_external_change:阻止外部变更,恢复快照到原生配置文件
    • allow_external_change:允许外部变更,更新快照为当前文件内容
    • get_watch_config / update_watch_config:监听配置管理
    • update_sensitive_fields / update_blacklist:字段配置管理
    • get_change_logs_page / clear_change_logs:变更历史管理
  4. 启动流程 (src-tauri/src/setup/initialization.rs)

    • initialize_snapshots():启动时初始化所有工具的配置快照
    • mark_expired_change_logs():标记未处理的变更日志为已过期
    • start_watcher():启动文件监听服务

前端(TypeScript + React)

  1. Hook (src/hooks/useConfigWatch.ts)

    • 队列管理:使用 useRef 存储待处理变更,避免闭包陷阱
    • 自动去重:同一工具的变更自动替换为最新
    • 自动进展:关闭对话框后自动显示下一个变更
    • 队列状态:暴露 queueLength 给 UI 显示
  2. 对话框组件

    • ConfigChangeDialog.tsx:显示变更详情,提供 Block/Allow 操作
      • 展示工具名称、文件路径、变更字段列表(标记新增/删除)
      • 敏感变更带警告图标和 Badge 标记
      • 显示队列中剩余变更数量
    • ChangeLogDialog.tsx:分页查看变更历史(10 条/页)
    • FieldManagementDialog.tsx:管理敏感字段和黑名单配置
  3. 设置页面 (src/pages/SettingsPage/components/ConfigGuardTab.tsx)

    • 双模式选择(默认模式/全量模式)
    • 扫描间隔配置(秒)
    • 敏感字段列表展示(按工具分组)
    • 黑名单字段列表展示(按工具分组)
    • 变更历史查看入口
  4. 全局集成 (src/App.tsx)

    • 使用 useConfigWatch hook,自动弹出配置变更对话框
    • 队列信息传递给对话框组件

📊 变更统计

  • 文件变更:37 个文件
  • 新增代码:2,998 行
  • 删除代码:1,239 行
  • 净增代码:1,759 行

主要文件

后端新增

  • src-tauri/src/data/changelogs.rs (155 行)
  • src-tauri/src/data/snapshots.rs (132 行)
  • src-tauri/src/commands/config_commands.rs (+316 行命令函数)

后端重构

  • src-tauri/src/services/config/watcher.rs (~650 行,增强差异分析)
  • src-tauri/src/models/config.rs (+140 行,ConfigWatchConfig 类型)
  • src-tauri/src/setup/initialization.rs (+34 行,初始化逻辑)

前端新增

  • src/hooks/useConfigWatch.ts (109 行)
  • src/components/dialogs/ConfigChangeDialog.tsx (253 行)
  • src/components/dialogs/ChangeLogDialog.tsx (273 行)
  • src/components/dialogs/FieldManagementDialog.tsx (319 行)
  • src/lib/tauri-commands/config-watch.ts (103 行)
  • src/types/config-watch.ts (126 行)
  • src/pages/SettingsPage/components/ConfigGuardTab.tsx (255 行)
  • src/pages/AboutPage/index.tsx (99 行)

删除文件

  • src-tauri/src/commands/watcher_commands.rs (125 行,功能整合到 config_commands.rs)
  • src/pages/SettingsPage/components/AboutTab.tsx (89 行,移到独立页面)
  • src/pages/SettingsPage/components/ConfigManagementTab.tsx (340 行,废弃旧实现)

✅ 测试情况

后端测试

  • 配置监听测试:15 个测试(11 个通过,4 个标记为 #[ignore])
    • 9 个差异分析测试(字段变更、通配符匹配、黑名单过滤)
    • 2 个轮询监听测试
  • Clippy 检查:通过(0 警告)
  • 格式检查:通过(cargo fmt)

前端测试

  • ESLint 检查:通过(3 个无关警告,已存在)
  • Prettier 格式:通过
  • TypeScript 编译:通过

手动验证

  • 配置守护:测试了 Claude Code、Codex、Gemini CLI 的配置监听
  • 队列机制:验证了连续修改多个工具配置时的队列处理
  • Block/Allow 操作:验证了快照恢复和更新功能
  • 变更历史:验证了分页查看和清除操作

🔑 核心实现

1. 配置变更队列机制

// Hook 实现:使用 useRef 避免闭包陷阱
const queueRef = useRef<ExternalConfigChange[]>([]);
const isShowingRef = useRef(false);

// 自动去重:同一工具的变更替换为最新
const existingIndex = queueRef.current.findIndex(c => c.tool_id === newChange.tool_id);
if (existingIndex >= 0) {
  queueRef.current[existingIndex] = newChange;
} else {
  queueRef.current.push(newChange);
}

// 自动进展:关闭对话框后显示下一个
if (queueRef.current.length > 0) {
  showNext();
}

2. 差异分析引擎

// 递归比较 JSON 对象
fn compute_diff(old: &Value, new: &Value, path: String) -> Vec<FieldChange> {
    match (old, new) {
        (Value::Object(old_map), Value::Object(new_map)) => {
            // 检测修改和删除
            // 检测新增
        }
        _ if old != new => vec![FieldChange::modified(path, old, new)],
        _ => vec![],
    }
}

// 通配符模式匹配
fn matches_pattern(field_path: &str, pattern: &str) -> bool {
    if pattern.ends_with(".*") {
        let prefix = &pattern[..pattern.len() - 2];
        field_path.starts_with(prefix)
    } else {
        field_path == pattern
    }
}

3. 快照管理

// 阻止外部变更:恢复快照
pub fn block_external_change(tool_id: String) -> Result<(), String> {
    let snapshot = snapshots::get_snapshot(&tool_id)?;
    for (filename, content) in &snapshot.files {
        restore_file(&config_path, content)?;
    }
    update_change_log_action(&tool_id, "block")?;
}

// 允许外部变更:更新快照
pub fn allow_external_change(tool_id: String) -> Result<(), String> {
    save_snapshot_for_tool(&tool)?;
    update_change_log_action(&tool_id, "allow")?;
}

⚠️ 风险评估

已缓解

  1. 配置文件操作

    • 风险:Block 操作可能导致用户配置丢失
    • 缓解:使用快照机制,所有操作可回滚
    • 缓解:启动时自动标记未处理变更为过期
  2. 队列机制

    • 风险:React useRef 闭包问题可能导致状态不同步
    • 缓解:使用 isShowingRefqueueRef 配合,避免闭包陷阱
    • 缓解:300ms 延迟确保对话框动画完成
  3. 文件监听性能

    • 风险:频繁文件变更可能导致性能问题
    • 缓解:默认 2 秒扫描间隔,可配置
    • 缓解:黑名单过滤减少无效通知

📦 依赖变更

无新增依赖

🔗 相关 Commit

  • bffc26d - feat(config-watch): 实现配置守护系统和变更通知队列机制
  • 31d3a8f - fix(settings): 添加更新管理 Tab 并修复 TypeScript 类型错误

📝 检查清单

  • 已运行 npm run check 且全部通过
  • 后端测试通过(11/15,4 个待 ProfileManager 重写)
  • 前端 TypeScript 编译无错误
  • 已更新 CLAUDE.md 项目记忆
  • 代码遵循项目规范(Conventional Commits)
  • 重要变更已充分测试
  • CI 检查已通过

审核建议

  1. 重点关注配置文件操作的安全性(Block/Allow 逻辑)
  2. 检查队列机制的 React Hook 实现(useRef + useCallback)
  3. 验证差异分析的准确性(通配符匹配、黑名单过滤)

测试建议

  1. 手动测试配置守护:连续修改多个工具配置,验证队列处理
  2. 测试 Block/Allow 操作,确认配置恢复和快照更新正确
  3. 测试变更历史查看和清除功能

- 后端:新增配置快照和变更日志管理模块(changelogs.rs、snapshots.rs)
- 后端:实现双模式监听(默认模式仅通知敏感字段,全量模式通知所有变更)
- 后端:添加完整的差异分析引擎,支持字段变更检测、通配符模式匹配和黑名单过滤
- 后端:实现 Block/Allow 操作,支持恢复快照和更新快照
- 前端:实现变更通知队列机制,解决多工具连续修改时弹窗丢失问题
- 前端:新增配置守护管理界面(ConfigGuardTab)和变更历史查看(ChangeLogDialog)
- 前端:支持敏感字段和黑名单配置管理(FieldManagementDialog)
- 重构:删除旧的 watcher_commands.rs,功能整合到 config_commands.rs
- 重构:启动流程更新,自动初始化配置快照和启动文件监听
- 测试:新增 9 个差异分析和模式匹配单元测试
@github-actions
Copy link

github-actions bot commented Jan 8, 2026

本评论会随各平台任务完成自动更新:
如首轮 npm run check 失败,请在本地执行 npm run check:fixnpm run check 并提交修复 commit。
如 fix 仍失败,请在本地排查并确保 npm run check 通过后再提交。
跨平台差异若无法复现,请复制日志交给 AI 获取排查建议。

平台 结果 明细 日志包 运行链接
ubuntu-22.04 ✅ 直接通过 check=success / fix=skipped / recheck=skipped pr-check-ubuntu-22.04 日志
windows-latest ✅ 直接通过 check=success / fix=skipped / recheck=skipped pr-check-windows-latest 日志
macos-14 (arm64) ✅ 直接通过 check=success / fix=skipped / recheck=skipped pr-check-macos-arm64 日志
macos-15 (x64) ✅ 直接通过 check=success / fix=skipped / recheck=skipped pr-check-macos-x64 日志

This comment auto-updates as each platform finishes:
If the first npm run check fails, run locally: npm run check:fixnpm run check and commit the fix.
If fix still fails, investigate locally and ensure npm run check passes before committing.
If cross-platform issues can't be reproduced, copy logs to an AI for hints.

Platform Status Detail Artifact Run
ubuntu-22.04 ✅ Passed check=success / fix=skipped / recheck=skipped pr-check-ubuntu-22.04 日志
windows-latest ✅ Passed check=success / fix=skipped / recheck=skipped pr-check-windows-latest 日志
macos-14 (arm64) ✅ Passed check=success / fix=skipped / recheck=skipped pr-check-macos-arm64 日志
macos-15 (x64) ✅ Passed check=success / fix=skipped / recheck=skipped pr-check-macos-x64 日志

- 修复 SettingsPageProps 缺少 updateInfo 和 onUpdateCheck 属性
- 添加 UpdateTab 导入和使用
- 在设置页面添加更新管理标签页
- 解决 CI 构建失败问题(TypeScript 编译错误)
@jsrcode jsrcode changed the title feat: v1.5.0 配置守护系统和供应商管理 feat(v1.5.1): 配置守护系统和变更通知队列机制 Jan 8, 2026
- 完全移除更新管理 Tab(包括 TabsTrigger 和 TabsContent)
- 删除 UpdateTab 组件导入
- 将 updateInfo 和 onUpdateCheck props 改为可选(与上游一致)
- 使用下划线前缀标记未使用的 props(_updateInfo, _onUpdateCheck)
@jsrcode jsrcode force-pushed the test-final-comment branch from 94dea90 to c35e440 Compare January 8, 2026 10:17
移除设置页面中已废弃的实验性功能组件,包括:
- 删除 ExperimentalSettingsTab 组件(透明代理 UI,已迁移到独立页面)
- 删除 InstallPackageSelector 组件(安装包选择器,更新功能已重构)
- 删除 UpdateTab 组件(更新管理,功能已移至其他模块)
- 删除 useMultiToolProxy hook(多工具代理钩子,已废弃)
- 删除 StatisticsPage 页面(统计页面,未使用)
- 清理 useSettingsForm 中的 generateProxyKey 废弃函数

清理范围:
- 删除 1222 行代码
- 移除 6 个文件/组件
- 简化设置表单逻辑

此次清理遵循代码库演进方向,相关功能已在新架构中重新实现,
保持代码库整洁,减少维护负担。
@DuckCoding-dev DuckCoding-dev merged commit 82a034b into DuckCoding-dev:main Jan 13, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants