From c346be5add87e54a823d6a865818b54240b035f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CNaeel=E2=80=9D?= Date: Mon, 24 Aug 2026 15:42:37 +0300 Subject: [PATCH] =?UTF-8?q?v0.0.66:=20=D0=B1=D0=BB=D0=BE=D0=BA=D0=B8=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D0=BA=D0=B0=20=D0=B2=D1=8B=D0=B1=D0=BE=D1=80=D0=B0?= =?UTF-8?q?/=D1=83=D0=B4=D0=B0=D0=BB=D0=B5=D0=BD=D0=B8=D1=8F=20=D1=84?= =?UTF-8?q?=D0=B0=D0=B9=D0=BB=D0=BE=D0=B2=20=D0=BD=D0=B0=20=D0=B2=D1=80?= =?UTF-8?q?=D0=B5=D0=BC=D1=8F=20=D0=B7=D0=B0=D0=B3=D1=80=D1=83=D0=B7=D0=BA?= =?UTF-8?q?=D0=B8=20=D0=B8=20=D0=BE=D0=B1=D1=80=D0=B0=D0=B1=D0=BE=D1=82?= =?UTF-8?q?=D0=BA=D0=B8,=20=D0=B7=D0=B0=D0=BC=D0=BE=D1=80=D0=BE=D0=B7?= =?UTF-8?q?=D0=BA=D0=B0=20=D1=81=D0=B5=D1=81=D1=81=D0=B8=D0=B8=20=D0=BF?= =?UTF-8?q?=D0=BE=D1=81=D0=BB=D0=B5=20=D1=80=D0=B5=D0=B7=D1=83=D0=BB=D1=8C?= =?UTF-8?q?=D1=82=D0=B0=D1=82=D0=B0=20+=20=D0=BA=D0=BD=D0=BE=D0=BF=D0=BA?= =?UTF-8?q?=D0=B0=20=D0=9D=D0=BE=D0=B2=D0=B0=D1=8F=20=D1=81=D0=B5=D1=81?= =?UTF-8?q?=D1=81=D0=B8=D1=8F?= 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, 36 insertions(+), 4 deletions(-) diff --git a/site/app.py b/site/app.py index 8dd5253..091cf5c 100644 --- a/site/app.py +++ b/site/app.py @@ -21,7 +21,7 @@ if _sys_path_root not in sys.path: sys.path.insert(0, _sys_path_root) # Версия приложения (меняется при изменениях) -VERSION = "0.0.65" +VERSION = "0.0.66" def setup_logging(): diff --git a/site/templates/index.html b/site/templates/index.html index 2efc0b0..495689a 100644 --- a/site/templates/index.html +++ b/site/templates/index.html @@ -73,6 +73,7 @@ padding: 2px 4px; font-size: 14px; line-height: 1; } .remove-btn:hover { color: var(--red); } + body.busy .remove-btn { display: none; } .footer-bar { margin-top: 10px; display: flex; justify-content: space-between; align-items: center; font-size: 12px; color: var(--muted); @@ -162,6 +163,7 @@
@@ -214,6 +216,8 @@ let procExtractDone = false; // true после extract_done (далее done let procExtractRate = null; // измеренная скорость извлечения (сек/МБ) для оценок ожидающих let procRefresh = null; // setInterval перерисовки таблицы let ptimer = null, liveRefresh = null; // таймеры статуса и live-блока +let busy = false; // идёт загрузка/обработка — список файлов заблокирован +let sessionDone = false; // результат готов — сессия заморожена до «Новая сессия» const MAX_FILE_BYTES = 50 * 1024 * 1024; // 50 МБ на один файл const MAX_SESSION_BYTES = 500 * 1024 * 1024; // 500 МБ суммарно на сессию const VM_UPLOAD_URL = 'https://contracts.kube5s.ru/drhider-upload/'; // ВМ-буфер: PUT больших файлов (шлюз кластера их рвёт) @@ -238,6 +242,9 @@ function resetAll() { procNameIdx = {}; procExtractDone = false; procExtractRate = null; + sessionDone = false; + setBusy(false); + document.getElementById('newSessionBtn').style.display = 'none'; document.getElementById('cancelBtn').style.display = 'none'; fi.value = ''; rr(); @@ -375,7 +382,13 @@ function finishProcUI() { rr(); } -function rm(i) { const nm = sf[i].name; overNames.delete(nm); fileMeta.delete(nm); sf.splice(i, 1); const d = new DataTransfer(); sf.forEach(f => d.items.add(f)); fi.files = d.files; rr(); } +function setBusy(b) { + busy = b; + fi.disabled = b; // «Выбрать файлы» — блокируется на время загрузки/обработки и в замороженной сессии + document.body.classList.toggle('busy', b); // скрывает кнопки «✕» в таблице +} + +function rm(i) { if (busy) return; const nm = sf[i].name; overNames.delete(nm); fileMeta.delete(nm); sf.splice(i, 1); const d = new DataTransfer(); sf.forEach(f => d.items.add(f)); fi.files = d.files; rr(); } window.addEventListener('load', () => { sf = []; fileMeta = new Map(); fi.value = ''; rr(); /* прогрев upstream-соединения */ fetch('/health').catch(() => {}); }); // ═══════════ Нативная распаковка ZIP (без внешних библиотек) ═══════════ @@ -516,6 +529,7 @@ function addFileWithDedup(file) { } fi.addEventListener('change', async () => { + if (busy) return; // во время загрузки/обработки менять список нельзя const incoming = Array.from(fi.files); const hasZip = incoming.some(f => f.name.toLowerCase().endsWith('.zip')); if (hasZip) { @@ -553,7 +567,11 @@ fi.addEventListener('change', async () => { function ss(idx, h) { const e = document.getElementById('st-' + idx); if (e) e.innerHTML = h; } async function uploadFiles() { + if (busy) return; // уже идёт загрузка/обработка if (sf.length === 0) return; + setBusy(true); + sessionDone = false; + document.getElementById('newSessionBtn').style.display = 'none'; // Сохранить список до очистки const files = sf.slice(); // Только учитываемые файлы идут на загрузку/обфускацию (переборные пропускаем) @@ -574,7 +592,7 @@ async function uploadFiles() { document.getElementById('liveBlock').classList.remove('show'); ub.disabled = true; const total = toSend.length; - if (total === 0) { st.className = 'status error'; st.textContent = 'Нет файлов для обфускации (все превышают лимит).'; ub.disabled = false; return; } + if (total === 0) { st.className = 'status error'; st.textContent = 'Нет файлов для обфускации (все превышают лимит).'; setBusy(false); ub.disabled = false; return; } // Фаза 1: загрузка на ВМ-буфер (PUT напрямую на nginx ВМ, минуя шлюз кластера) const token = crypto.randomUUID(); @@ -619,6 +637,7 @@ async function uploadFiles() { ss(i, ''); st.className = 'status error'; st.textContent = 'Ошибка загрузки на ВМ: ' + err.message; + setBusy(false); ub.disabled = false; return; } @@ -640,6 +659,7 @@ async function uploadFiles() { } catch (err) { st.className = 'status error'; st.textContent = 'Ошибка передачи ссылок: ' + err.message; + setBusy(false); ub.disabled = false; return; } @@ -648,6 +668,7 @@ async function uploadFiles() { if (TEST_UPLOAD_ONLY) { st.className = 'status done'; st.textContent = '✅ Загружено ' + uploadedCount + ' файлов (тест: обработка пропущена). Сессия: ' + currentSid; + setBusy(false); ub.disabled = false; return; } @@ -806,6 +827,11 @@ async function uploadFiles() { } sb.classList.add('show'); db.classList.add('show'); + // Заморозка сессии: результат готов (частичный), список файлов больше не меняем + sessionDone = true; + ub.disabled = true; + fi.disabled = true; + document.getElementById('newSessionBtn').style.display = 'inline-block'; resolve(); }); activeES.addEventListener('complete', function(e) { @@ -826,6 +852,11 @@ async function uploadFiles() { } sb.classList.add('show'); db.classList.add('show'); + // Заморозка сессии: результат готов, список файлов больше не меняем + sessionDone = true; + ub.disabled = true; + fi.disabled = true; + document.getElementById('newSessionBtn').style.display = 'inline-block'; resolve(); }); activeES.onerror = function() { @@ -835,10 +866,11 @@ async function uploadFiles() { }); } catch (err) { finishProcUI(); + setBusy(false); st.className = 'status error'; st.textContent = 'Ошибка: ' + err.message; } - ub.disabled = false; + if (!sessionDone) ub.disabled = false; } function downloadZip() {