- 新增 `get_secure()` 和 `set_secure()` 方法,优先从环境变量或系统 keyring 读取敏感配置,`config.json` 中仅存储占位符 - 将 `save()` 方法改为使用临时文件 + `os.replace()` 的原子写入,防止进程中断导致配置文件损坏 - 在 `add_llm_provider()` 和 `get_active_llm()` 中集成安全配置读写,自动迁移旧版明文 API Key ♻️ refactor(analytics): 实现分析数据原子写 - 将 `_save_analytics()` 和 `_save_weights()` 方法改为使用临时文件 + `os.replace()` 的原子写入 - 确保在写入过程中进程被终止时,原始数据文件保持完整 ♻️ refactor(main): 增强发布功能健壮性与代码模块化 - 在 `publish_to_xhs()` 中增加发布前输入校验【标题长度、图片数量、文件存在性】并在 `finally` 块中自动清理本次生成的临时图片文件 - 为全局笔记列表缓存 `_cached_proactive_entries` 和 `_cached_my_note_entries` 引入 `threading.RLock` 保护,新增 `_set_cache()` 和 `_get_cache()` 线程安全操作函数 - 将「内容创作」Tab 的 UI 构建代码拆分至 `ui/tab_create.py` 模块,主文件通过 `build_tab()` 函数调用并组装 - 将 Gradio 应用的 CSS 和主题配置提取为模块级变量,提升可维护性 📦 build(deps): 新增 keyring 依赖 - 在 `requirements.txt` 中添加 `keyring>=24.0.0` 以支持系统凭证管理 📝 docs(openspec): 新增生产就绪审计文档 - 在 `openspec/changes/archive/2026-02-24-production-readiness-audit/` 下新增设计文档、提案、任务清单及各功能规格说明 - 将核心功能规格同步至 `openspec/specs/` 目录
4.1 KiB
4.1 KiB
1. 依赖与环境准备
- 1.1 在
requirements.txt中添加keyring>=24.0.0 - 1.2 运行
pip install keyring并验证在当前系统(Windows)可正常使用keyring.get_password/keyring.set_password
2. 安全配置(secure-config)
- 2.1 在
config_manager.py中新增get_secure(key: str) -> str方法:优先读取环境变量AUTOBOT_<KEY.upper()>,其次读取系统 keyring,最后回退config.json明文(自动迁移一次),捕获keyring.errors.NoKeyringError并降级 - 2.2 在
config_manager.py中新增set_secure(key: str, value: str)方法:写入系统 keyring(降级模式下写入config.json),并将config.json对应字段更新为占位符"[keyring]" - 2.3 将
main.py中 LLM 提供商的api_key读写全部替换为cfg.get_secure()/cfg.set_secure() - 2.4 手动测试:重启应用后
config.json中api_key已变为"[keyring]",LLM 连接功能正常
3. JSON 原子写(atomic-persistence)
- 3.1 在
config_manager.py的save()方法中将直接open(CONFIG_FILE, "w")改为tempfile.mkstemp(dir=<same_dir>)+ 写入 +os.replace()原子重命名 - 3.2 在
analytics_service.py的_save_analytics()方法中同样改为原子写 - 3.3 在
analytics_service.py的_save_weights()方法中同样改为原子写 - 3.4 测试:在写入过程中(
time.sleep模拟)验证目标文件仍完整,临时文件被清理
4. 线程安全缓存(thread-safe-cache)
- 4.1 在
main.py顶部声明_cache_lock = threading.RLock() - 4.2 新增内部函数
_set_cache(name: str, entries: list)和_get_cache(name: str) -> list,内部使用with _cache_lock:保护 - 4.3 将
_fetch_and_cache()中对_cached_proactive_entries/_cached_my_note_entries的直接赋值改为调用_set_cache() - 4.4 将
_pick_from_cache()中读取缓存改为调用_get_cache()(在锁内完成列表快照拷贝) - 4.5 将
fetch_my_notes()中对_cached_my_note_entries的直接赋值改为调用_set_cache()
5. 发布前输入校验(publish-input-validation)
- 5.1 在
publish_to_xhs()函数内、MCP 调用前添加标题长度校验(len(title) > 20返回错误) - 5.2 添加图片数量下限校验(
len(image_paths) == 0返回「至少需要 1 张图片」) - 5.3 添加图片数量上限校验(
len(image_paths) > 18返回含实际数量的错误消息) - 5.4 添加图片文件存在性校验(遍历
image_paths,发现不存在的文件时返回含路径的错误) - 5.5 在 Gradio UI 的发布按钮标题输入框旁添加字符计数提示(
gr.Textbox的info参数)
6. 临时文件生命周期(temp-file-lifecycle)
- 6.1 在
publish_to_xhs()中记录本次写入的 AI 临时图片路径到局部变量ai_temp_files = [] - 6.2 在函数末尾添加
finally:块,遍历ai_temp_files逐一调用os.remove(),捕获OSError仅记录logger.warning - 6.3 验证:发布成功后
_temp_publish/目录中的ai_*.jpg文件已被删除;发布失败后同样被清理
7. UI 模块拆分(ui-module-split)
- 7.1 创建
ui/目录,添加ui/__init__.py(空文件) - 7.2 创建
ui/tab_create.py,将main.py中「内容创作 Tab」的所有 Gradio 组件定义和.click()/.change()事件绑定代码迁移至该文件,导出build_tab(cfg, mcp_url_box, ...)函数 - 7.3 在
main.py中用from ui.tab_create import build_tab+ 调用替换原有内容创作 Tab 代码 - 7.4 启动应用,验证内容创作 Tab 功能与迁移前完全一致(文案生成、图片生成、发布按钮均正常)
8. 集成验证
- 8.1 启动应用,依次测试:LLM 连接 → 文案生成 → 图片生成 → 发布(包含校验不通过和校验通过两个场景)
- 8.2 检查
config.json中无明文 API Key - 8.3 检查
_temp_publish/目录在发布后为空(或只含本次以外的文件) - 8.4 检查
autobot.log中无 ERROR 级别日志