- docker-compose.yml: профили docs (Nextcloud), office (ONLYOFFICE Document Server), ai (Ollama + скачивание модели) - автонастройка: адреса Nextcloud, ONLYOFFICE, портала и модели подставляются из .env или по именам сервисов во внутренней сети Docker; значения из интерфейса важнее - локальная модель в своей сети (имя сервиса, частный IP) подключается без ключа API - .env.example, docs/DEPLOY.md (раздел 7а), README
72 lines
3.2 KiB
Python
72 lines
3.2 KiB
Python
"""Автонастройка из окружения и внутренней сети Docker.
|
|
|
|
Пока администратор сам не заполнил поле в настройках, значение берётся:
|
|
1) из переменной окружения (docker-compose.yml передаёт их из .env), иначе
|
|
2) из внутренней сети Docker: если рядом запущен сервис с известным именем (nextcloud, onlyoffice, ollama — профили
|
|
docker-compose.yml), подставляется его внутренний адрес.
|
|
Значение, сохранённое в интерфейсе, всегда важнее. Имена сервисов проверяются через DNS Docker с кэшем на минуту."""
|
|
import ipaddress
|
|
import os
|
|
import socket
|
|
import time
|
|
from urllib.parse import urlsplit
|
|
|
|
CACHE_SEC = 60
|
|
_seen: dict[str, tuple[float, bool]] = {}
|
|
|
|
# настройка -> переменная окружения
|
|
ENV = {
|
|
"nc_url": "NC_URL", "nc_user": "NC_USER", "nc_password": "NC_PASSWORD", "nc_root": "NC_ROOT",
|
|
"onlyoffice_url": "ONLYOFFICE_URL", "onlyoffice_jwt": "ONLYOFFICE_JWT", "portal_internal_url": "PORTAL_INTERNAL_URL",
|
|
"assistant_provider": "ASSISTANT_PROVIDER", "assistant_base_url": "ASSISTANT_BASE_URL",
|
|
"assistant_model": "ASSISTANT_MODEL", "assistant_api_key": "ASSISTANT_API_KEY",
|
|
}
|
|
# настройка -> (имя сервиса в сети Docker, адрес, который подставить)
|
|
SERVICES = {
|
|
"nc_url": ("nextcloud", "http://nextcloud"),
|
|
"onlyoffice_url": ("onlyoffice", "http://onlyoffice"),
|
|
"assistant_base_url": ("ollama", "http://ollama:11434/v1"),
|
|
}
|
|
|
|
|
|
def resolves(host: str) -> bool:
|
|
now = time.monotonic()
|
|
hit = _seen.get(host)
|
|
if hit and now - hit[0] < CACHE_SEC:
|
|
return hit[1]
|
|
try:
|
|
socket.getaddrinfo(host, None)
|
|
ok = True
|
|
except OSError:
|
|
ok = False
|
|
_seen[host] = (now, ok)
|
|
return ok
|
|
|
|
|
|
def default(key: str) -> str | None:
|
|
"""Значение по умолчанию из окружения или сети Docker; None — автонастройки для ключа нет."""
|
|
env = ENV.get(key)
|
|
if env and os.getenv(env):
|
|
return os.getenv(env, "").strip()
|
|
svc = SERVICES.get(key)
|
|
if svc and resolves(svc[0]):
|
|
return svc[1]
|
|
if key == "assistant_model" and resolves("ollama"):
|
|
return os.getenv("OLLAMA_MODEL", "qwen2.5:7b")
|
|
if key == "portal_internal_url" and resolves("onlyoffice"):
|
|
return "http://app:8000" # по этому адресу ONLYOFFICE забирает документы у портала
|
|
return None
|
|
|
|
|
|
def is_local(url: str) -> bool:
|
|
"""Адрес модели внутри своей сети (ключ API не нужен): localhost, имя сервиса Docker без точки, частный IP."""
|
|
host = (urlsplit(url or "").hostname or "").lower()
|
|
if not host:
|
|
return False
|
|
if host in ("localhost", "127.0.0.1", "::1") or "." not in host:
|
|
return True
|
|
try:
|
|
return ipaddress.ip_address(host).is_private
|
|
except ValueError:
|
|
return False
|