diff --git a/DOCS/ARCHITECTURE.md b/DOCS/ARCHITECTURE.md index d91ab97..350ca87 100644 --- a/DOCS/ARCHITECTURE.md +++ b/DOCS/ARCHITECTURE.md @@ -1,6 +1,6 @@ # Архитектура app-autotest — полный документ -v1.0.65, 27.07.2026 +v1.0.66, 27.07.2026 --- diff --git a/DOCS/HISTORY.md b/DOCS/HISTORY.md index b1f5ba2..1fe43e1 100644 --- a/DOCS/HISTORY.md +++ b/DOCS/HISTORY.md @@ -1,5 +1,16 @@ # История разработки app-autotest +# v1.0.66 (27.07.2026) — backend status resolver for operations + +### Что изменилось +- В `site/routes/main.py` добавлен канонический резолвер статуса инстанса для `/api/operations/`. +- Backend теперь отдаёт `status` для инстансов, а UI читает именно его вместо угадывания по `explainedStatus`. +- В `site/templates/index.html` убрано раннее отображение «Готово» во время выполнения операции. + +### Что это чинит +- Путаницу между `creating`, `running` и фактическим статусом в UI. +- Ситуацию, когда фронтенд сам интерпретировал статусы и показывал не то состояние. + ## v1.0.65 (27.07.2026) — cloud list pagination fix ### Что изменилось diff --git a/site/app.py b/site/app.py index 32ca3ff..97b435a 100644 --- a/site/app.py +++ b/site/app.py @@ -6,7 +6,7 @@ from routes.main import bp as main_bp from routes.api import bp as api_bp from routes.api_test import bp as api_test_bp -VERSION = "1.0.65" +VERSION = "1.0.66" app = Flask(__name__, template_folder="templates", static_folder="static") app.config["NUBES_API_ENDPOINT"] = os.getenv("NUBES_API_ENDPOINT", "https://lk-api-gateway-dev.ngcloud.ru/api/v1/svc") diff --git a/site/routes/main.py b/site/routes/main.py index dfb4942..6e00910 100644 --- a/site/routes/main.py +++ b/site/routes/main.py @@ -10,6 +10,21 @@ bp = Blueprint("main", __name__) AUTOTEST_PREFIX = "autotest-" +def _resolve_instance_status(instance, tracked=None): + """Вернуть канонический статус инстанса для API и UI.""" + tracked = tracked or {} + cloud_status = str(instance.get("explainedStatus") or "").strip() + display_name = str(instance.get("displayName") or "") + + if cloud_status: + return cloud_status + + if tracked and display_name.startswith(AUTOTEST_PREFIX): + return "creating" + + return "unknown" + + def _mask(s): if not s or len(s) < 8: return "" @@ -143,10 +158,18 @@ def api_operations(svc_id): tracked_uids = set(tracked_by_uid.keys()) instances = get_instances(client) nubes_uids = {i["instanceUid"] for i in instances} - svc_instances = [i for i in instances - if i.get("instanceUid") in tracked_uids - and str(i.get("displayName", "")).startswith(AUTOTEST_PREFIX) - and i.get("explainedStatus") not in ("deleted",)] + svc_instances = [] + for i in instances: + uid = i.get("instanceUid") + if uid not in tracked_uids: + continue + if not str(i.get("displayName", "")).startswith(AUTOTEST_PREFIX): + continue + if i.get("explainedStatus") in ("deleted",): + continue + item = dict(i) + item["status"] = _resolve_instance_status(item, tracked_by_uid.get(uid)) + svc_instances.append(item) # Если Nubes уже не отдаёт tracked-инстанс после reload, добавляем его из трекера. for uid, t in tracked_by_uid.items(): if uid not in nubes_uids: @@ -155,7 +178,8 @@ def api_operations(svc_id): "displayName": t["displayName"], "serviceId": svc_id, "svc": detail.get("svc", ""), - "explainedStatus": "creating", + "explainedStatus": "", + "status": "creating", }) return jsonify({"svc": detail.get("svc", ""), "operations": ops, "instances": svc_instances}) except Exception as e: diff --git a/site/templates/index.html b/site/templates/index.html index f5c1055..a4e2e7c 100644 --- a/site/templates/index.html +++ b/site/templates/index.html @@ -114,10 +114,11 @@ async function selectService(svcId){ let html=''; svcInstances.forEach(i=>{ - const sc=i.explainedStatus==='running'?'badge-success':''; + const status=i.status||i.explainedStatus||'?'; + const sc=status==='running'?'badge-success':''; html+=`
${i.displayName} - ${i.explainedStatus||'?'} + ${status}
`; html+=`
`; }); @@ -235,7 +236,7 @@ function setFinishedState(statusText, statusClass, statusError, statusDuration){ function setRunningState(opName, displayName){ const btn=document.getElementById('btn-test'); const title=displayName || opName; - btn.textContent='Готово'; + btn.textContent=opName+'...'; document.getElementById('test-status').textContent=' ⏳ '+title+' выполняется...'; } @@ -307,10 +308,11 @@ async function refreshInstances(){ svcInstances=newInsts; let html=''; svcInstances.forEach(i=>{ - const sc=i.explainedStatus==='running'?'badge-success':''; + const status=i.status||i.explainedStatus||'?'; + const sc=status==='running'?'badge-success':''; html+=`
${i.displayName} - ${i.explainedStatus||'?'} + ${status}
`; html+=`
`; });