""" DrHider — Managed Flask приложение на платформе Штурвал. Приложение создаётся фабрикой create_app(). Штурвал запускает Flask самостоятельно (без Dockerfile/gunicorn). Роуты разнесены по blueprint'ам: - main_bp: GET / — веб-интерфейс - health_bp: GET /health — проверка живости - api_bp: POST /api/drhider — обфускация документов """ import os import sys import logging from flask import Flask # Добавляем корень проекта в 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) # Версия приложения (меняется при изменениях) VERSION = "0.0.74" def setup_logging(): """Настроить логирование. Уровень из env LOG_LEVEL (DEBUG/INFO/WARNING). Логи пишутся в stderr (для локального запуска) И в файл /tmp/drhider.log (для кубера, где stdout/stderr контейнера может не попадать в kubectl logs). Путь файла можно переопределить env LOG_FILE. """ level_name = os.environ.get("LOG_LEVEL", "INFO").upper() level = getattr(logging, level_name, logging.INFO) log_file = os.environ.get("LOG_FILE", "/tmp/drhider.log") fmt = logging.Formatter("%(asctime)s %(levelname)s [%(name)s] %(message)s", "%Y-%m-%d %H:%M:%S") root = logging.getLogger() root.setLevel(level) root.handlers.clear() sh = logging.StreamHandler(sys.stderr) sh.setLevel(level) sh.setFormatter(fmt) root.addHandler(sh) try: fh = logging.FileHandler(log_file, encoding="utf-8") fh.setLevel(level) fh.setFormatter(fmt) root.addHandler(fh) except Exception as e: # файл не открылся — не падаем sys.stderr.write(f"[app] cannot open log file {log_file}: {e}\n") for name in ("drhider", "app", "routes", "session", __name__): logging.getLogger(name).setLevel(level) log = logging.getLogger("app") log.info("Logging configured, level=%s, version=%s, file=%s", level_name, VERSION, log_file) def create_app(): """Создать и настроить Flask-приложение. Штурвал запускает из директории site/: from app import app Поэтому routes/ доступен как обычный подпакет. Returns: Экземпляр Flask с зарегистрированными blueprint'ами. """ setup_logging() app = Flask(__name__) # Конфигурация app.config["VERSION"] = VERSION app.config["MAX_CONTENT_LENGTH"] = 50 * 1024 * 1024 # 50 MB (лимит на один файл) # Регистрируем blueprint'ы from routes import register_routes register_routes(app) # Запрещаем кэширование — F5 всегда грузит свежую страницу @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 return app # Экземпляр приложения — точка входа для Штурвала app = create_app() # Штурвал запускает: cd site && python app.py if __name__ == "__main__": from waitress import serve serve(app, host="0.0.0.0", port=5000, threads=8)