v9.44: remove all graph code (backend endpoint, frontend SVG/panzoom/CSS, ~536 lines dead code)

This commit is contained in:
Naeel
2026-04-17 08:56:53 +03:00
parent b778f5554c
commit f6d15eb7e8
3 changed files with 3 additions and 536 deletions
-175
View File
@@ -3,7 +3,6 @@ from fastapi.responses import StreamingResponse
from fastapi.staticfiles import StaticFiles
import httpx
from typing import Annotated
import asyncio
import json
import time
from datetime import datetime, timezone
@@ -16,8 +15,6 @@ DECK_APIS = {
"test": "https://deck-api-test.ngcloud.ru/api/v1",
}
DEFAULT_DECK_ENV = "test"
CACHE_TTL_SECONDS = 180
GRAPH_CACHE: dict[str, tuple[float, dict]] = {}
# Centralized backend policy for table-related derived fields.
DELETE_POLICY = {
@@ -55,38 +52,6 @@ def _as_text(value) -> str:
return ""
def extract_platform_name(inst: dict | None) -> str:
if not isinstance(inst, dict):
return ""
sources = [
inst.get("parameters"),
inst.get("params"),
inst.get("instanceParameters"),
inst.get("inputParameters"),
]
needles = ("platform", "kuber", "k8s", "cluster", "shturval", "штурвал")
for src in sources:
if isinstance(src, dict):
for key, value in src.items():
key_s = str(key).strip().lower()
if any(n in key_s for n in needles):
txt = _as_text(value)
if txt:
return txt
elif isinstance(src, list):
for item in src:
if not isinstance(item, dict):
continue
key_s = str(item.get("name") or item.get("key") or item.get("code") or "").strip().lower()
if any(n in key_s for n in needles):
txt = _as_text(item.get("value") or item.get("displayValue") or item.get("text"))
if txt:
return txt
return ""
def _parse_ts(value) -> float | None:
if not value:
return None
@@ -204,146 +169,6 @@ async def list_instances(
return {"instances": all_results, "total": len(all_results)}
@app.get("/api/graph")
async def get_graph(
request: Request,
x_deck_token: Annotated[str | None, Header()] = None,
x_deck_env: Annotated[str | None, Header()] = None,
):
if not x_deck_token:
raise HTTPException(status_code=401, detail="Token required")
env, deck_api = resolve_deck_api(x_deck_env)
status_list = request.query_params.getlist("status")
root_uid = request.query_params.get("root_uid")
depth_raw = request.query_params.get("depth", "2")
try:
depth = max(1, min(5, int(depth_raw)))
except ValueError:
depth = 2
status_key = ",".join(sorted(status_list)) if status_list else "__all__"
cache_key = f"{x_deck_token[:24]}::{env}::{status_key}"
now = time.time()
cached = GRAPH_CACHE.get(cache_key)
if cached and (now - cached[0] < CACHE_TTL_SECONDS):
base_graph = cached[1]
else:
all_instances = await fetch_all_instances(x_deck_token, deck_api)
if status_list:
all_instances = [i for i in all_instances if i.get("explainedStatus") in status_list]
uid_to_inst = {
i.get("instanceUid"): i
for i in all_instances
if i.get("instanceUid")
}
allowed_uids = set(uid_to_inst.keys())
detail_map: dict[str, dict] = {}
async with httpx.AsyncClient(timeout=25) as shared_client:
for uid in allowed_uids:
try:
r = await shared_client.get(
f"{deck_api}/index.cfm/instances/{uid}",
headers=auth_headers(x_deck_token),
)
if r.status_code == 401:
raise HTTPException(status_code=401, detail="Invalid token")
if r.status_code >= 400:
continue
inst = r.json().get("instance")
if inst:
detail_map[uid] = inst
except httpx.TimeoutException:
continue
# Categorise singletons by service name
_PLATFORM_PATTERNS = ("виртуальный датацентр", "vdc", "virtual data center", "cloud director", "kubernetes", "k8s")
_SINGLETON_PATTERNS = ("object storage", "s3", "edge", "шлюз периметра", "dns", "container registry", "реестр контейнеров")
def _is_platform_svc(svc_name: str) -> bool:
s = svc_name.strip().lower()
return any(p in s for p in _PLATFORM_PATTERNS)
def _is_singleton_svc(svc_name: str) -> bool:
s = svc_name.strip().lower()
return any(p in s for p in _SINGLETON_PATTERNS)
# Build realm→platform_uid map: instances whose resourceRealmCnt > 0 ARE platforms
realm_to_platform: dict[str, str] = {}
for uid_d, inst_d in detail_map.items():
cnt = inst_d.get("resourceRealmCnt") or 0
realm_name = inst_d.get("resourceRealm") or ""
if cnt and realm_name:
realm_to_platform[realm_name] = uid_d
nodes = []
for uid_i in allowed_uids:
i = uid_to_inst.get(uid_i, {})
detail = detail_map.get(uid_i, {})
svc = i.get("svc") or ""
realm_cnt = detail.get("resourceRealmCnt") or 0
is_aux = bool(detail.get("isAuxiliary"))
# Determine lane by service name
if _is_platform_svc(svc):
lane = "platform"
elif _is_singleton_svc(svc):
lane = "singleton"
else:
lane = "regular"
nodes.append(
{
"id": uid_i,
"label": i.get("displayName") or uid_i,
"status": i.get("explainedStatus") or "unknown",
"svc": svc,
"realm": i.get("resourceRealm") or "",
"lane": lane,
"isAuxiliary": is_aux,
"created": i.get("instanceConfigDtCreated") or i.get("dtCreated") or "",
}
)
# Debug: log lane distribution
import sys
_lane_counts = {}
for _n in nodes:
_l = _n.get("lane", "unknown")
_lane_counts[_l] = _lane_counts.get(_l, 0) + 1
print(f"[GRAPH] Lanes: {_lane_counts}, nodes: {len(nodes)}", flush=True)
for _n in nodes:
if _n["lane"] in ("platform", "singleton"):
print(f" {_n['lane'].upper()}: {_n['label']} | svc={_n['svc']}", flush=True)
base_graph = {"nodes": nodes, "edges": []}
GRAPH_CACHE[cache_key] = (now, base_graph)
# Return full graph fast
if not root_uid:
return {
"nodes": base_graph["nodes"],
"edges": [],
"total_nodes": len(base_graph["nodes"]),
"total_edges": 0,
"cached": bool(cached),
}
node_ids = {n["id"] for n in base_graph["nodes"]}
if root_uid not in node_ids:
return {"nodes": [], "edges": [], "total_nodes": 0, "total_edges": 0, "cached": bool(cached)}
sub_nodes = [n for n in base_graph["nodes"] if n["id"] == root_uid]
return {
"nodes": sub_nodes,
"edges": [],
"total_nodes": len(sub_nodes),
"total_edges": 0,
"cached": bool(cached),
}
@app.get("/api/instances/stream")
async def stream_instances(
request: Request,