Files
edict/scripts/utils.py
cft0808 b8d06a3972 feat: 完成全部剩余功能修复 (P0-P3)
P0:
- 圣旨模板下旨真正创建任务: 新增 POST /api/create-task
  前端 executeTemplate 改为 API 调用(降级仍可剪贴板复制)

P1:
- morning-config POST 字段校验: 检查 categories/keywords/feishu_webhook 类型
- 早报幂等锁支持 --force 强制采集: 看板手动刷新默认 force=true
- sync_agent_config 补全 Copilot 模型列表(6个)

P2:
- utils.py 公共函数抽取: read_json/now_iso/validate_url/safe_name
- refresh_live_data.py 改用 utils.read_json 消除重复定义
- apply_model_changes 回滚标记: 失败时 rolledBack=true 写入日志+前端展示
- 早报日期 API 兼容 YYYY-MM-DD 自动转换 + 格式校验
- Request logging: log_message 改为只记录 4xx/5xx 错误请求
- 飞书 Webhook URL 校验: 限制 https + open.feishu.cn 域名

P3:
- 御批模式基础实现: Review/Menxia 状态显示准奏/封驳按钮
  新增 POST /api/review-action(approve推进/reject退回中书省+轮次+1)
  前端 reviewAction() + 变更日志回滚标记显示
2026-02-26 21:42:13 +08:00

55 lines
1.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""
三省六部 · 公共工具函数
避免 read_json / now_iso 等基础函数在多个脚本中重复定义
"""
import json, pathlib, datetime
def read_json(path, default=None):
"""安全读取 JSON 文件,失败返回 default"""
try:
return json.loads(pathlib.Path(path).read_text())
except Exception:
return default if default is not None else {}
def now_iso():
"""返回 UTC ISO 8601 时间字符串(末尾 Z"""
return datetime.datetime.now(datetime.timezone.utc).isoformat().replace('+00:00', 'Z')
def today_str(fmt='%Y%m%d'):
"""返回今天日期字符串,默认 YYYYMMDD"""
return datetime.date.today().strftime(fmt)
def safe_name(s: str) -> bool:
"""检查名称是否只含安全字符(字母、数字、下划线、连字符、中文)"""
import re
return bool(re.match(r'^[a-zA-Z0-9_\-\u4e00-\u9fff]+$', s))
def validate_url(url: str, allowed_schemes=('https',), allowed_domains=None) -> bool:
"""校验 URL 合法性,防 SSRF"""
from urllib.parse import urlparse
try:
parsed = urlparse(url)
if parsed.scheme not in allowed_schemes:
return False
if allowed_domains and parsed.hostname not in allowed_domains:
return False
if not parsed.hostname:
return False
# 禁止内网地址
import ipaddress
try:
ip = ipaddress.ip_address(parsed.hostname)
if ip.is_private or ip.is_loopback or ip.is_reserved:
return False
except ValueError:
pass # hostname 不是 IP放行
return True
except Exception:
return False