frontend: пофайловая загрузка с прогрессом, убран gunicorn
Deploy drhider / validate (push) Waiting to run
Deploy drhider / validate (push) Waiting to run
This commit is contained in:
+58
-23
@@ -170,39 +170,74 @@ fileInput.addEventListener('change', () => {
|
||||
renderFiles();
|
||||
});
|
||||
|
||||
function uploadFiles() {
|
||||
async function uploadFiles() {
|
||||
if (selectedFiles.length === 0) return;
|
||||
status.className = 'status info';
|
||||
status.textContent = 'Обфускация...';
|
||||
uploadBtn.disabled = true;
|
||||
|
||||
const formData = new FormData();
|
||||
selectedFiles.forEach(f => formData.append('files', f));
|
||||
const total = selectedFiles.length;
|
||||
let ok = 0;
|
||||
let fail = 0;
|
||||
let results = [];
|
||||
|
||||
fetch('/api/drhider', { method: 'POST', body: formData })
|
||||
.then(res => {
|
||||
if (!res.ok) return res.json().then(e => { throw new Error(e.error || 'Ошибка сервера'); });
|
||||
const disp = res.headers.get('Content-Disposition') || '';
|
||||
const nameMatch = disp.match(/filename="?(.+?)"?$/);
|
||||
const filename = nameMatch ? nameMatch[1] : 'drhider_output.zip';
|
||||
return res.blob().then(blob => ({ blob, filename }));
|
||||
})
|
||||
.then(({ blob, filename }) => {
|
||||
status.className = 'status success';
|
||||
status.textContent = 'Готово! Скачивание началось.';
|
||||
for (let i = 0; i < total; i++) {
|
||||
const f = selectedFiles[i];
|
||||
const n = i + 1;
|
||||
|
||||
// Показываем прогресс
|
||||
status.className = 'status info';
|
||||
status.textContent = 'Файл ' + n + '/' + total + ': ' + f.name + ' ...';
|
||||
|
||||
const t0 = performance.now();
|
||||
const formData = new FormData();
|
||||
formData.append('files', f);
|
||||
|
||||
try {
|
||||
const res = await fetch('/api/drhider', { method: 'POST', body: formData });
|
||||
|
||||
if (!res.ok) {
|
||||
const errData = await res.json().catch(() => ({}));
|
||||
throw new Error(errData.error || 'Ошибка сервера');
|
||||
}
|
||||
|
||||
const elapsed = ((performance.now() - t0) / 1000).toFixed(1);
|
||||
const blob = await res.blob();
|
||||
|
||||
// Скачиваем файл
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = filename;
|
||||
a.download = f.name.replace(/\.[^.]+$/, '') + '_obfuscated.zip';
|
||||
a.click();
|
||||
URL.revokeObjectURL(url);
|
||||
uploadBtn.disabled = false;
|
||||
})
|
||||
.catch(err => {
|
||||
|
||||
ok++;
|
||||
status.className = 'status success';
|
||||
status.textContent = 'Файл ' + n + '/' + total + ': ' + f.name + ' — ' + elapsed + ' сек ✓';
|
||||
results.push({ name: f.name, ok: true, time: elapsed });
|
||||
|
||||
} catch (err) {
|
||||
fail++;
|
||||
status.className = 'status error';
|
||||
status.textContent = 'Ошибка: ' + err.message;
|
||||
uploadBtn.disabled = false;
|
||||
});
|
||||
status.textContent = 'Файл ' + n + '/' + total + ': ' + f.name + ' — ошибка: ' + err.message;
|
||||
results.push({ name: f.name, ok: false, error: err.message });
|
||||
}
|
||||
|
||||
// Пауза между файлами чтобы не заддосить LLM
|
||||
if (i < total - 1) {
|
||||
await new Promise(r => setTimeout(r, 500));
|
||||
}
|
||||
}
|
||||
|
||||
// Итог
|
||||
if (fail === 0) {
|
||||
status.className = 'status success';
|
||||
status.textContent = 'Готово! ' + ok + '/' + total + ' файлов обработано.';
|
||||
} else {
|
||||
status.className = 'status error';
|
||||
status.textContent = 'Готово: ' + ok + ' OK, ' + fail + ' ошибок из ' + total;
|
||||
}
|
||||
|
||||
uploadBtn.disabled = false;
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
|
||||
Reference in New Issue
Block a user