feat: 添加客户端更新检测功能,支持版本检查和更新提示;更新相关组件和样式

This commit is contained in:
Pine
2026-08-19 13:14:00 +08:00
parent 5ed396aea4
commit 91e20c3a81
7 changed files with 251 additions and 3 deletions
+48
View File
@@ -6,6 +6,7 @@
import asyncio
import logging
import re
import threading
import time
from contextlib import asynccontextmanager
@@ -15,6 +16,7 @@ from fastapi import FastAPI, Request
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import FileResponse, JSONResponse
from fastapi.staticfiles import StaticFiles
from pydantic import BaseModel
from .admin import admin_login, admin_logout, admin_page
from .config import settings
@@ -120,6 +122,52 @@ async def download_installer():
return JSONResponse({"detail": "安装包不存在"}, status_code=404)
# ---------- 客户端更新检测(仅 Windows:比对版本 → 下发安装包下载地址) ----------
def _latest_installer():
"""返回 backend/ 下最新(按文件名排序取最大)的 *.setup.exe。"""
for f in sorted(_DOWNLOAD_ROOT.glob("*setup.exe"), reverse=True):
if f.is_file():
return f
return None
def _version_from_name(name: str) -> str:
"""从安装包文件名解析版本,如 `..._0.1.0_x64-setup.exe` → `0.1.0`。"""
m = re.search(r"_(\d+\.\d+(?:\.\d+)?)", name or "")
return m.group(1) if m else ""
def _version_key(v: str):
return tuple(int(x) for x in v.split(".") if x.isdigit())
class UpdateCheckBody(BaseModel):
current: str = "" # 前端上报的当前版本,如 0.1.0
@app.post("/api/update/check", include_in_schema=False)
async def update_check(body: UpdateCheckBody):
"""检测更新:返回最新版本号、安装包下载地址、是否需要更新(仅 Windows)。"""
inst = _latest_installer()
if not inst:
return {"ok": False, "error": "installer not found"}
latest = _version_from_name(inst.name)
current = (body.current or "").strip()
update_available = False
if latest and current and current != latest:
update_available = _version_key(latest) > _version_key(current)
elif latest and not current:
update_available = True
return {
"ok": True,
"latest_version": latest,
"installer_name": inst.name,
"download_url": "/download",
"update_available": update_available,
}
# ---------- 前端静态托管 + SPA 回退(浏览器直接访问 :8000 即可) ----------
@app.get("/{path:path}", include_in_schema=False)