62b8fef18a
- C1 (Critical): 添加 PyYAML 解析 frontmatter,支持多行值和列表 - M1 (Major): 添加 FTS5 查询验证,防止注入攻击 - M2/M3 (Major): 工具类使用显式 wiki_vault_path 参数 - M4 (Major): 集成 aiofiles 实现真正的异步文件 I/O - M5 (Major): 改用 logger.exception() 记录完整堆栈 Co-Authored-By: Claude Dev <noreply@anthropic.com>
109 lines
3.4 KiB
Python
109 lines
3.4 KiB
Python
"""
|
||
Tool Layer - daily_update 工具
|
||
|
||
日常维护(检查源新鲜度、更新 index、重新生成 hot.md)。
|
||
|
||
参考设计文档:第 2.2 节
|
||
"""
|
||
|
||
import logging
|
||
import os
|
||
from typing import Dict, Any
|
||
from datetime import datetime
|
||
from pathlib import Path
|
||
import aiofiles
|
||
from ..services import QueryService, IndexerService
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
|
||
class DailyUpdateTool:
|
||
"""daily_update 工具实现"""
|
||
|
||
def __init__(self, query_service: QueryService, indexer_service: IndexerService, wiki_vault_path: str = None):
|
||
self.query_service = query_service
|
||
self.indexer_service = indexer_service
|
||
# 优先使用传入的路径,否则使用环境变量
|
||
self.wiki_vault_path = Path(wiki_vault_path or os.environ.get("WIKI_VAULT_PATH", "/Volumes/KnowledgeBase/wiki-vault"))
|
||
|
||
async def handle(self) -> Dict[str, Any]:
|
||
"""
|
||
日常维护
|
||
|
||
Returns:
|
||
{
|
||
"updated": 5,
|
||
"new": 2,
|
||
"hot_md_generated": true,
|
||
"index_status": {...}
|
||
}
|
||
"""
|
||
# 1. 增量更新索引
|
||
index_status = await self.indexer_service.incremental_update()
|
||
|
||
# 2. 获取最近更新的页面
|
||
recent_pages = await self.query_service.db.get_recent_pages(days=7, limit=20)
|
||
|
||
# 3. 获取新增标签
|
||
new_tags = await self.query_service.db.get_new_tags(days=7)
|
||
|
||
# 4. 查找孤立页面
|
||
from ..services import GraphService
|
||
graph_service = GraphService(self.query_service.db)
|
||
orphans = await graph_service.find_orphans()
|
||
|
||
# 5. 生成 hot.md
|
||
hot_md_generated = await self._generate_hot_md(recent_pages, new_tags, orphans)
|
||
|
||
return {
|
||
"updated": len(recent_pages),
|
||
"new": len(new_tags),
|
||
"hot_md_generated": hot_md_generated,
|
||
"index_status": index_status
|
||
}
|
||
|
||
async def _generate_hot_md(self, recent_pages, new_tags, orphans) -> bool:
|
||
"""生成热点文件"""
|
||
try:
|
||
# 使用配置的 wiki vault 路径
|
||
hot_path = self.wiki_vault_path / "hot.md"
|
||
|
||
# 格式化内容
|
||
content = f"""# Wiki Hot - {datetime.now().strftime('%Y-%m-%d')}
|
||
|
||
## 最近更新(7 天内)
|
||
|
||
"""
|
||
for page in recent_pages[:10]:
|
||
content += f"- [[{page.path}|{page.title}]] - {page.updated_at}\n"
|
||
|
||
content += "\n## 新增标签(7 天内)\n\n"
|
||
for tag, updated_at in new_tags[:10]:
|
||
content += f"- `{tag}` - {updated_at}\n"
|
||
|
||
content += "\n## 待链接页面(孤立页面)\n\n"
|
||
for orphan in sorted(list(orphans))[:10]:
|
||
content += f"- [[{orphan}]]\n"
|
||
|
||
# 写入文件(使用 aiofiles 实现异步 I/O)
|
||
async with aiofiles.open(hot_path, 'w', encoding='utf-8') as f:
|
||
await f.write(content)
|
||
|
||
logger.info(f"Generated hot.md: {hot_path}")
|
||
return True
|
||
|
||
except Exception as e:
|
||
logger.error(f"Failed to generate hot.md: {e}")
|
||
return False
|
||
|
||
def get_schema(self) -> dict:
|
||
"""返回 MCP Tool schema"""
|
||
return {
|
||
"name": "daily_update",
|
||
"description": "日常维护(检查源新鲜度、更新 index、重新生成 hot.md)",
|
||
"inputSchema": {
|
||
"type": "object",
|
||
"properties": {}
|
||
}
|
||
}
|