From 53317304338d3b4b2b45f402a944ac4098e65a8e Mon Sep 17 00:00:00 2001 From: Kavalar Date: Thu, 17 Sep 2026 15:22:06 +0300 Subject: [PATCH] =?UTF-8?q?=D0=9F=D0=BE=D0=B4=D0=B4=D0=B5=D1=80=D0=B6?= =?UTF-8?q?=D0=BA=D0=B0=20=D1=85=D0=BE=D1=81=D1=82=D0=BE=D0=B2=20=D1=81=20?= =?UTF-8?q?=D0=BF=D1=83=D1=82=D1=91=D0=BC=20(domain.com/v1/giga)=20=D0=B2?= =?UTF-8?q?=20=D0=BA=D0=BE=D0=BD=D1=84=D0=B8=D0=B3=D1=83=D1=80=D0=B0=D1=86?= =?UTF-8?q?=D0=B8=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - split_host_path(): разделяет host/path на (scheme, hostname, base_path), поддерживает варианты без схемы и с ней - REST_BASE собирается с учётом пути - WS: hostname и base_path разделяются, WS_PATH_TEMPLATE включает base_path - FPG_WS_PORT теперь настраивается через env (было захардкожено 8443) - fpg_connect.py: варианты URI переписаны на WS_HOSTNAME/WS_BASE_PATH --- app.py | 29 +++++++++++++++++++++++++---- fpg_connect.py | 33 +++++++++++++++++++++++++++------ fpg_stream.py | 25 +++++++++++++++++++++---- 3 files changed, 73 insertions(+), 14 deletions(-) diff --git a/app.py b/app.py index 43b2a47..4373d79 100644 --- a/app.py +++ b/app.py @@ -35,16 +35,37 @@ import export # Секреты читаются из переменных окружения (не хранятся в репозитории). # На сервере задаются в /etc/systemd/system/med.service (Environment=). # Хосты стенда можно переопределить через env (FPG_REST_HOST и т.д.). +# ВАЖНО: хост может содержать не только домен, но и путь +# (например "domain.com/v1/giga") — это учитывается при сборке URL. OAUTH_URL = os.environ.get("FPG_OAUTH_URL", "https://oauth.sbermed.ai/token") CLIENT_ID = os.environ.get("FPG_CLIENT_ID", "client_fpg_front_dnr") CLIENT_SECRET = os.environ.get("FPG_CLIENT_SECRET", "") + +def split_host_path(host: str) -> tuple: + """Разделить 'host/path' на (scheme, hostname, base_path). + + Поддерживает варианты: "domain.com", "domain.com/v1/giga", + "https://domain.com/v1/giga". Схема возвращается без "://". + """ + host = host.strip().rstrip("/") + scheme = "" + if "://" in host: + scheme, host = host.split("://", 1) + parts = host.split("/", 1) + hostname = parts[0] + base_path = "/" + parts[1] if len(parts) > 1 else "" + return scheme, hostname, base_path + + REST_HOST = os.environ.get("FPG_REST_HOST", "gigadoc-dnr-sdk.sbermed.ai") -REST_BASE = f"https://{REST_HOST}" +_rest_scheme, _rest_hostname, _rest_base_path = split_host_path(REST_HOST) +REST_BASE = f"{_rest_scheme or 'https'}://{_rest_hostname}{_rest_base_path}" WS_HOST = os.environ.get("FPG_WS_HOST", "gigadoc-dnr-sdk.sbermed.ai") -WS_PORT = 8443 -WS_PATH_TEMPLATE = "/api/v2/ws/predict/{uid}/image" +WS_PORT = int(os.environ.get("FPG_WS_PORT", "8443")) +_ws_scheme, WS_HOSTNAME, WS_BASE_PATH = split_host_path(WS_HOST) +WS_PATH_TEMPLATE = f"{WS_BASE_PATH}/api/v2/ws/predict/{{uid}}/image" WS_ORIGIN = os.environ.get("FPG_WS_ORIGIN", "https://gigadoc-dnr-sdk.sbermed.ai") DEVICE_ID = "web_prod" @@ -154,7 +175,7 @@ async def ws_stream(token: str, uid: str, frame_files: list, fps: float = FPS): """Отправить кадры через WebSocket, вернуть результат.""" import websockets - uri = f"wss://{WS_HOST}:{WS_PORT}{WS_PATH_TEMPLATE.format(uid=uid)}" + uri = f"wss://{WS_HOSTNAME}:{WS_PORT}{WS_PATH_TEMPLATE.format(uid=uid)}" interval = 1.0 / fps try: diff --git a/fpg_connect.py b/fpg_connect.py index 5477f3a..a0565bd 100644 --- a/fpg_connect.py +++ b/fpg_connect.py @@ -27,17 +27,34 @@ from pathlib import Path import requests # ─── Конфигурация ───────────────────────────────────────────── +# ВАЖНО: хост может содержать не только домен, но и путь +# (например "domain.com/v1/giga") — это учитывается при сборке URL. OAUTH_URL = os.environ.get("FPG_OAUTH_URL", "https://oauth.sbermed.ai/token") CLIENT_ID = os.environ.get("FPG_CLIENT_ID", "client_fpg_front_dnr") CLIENT_SECRET = os.environ.get("FPG_CLIENT_SECRET", "") + +def split_host_path(host: str) -> tuple: + """Разделить 'host/path' на (scheme, hostname, base_path).""" + host = host.strip().rstrip("/") + scheme = "" + if "://" in host: + scheme, host = host.split("://", 1) + parts = host.split("/", 1) + hostname = parts[0] + base_path = "/" + parts[1] if len(parts) > 1 else "" + return scheme, hostname, base_path + + REST_HOST = os.environ.get("FPG_REST_HOST", "gigadoc-dnr-sdk.sbermed.ai") -REST_BASE = f"https://{REST_HOST}" +_rest_scheme, _rest_hostname, _rest_base_path = split_host_path(REST_HOST) +REST_BASE = f"{_rest_scheme or 'https'}://{_rest_hostname}{_rest_base_path}" # WebSocket (рабочий хост подтверждён: gigadoc-dnr-sdk.sbermed.ai:8443) WS_HOST = os.environ.get("FPG_WS_HOST", "gigadoc-dnr-sdk.sbermed.ai") -WS_PORT = 8443 -WS_PATH_TEMPLATE = "/api/v2/ws/predict/{uid}/image" +WS_PORT = int(os.environ.get("FPG_WS_PORT", "8443")) +_ws_scheme, WS_HOSTNAME, WS_BASE_PATH = split_host_path(WS_HOST) +WS_PATH_TEMPLATE = f"{WS_BASE_PATH}/api/v2/ws/predict/{{uid}}/image" WS_ORIGIN = os.environ.get("FPG_WS_ORIGIN", "https://gigadoc-dnr-sdk.sbermed.ai") DEVICE_ID = "web_prod" @@ -239,10 +256,14 @@ async def ws_scan(token: str, uid: str, ws_host: str, ws_port: int, image_dir: s logger.debug(f" {img} ({size} bytes)") # ── Пробуем несколько вариантов URI ── + # ws_host может быть с путём (domain.com/v1/giga) — разделяем. + _scheme, _h, _p = split_host_path(ws_host) + _ws_hostname = _h + _ws_path = _p or WS_BASE_PATH uris = [ - f"wss://{ws_host}:{ws_port}{WS_PATH_TEMPLATE.format(uid=uid)}", - f"wss://{REST_HOST}:{ws_port}{WS_PATH_TEMPLATE.format(uid=uid)}", - f"wss://{REST_HOST}:443{WS_PATH_TEMPLATE.format(uid=uid)}", + f"wss://{_ws_hostname}:{ws_port}{_ws_path}/api/v2/ws/predict/{uid}/image", + f"wss://{WS_HOSTNAME}:{ws_port}{WS_PATH_TEMPLATE.format(uid=uid)}", + f"wss://{WS_HOSTNAME}:443{WS_PATH_TEMPLATE.format(uid=uid)}", ] last_error = None diff --git a/fpg_stream.py b/fpg_stream.py index 19f87bc..d12b92b 100644 --- a/fpg_stream.py +++ b/fpg_stream.py @@ -19,16 +19,33 @@ import requests import websockets # ─── Конфигурация ───────────────────────────────────────────── +# ВАЖНО: хост может содержать не только домен, но и путь +# (например "domain.com/v1/giga") — это учитывается при сборке URL. OAUTH_URL = os.environ.get("FPG_OAUTH_URL", "https://oauth.sbermed.ai/token") CLIENT_ID = os.environ.get("FPG_CLIENT_ID", "client_fpg_front_dnr") CLIENT_SECRET = os.environ.get("FPG_CLIENT_SECRET", "") + +def split_host_path(host: str) -> tuple: + """Разделить 'host/path' на (scheme, hostname, base_path).""" + host = host.strip().rstrip("/") + scheme = "" + if "://" in host: + scheme, host = host.split("://", 1) + parts = host.split("/", 1) + hostname = parts[0] + base_path = "/" + parts[1] if len(parts) > 1 else "" + return scheme, hostname, base_path + + REST_HOST = os.environ.get("FPG_REST_HOST", "gigadoc-dnr-sdk.sbermed.ai") -REST_BASE = f"https://{REST_HOST}" +_rest_scheme, _rest_hostname, _rest_base_path = split_host_path(REST_HOST) +REST_BASE = f"{_rest_scheme or 'https'}://{_rest_hostname}{_rest_base_path}" WS_HOST = os.environ.get("FPG_WS_HOST", "gigadoc-dnr-sdk.sbermed.ai") -WS_PORT = 8443 -WS_PATH_TEMPLATE = "/api/v2/ws/predict/{uid}/image" +WS_PORT = int(os.environ.get("FPG_WS_PORT", "8443")) +_ws_scheme, WS_HOSTNAME, WS_BASE_PATH = split_host_path(WS_HOST) +WS_PATH_TEMPLATE = f"{WS_BASE_PATH}/api/v2/ws/predict/{{uid}}/image" WS_ORIGIN = os.environ.get("FPG_WS_ORIGIN", "https://gigadoc-dnr-sdk.sbermed.ai") DEVICE_ID = "web_prod" @@ -122,7 +139,7 @@ async def ws_stream(token: str, uid: str, image_path: str, frames: int, fps: flo logger.info("ЭТАП 3: WebSocket подключение") logger.info("=" * 60) - uri = f"wss://{WS_HOST}:{WS_PORT}{WS_PATH_TEMPLATE.format(uid=uid)}" + uri = f"wss://{WS_HOSTNAME}:{WS_PORT}{WS_PATH_TEMPLATE.format(uid=uid)}" logger.info(f"URL: {uri}") # Читаем изображение (или директорию с кадрами)