fix(web): браузер отдавал старый app.js, интерфейс выглядел не обновлённым
Владелец сообщил, что в маршрутизации не переставляются блоки, нигде нет выбора аккаунта для роли и на «Обзоре» ничего нельзя изменить. На скриншоте при этом видна кнопка «Изменить цепочку», которой в коде уже нет: A24 её удалил вместе с renderTeam и renderProviders. Причина не в логике. FileResponse отдавал app.js и style.css без заголовка Cache-Control, поэтому браузер применял эвристическое кэширование и держал скрипт от прошлой сборки. index.html при навигации перепроверялся и был свежим — отсюда смесь нового текста подсказки со старыми кнопками, а перетаскивание и выбор модели просто отсутствовали в загруженном коде. Действие reorder_chain при этом исправно: проверено вызовом, порядок цепочки меняется и сохраняется в router_profiles.yaml. Заодно подключённые аккаунты выводятся первыми, а пустые слоты («не подключён») — в конце группы: рабочие карточки были разбросаны между пустыми и их приходилось выискивать. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
c49e6eac31
commit
158210dc01
2 changed files with 34 additions and 3 deletions
|
|
@ -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():
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue