Фаза 2: site/app.py → blueprint'ы (main, health, api), старый drhider.py удалён
Deploy drhider / validate (push) Waiting to run

This commit is contained in:
2026-07-12 08:34:06 +04:00
parent 2cf4e08100
commit 51bc1a79e7
7 changed files with 182 additions and 632 deletions
+37 -74
View File
@@ -1,87 +1,50 @@
"""
DrHider — Managed Flask приложение на платформе Штурвал.
Приложение создаётся фабрикой create_app().
Штурвал запускает Flask самостоятельно (без Dockerfile/gunicorn).
Роуты разнесены по blueprint'ам:
- main_bp: GET / — веб-интерфейс
- health_bp: GET /health — проверка живости
- api_bp: POST /api/drhider — обфускация документов
"""
import os
import io
import sys
import json
import zipfile
from flask import Flask, render_template, request, send_file, jsonify
from flask import Flask
# Добавляем корень в sys.path чтобы import drhider работал
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
# Добавляем корень проекта в sys.path для импорта пакета drhider
_sys_path_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
if _sys_path_root not in sys.path:
sys.path.insert(0, _sys_path_root)
from drhider import obfuscate_files
app = Flask(__name__)
VERSION = "1.0.0"
MAX_BODY = 200 * 1024 * 1024 # 200 MB
# Версия приложения (меняется при изменениях)
VERSION = "2.0.0"
class LLMClient:
"""LLM-клиент для drhider (обнаружение компаний/ФИО)."""
def create_app():
"""Создать и настроить Flask-приложение.
def __init__(self):
import httpx
self._httpx = httpx
Returns:
Экземпляр Flask с зарегистрированными blueprint'ами.
"""
app = Flask(__name__)
def complete(self, prompt: str) -> str:
key = os.environ.get("LLM_KEY") or os.environ.get("LLM_API_KEY", "")
r = self._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"]
# Конфигурация
app.config["VERSION"] = VERSION
app.config["MAX_CONTENT_LENGTH"] = 200 * 1024 * 1024 # 200 MB
# Регистрируем blueprint'ы
from routes import register_routes
register_routes(app)
return app
@app.route("/")
def index():
return render_template("index.html", version=VERSION)
@app.route("/health")
def health():
return jsonify({"ok": True, "version": VERSION})
@app.route("/api/drhider", methods=["POST"])
def api_drhider():
"""Обфускация: multipart/form-data с файлами → ZIP."""
uploaded = request.files.getlist("files")
if not uploaded:
return jsonify({"ok": False, "error": "Нет файлов"}), 400
files = []
for f in uploaded:
if f.filename:
files.append((f.filename, f.read(), f.mimetype or ""))
if not files:
return jsonify({"ok": False, "error": "Нет файлов"}), 400
try:
llm = LLMClient()
zip_data, _csv = obfuscate_files(files, llm_client=llm)
return send_file(
io.BytesIO(zip_data),
mimetype="application/zip",
as_attachment=True,
download_name="drhider_output.zip",
)
except Exception as e:
import traceback
traceback.print_exc()
return jsonify({"ok": False, "error": str(e)}), 500
# Экземпляр приложения — точка входа для Штурвала
app = create_app()
+20
View File
@@ -0,0 +1,20 @@
"""
Регистрация всех blueprint'ов приложения.
Импортируется из site/app.py при создании Flask-приложения.
"""
from .main_bp import main_bp
from .health_bp import health_bp
from .api_bp import api_bp
def register_routes(app):
"""Зарегистрировать все blueprint'ы на Flask-приложении.
Args:
app: Экземпляр Flask-приложения
"""
app.register_blueprint(main_bp)
app.register_blueprint(health_bp)
app.register_blueprint(api_bp)
+73
View File
@@ -0,0 +1,73 @@
"""
Blueprint: API обфускации (POST /api/drhider).
Принимает multipart/form-data с файлами, возвращает ZIP-архив
с обфусцированными документами.
"""
import io
import traceback
from flask import Blueprint, request, send_file, jsonify
from drhider import obfuscate_files, LLMClient
# ═══════════════════════════════════════════════════════════════════════════
# Blueprint: API DrHider
# ═══════════════════════════════════════════════════════════════════════════
api_bp = Blueprint("api", __name__, url_prefix="/api")
# Максимальный размер тела запроса: 200 MB
MAX_BODY = 200 * 1024 * 1024
@api_bp.route("/drhider", methods=["POST"])
def drhider():
"""Обфускация документов.
Принимает multipart/form-data с полем 'files' (один или несколько файлов).
Возвращает ZIP-архив с обфусцированными документами + mapping.csv.
Поддерживаемые форматы:
.docx, .pdf, .txt, .doc (бинарный — без изменений)
.zip (распаковывается, содержимое обфусцируется)
Returns:
200: ZIP-архив (application/zip)
400: {"ok": false, "error": "..."}
500: {"ok": false, "error": "..."}
"""
# ── Получаем файлы из запроса ──
uploaded = request.files.getlist("files")
if not uploaded:
return jsonify({"ok": False, "error": "Нет файлов"}), 400
# Формируем список для obfuscate_files: [(name, bytes, mimetype), ...]
files = []
for f in uploaded:
if f.filename:
files.append((f.filename, f.read(), f.mimetype or ""))
if not files:
return jsonify({"ok": False, "error": "Нет файлов"}), 400
# ── Обфускация ──
try:
# Создаём LLM-клиент для NER-сканирования
llm = LLMClient()
# Вызываем ядро обфускации
zip_data, _csv = obfuscate_files(files, llm_client=llm)
# Отдаём ZIP-архив
return send_file(
io.BytesIO(zip_data),
mimetype="application/zip",
as_attachment=True,
download_name="drhider_output.zip",
)
except Exception as e:
# Логируем полный трейсбек на сервере
traceback.print_exc()
return jsonify({"ok": False, "error": str(e)}), 500
+27
View File
@@ -0,0 +1,27 @@
"""
Blueprint: health-check (GET /health).
Используется платформой Штурвал для проверки живости приложения.
"""
from flask import Blueprint, jsonify, current_app
# ═══════════════════════════════════════════════════════════════════════════
# Blueprint: health check
# ═══════════════════════════════════════════════════════════════════════════
health_bp = Blueprint("health", __name__)
@health_bp.route("/health")
def health():
"""Эндпоинт проверки живости.
Возвращает JSON с версией приложения.
Используется платформой для readiness/liveness probes.
Returns:
{"ok": true, "version": "X.Y.Z"}
"""
version = current_app.config.get("VERSION", "0.0.0")
return jsonify({"ok": True, "version": version})
+23
View File
@@ -0,0 +1,23 @@
"""
Blueprint: главная страница (GET /).
Отдаёт HTML-интерфейс DrHider.
"""
from flask import Blueprint, render_template, current_app
# ═══════════════════════════════════════════════════════════════════════════
# Blueprint: главная страница
# ═══════════════════════════════════════════════════════════════════════════
main_bp = Blueprint("main", __name__)
@main_bp.route("/")
def index():
"""Главная страница — веб-интерфейс DrHider.
Передаёт в шаблон текущую версию приложения.
"""
version = current_app.config.get("VERSION", "0.0.0")
return render_template("index.html", version=version)