feat: initial import (exclude templates and runtime temp files)

This commit is contained in:
237899745
2026-02-27 15:21:15 +08:00
commit 0951732c7a
33 changed files with 11698 additions and 0 deletions

199
app/api_get_routes.py Normal file
View File

@@ -0,0 +1,199 @@
from __future__ import annotations
import json
import mimetypes
from http import HTTPStatus
from pathlib import Path
from typing import Any
from urllib.parse import parse_qs
def handle_get_routes(handler: Any, parsed: Any, ctx: dict[str, Any]) -> bool:
load_config = ctx["load_config"]
resolve_template_path = ctx["resolve_template_path"]
resolve_output_dir = ctx["resolve_output_dir"]
resolve_history_path = ctx["resolve_history_path"]
today_log_path = ctx["today_log_path"]
list_issue_marks = ctx["list_issue_marks"]
count_log_lines = ctx["count_log_lines"]
load_history = ctx["load_history"]
build_history_view_items = ctx["build_history_view_items"]
get_generation_progress = ctx["get_generation_progress"]
resolve_image_delivery_options = ctx["resolve_image_delivery_options"]
ISSUE_MARKS_PATH = ctx["ISSUE_MARKS_PATH"]
_HISTORY_LOCK = ctx["_HISTORY_LOCK"]
_LOG_LOCK = ctx["_LOG_LOCK"]
_DOWNLOAD_LOCK = ctx["_DOWNLOAD_LOCK"]
DOWNLOAD_CACHE = ctx["DOWNLOAD_CACHE"]
if parsed.path == "/api/config":
try:
config = load_config()
template = resolve_template_path(config)
output = resolve_output_dir(config)
except Exception as exc:
handler._send_json({"ok": False, "error": str(exc)}, HTTPStatus.INTERNAL_SERVER_ERROR)
return True
history_path = resolve_history_path(config)
log_path = today_log_path()
with _HISTORY_LOCK:
history = load_history(history_path)
_, active_issue_count = list_issue_marks(status="active", limit=1)
handler._send_json(
{
"ok": True,
"config": {
"template_file": str(template),
"output_dir": str(output),
"trigger_keyword": config.get("relay_handling", {}).get("trigger_keyword", "#接龙"),
"allowed_branches": config.get("branches", {}).get("allowed", []),
"type_matching": config.get("type_matching", {}),
"status_fallback": config.get("status_extraction", {}).get("fallback", "成功营销"),
"image_delivery": resolve_image_delivery_options(config),
},
"resolved_history_file": str(history_path),
"history_count": len(history),
"review_log_file": str(log_path),
"review_log_count": count_log_lines(log_path),
"issue_marks_file": str(ISSUE_MARKS_PATH),
"active_issue_count": active_issue_count,
"runtime_note": (
"当前版本支持多条解析、保险年限选择、生成PPT并按页导出PNG、逐图下载。"
),
}
)
return True
if parsed.path == "/api/log/today":
path = today_log_path()
logs: list[dict[str, Any]] = []
if path.exists():
with _LOG_LOCK:
with path.open("r", encoding="utf-8") as f:
for line in f:
s = line.strip()
if not s:
continue
try:
obj = json.loads(s)
except Exception:
continue
if isinstance(obj, dict):
logs.append(obj)
handler._send_json({"ok": True, "log_file": str(path), "count": len(logs), "logs": logs[-500:]})
return True
if parsed.path == "/api/issues":
try:
params = parse_qs(parsed.query)
status = str(params.get("status", ["active"])[0]).strip().lower()
try:
limit = int(params.get("limit", ["500"])[0])
except Exception:
limit = 500
limit = max(1, min(2000, limit))
items, total = list_issue_marks(status=status, limit=limit)
except Exception as exc:
handler._send_json({"ok": False, "error": str(exc)}, HTTPStatus.INTERNAL_SERVER_ERROR)
return True
handler._send_json(
{
"ok": True,
"status": status if status in {"active", "resolved", "all"} else "active",
"count": total,
"limit": limit,
"items": items,
}
)
return True
if parsed.path.startswith("/api/progress/"):
token = parsed.path.split("/api/progress/", 1)[1].strip()
if not token:
handler._send_json({"ok": False, "error": "progress token missing"}, HTTPStatus.BAD_REQUEST)
return True
item = get_generation_progress(token)
if item is None:
handler._send_json({"ok": False, "error": "progress token not found"}, HTTPStatus.NOT_FOUND)
return True
handler._send_json({"ok": True, "progress": item})
return True
if parsed.path == "/api/history":
try:
config = load_config()
history_path = resolve_history_path(config)
with _HISTORY_LOCK:
history = load_history(history_path)
except Exception as exc:
handler._send_json({"ok": False, "error": str(exc)}, HTTPStatus.INTERNAL_SERVER_ERROR)
return True
handler._send_json({"ok": True, "count": len(history), "history": history})
return True
if parsed.path == "/api/history/view":
try:
config = load_config()
history_path = resolve_history_path(config)
params = parse_qs(parsed.query)
try:
limit = int(params.get("limit", ["300"])[0])
except Exception:
limit = 300
limit = max(1, min(2000, limit))
with _HISTORY_LOCK:
history = load_history(history_path)
items = build_history_view_items(history, config, limit=limit)
except Exception as exc:
handler._send_json({"ok": False, "error": str(exc)}, HTTPStatus.INTERNAL_SERVER_ERROR)
return True
handler._send_json({"ok": True, "count": len(history), "limit": limit, "items": items})
return True
if parsed.path.startswith("/api/download/"):
token = parsed.path.split("/api/download/", 1)[1].strip()
if not token:
handler._send_json({"ok": False, "error": "download token missing"}, HTTPStatus.BAD_REQUEST)
return True
with _DOWNLOAD_LOCK:
meta = DOWNLOAD_CACHE.get(token)
if not meta:
handler._send_json({"ok": False, "error": "download expired or invalid"}, HTTPStatus.NOT_FOUND)
return True
file_path = Path(str(meta.get("file_path", "")))
if not file_path.exists():
handler._send_json({"ok": False, "error": "file missing"}, HTTPStatus.NOT_FOUND)
return True
content_type = str(meta.get("content_type", "")).strip()
if not content_type:
content_type = mimetypes.guess_type(file_path.name)[0] or "application/octet-stream"
filename = str(meta.get("filename", file_path.name))
params = parse_qs(parsed.query)
inline = str(params.get("inline", ["0"])[0]).lower() in {"1", "true", "yes"}
rate_opts = {"max_kbps": 300, "chunk_size": 16 * 1024}
try:
config = load_config()
cfg_opts = resolve_image_delivery_options(config)
rate_opts["max_kbps"] = int(cfg_opts.get("max_kbps", 300))
rate_opts["chunk_size"] = int(cfg_opts.get("chunk_size", 16 * 1024))
except Exception:
pass
handler._send_file(
file_path,
content_type,
filename=None if inline else filename,
max_kbps=rate_opts["max_kbps"],
chunk_size=rate_opts["chunk_size"],
)
return True
return False