""" routes/root.py — корневые эндпоинты (/health, /, /swagger). Blueprint "root" регистрируется в app.py БЕЗ url_prefix. Отвечает за healthcheck (для Nubes), HTML-страницу с информацией о сервисе, и страницу Swagger UI. """ import os from flask import Blueprint, g, render_template from werkzeug.local import LocalProxy import mock_state import config.loader as _cfg state = LocalProxy(lambda: mock_state.get_state(g.stand_id)) SERVICES = LocalProxy(lambda: _cfg.get_services(g.stand_id)) VERSION = _cfg.VERSION # Токен для авторизации в Swagger UI (предзаполняется) _MOCK_AUTH_TOKEN = os.getenv("MOCK_AUTH_TOKEN", "test-token-123") # URL для OpenAPI-спеке (сервер) _POLYGON_ENDPOINT = os.getenv("POLYGON_ENDPOINT", "https://polygon.pythonk8s.dev.nubes.ru") # Blueprint без префикса — роуты /health и / будут на корне домена bp = Blueprint("root", __name__) @bp.route("/health") def health(): """Healthcheck для Nubes. Платформа периодически дёргает этот эндпоинт. Если вернёт не 200 — контейнер будет перезапущен. Возвращаем просто "OK", без JSON. """ return "OK" @bp.route("/") def index(): """Корневая HTML-страница с информацией о сервисе. Показывает: версию, количество загруженных сервисов, количество инстансов, текущую задержку операций, список сервисов с операциями. Использует Jinja2-шаблон templates/index.html и внешний CSS из static/style.css. """ svc_count = len(_cfg.SERVICES) inst_count = len(state.instances) # Список стендов stands_list = [] for sid in _cfg.list_stands(): svc = _cfg.get_services(sid) stands_list.append({ "id": sid, "count": len(svc), }) current_stand = g.stand_id # Список сервисов для таблицы: name, id, кол-во операций svc_list = [] for sid, svc in sorted(_cfg.SERVICES.items(), key=lambda x: x[1].get("name", x[0])): svc_list.append({ "id": sid, "name": svc.get("name", sid), "display_name": svc.get("service_display_name", svc.get("name", "")), "op_count": len(svc.get("operations", [])), "has_cfs": bool(svc.get("cfsParams")), "has_state": bool(svc.get("stateParams")), "has_out": bool(svc.get("stateOut")), }) return render_template( "index.html", version=_cfg.VERSION, svc_count=svc_count, inst_count=inst_count, delay=_cfg.DELAY, svc_list=svc_list, stands_list=stands_list, current_stand=current_stand, endpoint=_POLYGON_ENDPOINT, ) @bp.route("/swagger") def swagger(): """Swagger UI — интерактивная документация API. Загружает Swagger UI с CDN, указывает на /api/v1/svc/openapi.json. Токен авторизации предзаполняется автоматически (MOCK_AUTH_TOKEN). """ return render_template( "swagger.html", version=_cfg.VERSION, openapi_url="/api/v1/svc/openapi.json", auth_token=_MOCK_AUTH_TOKEN, )