Fix control unauthorized: map strategy 401 to 502 and keep fleet headers on redirect.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -33,6 +33,20 @@ def _http_detail(data: Any) -> str:
|
||||
return str(data)
|
||||
|
||||
|
||||
def _raise_node_error(code: int, data: Any) -> None:
|
||||
"""策略机错误不得用 401 回传,否则中控前端会误清登录态。"""
|
||||
detail = _http_detail(data)
|
||||
if code in (401, 403):
|
||||
raise HTTPException(
|
||||
status_code=502,
|
||||
detail=f"策略机鉴权失败({detail})。请在中控重新生成 Token,并到策略机「系统设置→登录账户」保存同一 Token。",
|
||||
)
|
||||
raise HTTPException(
|
||||
status_code=code if 400 <= code < 600 else 502,
|
||||
detail=detail,
|
||||
)
|
||||
|
||||
|
||||
class NodeCreate(BaseModel):
|
||||
name: str = Field(min_length=1, max_length=64)
|
||||
base_url: str = Field(min_length=8, max_length=256)
|
||||
@@ -74,6 +88,13 @@ async def status_all(_user: Annotated[str, Depends(require_control_user)]) -> di
|
||||
code, data = await call_node(node, "GET", "/api/fleet/status")
|
||||
if code == 200 and isinstance(data, dict):
|
||||
item["fleet"] = data
|
||||
item["fleet_ok"] = True
|
||||
else:
|
||||
item["fleet_ok"] = False
|
||||
item["fleet_error"] = _http_detail(data) if data else f"HTTP {code}"
|
||||
elif not node.get("token_sealed"):
|
||||
item["fleet_ok"] = False
|
||||
item["fleet_error"] = "未生成 Token"
|
||||
items.append(item)
|
||||
return {"nodes": items}
|
||||
|
||||
@@ -178,10 +199,7 @@ async def node_start(
|
||||
raise HTTPException(status_code=404, detail="节点不存在")
|
||||
code, data = await call_node(node, "POST", "/api/fleet/start")
|
||||
if code >= 400:
|
||||
raise HTTPException(
|
||||
status_code=code if 400 <= code < 600 else 502,
|
||||
detail=_http_detail(data),
|
||||
)
|
||||
_raise_node_error(code, data)
|
||||
return {"ok": True, "result": data}
|
||||
|
||||
|
||||
@@ -196,10 +214,7 @@ async def node_pause(
|
||||
raise HTTPException(status_code=404, detail="节点不存在")
|
||||
code, data = await call_node(node, "POST", "/api/fleet/pause")
|
||||
if code >= 400:
|
||||
raise HTTPException(
|
||||
status_code=code if 400 <= code < 600 else 502,
|
||||
detail=_http_detail(data),
|
||||
)
|
||||
_raise_node_error(code, data)
|
||||
return {"ok": True, "result": data}
|
||||
|
||||
|
||||
@@ -214,10 +229,7 @@ async def node_update(
|
||||
raise HTTPException(status_code=404, detail="节点不存在")
|
||||
code, data = await call_node(node, "POST", "/api/fleet/update")
|
||||
if code >= 400:
|
||||
raise HTTPException(
|
||||
status_code=code if 400 <= code < 600 else 502,
|
||||
detail=_http_detail(data),
|
||||
)
|
||||
_raise_node_error(code, data)
|
||||
return {"ok": True, "result": data}
|
||||
|
||||
|
||||
@@ -233,10 +245,7 @@ async def node_login_url(
|
||||
raise HTTPException(status_code=404, detail="节点不存在")
|
||||
code, data = await call_node(node, "POST", "/api/fleet/issue-login")
|
||||
if code >= 400:
|
||||
raise HTTPException(
|
||||
status_code=code if 400 <= code < 600 else 502,
|
||||
detail=_http_detail(data),
|
||||
)
|
||||
_raise_node_error(code, data)
|
||||
path = ""
|
||||
if isinstance(data, dict):
|
||||
path = str(data.get("login_path") or "")
|
||||
|
||||
@@ -41,11 +41,20 @@ async def call_node(
|
||||
if not tok:
|
||||
return 400, {"detail": "该机尚未生成/保存中控 Token"}
|
||||
headers["X-Fleet-Token"] = tok
|
||||
headers["Authorization"] = f"Fleet {tok}"
|
||||
url = f"{base}{path}"
|
||||
timeout = settings.control_http_timeout_sec
|
||||
try:
|
||||
async with httpx.AsyncClient(timeout=timeout, follow_redirects=True) as client:
|
||||
async with httpx.AsyncClient(timeout=timeout, follow_redirects=False) as client:
|
||||
res = await client.request(method, url, headers=headers, json=json_body)
|
||||
# 少数反代会 301/302 补尾斜杠;手动跟一次并保留 Token 头
|
||||
if res.status_code in (301, 302, 307, 308) and res.headers.get("location"):
|
||||
loc = res.headers["location"]
|
||||
if loc.startswith("/"):
|
||||
from urllib.parse import urljoin
|
||||
|
||||
loc = urljoin(url, loc)
|
||||
res = await client.request(method, loc, headers=headers, json=json_body)
|
||||
try:
|
||||
data = res.json()
|
||||
except Exception:
|
||||
|
||||
Reference in New Issue
Block a user