From 17bc4aebf88fc65564ea1a47f5cc698045bf789a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CNaeel=E2=80=9D?= Date: Tue, 14 Jul 2026 19:22:36 +0400 Subject: [PATCH] =?UTF-8?q?fix:=20=D1=81=D0=B1=D1=80=D0=BE=D1=81=20=D1=81?= =?UTF-8?q?=D0=BE=D1=81=D1=82=D0=BE=D1=8F=D0=BD=D0=B8=D1=8F=20=D0=BF=D1=80?= =?UTF-8?q?=D0=B8=20F5/=D0=B7=D0=B0=D0=BA=D1=80=D1=8B=D1=82=D0=B8=D0=B8=20?= =?UTF-8?q?=E2=80=94=20beforeunload,=20abort=20XHR,=20close=20EventSource?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- site/app.py | 2 +- site/templates/index.html | 38 ++++++++++++++++++++++++++++++++------ 2 files changed, 33 insertions(+), 7 deletions(-) diff --git a/site/app.py b/site/app.py index 24171f6..5481cd9 100644 --- a/site/app.py +++ b/site/app.py @@ -20,7 +20,7 @@ if _sys_path_root not in sys.path: sys.path.insert(0, _sys_path_root) # Версия приложения (меняется при изменениях) -VERSION = "0.0.22" +VERSION = "0.0.23" def create_app(): diff --git a/site/templates/index.html b/site/templates/index.html index f7e44d9..9ecbd98 100644 --- a/site/templates/index.html +++ b/site/templates/index.html @@ -139,6 +139,24 @@ const st = document.getElementById('status'); const db = document.getElementById('dlBtns'); let sf = []; let currentSid = ''; +let activeES = null; // активный EventSource +let activeXHR = null; // активный XHR + +function resetAll() { + if (activeES) { activeES.close(); activeES = null; } + if (activeXHR) { activeXHR.abort(); activeXHR = null; } + currentSid = ''; + sf = []; + fi.value = ''; + rr(); + st.className = ''; + st.textContent = ''; + db.classList.remove('show'); + ub.disabled = false; +} + +// При F5 / закрытии вкладки — обрубить всё +window.addEventListener('beforeunload', () => resetAll()); function fs(b) { return b < 1024 ? b + ' B' : b < 1048576 ? (b / 1024).toFixed(1) + ' KB' : (b / 1048576).toFixed(1) + ' MB'; } @@ -158,6 +176,11 @@ function ss(idx, h) { const e = document.getElementById('st-' + idx); if (e) e.i async function uploadFiles() { if (sf.length === 0) return; + // Обрубить всё что могло остаться от предыдущего раза + resetAll(); + ub.disabled = true; + sf = Array.from(fi.files); // восстановить список после resetAll + rr(); ub.disabled = true; db.classList.remove('show'); const total = sf.length; @@ -201,6 +224,7 @@ async function uploadFiles() { }; xhr.onerror = function() { reject(new Error('Сеть')); }; xhr.ontimeout = function() { reject(new Error('Таймаут 30с')); }; + activeXHR = xhr; xhr.send(fd); }); } catch (err) { @@ -226,8 +250,8 @@ async function uploadFiles() { try { await new Promise((resolve, reject) => { - const es = new EventSource('/api/process_stream/' + currentSid); - es.addEventListener('start', function(e) { + activeES = new EventSource('/api/process_stream/' + currentSid); + activeES.addEventListener('start', function(e) { const d = JSON.parse(e.data); fileTimers[d.idx] = performance.now(); fileIntervals[d.idx] = setInterval(function() { @@ -241,8 +265,9 @@ async function uploadFiles() { const elapsed = ((performance.now() - (fileTimers[d.idx] || performance.now())) / 1000).toFixed(1); ss(d.idx, '✓ ' + elapsed + 'с'); }); - es.addEventListener('complete', function(e) { - es.close(); + activeES.addEventListener('complete', function(e) { + activeES.close(); + activeES = null; clearInterval(ptimer); const d = JSON.parse(e.data); st.className = 'status done'; @@ -250,8 +275,9 @@ async function uploadFiles() { db.classList.add('show'); resolve(); }); - es.onerror = function() { - es.close(); + activeES.onerror = function() { + activeES.close(); + activeES = null; clearInterval(ptimer); reject(new Error('SSE connection failed')); };