From 6aadf66a38ec90cb39f45b11fb089746044cc5ba Mon Sep 17 00:00:00 2001 From: Repinoid Date: Sun, 7 Jun 2026 18:27:13 +0400 Subject: [PATCH] =?UTF-8?q?fix(server):=20X-Api-Key=20=D0=BF=D1=80=D0=BE?= =?UTF-8?q?=D0=B2=D0=B5=D1=80=D0=BA=D0=B0=20=D0=BD=D0=B0=20upload/chat/pin?= =?UTF-8?q?g-llm,=20=D0=BB=D0=B8=D0=BC=D0=B8=D1=82=202000=20=D0=BE=D1=82?= =?UTF-8?q?=D0=B2=D0=B5=D1=82=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/ping.py | 7 +++++-- api/routes.py | 22 ++++++++++++++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/api/ping.py b/api/ping.py index 9bb802e..35d4dbb 100644 --- a/api/ping.py +++ b/api/ping.py @@ -28,8 +28,11 @@ def register(app): @app.route("/api/v1/ping-llm", methods=["GET"]) def ping_llm(): - """Проверка LLM с адаптивным кэшем. - + """Проверка LLM с адаптивным кэшем. # API key check + cfg = load() + required = cfg.get("api", {}).get("key", "") + if required and request.headers.get("X-Api-Key", "") != required: + return jsonify({"ok": False, "error": "unauthorized"}), 401 - Успех → кэш 60с - Ошибка → кэш 7с (LLM мог уже ожить) """ diff --git a/api/routes.py b/api/routes.py index 37de0fe..e0b3459 100644 --- a/api/routes.py +++ b/api/routes.py @@ -23,6 +23,20 @@ from brain.prompts import SYSTEM_PROMPT, DYNAMIC_PROMPT logger = logging.getLogger("elmer.script") +def _check_api_key(): + """Проверяет X-Api-Key. Если в конфиге нет ключа — пропускаем (dev-режим).""" + cfg = load() + required = cfg.get("api", {}).get("key", "") + if not required: + return True # dev-режим, без ключа + provided = request.headers.get("X-Api-Key", "") + return provided == required + + +def _auth_error(): + return jsonify({"error": "unauthorized", "hint": "передайте X-Api-Key"}), 401 + + def _build_diagnosis_prompt(data: dict, car_info: str = "") -> str: """Строит промпт для LLM из распарсенных данных.""" parts = ["## Данные диагностики\n"] @@ -87,11 +101,15 @@ def register(app): @app.route("/api/v1/session/upload", methods=["POST"]) def upload_session(): + if not _check_api_key(): + return _auth_error() data = request.get_json(silent=True) if not data or "responses" not in data: return jsonify({"error": "missing 'responses'"}), 400 responses = data["responses"] + if len(responses) > 2000: + return jsonify({"error": "too many responses (max 2000)"}), 400 # Динамический тест: если есть — добавляем к обычным ответам dynamic_samples = data.get("dynamic_samples") @@ -181,6 +199,8 @@ def register(app): @app.route("/api/v1/chat", methods=["POST"]) def chat(): + if not _check_api_key(): + return _auth_error() """Свободный вопрос к LLM (без ELM).""" data = request.get_json(silent=True) if not data or "question" not in data: @@ -245,6 +265,8 @@ def register(app): mac = data["mac"].strip() responses = data["responses"] + if len(responses) > 2000: + return jsonify({"error": "too many responses (max 2000)"}), 400 if not mac: return jsonify({"error": "empty mac"}), 400