feat: 更新页面实时日志 — SSE轮询 + logs/updates_logs 文件记录

This commit is contained in:
qinglong
2026-06-14 10:45:08 +08:00
parent b3a6f73db4
commit cb5f900c39
2 changed files with 33 additions and 1 deletions
+28 -1
View File
@@ -76,6 +76,18 @@ class UpdateService:
self.sm = service_manager
self._task: Optional[asyncio.Task] = None
self._running = False
# 日志目录
import logging as _logging
self.update_logger = _logging.getLogger("senSu.update")
log_dir = _PROJECT_ROOT / "logs" / "updates_logs"
log_dir.mkdir(parents=True, exist_ok=True)
fh = _logging.FileHandler(
log_dir / f"update_{time.strftime('%Y%m%d_%H%M%S')}.log",
encoding='utf-8'
)
fh.setFormatter(_logging.Formatter('%(asctime)s [%(levelname)s] %(message)s'))
self.update_logger.addHandler(fh)
self.update_logger.setLevel(_logging.INFO)
self.status: dict = {"state": "idle", "progress": 0, "error": "",
"last_check": 0, "checked": False, "update_available": False,
"current_version": "", "latest_version": ""}
@@ -179,8 +191,10 @@ class UpdateService:
url = self.download_url
logger.info(f"📥 下载更新包: {url}")
self._log("开始更新...")
# 0. 备份当前版本
self.status["progress"] = 2
self._log("创建备份...")
backup_name = f"SenSu_backup_{self._current_version()}_{time.strftime('%Y%m%d_%H%M%S')}.zip"
backup_dir = _PROJECT_ROOT / "data" / "backups"
backup_dir.mkdir(parents=True, exist_ok=True)
@@ -199,12 +213,14 @@ class UpdateService:
with urllib.request.urlopen(req, timeout=300) as resp:
shutil.copyfileobj(resp, tmp_zip)
tmp_zip.close()
self._log("下载完成")
self.status["progress"] = 40
# 2. 解压到临时目录
tmp_dir = tempfile.mkdtemp()
with zipfile.ZipFile(tmp_zip.name, "r") as zf:
zf.extractall(tmp_dir)
self._log("解压更新包...")
self.status["progress"] = 60
# 3. Gitea zip 内层目录: 通常是 repo_name-branch/
@@ -229,6 +245,7 @@ class UpdateService:
os.makedirs(os.path.dirname(dst), exist_ok=True)
shutil.copy2(src, dst)
self._log("覆盖文件 (跳过用户配置)...")
self.status["progress"] = 80
# 4.5 同步版本号到旧配置文件
@@ -246,10 +263,12 @@ class UpdateService:
old_cfg.setdefault("framework", {})["version"] = new_ver
with open(old_cfg_path, "w") as f:
_yaml.dump(old_cfg, f, default_flow_style=False, allow_unicode=True)
logger.info(f"📝 版本号已同步: {new_ver}")
self._log(f"版本号已同步: {new_ver}")
logger.info(f"📝 版本号已同步: {new_ver}")
except Exception as e:
logger.warning(f"版本号同步失败: {e}")
self._log("安装依赖...")
# 5. 安装依赖 (子进程)
req_file = str(_PROJECT_ROOT / "requirements.txt")
if os.path.exists(req_file):
@@ -259,12 +278,15 @@ class UpdateService:
cwd=str(_PROJECT_ROOT),
)
if r.returncode != 0:
self._log(f"依赖安装警告: {r.stderr[-200:]}")
logger.warning(f"依赖安装警告: {r.stderr[-300:]}")
self.status["progress"] = 100
self._log("更新完成, 框架将重启")
return {"ok": True, "msg": "更新完成, 框架将重启"}
except Exception as e:
self._log(f"更新失败: {e}")
logger.error(f"更新失败: {e}")
return {"ok": False, "error": str(e)}
finally:
@@ -273,6 +295,11 @@ class UpdateService:
if tmp_dir and os.path.exists(tmp_dir):
shutil.rmtree(tmp_dir, ignore_errors=True)
def _log(self, msg: str):
"""写入更新日志 + 更新SSE状态"""
self.update_logger.info(msg)
self.status["log"] = self.status.get("log", "") + msg + "\n"
async def check_now(self):
await self._check_version()