diff --git a/GUI/src/vast/dashboard_api.py b/GUI/src/vast/dashboard_api.py index 3c415698c..c58037090 100644 --- a/GUI/src/vast/dashboard_api.py +++ b/GUI/src/vast/dashboard_api.py @@ -8,16 +8,17 @@ from requests.adapters import HTTPAdapter from urllib3.util.retry import Retry +import json, time, pathlib, base64, requests +from urllib.parse import quote +from requests.adapters import HTTPAdapter +from urllib3.util.retry import Retry -# ---------- CONFIG ---------- -DB_API_BASE = "http://host.docker.internal:8001" +DB_API_BASE = "http://db_api_service:8001" DB_API_AUTH_MODE = "service" DB_API_TOKEN_FILE = "/app/secrets/db_api_token" DB_API_TOKEN = "auto" DB_API_SERVICE_NAME = "GUI_H" - -# ---------- TOKEN BOOTSTRAP ---------- def _safe_join_url(base: str, path: str) -> str: return f"{base.rstrip('/')}/{path.lstrip('/')}" @@ -44,64 +45,71 @@ def _fetch_token_via_dev_bootstrap(base: str, retries: int = 3, backoff: float = time.sleep(backoff * attempt) return None - def get_or_bootstrap_token() -> str | None: - print(f"[DEBUG] Checking for existing token file at: {DB_API_TOKEN_FILE}", flush=True) - if DB_API_TOKEN and DB_API_TOKEN.lower() != "auto": - print(f"[DEBUG] Using static token from config", flush=True) return DB_API_TOKEN - token = _read_token_from_file(DB_API_TOKEN_FILE) if token: - print(f"[DEBUG] Loaded token from {DB_API_TOKEN_FILE}", flush=True) return token - - print(f"[DEBUG] No existing token found, bootstrapping via {DB_API_BASE}/auth/_dev_bootstrap", flush=True) token = _fetch_token_via_dev_bootstrap(DB_API_BASE) if token: pathlib.Path(DB_API_TOKEN_FILE).parent.mkdir(parents=True, exist_ok=True) pathlib.Path(DB_API_TOKEN_FILE).write_text(token, encoding="utf-8") - print(f"[BOOTSTRAP] wrote token to {DB_API_TOKEN_FILE}", flush=True) return token - - print("[BOOTSTRAP][ERROR] Failed to obtain token.", flush=True) return None - - - -# ---------- API CLIENT ---------- class DashboardApi: def __init__(self): self.base = DB_API_BASE.rstrip("/") self.http = requests.Session() token = get_or_bootstrap_token() + self.token = token if token: if DB_API_AUTH_MODE == "service": self.http.headers.update({"X-Service-Token": token}) else: self.http.headers.update({"Authorization": f"Bearer {token}"}) self.http.headers.update({"Content-Type": "application/json"}) - self.http.mount("http://", HTTPAdapter(max_retries=Retry(total=5, backoff_factor=0.5, status_forcelist=[500, 502, 503, 504]))) + self.http.mount("http://", HTTPAdapter(max_retries=Retry(total=5, backoff_factor=0.5, status_forcelist=[500, 502, 503, 504]))) self.http.mount("https://", HTTPAdapter(max_retries=Retry(total=5, backoff_factor=0.5, status_forcelist=[500, 502, 503, 504]))) - - # ---------- METHODS ---------- + self.token_type = "service" if DB_API_AUTH_MODE == "service" else "bearer" def list_devices(self, model: str | None = None) -> list[dict]: - - url = f"{self.base}/api/devices" + url = f"{self.base}/api/tables/devices" if model: url += f"?model={model}" try: r = self.http.get(url, timeout=10) if r.status_code == 200: return r.json() - print(f"[API ERROR] {r.status_code}: {r.text[:100]}") except Exception as e: print(f"[API FAIL] {e}") return [] + def get_token_info(self) -> dict: + t = self.token + if not t: + return {"type": self.token_type, "status": "missing"} + if "." in t: + try: + payload_b64 = t.split(".")[1] + padded = payload_b64 + "=" * (-len(payload_b64) % 4) + data = json.loads(base64.urlsafe_b64decode(padded)) + exp = data.get("exp") + secs_left = exp - int(time.time()) if exp else None + return {"type": "jwt", "exp": exp, "secs_left": secs_left, "payload": data} + except Exception: + pass + return {"type": self.token_type, "token_length": len(t)} + + def refresh_token(self): + new_token = _fetch_token_via_dev_bootstrap(self.base) + if new_token: + pathlib.Path(DB_API_TOKEN_FILE).write_text(new_token, encoding="utf-8") + self.token = new_token + self.http.headers.update({"X-Service-Token": new_token}) + return True + return False # ---------- THRESHOLDS ---------- def bulk_set_task_thresholds_labeled( self, @@ -123,7 +131,7 @@ def bulk_set_task_thresholds_labeled( r = self.http.post(url, json=items, timeout=20) if r.status_code in (200, 201): data = r.json() - # ודאי שמבנה ok/fail תואם + return { "ok": list(data.get("ok", [])), "fail": list(data.get("fail", [])), @@ -133,4 +141,4 @@ def bulk_set_task_thresholds_labeled( "fail": [[ [i.get("task"), i.get("label","")], f"http-{r.status_code} {r.text[:200]}"] for i in items], } except Exception as e: - return {"ok": [], "fail": [[ [i.get("task"), i.get("label","")], str(e)] for i in items]} \ No newline at end of file + return {"ok": [], "fail": [[ [i.get("task"), i.get("label","")], str(e)] for i in items]}