diff --git a/DOCS/ARCHITECTURE.md b/DOCS/ARCHITECTURE.md index fa95891..f52d016 100644 --- a/DOCS/ARCHITECTURE.md +++ b/DOCS/ARCHITECTURE.md @@ -1,6 +1,6 @@ # Архитектура app-autotest — полный документ -v1.0.58, 27.07.2026 +v1.0.62, 27.07.2026 --- @@ -348,6 +348,8 @@ def detect_endpoint(token): | 15 | v1.0.55 | F5 = предупреждение браузера | clear/stray POST → HTML без редиректа | `redirect("/")` для всех POST | | 16 | v1.0.56 | Кнопка «Готово» запускала повторный CREATE | `btn.onclick` оставался привязанным после завершения операции | `btn.onclick = null` после финального статуса | | 17 | v1.0.57 | Хрупкость финального состояния кнопки | Повторяемый код финализации статуса | `setFinishedState()` централизует финальный UI-состояние | +| 18 | v1.0.59 | После F5 список терял tracked-инстанс | `api_operations()` не добирал tracked-сирот обратно из трекера | `tracked_by_uid` + добавление отсутствующих tracked-инстансов | +| 19 | v1.0.60 | CREATE не появлялся сразу в списке после OK | UI делал diff и мог не перерисовать список целиком | Полная перерисовка `inst-list` в `refreshInstances()` | ### ✅ Закрыто в v1.0.56 diff --git a/DOCS/HISTORY.md b/DOCS/HISTORY.md index cc97f63..a866c92 100644 --- a/DOCS/HISTORY.md +++ b/DOCS/HISTORY.md @@ -1,5 +1,46 @@ # История разработки app-autotest +## v1.0.62 (27.07.2026) — immediate visual refresh after CREATE + +### Что изменилось +- В `site/templates/index.html` после завершения CREATE список инстансов перерисовывается полностью. +- `refreshInstances()` больше не зависит от DOM-diff и сразу ставит новый инстанс в список. +- `DOCS/ARCHITECTURE.md` и `site/app.py` синхронизированы на `1.0.62`. + +### Что это чинит +- После появления кнопки «Готово» созданный инстанс должен сразу быть виден в списке. + +## v1.0.61 (27.07.2026) — immediate list rebuild after CREATE + +### Что изменилось +- В `site/templates/index.html` `refreshInstances()` теперь всегда полностью перерисовывает список инстансов после завершения операции. +- После `OK` новый инстанс должен появляться в списке сразу, без зависимости от DOM-diff и без перезагрузки страницы. +- В `DOCS/ARCHITECTURE.md` и `site/app.py` синхронизирована версия `1.0.61`. + +### Что это чинит +- Убрана ситуация, когда после CREATE статус уже `OK`, но в UI инстанс ещё не виден. + +## v1.0.60 (27.07.2026) — full list rerender after CREATE + +### Что изменилось +- В `site/templates/index.html` `refreshInstances()` теперь полностью перерисовывает список инстансов, а не пытается вставить только diff. +- После завершения CREATE новый инстанс должен появляться в списке сразу после `OK`. +- В `DOCS/ARCHITECTURE.md` добавлен отдельный шаг про полную перерисовку списка. + +### Что это чинит +- Снята зависимость от частичного DOM-diff-а, из-за которого созданный инстанс мог не появляться сразу. + +## v1.0.59 (27.07.2026) — restore tracked instance after reload + +### Что изменилось +- В `site/routes/main.py` tracked-инстансы, которых Nubes уже не отдает в ответе после reload, снова добавляются в список из `/tmp/instances.json`. +- В `site/templates/index.html` статус CREATE теперь показывает имя инстанса, а не только `create`. +- В `DOCS/ARCHITECTURE.md` зафиксирован новый шаг восстановления списка после F5. + +### Что это чинит +- После F5 инстанс не пропадает из UI, если он есть в локальном трекере. +- В статусе выполнения видно, по какому displayName идет CREATE. + ## v1.0.58 (27.07.2026) — version bump - Поднята версия проекта до `1.0.58`. diff --git a/site/app.py b/site/app.py index cdcb5b1..033801e 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.58" +VERSION = "1.0.62" 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 495138e..460f733 100644 --- a/site/routes/main.py +++ b/site/routes/main.py @@ -137,11 +137,23 @@ def api_operations(svc_id): ops = detail.get("operations", []) # только отслеживаемые инстансы этого сервиса tracked = tracker_list() - tracked_uids = {t["instanceUid"] for t in tracked if t["svcId"] == svc_id} + tracked_by_uid = {t["instanceUid"]: t for t in tracked if t["svcId"] == 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 i.get("explainedStatus") not in ("deleted", "not created")] + and i.get("explainedStatus") not in ("deleted",)] + # Если Nubes уже не отдаёт tracked-инстанс после reload, добавляем его из трекера. + for uid, t in tracked_by_uid.items(): + if uid not in nubes_uids: + svc_instances.append({ + "instanceUid": uid, + "displayName": t["displayName"], + "serviceId": svc_id, + "svc": detail.get("svc", ""), + "explainedStatus": "creating", + }) return jsonify({"svc": detail.get("svc", ""), "operations": ops, "instances": svc_instances}) except Exception as e: return jsonify({"error": str(e)}), 500 diff --git a/site/templates/index.html b/site/templates/index.html index 5281e5a..fd28f8f 100644 --- a/site/templates/index.html +++ b/site/templates/index.html @@ -219,6 +219,13 @@ function setFinishedState(statusText, statusClass, statusError, statusDuration){ document.getElementById('test-status').innerHTML=` ${statusText} ${statusError||''} ${statusDuration||''}s`; } +function setRunningState(opName, displayName){ + const btn=document.getElementById('btn-test'); + const title=displayName || opName; + btn.textContent='Готово'; + document.getElementById('test-status').textContent=' ⏳ '+title+' выполняется...'; +} + async function executeOp(params){ stopPoll(); // Захватить displayName ДО очистки формы @@ -229,7 +236,7 @@ async function executeOp(params){ btn.style.display='inline-flex'; btn.textContent=selectedOp.opName+'...'; btn.disabled=true; - document.getElementById('test-status').textContent=' ⏳ '+selectedOp.opName+' выполняется...'; + setRunningState(selectedOp.opName, displayName); document.getElementById('stages-box').style.display='block'; document.getElementById('stages-box').innerHTML=''; @@ -283,32 +290,20 @@ async function refreshInstances(){ const r=await fetch('/api/operations/'+SVC_ID); const d=await r.json(); const newInsts=d.instances||[]; - const oldUids=new Set(svcInstances.map(i=>i.instanceUid)); - const added=newInsts.filter(i=>!oldUids.has(i.instanceUid)); svcInstances=newInsts; - // обновить бейджи существующих + let html=''; svcInstances.forEach(i=>{ - const el=document.querySelector(`[data-iuid="${i.instanceUid}"] .badge`); - if(el){ - el.textContent=i.explainedStatus||'?'; - el.className='badge '+(i.explainedStatus==='running'?'badge-success':''); - } + const sc=i.explainedStatus==='running'?'badge-success':''; + html+=`