启动器:接入 Gitea Releases 自动检查更新 + 更新日志页
- server.py: 新增 /api/update/check、/api/update/changelog 接口, 用标准库 urllib 请求自托管仓库 Lingbao/90CNServer 的 Releases, 带 5 分钟缓存与 8 秒超时;本地版本号写死 v2026.11.04 - shell.js: 版本卡片改为动态读取远程最新 tag,发现新版本绿色可点击 跳转;新增 loadChangelog 渲染发布记录 - index.html/shell.css: 更新日志页改为动态容器,补充卡片样式
This commit is contained in:
1 parent
d31a34e9aa
commit
f2debaf9bf
4 files changed
+181
-3
No files matched your search
@@ -26,6 +26,7 @@ import uuid
|
||||
import webbrowser
|
||||
|
||||
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
|
||||
from urllib.request import Request, urlopen
|
||||
from urllib.parse import urlparse, parse_qs
|
||||
|
||||
from db import DNFDatabase, set_gm_config
|
||||
@@ -40,6 +41,11 @@ DETACHED_PROCESS = 0x00000008
|
||||
|
||||
DEFAULT_PORT = 19527 # 启动器本地端口
|
||||
|
||||
# 更新检查配置:Gitea 自托管仓库的 Releases 地址
|
||||
UPDATE_REPO_API = "https://git.hanfuit.com/api/v1/repos/Lingbao/90CNServer/releases"
|
||||
UPDATE_REPO_WEB = "https://git.hanfuit.com/Lingbao/90CNServer/releases"
|
||||
CURRENT_VERSION = "v2026.11.04" # 本地当前版本号
|
||||
|
||||
|
||||
def base_dir():
|
||||
"""程序所在目录(源码运行时 = 脚本目录,冻结运行时 = exe 目录)。"""
|
||||
@@ -274,6 +280,51 @@ def log(msg):
|
||||
_logq.put(str(msg))
|
||||
|
||||
|
||||
# ------------------------------------------------------------------ 更新检查
|
||||
_update_cache = {"ts": 0.0, "releases": None, "error": None}
|
||||
_update_lock = threading.Lock()
|
||||
|
||||
|
||||
def _fetch_releases(force=False):
|
||||
"""拉取 Gitea Releases 列表(带 5 分钟缓存)。"""
|
||||
now = time.time()
|
||||
with _update_lock:
|
||||
if not force and _update_cache["ts"] and now - _update_cache["ts"] < 300:
|
||||
return _update_cache["releases"], _update_cache["error"]
|
||||
try:
|
||||
req = Request(UPDATE_REPO_API, headers={
|
||||
"User-Agent": "DNF90Launcher/2.0",
|
||||
"Accept": "application/json",
|
||||
})
|
||||
with urlopen(req, timeout=8) as resp:
|
||||
data = json.loads(resp.read().decode("utf-8"))
|
||||
releases = data if isinstance(data, list) else []
|
||||
with _update_lock:
|
||||
_update_cache.update(ts=now, releases=releases, error=None)
|
||||
return releases, None
|
||||
except Exception as e:
|
||||
with _update_lock:
|
||||
_update_cache.update(ts=now, releases=None, error=str(e))
|
||||
return None, str(e)
|
||||
|
||||
|
||||
def _release_summary(rel):
|
||||
"""把一个 release 对象规整成前端需要的字段。"""
|
||||
return {
|
||||
"tag": rel.get("tag_name") or rel.get("name") or "",
|
||||
"name": rel.get("name") or rel.get("tag_name") or "",
|
||||
"body": rel.get("body") or "",
|
||||
"published_at": rel.get("published_at") or "",
|
||||
"prerelease": bool(rel.get("prerelease")),
|
||||
"html_url": rel.get("html_url") or UPDATE_REPO_WEB,
|
||||
"assets": [
|
||||
{"name": a.get("name"), "size": a.get("size"),
|
||||
"download_url": a.get("browser_download_url")}
|
||||
for a in (rel.get("assets") or [])
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
def flow_start_game(account_username=None):
|
||||
"""一键开始游戏;account_username 为空时使用默认账号。"""
|
||||
log("== 开始游戏 ==")
|
||||
@@ -515,6 +566,10 @@ class Handler(BaseHTTPRequestHandler):
|
||||
self._api_logs()
|
||||
elif path == "/api/gm/health":
|
||||
self._api_gm_health()
|
||||
elif path == "/api/update/check":
|
||||
self._api_update_check()
|
||||
elif path == "/api/update/changelog":
|
||||
self._api_update_changelog(qs)
|
||||
elif path == "/api/accounts-config":
|
||||
self._api_accounts_config()
|
||||
# GM 只读接口
|
||||
@@ -685,6 +740,44 @@ class Handler(BaseHTTPRequestHandler):
|
||||
"native_available": NATIVE is not None,
|
||||
})
|
||||
|
||||
def _api_update_check(self):
|
||||
"""检查最新发布版本(对比本地 CURRENT_VERSION)。"""
|
||||
releases, err = _fetch_releases()
|
||||
if err:
|
||||
self._json({
|
||||
"ok": True,
|
||||
"current": CURRENT_VERSION,
|
||||
"latest": None,
|
||||
"has_update": False,
|
||||
"error": err,
|
||||
})
|
||||
return
|
||||
latest = releases[0] if releases else None
|
||||
latest_tag = (latest.get("tag_name") or "").lstrip("v") if latest else ""
|
||||
current = CURRENT_VERSION.lstrip("v")
|
||||
self._json({
|
||||
"ok": True,
|
||||
"current": CURRENT_VERSION,
|
||||
"latest": latest.get("tag_name") if latest else None,
|
||||
"has_update": bool(latest_tag) and latest_tag != current,
|
||||
"latest_url": latest.get("html_url") if latest else UPDATE_REPO_WEB,
|
||||
"published_at": latest.get("published_at") if latest else None,
|
||||
})
|
||||
|
||||
def _api_update_changelog(self, qs):
|
||||
"""获取所有发布的更新日志。"""
|
||||
force = qs.get("force", ["0"])[0] == "1"
|
||||
releases, err = _fetch_releases(force=force)
|
||||
if err:
|
||||
self._json({"ok": False, "error": err, "releases": []})
|
||||
return
|
||||
self._json({
|
||||
"ok": True,
|
||||
"current": CURRENT_VERSION,
|
||||
"releases": [_release_summary(r) for r in (releases or [])],
|
||||
"web_url": UPDATE_REPO_WEB,
|
||||
})
|
||||
|
||||
def _api_logs(self):
|
||||
logs = []
|
||||
while True:
|
||||
|
||||
Reference in new issue
Block a user