v0.0.66: блокировка выбора/удаления файлов на время загрузки и обработки, заморозка сессии после результата + кнопка Новая сессия
Deploy drhider / validate (push) Canceled after 0s
Deploy drhider / validate (push) Canceled after 0s
This commit is contained in:
+1
-1
@@ -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():
|
||||
|
||||
@@ -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 @@
|
||||
<div class="footer-bar">
|
||||
<span id="fileCount">0 файлов</span>
|
||||
<button class="btn" id="cancelBtn" style="display:none" onclick="confirmCancel()">⏹ Прервать</button>
|
||||
<button class="btn" id="newSessionBtn" style="display:none" onclick="resetAll()">🔄 Новая сессия</button>
|
||||
<button class="btn btn-primary" id="uploadBtn" disabled onclick="uploadFiles()">🛡️ Обфусцировать</button>
|
||||
</div>
|
||||
<div class="status" id="status"></div>
|
||||
@@ -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, '<span style="color:#ef4444">✗</span>');
|
||||
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() {
|
||||
|
||||
Reference in New Issue
Block a user