diff --git a/site/app.py b/site/app.py index c458a94..f9cfb4d 100644 --- a/site/app.py +++ b/site/app.py @@ -1,7 +1,7 @@ """contracts-flask v2.0 — полный перенос с ВМ на Flask. Больше никаких прокси на contracts.kube5s.ru — всё локально. """ -import sys, os, hmac +import sys, os # site/ в sys.path — импортируем модули напрямую, без префиксов sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) # корень репо — для переиспользуемого модуля upload/ (как в drhider) @@ -9,8 +9,8 @@ _REPO_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) if _REPO_ROOT not in sys.path: sys.path.insert(0, _REPO_ROOT) -from flask import Flask, jsonify, request -from config import VERSION, MAX_CONTENT_LENGTH, API_KEY +from flask import Flask +from config import VERSION, MAX_CONTENT_LENGTH from routes import register_routes @@ -18,29 +18,10 @@ def create_app(): app = Flask(__name__) app.config["VERSION"] = VERSION app.config["MAX_CONTENT_LENGTH"] = MAX_CONTENT_LENGTH - app.config["API_KEY"] = API_KEY _init_db() register_routes(app) - @app.before_request - def authorize_request(): - if request.endpoint == "health.health": - return None - key = app.config.get("API_KEY", "") - if not key: - return jsonify(ok=False, error="application API_KEY is not configured"), 503 - if "X-API-Key" in request.headers: - supplied = request.headers.get("X-API-Key", "") - else: - authorization = request.authorization - supplied = authorization.password if authorization and authorization.type.lower() == "basic" else "" - if not hmac.compare_digest((supplied or "").encode("utf-8"), key.encode("utf-8")): - return jsonify(ok=False, error="unauthorized"), 401, { - "WWW-Authenticate": 'Basic realm="Contracts", charset="UTF-8"', - } - return None - @app.after_request def no_cache(response): response.headers["Cache-Control"] = "no-cache, no-store, must-revalidate" diff --git a/site/config.py b/site/config.py index 33bf5e1..47851b5 100644 --- a/site/config.py +++ b/site/config.py @@ -1,7 +1,7 @@ """Конфигурация приложения — все настройки в одном месте.""" import os -VERSION = "3.0.1" +VERSION = "3.0.2" def _load_env(): diff --git a/site/routes/pipeline_bp.py b/site/routes/pipeline_bp.py index 415d514..1f94612 100644 --- a/site/routes/pipeline_bp.py +++ b/site/routes/pipeline_bp.py @@ -1,6 +1,7 @@ """Pipeline blueprint — SSE-сравнение + classify.""" -import hashlib, json, re, os, threading, time +import hashlib, hmac, json, re, os, threading, time from flask import Blueprint, request, jsonify, Response, stream_with_context +from config import API_KEY from services.process import run_pipeline from services import extractor as stateless_extractor from services.engine import analyze_batch @@ -143,6 +144,11 @@ def engine_analyze_route(): supplies parsed elements only; extraction passports and dossier decisions are always produced by the in-process Engine and persisted by its adapters. """ + if not API_KEY: + return jsonify(ok=False, error="engine API is not configured"), 503 + supplied_key = request.headers.get("X-API-Key", "") + if not hmac.compare_digest(supplied_key.encode("utf-8"), API_KEY.encode("utf-8")): + return jsonify(ok=False, error="unauthorized"), 401 if _engine_rate_limited(): return jsonify(ok=False, error="rate limit exceeded"), 429, {"Retry-After": "60"}