frontend: пофайловая загрузка с прогрессом, убран gunicorn
Deploy drhider / validate (push) Waiting to run

This commit is contained in:
2026-07-12 10:33:09 +04:00
parent 2731bc7f09
commit 7fc41aabce
3 changed files with 59 additions and 35 deletions
-2
View File
@@ -1,6 +1,4 @@
flask flask
gunicorn
gevent
python-docx python-docx
pdfplumber pdfplumber
httpx httpx
+1 -10
View File
@@ -52,17 +52,8 @@ def create_app():
app = create_app() app = create_app()
# Штурвал запускает: cd site && python app.py # Штурвал запускает: cd site && python app.py
# Внутри — gunicorn с gevent для долгих LLM-запросов
if __name__ == "__main__": if __name__ == "__main__":
import subprocess, sys app.run(host="0.0.0.0", port=5000)
subprocess.run([
sys.executable, "-m", "gunicorn", "app:app",
"--bind", "0.0.0.0:5000",
"--worker-class", "gevent",
"--workers", "1",
"--worker-connections", "1000",
"--timeout", "300",
])
+58 -23
View File
@@ -170,39 +170,74 @@ fileInput.addEventListener('change', () => {
renderFiles(); renderFiles();
}); });
function uploadFiles() { async function uploadFiles() {
if (selectedFiles.length === 0) return; if (selectedFiles.length === 0) return;
status.className = 'status info';
status.textContent = 'Обфускация...';
uploadBtn.disabled = true; uploadBtn.disabled = true;
const formData = new FormData(); const total = selectedFiles.length;
selectedFiles.forEach(f => formData.append('files', f)); let ok = 0;
let fail = 0;
let results = [];
fetch('/api/drhider', { method: 'POST', body: formData }) for (let i = 0; i < total; i++) {
.then(res => { const f = selectedFiles[i];
if (!res.ok) return res.json().then(e => { throw new Error(e.error || 'Ошибка сервера'); }); const n = i + 1;
const disp = res.headers.get('Content-Disposition') || '';
const nameMatch = disp.match(/filename="?(.+?)"?$/); // Показываем прогресс
const filename = nameMatch ? nameMatch[1] : 'drhider_output.zip'; status.className = 'status info';
return res.blob().then(blob => ({ blob, filename })); status.textContent = 'Файл ' + n + '/' + total + ': ' + f.name + ' ...';
})
.then(({ blob, filename }) => { const t0 = performance.now();
status.className = 'status success'; const formData = new FormData();
status.textContent = 'Готово! Скачивание началось.'; 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 url = URL.createObjectURL(blob);
const a = document.createElement('a'); const a = document.createElement('a');
a.href = url; a.href = url;
a.download = filename; a.download = f.name.replace(/\.[^.]+$/, '') + '_obfuscated.zip';
a.click(); a.click();
URL.revokeObjectURL(url); URL.revokeObjectURL(url);
uploadBtn.disabled = false;
}) ok++;
.catch(err => { 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.className = 'status error';
status.textContent = 'Ошибка: ' + err.message; status.textContent = 'Файл ' + n + '/' + total + ': ' + f.name + ' — ошибка: ' + err.message;
uploadBtn.disabled = false; 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> </script>
</body> </body>