@@ -0,0 +1,135 @@
|
||||
"""API-слой для /api/updates/* (статус, принудительная проверка, установка по выбору пользователя)."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import time
|
||||
from datetime import datetime
|
||||
from typing import Any, Dict, Optional, Tuple
|
||||
|
||||
from flask import current_app
|
||||
|
||||
from app.services.auto_update_runtime import get_auto_updater
|
||||
from app.services.update_state_store import read_update_state
|
||||
|
||||
FORCED_CHECK_MIN_INTERVAL_SEC = 60
|
||||
_BODY_PREVIEW_LEN = 500
|
||||
|
||||
|
||||
def _iso(dt: Optional[datetime]) -> Optional[str]:
|
||||
if dt is None:
|
||||
return None
|
||||
return dt.replace(microsecond=0).isoformat() + "Z"
|
||||
|
||||
|
||||
def build_updates_status_payload() -> Dict[str, Any]:
|
||||
updater = get_auto_updater()
|
||||
ver = str(current_app.config.get("SYNC_CLIENT_VERSION", "2.0.0") or "2.0.0")
|
||||
restart_cmd = str(current_app.config.get("UPDATE_RESTART_CMD", "") or "").strip()
|
||||
|
||||
if updater is None:
|
||||
base = str(current_app.config.get("WESP_BASE_DIR") or "")
|
||||
last_state = read_update_state(base) if base else {"status": "idle"}
|
||||
return {
|
||||
"initialized": False,
|
||||
"check_enabled": False,
|
||||
"current_version": ver,
|
||||
"update_available": False,
|
||||
"pending_version": None,
|
||||
"pending_name": None,
|
||||
"pending_body": None,
|
||||
"published_at": None,
|
||||
"last_check_at": None,
|
||||
"is_running": False,
|
||||
"is_updating": False,
|
||||
"update_progress": None,
|
||||
"last_update_state": last_state,
|
||||
"gitea_configured": False,
|
||||
"restart_configured": bool(restart_cmd),
|
||||
"gitea_url": None,
|
||||
"gitea_repo": None,
|
||||
}
|
||||
|
||||
pending = updater.get_pending_update()
|
||||
gitea_ok = bool(getattr(updater, "gitea_url", "") and getattr(updater, "gitea_owner", "") and getattr(updater, "gitea_repo", ""))
|
||||
body = (pending or {}).get("body") or ""
|
||||
if isinstance(body, str) and len(body) > _BODY_PREVIEW_LEN:
|
||||
body = body[:_BODY_PREVIEW_LEN].rstrip() + "…"
|
||||
|
||||
progress = None
|
||||
if getattr(updater, "is_updating", False):
|
||||
progress = getattr(updater, "get_update_progress", lambda: None)()
|
||||
|
||||
last_state = read_update_state(getattr(updater, "base_dir", "") or "")
|
||||
|
||||
return {
|
||||
"initialized": True,
|
||||
"check_enabled": bool(getattr(updater, "enabled", False)),
|
||||
"current_version": getattr(updater, "current_version", ver) or ver,
|
||||
"update_available": pending is not None,
|
||||
"pending_version": (pending or {}).get("version"),
|
||||
"pending_name": (pending or {}).get("name"),
|
||||
"pending_body": body if pending else None,
|
||||
"published_at": (pending or {}).get("published_at"),
|
||||
"last_check_at": _iso(getattr(updater, "last_check_at", None)),
|
||||
"is_running": bool(getattr(updater, "is_running", False)),
|
||||
"is_updating": bool(getattr(updater, "is_updating", False)),
|
||||
"update_progress": progress,
|
||||
"last_update_state": last_state,
|
||||
"gitea_configured": gitea_ok,
|
||||
"restart_configured": bool(restart_cmd or getattr(updater, "restart_cmd", "")),
|
||||
"gitea_url": getattr(updater, "gitea_url", None) if gitea_ok else None,
|
||||
"gitea_repo": (
|
||||
f"{updater.gitea_owner}/{updater.gitea_repo}"
|
||||
if gitea_ok and getattr(updater, "gitea_owner", None)
|
||||
else None
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
def force_check_updates() -> Dict[str, Any]:
|
||||
updater = get_auto_updater()
|
||||
if updater is None:
|
||||
payload = build_updates_status_payload()
|
||||
payload["message"] = "Проверка обновлений не инициализирована"
|
||||
return payload
|
||||
|
||||
if not getattr(updater, "enabled", False):
|
||||
payload = build_updates_status_payload()
|
||||
payload["message"] = "Проверка обновлений отключена"
|
||||
return payload
|
||||
|
||||
now = time.monotonic()
|
||||
if now - float(getattr(updater, "_last_forced_check_mono", 0.0)) >= FORCED_CHECK_MIN_INTERVAL_SEC:
|
||||
updater._last_forced_check_mono = now
|
||||
updater.check_for_updates()
|
||||
|
||||
payload = build_updates_status_payload()
|
||||
payload["forced_check"] = True
|
||||
return payload
|
||||
|
||||
|
||||
def install_pending_update() -> Tuple[Dict[str, Any], int]:
|
||||
updater = get_auto_updater()
|
||||
if updater is None:
|
||||
return {"error": "Проверка обновлений не инициализирована", "status": "error"}, 500
|
||||
|
||||
if not getattr(updater, "enabled", False):
|
||||
return {"error": "Проверка обновлений отключена", "status": "error"}, 400
|
||||
|
||||
if getattr(updater, "is_updating", False):
|
||||
return {"error": "Обновление уже выполняется", "status": "error"}, 409
|
||||
|
||||
pending = updater.get_pending_update()
|
||||
if not pending:
|
||||
return {"error": "Нет доступного обновления для установки", "status": "error"}, 404
|
||||
|
||||
success = updater.update(pending)
|
||||
if not success:
|
||||
return {"error": "Не удалось применить обновление", "status": "error"}, 500
|
||||
|
||||
return {
|
||||
"status": "success",
|
||||
"success": True,
|
||||
"message": "Обновление применено. Система перезапускается…",
|
||||
"new_version": getattr(updater, "current_version", None),
|
||||
}, 200
|
||||
Reference in New Issue
Block a user