diff --git a/lib/bridge_client_lib.py b/lib/bridge_client_lib.py index 14c12e2..4285c15 100644 --- a/lib/bridge_client_lib.py +++ b/lib/bridge_client_lib.py @@ -33,6 +33,28 @@ def _headers(token: str) -> dict[str, str]: } +def _response_json(r: requests.Response) -> tuple[dict[str, Any] | None, str]: + """安全解析 JSON;失败返回 (None, 可读原因).""" + raw = (r.content or b"").strip() + if not raw: + return None, f"HTTP {r.status_code} 空响应" + try: + data = r.json() + except ValueError: + snippet = raw[:120].decode("utf-8", errors="replace").replace("\n", " ") + hint = "" + if r.status_code in (401, 403): + hint = ";检查 Bridge Token" + elif r.status_code == 404: + hint = ";客户端可能未部署 /api/hub/version(请先手动更新实例)" + elif snippet.lstrip().startswith("<"): + hint = ";收到 HTML(可能被登录页/反代拦截)" + return None, f"HTTP {r.status_code} 非 JSON{hint}: {snippet}" + if not isinstance(data, dict): + return None, f"HTTP {r.status_code} 响应不是对象" + return data, "" + + def fetch_one_snapshot(inst: dict[str, Any]) -> dict[str, Any]: base = (inst.get("base_url") or "").rstrip("/") token = (inst.get("bridge_token") or "").strip() @@ -294,9 +316,9 @@ def fetch_client_version(inst: dict[str, Any]) -> dict[str, Any]: headers={"X-Hub-Token": token, "Accept": "application/json"}, timeout=_timeout(), ) - data = r.json() if r.content else {} - if not isinstance(data, dict): - out["msg"] = f"HTTP {r.status_code}" + data, err = _response_json(r) + if data is None: + out["msg"] = err or f"HTTP {r.status_code}" return out for key in ("sha", "short_sha", "branch", "dirty", "msg", "repo"): if key in data: @@ -304,7 +326,7 @@ def fetch_client_version(inst: dict[str, Any]) -> dict[str, Any]: out["ok"] = bool(data.get("ok")) and r.status_code == 200 out["online"] = out["ok"] if not out["ok"]: - out["msg"] = data.get("msg") or f"HTTP {r.status_code}" + out["msg"] = data.get("msg") or err or f"HTTP {r.status_code}" return out except requests.Timeout: out["msg"] = "超时" @@ -333,14 +355,14 @@ def post_client_update(inst: dict[str, Any]) -> dict[str, Any]: json={}, timeout=_timeout(), ) - data = r.json() if r.content else {} - if not isinstance(data, dict): - data = {"ok": False, "msg": f"HTTP {r.status_code}"} + data, err = _response_json(r) + if data is None: + data = {"ok": False, "msg": err or f"HTTP {r.status_code}"} data["instance_id"] = inst.get("id") data["instance_name"] = inst.get("name") - if r.status_code >= 400 and "ok" not in data: + if r.status_code >= 400 and not data.get("ok"): data["ok"] = False - data.setdefault("msg", f"HTTP {r.status_code}") + data.setdefault("msg", err or f"HTTP {r.status_code}") return data except requests.Timeout: return { @@ -370,12 +392,18 @@ def fetch_backup_list(inst: dict[str, Any]) -> dict[str, Any]: headers={"X-Hub-Token": token, "Accept": "application/json"}, timeout=_timeout(), ) - data = r.json() if r.content else {} - if isinstance(data, dict): - data.setdefault("instance_id", inst.get("id")) - data.setdefault("instance_name", inst.get("name")) - return data - return {"ok": False, "msg": f"HTTP {r.status_code}", "items": []} + data, err = _response_json(r) + if data is None: + return { + "ok": False, + "msg": err or f"HTTP {r.status_code}", + "items": [], + "instance_id": inst.get("id"), + "instance_name": inst.get("name"), + } + data.setdefault("instance_id", inst.get("id")) + data.setdefault("instance_name", inst.get("name")) + return data except Exception as e: return { "ok": False, diff --git a/templates/settings.html b/templates/settings.html index d6269af..226b905 100644 --- a/templates/settings.html +++ b/templates/settings.html @@ -183,7 +183,7 @@ {% endblock %} {% block scripts %} - +