From 853af0ff2f4e580dc25ee6196f06ce86be646171 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CNaeel=E2=80=9D?= Date: Mon, 29 Jun 2026 14:39:47 +0400 Subject: [PATCH] =?UTF-8?q?fix:=20DrHider=20via=20VM=20=E2=80=94=20managed?= =?UTF-8?q?=20Flask=20proxies,=20VM=20does=20LLM-NER=20obfuscation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- deploy/convert_server.py | 40 +++++++++++++++++++++++++++++ site/app.py | 55 +++++++++++++++------------------------- 2 files changed, 61 insertions(+), 34 deletions(-) diff --git a/deploy/convert_server.py b/deploy/convert_server.py index c27853c..fb314b6 100755 --- a/deploy/convert_server.py +++ b/deploy/convert_server.py @@ -86,6 +86,8 @@ class Handler(BaseHTTPRequestHandler): self._handle_api_prompts_delete() elif self.path == "/api/sync": self._handle_api_sync() + elif self.path == "/api/drhider": + self._handle_drhider() else: self.send_error(404) @@ -210,6 +212,44 @@ class Handler(BaseHTTPRequestHandler): execute("DELETE FROM contracts WHERE id NOT IN (SELECT DISTINCT contract_id FROM supplements)") self._json({"ok": True, "deleted": deleted}) + # ── POST /api/drhider ──────────────────────────────────────────────── + + def _handle_drhider(self): + """Обфускация: multipart files → LLM-NER → ZIP.""" + from services.drhider import obfuscate_files + from services.llm_client import HttpxLLMClient + from services.upload import parse_multipart + + length = int(self.headers.get("Content-Length", 0)) + raw = self.rfile.read(length) + ctype = self.headers.get("Content-Type", "") + + files = parse_multipart(raw, ctype) + if not files: + self._json({"ok": False, "error": "Нет файлов"}, 400) + return + + file_list = [(f.filename, f.content, f.content_type) for f in files] + + try: + llm = HttpxLLMClient( + os.environ.get("LLM_API_URL", "https://api.aillm.ru/v1/chat/completions"), + os.environ.get("LLM_API_KEY", ""), + os.environ.get("LLM_MODEL", "gpt-oss-120b") + ) + zip_data, _csv = obfuscate_files(file_list, llm_client=llm) + + self.send_response(200) + self.send_header("Content-Type", "application/zip") + self.send_header("Content-Disposition", 'attachment; filename="drhider_output.zip"') + self.send_header("Content-Length", str(len(zip_data))) + self.end_headers() + self.wfile.write(zip_data) + except Exception as e: + import traceback + traceback.print_exc() + self._json({"ok": False, "error": str(e)}, 500) + # ── /api/groups ?batch=X ───────────────────────────────────────────── def _handle_api_groups(self, parsed): diff --git a/site/app.py b/site/app.py index 39296c1..a70478a 100644 --- a/site/app.py +++ b/site/app.py @@ -110,51 +110,38 @@ def drhider(): @app.route("/api/drhider", methods=["POST"]) def api_drhider(): - """Обфускация: файлы → ZIP с обезличенными копиями + mapping.csv.""" - import io - import zipfile + """Обфускация: файлы → VM (LLM-NER) → ZIP.""" + import httpx from flask import send_file uploaded = request.files.getlist("files") if not uploaded: return jsonify({"ok": False, "error": "Нет файлов"}), 400 - files = [] - for f in uploaded: - if not f.filename: - continue - content = f.read() - files.append((f.filename, content, f.content_type or "")) - - if not files: - return jsonify({"ok": False, "error": "Нет файлов"}), 400 - + # Отправляем файлы на VM try: - from services.drhider import obfuscate_files + files_data = [] + for f in uploaded: + if f.filename: + files_data.append(("files", (f.filename, f.read(), f.content_type or ""))) - llm = None - if LLM_KEY: - try: - from services.llm_client import HttpxLLMClient - llm = HttpxLLMClient(LLM_URL, LLM_KEY, LLM_MODEL) - except Exception: - pass - - zip_data, csv_data = obfuscate_files(files, llm_client=llm) - - buf = io.BytesIO() - buf.write(zip_data) - buf.seek(0) - - return send_file( - buf, - mimetype="application/zip", - as_attachment=True, - download_name="drhider_output.zip" - ) + with httpx.Client(timeout=300) as client: + resp = client.post(f"{VM_API}/api/drhider", files=files_data) + resp.raise_for_status() + except httpx.HTTPStatusError as e: + try: + detail = e.response.json().get("error", str(e)) + except Exception: + detail = str(e) + return jsonify({"ok": False, "error": detail}), 500 except Exception as e: return jsonify({"ok": False, "error": str(e)}), 500 + import io + buf = io.BytesIO(resp.content) + buf.seek(0) + return send_file(buf, mimetype="application/zip", as_attachment=True, download_name="drhider_output.zip") + @app.route("/health") def health():