fix: XHR+FormData like main service, multipart on VM — v1.3
Deploy contracts-flask / validate (push) Successful in 0s
Deploy contracts-flask / validate (push) Successful in 0s
This commit is contained in:
+24
-12
@@ -215,24 +215,36 @@ class Handler(BaseHTTPRequestHandler):
|
||||
# ── POST /api/drhider ────────────────────────────────────────────────
|
||||
|
||||
def _handle_drhider(self):
|
||||
"""Обфускация: JSON {files: [{name, data: base64}]} → ZIP."""
|
||||
"""Обфускация: multipart files → ZIP."""
|
||||
from services.drhider import obfuscate_files
|
||||
import base64
|
||||
import cgi, io as io_mod
|
||||
|
||||
length = int(self.headers.get("Content-Length", 0))
|
||||
raw = self.rfile.read(length)
|
||||
ctype = self.headers.get("Content-Type", "")
|
||||
|
||||
body = json.loads(raw) if raw else {}
|
||||
raw_files = body.get("files", [])
|
||||
if not raw_files:
|
||||
self._json({"ok": False, "error": "No files"}, 400)
|
||||
return
|
||||
|
||||
# Простой multipart приём (как в upload.py)
|
||||
import cgi, io as io_mod
|
||||
_, params = cgi.parse_header(ctype)
|
||||
fs = cgi.FieldStorage(
|
||||
fp=io_mod.BytesIO(raw),
|
||||
environ={'REQUEST_METHOD': 'POST', 'CONTENT_TYPE': ctype},
|
||||
keep_blank_values=True
|
||||
)
|
||||
items = fs.getlist("files") if hasattr(fs, 'getlist') else ([fs["files"]] if "files" in fs else [])
|
||||
files = []
|
||||
for f in raw_files:
|
||||
name = f.get("name", "unnamed")
|
||||
data = base64.b64decode(f.get("data", ""))
|
||||
files.append((name, data, f.get("type", "")))
|
||||
for item in items:
|
||||
if not getattr(item, 'filename', None):
|
||||
continue
|
||||
name = os.path.basename(item.filename)
|
||||
if not name or ".." in name or "/" in name or "\\" in name:
|
||||
continue
|
||||
data = item.file.read()
|
||||
files.append((name, data, ""))
|
||||
|
||||
if not files:
|
||||
self._json({"ok": False, "error": "Нет файлов"}, 400)
|
||||
return
|
||||
|
||||
class _VMLLM:
|
||||
def complete(self, prompt):
|
||||
|
||||
+27
-36
@@ -118,45 +118,36 @@ function render() {
|
||||
BTN.disabled = !files.length;
|
||||
}
|
||||
|
||||
BTN.onclick = async () => {
|
||||
BTN.onclick = () => {
|
||||
if (!files.length) return;
|
||||
BTN.disabled = true;
|
||||
setStatus('progress', '⏳ Чтение файлов...');
|
||||
try {
|
||||
const payload = [];
|
||||
for (const f of files) {
|
||||
const buf = await f.arrayBuffer();
|
||||
const bytes = new Uint8Array(buf);
|
||||
let b64 = '';
|
||||
for (let i = 0; i < bytes.length; i += 8192) {
|
||||
b64 += String.fromCharCode.apply(null, bytes.slice(i, i + 8192));
|
||||
}
|
||||
b64 = btoa(b64);
|
||||
payload.push({name: f.name, data: b64, type: f.type || ''});
|
||||
}
|
||||
setStatus('progress', '⏳ Обработка... 0с');
|
||||
const startTime = Date.now();
|
||||
const timer = setInterval(() => {
|
||||
const elapsed = Math.round((Date.now() - startTime) / 1000);
|
||||
setStatus('progress', '⏳ Обработка... ' + elapsed + 'с');
|
||||
}, 1000);
|
||||
const r = await fetch('https://contracts.kube5s.ru/api/drhider', {
|
||||
method:'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({files: payload})
|
||||
});
|
||||
setStatus('progress', '⏳ Отправка...');
|
||||
const xhr = new XMLHttpRequest();
|
||||
const fd = new FormData();
|
||||
files.forEach(f => fd.append('files', f, f.name));
|
||||
xhr.open('POST', 'https://contracts.kube5s.ru/api/drhider');
|
||||
xhr.responseType = 'blob';
|
||||
const startTime = Date.now();
|
||||
const timer = setInterval(() => {
|
||||
setStatus('progress', '⏳ Обработка... ' + Math.round((Date.now() - startTime) / 1000) + 'с');
|
||||
}, 1000);
|
||||
xhr.onload = () => {
|
||||
clearInterval(timer);
|
||||
if (!r.ok) throw new Error(await r.text());
|
||||
const blob = await r.blob();
|
||||
const a = document.createElement('a');
|
||||
a.href = URL.createObjectURL(blob);
|
||||
a.download = 'drhider_output.zip';
|
||||
a.click();
|
||||
setStatus('done', '✅ Готово!');
|
||||
} catch(e) {
|
||||
setStatus('error', '❌ ' + (e.message || 'Ошибка'));
|
||||
}
|
||||
BTN.disabled = false;
|
||||
if (xhr.status === 200) {
|
||||
const a = document.createElement('a');
|
||||
a.href = URL.createObjectURL(xhr.response);
|
||||
a.download = 'drhider_output.zip';
|
||||
a.click();
|
||||
setStatus('done', '✅ Готово!');
|
||||
} else {
|
||||
setStatus('error', '❌ Ошибка ' + xhr.status);
|
||||
}
|
||||
BTN.disabled = false;
|
||||
};
|
||||
xhr.onerror = () => { clearInterval(timer); setStatus('error', '❌ Сеть'); BTN.disabled = false; };
|
||||
xhr.ontimeout = () => { clearInterval(timer); setStatus('error', '❌ Таймаут'); BTN.disabled = false; };
|
||||
xhr.timeout = 600000;
|
||||
xhr.send(fd);
|
||||
};
|
||||
|
||||
function setStatus(c,m) { STATUS.className = 'show '+c; STATUS.textContent = m; }
|
||||
|
||||
Reference in New Issue
Block a user