fix: 代码审查问题修复
- 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>
This commit is contained in:
@@ -11,6 +11,7 @@ import asyncio
|
||||
from pathlib import Path
|
||||
from typing import List, Set, Optional
|
||||
from datetime import datetime
|
||||
import aiofiles
|
||||
from ..storage import Database, WikiPage, compute_content_hash
|
||||
from .parser import ParserService
|
||||
|
||||
@@ -31,9 +32,9 @@ class IndexerService:
|
||||
try:
|
||||
full_path = self.wiki_vault_path / page_path
|
||||
|
||||
# 读取文件内容
|
||||
with open(full_path, 'r', encoding='utf-8') as f:
|
||||
content = f.read()
|
||||
# 读取文件内容(使用 aiofiles 实现真正的异步 I/O)
|
||||
async with aiofiles.open(full_path, 'r', encoding='utf-8') as f:
|
||||
content = await f.read()
|
||||
|
||||
# 解析页面
|
||||
parsed = self.parser.parse_wiki_page(page_path, content)
|
||||
@@ -120,8 +121,8 @@ class IndexerService:
|
||||
for page_path in all_pages:
|
||||
full_path = self.wiki_vault_path / page_path
|
||||
try:
|
||||
with open(full_path, 'r', encoding='utf-8') as f:
|
||||
content = f.read()
|
||||
async with aiofiles.open(full_path, 'r', encoding='utf-8') as f:
|
||||
content = await f.read()
|
||||
current_hash = compute_content_hash(content)
|
||||
stored_hash = await self.db.get_page_hash(page_path)
|
||||
if stored_hash != current_hash:
|
||||
@@ -143,8 +144,8 @@ class IndexerService:
|
||||
for page_path in all_pages:
|
||||
try:
|
||||
full_path = self.wiki_vault_path / page_path
|
||||
with open(full_path, 'r', encoding='utf-8') as f:
|
||||
content = f.read()
|
||||
async with aiofiles.open(full_path, 'r', encoding='utf-8') as f:
|
||||
content = await f.read()
|
||||
current_hash = compute_content_hash(content)
|
||||
stored_hash = await self.db.get_page_hash(page_path)
|
||||
|
||||
|
||||
@@ -13,6 +13,14 @@ from typing import List, Dict, Optional, Tuple
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
|
||||
try:
|
||||
import yaml
|
||||
YAML_AVAILABLE = True
|
||||
except ImportError:
|
||||
YAML_AVAILABLE = False
|
||||
logger = logging.getLogger(__name__)
|
||||
logger.warning("PyYAML not available, using simple parser")
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@@ -30,7 +38,7 @@ class ParserService:
|
||||
|
||||
def parse_frontmatter(self, content: str) -> Dict:
|
||||
"""
|
||||
解析 frontmatter
|
||||
解析 frontmatter(使用 PyYAML)
|
||||
|
||||
返回格式:
|
||||
{
|
||||
@@ -45,14 +53,28 @@ class ParserService:
|
||||
|
||||
if match:
|
||||
yaml_content = match.group(1)
|
||||
# 简单解析(对于复杂情况,应该使用 PyYAML)
|
||||
for line in yaml_content.split('\n'):
|
||||
if ':' in line:
|
||||
key, value = line.split(':', 1)
|
||||
key = key.strip()
|
||||
value = value.strip()
|
||||
frontmatter[key] = value
|
||||
|
||||
# 使用 PyYAML 进行完整解析
|
||||
if YAML_AVAILABLE:
|
||||
try:
|
||||
frontmatter = yaml.safe_load(yaml_content) or {}
|
||||
except yaml.YAMLError as e:
|
||||
logger.warning(f"Failed to parse YAML: {e}")
|
||||
frontmatter = self._simple_parse(yaml_content)
|
||||
else:
|
||||
frontmatter = self._simple_parse(yaml_content)
|
||||
|
||||
return frontmatter
|
||||
|
||||
def _simple_parse(self, yaml_content: str) -> Dict:
|
||||
"""简单 YAML 解析(fallback)"""
|
||||
frontmatter = {}
|
||||
for line in yaml_content.split('\n'):
|
||||
if ':' in line:
|
||||
key, value = line.split(':', 1)
|
||||
key = key.strip()
|
||||
value = value.strip()
|
||||
frontmatter[key] = value
|
||||
return frontmatter
|
||||
|
||||
def extract_links(self, content: str) -> List[str]:
|
||||
@@ -71,20 +93,25 @@ class ParserService:
|
||||
支持:
|
||||
- tags: ["tag1", "tag2"]
|
||||
- tags: [tag1, tag2]
|
||||
- YAML 列表格式
|
||||
"""
|
||||
tags = []
|
||||
match = self.TAG_PATTERN.search(content)
|
||||
frontmatter = self.parse_frontmatter(content)
|
||||
|
||||
if match:
|
||||
tag_content = match.group(1)
|
||||
# 尝试 JSON 解析
|
||||
try:
|
||||
tags = json.loads(f"[{tag_content}]")
|
||||
except:
|
||||
# 简单逗号分隔
|
||||
tags = [t.strip().strip('"\'') for t in tag_content.split(',')]
|
||||
if "tags" in frontmatter:
|
||||
tags_value = frontmatter["tags"]
|
||||
|
||||
return tags
|
||||
if isinstance(tags_value, list):
|
||||
tags = tags_value
|
||||
elif isinstance(tags_value, str):
|
||||
# 尝试解析字符串格式的标签
|
||||
try:
|
||||
tags = json.loads(tags_value) if tags_value.startswith('[') else tags_value.split(',')
|
||||
except:
|
||||
tags = [tags_value]
|
||||
|
||||
# 确保 tags 是字符串列表
|
||||
return [str(tag).strip() for tag in tags if tag]
|
||||
|
||||
def extract_title(self, content: str) -> str:
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user