feat:启动时检查webui版本并尝试自动更新

This commit is contained in:
SengokuCola
2026-05-07 13:06:05 +08:00
parent 57100797a5
commit 16ece263a6
12 changed files with 372 additions and 118 deletions

View File

@@ -5,29 +5,28 @@
"""
from datetime import datetime
from importlib.metadata import PackageNotFoundError, version as get_package_version
from typing import Any, Dict, Optional
from typing import Optional
from fastapi import APIRouter, Depends, HTTPException
from pydantic import BaseModel
import httpx
import os
import time
from src.common.logger import get_logger
from src.config.config import MMC_VERSION
from src.webui.dashboard_update import (
DASHBOARD_PACKAGE_NAME,
PYPI_PROJECT_URL,
detect_package_runner,
get_dashboard_version_info,
)
from src.webui.dependencies import require_auth
router = APIRouter(prefix="/system", tags=["system"], dependencies=[Depends(require_auth)])
logger = get_logger("webui_system")
# 记录启动时间
_start_time = time.time()
_DASHBOARD_PACKAGE_NAME = "maibot-dashboard"
_PYPI_JSON_URL = f"https://pypi.org/pypi/{_DASHBOARD_PACKAGE_NAME}/json"
_PYPI_CACHE_TTL_SECONDS = 60 * 60 * 6
_pypi_version_cache: Dict[str, Any] = {"checked_at": 0.0, "latest_version": None}
class RestartResponse(BaseModel):
@@ -52,64 +51,9 @@ class DashboardVersionResponse(BaseModel):
current_version: str
latest_version: Optional[str] = None
has_update: bool = False
package_name: str = _DASHBOARD_PACKAGE_NAME
pypi_url: str = f"https://pypi.org/project/{_DASHBOARD_PACKAGE_NAME}/"
def _get_installed_dashboard_version() -> str:
try:
return get_package_version(_DASHBOARD_PACKAGE_NAME)
except PackageNotFoundError:
return "unknown"
def _normalize_version(version: str) -> tuple[int, ...]:
clean_version = version.strip().lower().removeprefix("v")
numeric_part = clean_version.split("-", 1)[0].split("+", 1)[0]
parts = []
for item in numeric_part.split("."):
number = ""
for char in item:
if not char.isdigit():
break
number += char
parts.append(int(number) if number else 0)
return tuple(parts)
def _is_newer_version(latest_version: Optional[str], current_version: str) -> bool:
if not latest_version or not current_version or current_version == "unknown":
return False
latest_parts = _normalize_version(latest_version)
current_parts = _normalize_version(current_version)
width = max(len(latest_parts), len(current_parts))
return latest_parts + (0,) * (width - len(latest_parts)) > current_parts + (0,) * (width - len(current_parts))
async def _get_latest_dashboard_version_from_pypi() -> Optional[str]:
now = time.time()
cached_version = _pypi_version_cache.get("latest_version")
checked_at = float(_pypi_version_cache.get("checked_at", 0.0))
if cached_version and now - checked_at < _PYPI_CACHE_TTL_SECONDS:
return str(cached_version)
try:
async with httpx.AsyncClient(timeout=5.0) as client:
response = await client.get(_PYPI_JSON_URL)
response.raise_for_status()
payload = response.json()
except Exception as e:
logger.debug(f"检查 WebUI PyPI 版本失败: {e}")
return str(cached_version) if cached_version else None
latest_version = payload.get("info", {}).get("version")
if isinstance(latest_version, str) and latest_version.strip():
_pypi_version_cache["checked_at"] = now
_pypi_version_cache["latest_version"] = latest_version.strip()
return latest_version.strip()
return str(cached_version) if cached_version else None
runner: str = "unknown"
package_name: str = DASHBOARD_PACKAGE_NAME
pypi_url: str = PYPI_PROJECT_URL
@router.post("/restart", response_model=RestartResponse)
@@ -166,13 +110,13 @@ async def get_maibot_status():
@router.get("/dashboard-version", response_model=DashboardVersionResponse)
async def get_dashboard_version(current_version: Optional[str] = None):
"""获取 WebUI 当前版本和 PyPI 最新版本。"""
resolved_current_version = current_version or _get_installed_dashboard_version()
latest_version = await _get_latest_dashboard_version_from_pypi()
version_info = await get_dashboard_version_info(current_version)
return DashboardVersionResponse(
current_version=resolved_current_version,
latest_version=latest_version,
has_update=_is_newer_version(latest_version, resolved_current_version),
current_version=version_info.current_version,
latest_version=version_info.latest_version,
has_update=version_info.has_update,
runner=detect_package_runner(),
)