прошивальщик: баннер и проверка свежести — не отдаём ссылки на удалённые файлы из старой вкладки
This commit is contained in:
1 parent
b8d38db198
commit
a4e80805d6
3 files changed
+64
-3
No files matched your search
@@ -4,6 +4,7 @@ const $ = (id) => document.getElementById(id);
|
||||
const logEl = $('log');
|
||||
|
||||
let index = null;
|
||||
let manifestStamp = ''; // generated_at манифеста, который сейчас в памяти
|
||||
let selected = null; // выбранный файл из index.json
|
||||
let ownFile = null; // файл, загруженный пользователем с компьютера
|
||||
let transport = null;
|
||||
@@ -23,6 +24,36 @@ function log(msg, cls = '') {
|
||||
logEl.scrollTop = logEl.scrollHeight;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------- устаревшая страница
|
||||
|
||||
// Страница, открытая давно, держит манифест в памяти: после выкладки новой
|
||||
// сборки его ссылки ведут на удалённые файлы и скачивание молча не работает.
|
||||
// Показываем баннер и предлагаем обновить страницу.
|
||||
function showStale(msg) {
|
||||
const box = $('stale');
|
||||
if (!box) return;
|
||||
if (msg) $('stale-text').textContent = msg;
|
||||
if (box.hidden) {
|
||||
box.hidden = false;
|
||||
log('Доступна новая версия прошивальщика — обновите страницу.', 'warn');
|
||||
}
|
||||
}
|
||||
|
||||
// Сверяет generated_at манифеста в памяти со свежим ответом сервера.
|
||||
async function checkFreshness() {
|
||||
if (!manifestStamp) return false;
|
||||
try {
|
||||
const res = await fetch('index.json', { cache: 'no-store' });
|
||||
if (!res.ok) return false;
|
||||
const fresh = await res.json();
|
||||
if (fresh.generated_at && fresh.generated_at !== manifestStamp) {
|
||||
showStale();
|
||||
return true;
|
||||
}
|
||||
} catch (e) { /* сеть недоступна — молчим, это не ошибка страницы */ }
|
||||
return false;
|
||||
}
|
||||
|
||||
function setProgress(ratio, label = '') {
|
||||
const pct = Math.max(0, Math.min(1, ratio || 0)) * 100;
|
||||
$('bar').style.width = pct.toFixed(1) + '%';
|
||||
@@ -75,12 +106,25 @@ function fileRow(section, board, file) {
|
||||
const btn = document.createElement('button');
|
||||
btn.className = 'small' + (file.flashable ? '' : '');
|
||||
btn.textContent = file.flashable ? 'Прошить' : 'Скачать';
|
||||
btn.addEventListener('click', (e) => {
|
||||
btn.addEventListener('click', async (e) => {
|
||||
e.preventDefault();
|
||||
if (file.flashable) {
|
||||
select(file, row);
|
||||
flash();
|
||||
} else {
|
||||
// Перед скачиванием проверяем, что файл ещё на месте: у давно открытой
|
||||
// страницы в памяти может быть старый манифест со ссылками на удалённые
|
||||
// файлы. Тогда честно показываем баннер «обновите страницу».
|
||||
btn.disabled = true;
|
||||
try {
|
||||
const head = await fetch(file.url, { method: 'HEAD', cache: 'no-store' });
|
||||
if (!head.ok) {
|
||||
showStale('Файл ' + file.name + ' не найден — на сервере уже новая версия прошивальщика.');
|
||||
await checkFreshness();
|
||||
return;
|
||||
}
|
||||
} catch (err) { /* сеть недоступна — пробуем скачать как раньше */ }
|
||||
finally { btn.disabled = false; }
|
||||
const a = document.createElement('a');
|
||||
a.href = file.url;
|
||||
a.download = file.name;
|
||||
@@ -212,6 +256,7 @@ async function loadIndex() {
|
||||
const res = await fetch('index.json', { cache: 'no-store' });
|
||||
if (!res.ok) throw new Error('HTTP ' + res.status);
|
||||
index = await res.json();
|
||||
manifestStamp = index.generated_at || '';
|
||||
const host = $('sections');
|
||||
host.textContent = '';
|
||||
index.sections.forEach((s) => host.appendChild(sectionBlock(s)));
|
||||
@@ -479,3 +524,10 @@ if ('serial' in navigator) {
|
||||
|
||||
loadIndex();
|
||||
updateButtons();
|
||||
|
||||
// Раз в минуту и при возврате на вкладку сверяем манифест: если на сервере уже
|
||||
// новая сборка, показываем баннер вместо тихих 404 при скачивании файлов.
|
||||
setInterval(checkFreshness, 60000);
|
||||
document.addEventListener('visibilitychange', () => {
|
||||
if (!document.hidden) checkFreshness();
|
||||
});
|
||||
@@ -5,7 +5,7 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Прошивальщик MeshCore и PosadMesh</title>
|
||||
<meta name="description" content="Прошивка плат MeshCore и MQTT-репитеров PosadMesh прямо из браузера">
|
||||
<link rel="stylesheet" href="style.css?v=20261016">
|
||||
<link rel="stylesheet" href="style.css?v=20261018">
|
||||
</head>
|
||||
<body>
|
||||
<div class="wrap">
|
||||
@@ -23,6 +23,10 @@
|
||||
|
||||
<div class="layout">
|
||||
<main>
|
||||
<div class="stale" id="stale" hidden>
|
||||
<span id="stale-text">На сервере уже новая версия прошивальщика — список файлов на этой странице устарел.</span>
|
||||
<button type="button" class="small" onclick="location.reload()">Обновить страницу</button>
|
||||
</div>
|
||||
<div class="notes" style="margin-top:0;border-top:none;padding-top:0">
|
||||
<h2>Что означают файлы</h2>
|
||||
<div class="cards">
|
||||
@@ -153,6 +157,6 @@
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
<script type="module" src="app.js?v=20261016"></script>
|
||||
<script type="module" src="app.js?v=20261018"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -197,3 +197,8 @@ details.verblock.newest > summary { background: #14241c; }
|
||||
.drop.filled { border-style: solid; border-color: #2f6f4f; color: #cdebd8; }
|
||||
.drop input[type=file] { display: none; }
|
||||
.hint { font-size: 13px; color: #7d8ea6; line-height: 1.45; }
|
||||
|
||||
/* --- устаревшая страница прошивальщика --- */
|
||||
.stale { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; margin: 0 0 14px; padding: 12px 14px; border: 1px solid #7a5b1f; border-radius: 10px; background: rgba(122,91,31,.18); color: #f0d9a8; }
|
||||
.stale[hidden] { display: none; }
|
||||
.stale button { flex: 0 0 auto; }
|
||||
Reference in new issue
Block a user