diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index 25cb3c6..0a35cbb 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -178,6 +178,108 @@ api_bp.drhider() │ → [(filename, bytes, mimetype), ...] ▼ obfuscate_files(files, llm_client=LLMClient()) + │ Scanner → Extractor → LLM NER → Obfuscator → Replacer → Builder + ▼ +bytes (ZIP-архив с обезличенными документами + mapping.csv) +``` + +--- + +## Требования к Flask-приложению для Штурвала + +### 1. `/health` — liveness probe + +```python +@health_bp.route("/health") +def health(): + return jsonify({"ok": True, "version": "0.0.26"}) +``` + +**Обязательно.** Штурвал дёргает `/health` для readiness/liveness checks. Без этого под не поднимется. Ответ должен быть `200 OK`. + +### 2. `no_cache` на все ответы + +```python +@app.after_request +def no_cache(response): + response.headers["Cache-Control"] = "no-cache, no-store, must-revalidate" + response.headers["Pragma"] = "no-cache" + response.headers["Expires"] = "0" + return response +``` + +**Критично для SPA-подобного UI.** Без этого nginx/браузер кэшируют страницу → F5 показывает старую версию, кнопки не работают, версия не обновляется. + +### 3. `X-Accel-Buffering: no` для SSE + +```python +return Response( + stream_with_context(generate()), + content_type="text/event-stream", + headers={"Cache-Control": "no-cache", "X-Accel-Buffering": "no"} +) +``` + +**Критично для SSE-прогресса.** Без этого nginx буферизует поток → прогресс не отображается в реальном времени, все события приходят одним блоком в конце. + +### 4. `MAX_CONTENT_LENGTH` + +```python +app.config["MAX_CONTENT_LENGTH"] = 200 * 1024 * 1024 # 200 MB +``` + +Без этого Flask использует дефолтный лимит (~1MB) и отвергает docx/pdf. + +### 5. `GeneratorExit` в SSE-генераторе + +```python +try: + yield f"event: start\n..." +except GeneratorExit: + return # клиент отключился — не продолжаем обработку +``` + +**Критично.** При закрытии вкладки браузер рвёт SSE-соединение. Без `GeneratorExit` генератор продолжает слать запросы в LLM, тратя токены впустую. + +### 6. `stream_with_context` + +```python +from flask import stream_with_context + +return Response(stream_with_context(generate()), ...) +``` + +**Обязательно для SSE.** Без этого Flask держит приложение заблокированным на время SSE-соединения, другие запросы не обрабатываются. + +### 7. `sys.path` для импорта пакета drhider + +```python +_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) +``` + +Штурвал запускает из `site/`, пакет `drhider/` на уровень выше. Без этого импорт не работает. + +### 8. In-memory session storage + +Сессии хранятся в `site/session.py` как `Dict[str, dict]`. **Не персистентны** — теряются при рестарте пода. Очистка по таймауту (2 часа). + +### 9. LLM-клиент через переменные окружения + +```python +LLM_KEY # API ключ +LLM_URL # эндпоинт (по умолчанию https://api.aillm.ru/v1/chat/completions) +LLM_MODEL # модель (по умолчанию gpt-oss-120b) +``` + +### 10. BOM в CSV + +```python +buf.write('\ufeff'.encode('utf-8') + csv_str.encode('utf-8')) +``` + +BOM (`\ufeff`) нужен для корректного открытия CSV в Excel (чтобы русские буквы не ломались). │ ▼ TwoPassObfuscator.obfuscate(files)