diff --git a/src/antigravity_provider/router/web/server.py b/src/antigravity_provider/router/web/server.py index 5321228..3acea70 100644 --- a/src/antigravity_provider/router/web/server.py +++ b/src/antigravity_provider/router/web/server.py @@ -199,18 +199,32 @@ _STATIC_DIR = Path(__file__).resolve().parent / "static" if _STATIC_DIR.is_dir(): app.mount("/static", StaticFiles(directory=str(_STATIC_DIR)), name="static") + # Ассеты обязаны перепроверяться при каждой загрузке. + # Без Cache-Control браузер применяет эвристическое кэширование и может + # неделями отдавать старый app.js: интерфейс выглядит обновлённым (index.html + # перепроверяется при навигации), а поведение остаётся от прошлой сборки. + _NO_CACHE = {"Cache-Control": "no-cache, must-revalidate"} + @app.get("/") @app.get("/index.html") def index(): - return FileResponse(str(_STATIC_DIR / "index.html")) + return FileResponse(str(_STATIC_DIR / "index.html"), headers=_NO_CACHE) @app.get("/app.js") def _app_js(): - return FileResponse(str(_STATIC_DIR / "app.js"), media_type="application/javascript") + return FileResponse( + str(_STATIC_DIR / "app.js"), + media_type="application/javascript", + headers=_NO_CACHE, + ) @app.get("/style.css") def _style_css(): - return FileResponse(str(_STATIC_DIR / "style.css"), media_type="text/css") + return FileResponse( + str(_STATIC_DIR / "style.css"), + media_type="text/css", + headers=_NO_CACHE, + ) @app.get("/snapshot.example.json") def _fixture(): diff --git a/src/antigravity_provider/router/web/static/app.js b/src/antigravity_provider/router/web/static/app.js index 0644f00..dd3e83f 100644 --- a/src/antigravity_provider/router/web/static/app.js +++ b/src/antigravity_provider/router/web/static/app.js @@ -434,6 +434,23 @@ function renderAccountsView() { return matchesSearch && matchesHealth; }); + // Подключённые аккаунты идут первыми: владелец жаловался, что рабочие + // карточки разбросаны между пустыми слотами и их приходится выискивать. + // Пустые слоты ('не подключён') — всегда в конце группы. + filtered.sort((a, b) => { + const rank = (p) => { + if (p.health_state === 'not_configured') return 5; + if (p.is_cold_spare || !p.enabled || p.health_state === 'disabled') return 4; + if (p.health_state === 'not_tested') return 3; + if (p.health_state === 'auth_required' || p.health_state === 'auth_expired') return 2; + if (p.health_state === 'healthy') return 0; + return 1; + }; + const d = rank(a) - rank(b); + if (d !== 0) return d; + return (a.display_name || '').localeCompare(b.display_name || '', 'ru'); + }); + if (filtered.length === 0) continue; visibleProfiles += filtered.length;