feat(dashboard): service health widget, Cmd+K palette, CI runs, pinned shortcuts
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import asyncio
|
||||
import os
|
||||
import platform
|
||||
import shutil
|
||||
@@ -9,7 +10,7 @@ from fastapi import APIRouter, Depends, HTTPException, Query, Request
|
||||
|
||||
import auth_service as auth
|
||||
import config
|
||||
from config import OPENCLAW_GATEWAY_TOKEN, TELEGRAM_BOT_TOKEN, TELEGRAM_CHAT_ID, VOLUME_ROOT
|
||||
from config import GITEA_TOKEN, GITEA_URL, OPENCLAW_GATEWAY_TOKEN, TELEGRAM_BOT_TOKEN, TELEGRAM_CHAT_ID, VOLUME_ROOT
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@@ -151,6 +152,61 @@ def openclaw_token(request: Request):
|
||||
return {"token": OPENCLAW_GATEWAY_TOKEN}
|
||||
|
||||
|
||||
@router.get("/service-health")
|
||||
async def service_health(current_user=Depends(auth.get_current_user)):
|
||||
"""Ping all external services and return their status with latency."""
|
||||
|
||||
async def _ping(key: str, url: str) -> dict:
|
||||
start = time.monotonic()
|
||||
try:
|
||||
async with httpx.AsyncClient(timeout=5.0) as client:
|
||||
r = await client.head(url, follow_redirects=True)
|
||||
latency_ms = round((time.monotonic() - start) * 1000)
|
||||
status = "up" if r.status_code < 500 else "down"
|
||||
except httpx.TimeoutException:
|
||||
latency_ms = round((time.monotonic() - start) * 1000)
|
||||
status = "down"
|
||||
except Exception:
|
||||
latency_ms = round((time.monotonic() - start) * 1000)
|
||||
status = "error"
|
||||
return key, {"url": url, "status": status, "latency_ms": latency_ms}
|
||||
|
||||
results = await asyncio.gather(
|
||||
*(_ping(key, url) for key, url in config.EXTERNAL_SERVICES.items())
|
||||
)
|
||||
return {"services": dict(results)}
|
||||
|
||||
|
||||
_CI_HEADERS = {"Authorization": f"token {GITEA_TOKEN}"} if GITEA_TOKEN else {}
|
||||
|
||||
|
||||
@router.get("/ci-runs")
|
||||
async def ci_runs():
|
||||
"""Fetch recent Gitea Actions CI runs for jimmy/nas-tools."""
|
||||
async with httpx.AsyncClient(timeout=10) as c:
|
||||
r = await c.get(
|
||||
f"{GITEA_URL}/api/v1/repos/jimmy/nas-tools/actions/runs",
|
||||
headers=_CI_HEADERS,
|
||||
params={"limit": 5},
|
||||
)
|
||||
r.raise_for_status()
|
||||
data = r.json()
|
||||
runs = []
|
||||
for wf in data.get("workflow_runs", []):
|
||||
runs.append(
|
||||
{
|
||||
"id": wf.get("id"),
|
||||
"status": wf.get("status"),
|
||||
"event": wf.get("event"),
|
||||
"head_branch": wf.get("head_branch"),
|
||||
"head_sha": (wf.get("head_sha") or "")[:12],
|
||||
"display_title": wf.get("display_title"),
|
||||
"run_number": wf.get("run_number"),
|
||||
}
|
||||
)
|
||||
return {"workflow_runs": runs}
|
||||
|
||||
|
||||
@router.get("/external-services")
|
||||
def external_services():
|
||||
"""Return external service URLs and network config for the frontend sidebar."""
|
||||
|
||||
Reference in New Issue
Block a user