frontend: пофайловая загрузка с прогрессом, убран gunicorn
Deploy drhider / validate (push) Waiting to run
Deploy drhider / validate (push) Waiting to run
This commit is contained in:
@@ -1,6 +1,4 @@
|
|||||||
flask
|
flask
|
||||||
gunicorn
|
|
||||||
gevent
|
|
||||||
python-docx
|
python-docx
|
||||||
pdfplumber
|
pdfplumber
|
||||||
httpx
|
httpx
|
||||||
|
|||||||
+1
-10
@@ -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
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user