fix: drhider standalone server port 8767, inline SVG, DB try/except
Deploy contracts-flask / validate (push) Successful in 0s

This commit is contained in:
2026-06-29 18:39:35 +04:00
parent 78f0e4d269
commit de66eb9b1e
4 changed files with 193 additions and 21 deletions
+35 -18
View File
@@ -18,25 +18,41 @@ if os.path.exists(_env_path):
os.environ[_k] = _v
# ── DB auto-seed ──────────────────────────────────────────────────────────
from db import prompts as db_prompts
from db.connection import DB_CONFIG, execute
db_prompts.seed_defaults()
# Schema migration: classify_raw column (idempotent)
execute("ALTER TABLE documents ADD COLUMN IF NOT EXISTS classify_raw text")
execute("ALTER TABLE documents ADD COLUMN IF NOT EXISTS classify_input text")
execute("ALTER TABLE documents ADD COLUMN IF NOT EXISTS zip_source text")
execute("ALTER TABLE documents ADD COLUMN IF NOT EXISTS content_hash text")
try:
from db import prompts as db_prompts
from db.connection import DB_CONFIG, execute
db_prompts.seed_defaults()
# Schema migration: classify_raw column (idempotent)
execute("ALTER TABLE documents ADD COLUMN IF NOT EXISTS classify_raw text")
execute("ALTER TABLE documents ADD COLUMN IF NOT EXISTS classify_input text")
execute("ALTER TABLE documents ADD COLUMN IF NOT EXISTS zip_source text")
execute("ALTER TABLE documents ADD COLUMN IF NOT EXISTS content_hash text")
# ── DB modules ────────────────────────────────────────────────────────────
from db import supplements as db_supplements
from db import documents as db_documents
from db import spec_current as db_spec_current
# ── DB modules ────────────────────────────────────────────────────────────
from db import supplements as db_supplements
from db import documents as db_documents
from db import spec_current as db_spec_current
DB_OK = True
except Exception as _db_err:
import sys as _sys
print(f"WARNING: DB init failed ({_db_err}), running without DB", file=_sys.stderr)
DB_CONFIG = {"dbname": "NONE"}
DB_OK = False
# Stubs for DB-dependent handlers
db_prompts = None
db_supplements = None
db_documents = None
db_spec_current = None
# ── Services ──────────────────────────────────────────────────────────────
from services.upload import handle_upload
from services.unzip import handle_unzip
from services.process import run_pipeline
from llm_prompt import build_prompt
if DB_OK:
from services.upload import handle_upload
from services.unzip import handle_unzip
from services.process import run_pipeline
from llm_prompt import build_prompt
else:
handle_upload = handle_unzip = run_pipeline = None
build_prompt = None
class ThreadingHTTPServer(ThreadingMixIn, HTTPServer):
@@ -56,7 +72,7 @@ class Handler(BaseHTTPRequestHandler):
if parsed.path == "/process-v2":
self._handle_process_v2(parsed)
elif parsed.path == "/health":
self._json({"ok": True, "db": DB_CONFIG["dbname"]})
self._json({"ok": True, "db": DB_CONFIG.get("dbname", "NONE")})
elif parsed.path == "/app.js" or parsed.path.startswith("/static/"):
self._handle_app_js()
elif parsed.path == "/api/supplements":
@@ -590,7 +606,8 @@ if __name__ == "__main__":
TCPServer.allow_reuse_address = True
port = int(os.environ.get("PORT", "8766"))
server = ThreadingHTTPServer(("0.0.0.0", port), Handler)
print(f"Contracts VM server on :{port}, db={DB_CONFIG['dbname']}")
db_name = DB_CONFIG.get("dbname", "NONE") if DB_CONFIG else "NONE"
print(f"Contracts VM server on :{port}, db={db_name}")
try:
server.serve_forever()
except KeyboardInterrupt:
+13
View File
@@ -15,6 +15,19 @@ server {
proxy_read_timeout 10s;
}
location /api/drhider {
proxy_pass http://127.0.0.1:8767;
proxy_read_timeout 600s;
proxy_buffering off;
client_max_body_size 50m;
add_header Access-Control-Allow-Origin * always;
}
location = /drhider {
alias /var/www/docs/drhider.html;
default_type text/html;
}
location /api/ {
proxy_pass http://127.0.0.1:8766;
proxy_read_timeout 30s;
+142
View File
@@ -0,0 +1,142 @@
#!/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, io as io_mod
DRHIDER_SERVER_VERSION = "1.0"
# ── Загрузка .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))
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()