Files
MuMuAINovel/backend/app/api/changelog.py
T
xiamuceer f831d07864 update:1.新增手动创建大纲和章节,编写章节规划内容
2.新增项目更新日志页面,同步GitHub更新日志
3.新增章节内容生成时,选择本次生成人称
4.修复1 - N模式下,章节标题无法修改的问题
5.修复章节管理界面,批量生成后没有更新页面内容和状态
2025-12-06 14:08:20 +08:00

233 lines
6.9 KiB
Python

"""
更新日志API
提供GitHub提交历史的缓存和代理服务
"""
from fastapi import APIRouter, HTTPException, Query
from typing import List, Optional
import httpx
from datetime import datetime, timedelta
from pydantic import BaseModel
import logging
logger = logging.getLogger(__name__)
router = APIRouter()
# GitHub API配置
GITHUB_API_BASE = "https://api.github.com"
REPO_OWNER = "xiamuceer-j"
REPO_NAME = "MuMuAINovel"
# 缓存配置
_cache = {
"data": None,
"timestamp": None,
"ttl": timedelta(hours=1) # 缓存1小时
}
class GitHubAuthor(BaseModel):
"""GitHub作者信息"""
name: str
email: str
date: str
class GitHubCommitInfo(BaseModel):
"""GitHub提交信息"""
author: GitHubAuthor
message: str
class GitHubUser(BaseModel):
"""GitHub用户信息"""
login: str
avatar_url: str
class GitHubCommit(BaseModel):
"""GitHub提交数据"""
sha: str
commit: GitHubCommitInfo
html_url: str
author: Optional[GitHubUser] = None
class ChangelogResponse(BaseModel):
"""更新日志响应"""
commits: List[GitHubCommit]
cached: bool
cache_time: Optional[str] = None
def is_cache_valid() -> bool:
"""检查缓存是否有效"""
if _cache["data"] is None or _cache["timestamp"] is None:
return False
now = datetime.now()
cache_age = now - _cache["timestamp"]
return cache_age < _cache["ttl"]
async def fetch_github_commits(page: int = 1, per_page: int = 30) -> List[dict]:
"""从GitHub API获取提交历史"""
url = f"{GITHUB_API_BASE}/repos/{REPO_OWNER}/{REPO_NAME}/commits"
params = {
"author": REPO_OWNER,
"page": page,
"per_page": per_page
}
headers = {
"Accept": "application/vnd.github.v3+json",
"User-Agent": "MuMuAINovel-App"
}
try:
async with httpx.AsyncClient(timeout=30.0) as client:
response = await client.get(url, params=params, headers=headers)
response.raise_for_status()
return response.json()
except httpx.HTTPError as e:
logger.error(f"GitHub API请求失败: {str(e)}")
raise HTTPException(
status_code=502,
detail=f"获取GitHub提交历史失败: {str(e)}"
)
@router.get("/changelog", response_model=ChangelogResponse)
async def get_changelog(
page: int = Query(1, ge=1, description="页码"),
per_page: int = Query(30, ge=1, le=100, description="每页数量")
):
"""
获取更新日志
从GitHub获取项目的提交历史,支持缓存以减少API调用
- **page**: 页码,从1开始
- **per_page**: 每页返回的提交数量,最大100
"""
try:
# 只缓存第一页
if page == 1 and is_cache_valid():
logger.info("使用缓存的更新日志")
return ChangelogResponse(
commits=_cache["data"],
cached=True,
cache_time=_cache["timestamp"].isoformat()
)
# 从GitHub获取数据
logger.info(f"从GitHub获取更新日志 (page={page}, per_page={per_page})")
commits_data = await fetch_github_commits(page, per_page)
# 解析数据
commits = []
for commit_data in commits_data:
try:
commit = GitHubCommit(
sha=commit_data["sha"],
commit=GitHubCommitInfo(
author=GitHubAuthor(
name=commit_data["commit"]["author"]["name"],
email=commit_data["commit"]["author"]["email"],
date=commit_data["commit"]["author"]["date"]
),
message=commit_data["commit"]["message"]
),
html_url=commit_data["html_url"],
author=GitHubUser(
login=commit_data["author"]["login"],
avatar_url=commit_data["author"]["avatar_url"]
) if commit_data.get("author") else None
)
commits.append(commit)
except (KeyError, TypeError) as e:
logger.warning(f"解析提交数据失败: {str(e)}")
continue
# 缓存第一页数据
if page == 1:
_cache["data"] = commits
_cache["timestamp"] = datetime.now()
logger.info("已缓存更新日志")
return ChangelogResponse(
commits=commits,
cached=False,
cache_time=None
)
except HTTPException:
raise
except Exception as e:
logger.error(f"获取更新日志时发生错误: {str(e)}")
raise HTTPException(
status_code=500,
detail=f"获取更新日志失败: {str(e)}"
)
@router.post("/changelog/refresh")
async def refresh_changelog():
"""
刷新更新日志缓存
强制从GitHub重新获取最新的提交历史
"""
try:
logger.info("刷新更新日志缓存")
# 清除缓存
_cache["data"] = None
_cache["timestamp"] = None
# 重新获取
commits_data = await fetch_github_commits(1, 30)
# 解析数据
commits = []
for commit_data in commits_data:
try:
commit = GitHubCommit(
sha=commit_data["sha"],
commit=GitHubCommitInfo(
author=GitHubAuthor(
name=commit_data["commit"]["author"]["name"],
email=commit_data["commit"]["author"]["email"],
date=commit_data["commit"]["author"]["date"]
),
message=commit_data["commit"]["message"]
),
html_url=commit_data["html_url"],
author=GitHubUser(
login=commit_data["author"]["login"],
avatar_url=commit_data["author"]["avatar_url"]
) if commit_data.get("author") else None
)
commits.append(commit)
except (KeyError, TypeError) as e:
logger.warning(f"解析提交数据失败: {str(e)}")
continue
# 更新缓存
_cache["data"] = commits
_cache["timestamp"] = datetime.now()
return {
"success": True,
"message": "缓存已刷新",
"commit_count": len(commits),
"cache_time": _cache["timestamp"].isoformat()
}
except Exception as e:
logger.error(f"刷新缓存时发生错误: {str(e)}")
raise HTTPException(
status_code=500,
detail=f"刷新缓存失败: {str(e)}"
)