@startuml geo_balancing_sequence title Получение пути до камеры через Geo (пример: Analytics запрашивает стрим) actor "Сервис Аналитики" as analytics participant "geo-backend\n(handlers.py)" as geo_handler participant "GeoResolver\nClient" as resolver participant "geo-resolver" as geo_resolver database "place_cache\ngroup_cache" as cache participant "balancing.py" as balancing participant "auth.py" as auth participant "auth-server" as auth_server participant "billing.py" as billing database "MongoDB" as mongo participant "Выбранная\nНода" as node == Запрос стрима == analytics -> geo_handler: GET /flv?server=UIN&camera=CAM_ID&access_token=TOKEN note right Параметры запроса: • server: UIN видеосервера • camera: ID камеры • access_token: токен доступа • format: flv/hls/ws-fmp4 end note geo_handler -> geo_handler: Извлечь IP клиента\nиз запроса activate geo_handler == Определение геолокации == geo_handler -> resolver: resolve(client_ip) activate resolver resolver -> geo_resolver: HTTP GET /resolve?ip=CLIENT_IP activate geo_resolver geo_resolver -> geo_resolver: Проверить кэш geo_resolver -> geo_resolver: Запросить у Geo Providers\n(IP-API, MaxMind) geo_resolver --> resolver: IPLocation(\n country_code="RU",\n region_iso="MOW",\n city="Москва",\n coordinates=[55.75, 37.61]\n) deactivate geo_resolver resolver --> geo_handler: IPLocation deactivate resolver == Поиск оптимальной ноды == geo_handler -> cache: Получить группу для локации activate cache cache --> geo_handler: geo_group_id deactivate cache geo_handler -> balancing: select_server(\n partner_id,\n geo_group,\n pattern="streaming"\n) activate balancing balancing -> cache: node_for_pattern(\n partner_id,\n geo_group,\n "streaming"\n) activate cache cache -> cache: Выбор по весам\nи доступности cache --> balancing: selected_node_id deactivate cache alt Нода не найдена balancing -> balancing: select_fallback_pattern() balancing -> cache: Повторный поиск с fallback cache --> balancing: fallback_node_id end balancing --> geo_handler: BalancingResponse(\n node_id,\n node_host\n) deactivate balancing == Авторизация == geo_handler -> auth: authorize_token(\n token,\n rights="view",\n object_type="camera",\n object_id=CAM_ID\n) activate auth auth -> auth_server: POST /authorize activate auth_server auth_server -> mongo: Проверить права\nпользователя activate mongo mongo --> auth_server: user_rights deactivate mongo auth_server --> auth: {\n owner_type: "user",\n owner_id: "USER_ID"\n} deactivate auth_server auth --> geo_handler: token_info deactivate auth == Проверка биллинга == geo_handler -> billing: check_limits(user_id, camera_id) activate billing billing -> mongo: Получить тариф\nи лимиты activate mongo mongo --> billing: tariff_info deactivate mongo billing --> geo_handler: limits_ok deactivate billing == Получение данных о сервере == geo_handler -> mongo: Получить информацию\nо сервере и камере activate mongo mongo --> geo_handler: {\n server: {dc, online, cameras},\n camera: {online, streams}\n} deactivate mongo == Формирование редиректа == geo_handler -> geo_handler: Сформировать URL редиректа:\n• Схема (http/https/wss)\n• Хост выбранной ноды\n• Параметры (server, camera, token)\n• Подпись запроса geo_handler --> analytics: HTTP 302 Redirect\nLocation: https://node-moscow-1.ivideon.com/flv?server=UIN&camera=CAM_ID deactivate geo_handler == Подключение к ноде == analytics -> node: GET /flv?server=UIN&camera=CAM_ID&sig=... activate node node --> analytics: FLV Stream (видеопоток) deactivate node note over analytics, node После редиректа сервис аналитики получает прямое подключение к ноде, географически близкой к нему end note @enduml