feat: документация из Nextcloud, график у сотрудника, автоочистка фото, мониторинг, итог мастера, фильтр по местам; доп. сервисы в docker-compose
- docker-compose.yml: профили docs (Nextcloud), office (ONLYOFFICE Document Server), ai (Ollama + скачивание модели) - автонастройка: адреса Nextcloud, ONLYOFFICE, портала и модели подставляются из .env или по именам сервисов во внутренней сети Docker; значения из интерфейса важнее - локальная модель в своей сети (имя сервиса, частный IP) подключается без ключа API - .env.example, docs/DEPLOY.md (раздел 7а), README
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
"""Автонастройка из окружения и внутренней сети 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
|
||||
Reference in New Issue
Block a user