#!/usr/bin/env python3 """DrHider standalone server — NO DB dependency. Port 8767.""" from http.server import HTTPServer, BaseHTTPRequestHandler from socketserver import ThreadingMixIn, TCPServer from urllib.parse import urlparse import json, re, os, sys, cgi DRHIDER_SERVER_VERSION = "1.3" MAX_BODY = 200 * 1024 * 1024 # 200 MB # ── Загрузка .env ──────────────────────────────────────────────────────── _env_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", ".env") if os.path.exists(_env_path): with open(_env_path) as _f: for _line in _f: _line = _line.strip() if _line and not _line.startswith("#") and "=" in _line: _k, _v = _line.split("=", 1) if _k not in os.environ: os.environ[_k] = _v class ThreadingHTTPServer(ThreadingMixIn, HTTPServer): daemon_threads = True class Handler(BaseHTTPRequestHandler): def do_OPTIONS(self): self.send_response(200) self._send_cors() self.send_header("Access-Control-Allow-Methods", "GET, POST, OPTIONS") self.send_header("Access-Control-Allow-Headers", "Content-Type") self.end_headers() def do_GET(self): parsed = urlparse(self.path) if parsed.path == "/health": self._json({"ok": True, "server": "drhider", "version": DRHIDER_SERVER_VERSION}) else: self.send_error(404) def do_POST(self): if self.path == "/api/drhider": self._handle_drhider() else: self.send_error(404) # ── DrHider ────────────────────────────────────────────────────────── def _handle_drhider(self): """Обфускация: multipart files → ZIP.""" from drhider import obfuscate_files length = int(self.headers.get("Content-Length", 0)) if length <= 0 or length > MAX_BODY: self._json({"ok": False, "error": "Файл слишком большой"}, 413) return raw = self.rfile.read(length) ctype = self.headers.get("Content-Type", "") _, params = cgi.parse_header(ctype) boundary = params.get("boundary", "") if not boundary: self._json({"ok": False, "error": "No boundary"}, 400) return parts = raw.split(b"--" + boundary.encode()) files = [] for part in parts: if b"Content-Disposition" not in part: continue try: hdr_end = part.index(b"\r\n\r\n") except ValueError: continue hdrs = part[:hdr_end].decode("latin-1", errors="replace") body = part[hdr_end + 4:] if body.endswith(b"\r\n"): body = body[:-2] m = re.search(r'filename="([^"]*)"', hdrs) if not m: continue name = os.path.basename(m.group(1)) if not name or ".." in name or "/" in name or "\\" in name: continue files.append((name, body, "")) if not files: self._json({"ok": False, "error": "Нет файлов"}, 400) return class _VMLLM: def complete(self, prompt): import httpx key = os.environ.get("LLM_KEY") or os.environ.get("LLM_API_KEY", "") r = httpx.post( os.environ.get("LLM_URL", "https://api.aillm.ru/v1/chat/completions"), json={"model": os.environ.get("LLM_MODEL", "gpt-oss-120b"), "messages": [{"role": "user", "content": prompt}], "max_tokens": 8000, "temperature": 0.1}, headers={"Authorization": f"Bearer {key}", "Content-Type": "application/json"}, timeout=120 ) r.raise_for_status() return r.json()["choices"][0]["message"]["content"] try: zip_data, _csv = obfuscate_files(files, llm_client=_VMLLM()) 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.send_header("Access-Control-Allow-Origin", "*") 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) # ── Helpers ─────────────────────────────────────────────────────────── def _json(self, data, status=200): self.send_response(status) self._send_cors() self.send_header("Content-Type", "application/json; charset=utf-8") self.end_headers() self.wfile.write(json.dumps(data, ensure_ascii=False).encode()) def _send_cors(self): self.send_header("Access-Control-Allow-Origin", "*") def log_message(self, format, *args): pass if __name__ == "__main__": TCPServer.allow_reuse_address = True port = int(os.environ.get("PORT", "8767")) server = ThreadingHTTPServer(("0.0.0.0", port), Handler) print(f"DrHider server on :{port} (NO DB)") try: server.serve_forever() except KeyboardInterrupt: server.shutdown()