diff --git a/deploy/convert_server.py b/deploy/convert_server.py index afff401..2658625 100755 --- a/deploy/convert_server.py +++ b/deploy/convert_server.py @@ -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): diff --git a/site/templates/drhider.html b/site/templates/drhider.html index 943c6c4..5cf83f6 100644 --- a/site/templates/drhider.html +++ b/site/templates/drhider.html @@ -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; }