- 更新 backend 平台 API、platform_store、compute_gateway sync - 更新 compute agent/engine/adapter 及 API - 更新 Docker 部署配置(app/compute) - 新增 frontend/src/utils/ 工具模块 - 新增 scripts/ops_diagnostics.py 运维诊断脚本 - 新增 docs/2026-07-23-development-summary.md 开发总结 - 重构 frontend/dist 构建产物(新 hash) - 更新前端多个视图组件及 API 模块 Co-Authored-By: Claude <noreply@anthropic.com>
105 lines
4.1 KiB
Python
105 lines
4.1 KiB
Python
from __future__ import annotations
|
|
|
|
import json
|
|
import os
|
|
import sys
|
|
import time
|
|
from typing import Any
|
|
from urllib.error import HTTPError, URLError
|
|
from urllib.request import Request, urlopen
|
|
|
|
|
|
def _json_request(url: str, token: str | None = None, timeout: float = 5.0) -> dict[str, Any]:
|
|
headers = {"Accept": "application/json"}
|
|
if token:
|
|
headers["X-Compute-Token"] = token
|
|
request = Request(url, headers=headers)
|
|
started = time.perf_counter()
|
|
try:
|
|
with urlopen(request, timeout=timeout) as response:
|
|
body = response.read().decode("utf-8", errors="replace")
|
|
data = json.loads(body) if body else {}
|
|
return {
|
|
"ok": 200 <= response.status < 300,
|
|
"status": response.status,
|
|
"duration_ms": int((time.perf_counter() - started) * 1000),
|
|
"data": data,
|
|
}
|
|
except HTTPError as exc:
|
|
body = exc.read().decode("utf-8", errors="replace")
|
|
return {
|
|
"ok": False,
|
|
"status": exc.code,
|
|
"duration_ms": int((time.perf_counter() - started) * 1000),
|
|
"error": body or str(exc),
|
|
}
|
|
except (URLError, TimeoutError, OSError) as exc:
|
|
return {
|
|
"ok": False,
|
|
"duration_ms": int((time.perf_counter() - started) * 1000),
|
|
"error": str(exc),
|
|
}
|
|
|
|
|
|
def _postgres_check(database_url: str | None) -> dict[str, Any]:
|
|
if not database_url:
|
|
return {"ok": False, "skipped": True, "error": "DATABASE_URL is not set"}
|
|
try:
|
|
import psycopg
|
|
except ImportError:
|
|
return {"ok": False, "skipped": True, "error": "psycopg is not installed"}
|
|
started = time.perf_counter()
|
|
try:
|
|
with psycopg.connect(database_url.replace("postgresql+psycopg://", "postgresql://"), connect_timeout=5) as conn:
|
|
with conn.cursor() as cursor:
|
|
cursor.execute(
|
|
"""
|
|
SELECT table_name
|
|
FROM information_schema.tables
|
|
WHERE table_schema='public'
|
|
AND table_name IN ('users','fine_tune_tasks','compute_nodes','compute_jobs','gpu_allocations')
|
|
ORDER BY table_name
|
|
"""
|
|
)
|
|
tables = [row[0] for row in cursor.fetchall()]
|
|
return {"ok": len(tables) >= 5, "duration_ms": int((time.perf_counter() - started) * 1000), "tables": tables}
|
|
except Exception as exc: # noqa: BLE001 - diagnostics should report instead of crashing
|
|
return {"ok": False, "duration_ms": int((time.perf_counter() - started) * 1000), "error": str(exc)}
|
|
|
|
|
|
def main() -> int:
|
|
route_prefix = os.getenv("ROUTE_PREFIX", "/modelTF").rstrip("/")
|
|
app_base_url = os.getenv("APP_BASE_URL", "http://localhost:17861").rstrip("/")
|
|
compute_base_url = os.getenv("COMPUTE_BASE_URL", "").rstrip("/")
|
|
compute_token = os.getenv("COMPUTE_SERVICE_TOKEN")
|
|
checks: list[dict[str, Any]] = [
|
|
{"name": "backend health", **_json_request(f"{app_base_url}{route_prefix}/health")},
|
|
{"name": "postgres schema", **_postgres_check(os.getenv("DATABASE_URL"))},
|
|
]
|
|
if compute_base_url:
|
|
checks.extend(
|
|
[
|
|
{
|
|
"name": "compute health",
|
|
**_json_request(f"{compute_base_url}{route_prefix}/v1/compute/health", compute_token),
|
|
},
|
|
{
|
|
"name": "compute gpus",
|
|
**_json_request(f"{compute_base_url}{route_prefix}/compute/resources/gpus", compute_token),
|
|
},
|
|
{
|
|
"name": "compute jobs",
|
|
**_json_request(f"{compute_base_url}{route_prefix}/compute/jobs", compute_token),
|
|
},
|
|
]
|
|
)
|
|
else:
|
|
checks.append({"name": "compute health", "ok": False, "skipped": True, "error": "COMPUTE_BASE_URL is not set"})
|
|
report = {"ok": all(item.get("ok") or item.get("skipped") for item in checks), "checks": checks}
|
|
print(json.dumps(report, ensure_ascii=False, indent=2))
|
|
return 0 if report["ok"] else 1
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|