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
+1 -10
View File
@@ -52,17 +52,8 @@ def create_app():
app = create_app()
# Штурвал запускает: cd site && python app.py
# Внутри — gunicorn с gevent для долгих LLM-запросов
if __name__ == "__main__":
import subprocess, sys
subprocess.run([
sys.executable, "-m", "gunicorn", "app:app",
"--bind", "0.0.0.0:5000",
"--worker-class", "gevent",
"--workers", "1",
"--worker-connections", "1000",
"--timeout", "300",
])
app.run(host="0.0.0.0", port=5000)
+58 -23
View File
@@ -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>