diff --git a/Dockerfile b/Dockerfile index df8d8c5..c9e9c41 100644 --- a/Dockerfile +++ b/Dockerfile @@ -29,4 +29,4 @@ EXPOSE 8000 # По умолчанию запускаем uvicorn — принимаем HOST и PORT из переменных окружения # Если переменные не заданы, используются значения по умолчанию -CMD ["sh", "-c", "uvicorn server:app --host ${HOST:-0.0.0.0} --port ${PORT:-8000}"] +CMD ["sh", "-c", "uvicorn server:app --host ${BIND_HOST:-0.0.0.0} --port ${PORT:-8000}"] diff --git a/README_DOCKER.md b/README_DOCKER.md index 002d620..84fb047 100644 --- a/README_DOCKER.md +++ b/README_DOCKER.md @@ -26,3 +26,20 @@ docker-compose up --build Примечания: - Если у вас headless-сервер и вы не используете GUI-возможности OpenCV, рассмотрите замену `opencv-python` на `opencv-python-headless` в `req.txt`. - При проблемах со сборкой на некоторых платформах установите необходимые системные пакеты (в Dockerfile уже перечислены распространённые зависимости). + +Разделение адресов для биндинга и отображения (важно) +- BIND_HOST — адрес, на котором uvicorn внутри контейнера будет слушать соединения. По умолчанию это 0.0.0.0 (в контейнере). Не ставьте сюда внешний IP хоста, если вы запускаете в контейнере. +- PUBLIC_HOST (или ADVERTISED_HOST) — адрес, который будет показан в UI (dashboards, ws-URL). Укажите сюда реальный IP хоста (например, 192.168.0.112) или публичный адрес. + +Пример `.env` для корректной работы в Docker: + +``` +# BIND_HOST указывает, на каком интерфейсе внутри контейнера слушать (оставьте 0.0.0.0) +BIND_HOST=0.0.0.0 +# PORT — хостовый порт (docker-compose подставит его в маппинг) +PORT=8000 +# PUBLIC_HOST — адрес, который будет отображаться в UI (реальный адрес машины) +PUBLIC_HOST=192.168.0.112 +``` + +Если в `.env` у вас был `HOST=192.168.0.112`, то Docker раньше пробрасывал эту переменную в контейнер и uvicorn пытался биндиться на этот адрес — внутри контейнера такого адреса может не быть, и bind завершался ошибкой `cannot assign requested address`. Поэтому важно разделять `BIND_HOST` (bind) и `PUBLIC_HOST` (advertised). diff --git a/docker-compose.yml b/docker-compose.yml index 87d39e7..c7babcc 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -5,7 +5,8 @@ services: env_file: - .env ports: - - "8000:8000" + # Хостовый порт можно переопределить через переменную PORT в .env, по умолчанию 8000 + - "${PORT:-8000}:8000" restart: unless-stopped volumes: - ./static:/app/static diff --git a/exporting b/exporting new file mode 100644 index 0000000..e69de29 diff --git a/naming b/naming new file mode 100644 index 0000000..e69de29 diff --git a/server.py b/server.py index 3030d2c..9034c0f 100644 --- a/server.py +++ b/server.py @@ -38,7 +38,8 @@ load_dotenv() # ========== КОНФИГУРАЦИЯ ========== SERVER_CONFIG = { - "host": os.getenv("host", "0.0.0.0"), # сюда IP смотрящий наружу или 0.0.0.0 для всех интерфейсов + # считываем переменную окружения HOST (или legacy 'host'), чтобы можно было управлять через .env / docker-compose + "host": os.getenv("HOST", os.getenv("host", "0.0.0.0")), # сюда IP смотрящий наружу или 0.0.0.0 для всех интерфейсов "port": 8000, "debug": False, "max_clients_per_room": 50, @@ -97,13 +98,42 @@ def safe_json_serializer(obj: Any) -> Any: def get_server_host(): """Получение IP адреса сервера""" + import socket + + # 0) Если явно задан адрес, который должен отображаться в UI (рекомендуется при запуске в Docker), используем его + public_host = os.getenv("PUBLIC_HOST") or os.getenv("ADVERTISED_HOST") + if public_host and public_host not in ("", "0.0.0.0", "127.0.0.1", "localhost"): + return public_host + + # 1) Если в конфиге явно указан хост (и это не 0.0.0.0 / localhost), используем его + cfg_host = SERVER_CONFIG.get("host") + if cfg_host and cfg_host not in ("0.0.0.0", "127.0.0.1", "localhost", ""): + return cfg_host + + # 2) Попытка определить внешний IP без отправки данных — UDP socket к публичному адресу + try: + s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + # Не устанавливаем реального соединения, просто используем маршрутную информацию + s.connect(("8.8.8.8", 80)) + ip_address = s.getsockname()[0] + s.close() + + if ip_address and not ip_address.startswith("127."): + return ip_address + except Exception: + pass + + # 3) Попытка через hostname (может вернуть 127.0.1.1 на некоторых системах) try: - import socket hostname = socket.gethostname() ip_address = socket.gethostbyname(hostname) - return ip_address - except: - return SERVER_CONFIG["host"] + if ip_address and not ip_address.startswith("127."): + return ip_address + except Exception: + pass + + # 4) Фолбэк — вернуть значение из конфига (возможно 0.0.0.0 или что задано в ENV) + return cfg_host or "0.0.0.0" # ========== КЛАССЫ ДЛЯ УПРАВЛЕНИЯ ========== class RoomManager: diff --git a/writing b/writing new file mode 100644 index 0000000..e69de29