diff --git a/.env.example b/.env.example index ece3ac8..51b26eb 100644 --- a/.env.example +++ b/.env.example @@ -36,8 +36,13 @@ CLOUDFLARE_TUNNEL_URL=http://app:3003 # туннеля без VPN (сек). Если VPN-провайдер не отвечает — сайт всё равно поднимется. WG_HANDSHAKE_TIMEOUT=60 -# ИИ-улучшение фото (Real-ESRGAN), пусто = контейнер photo-ai -PHOTO_AI_URL= +# === ИИ-улучшение фото (контейнер photo-ai, Real-ESRGAN) === +# По умолчанию фото-ИИ включено: сервис photo-ai поднимается вместе со стеком +# и работает на CPU. Оставьте значение пустым, чтобы выключить сервис — +# тогда кнопка «🤖 ИИ» скрыта, а /api/entries/:id/photo/enhance-ai отвечает 503. +# Ручные проверки сервиса идут по loopback-порту 127.0.0.1:8081 (наружу не публикуется): +# curl http://127.0.0.1:8081/health +PHOTO_AI_URL=http://photo-ai:8080 PHOTO_AI_MAX_PIXELS=4000000 # Сколько раз воркер повторит задание, если photo-ai недоступен (503/обрыв сети), # не увеличивая attempts; после исчерпания — одна честная ошибка в задании. diff --git a/README.md b/README.md index 11f20b1..033e46a 100644 --- a/README.md +++ b/README.md @@ -119,6 +119,32 @@ REDIS_PASSWORD=случайная-длинная-строка Имя узла Tailscale задаётся в `docker-compose.yml` (`tailscale.hostname`, по умолчанию `whatido`). +## ИИ-улучшение фото (photo-ai) + +Сервис `photo-ai` (Real-ESRGAN) поднимается вместе со стеком и **включён по умолчанию**: `PHOTO_AI_URL` +в `docker-compose.yml` равен `http://photo-ai:8080`, кнопка «🤖 ИИ» активна, а задания обрабатывает +фоновый воркер. Работает на CPU, GPU не требуется. + +| Переменная | По умолчанию | Назначение | +|---|---|---| +| `PHOTO_AI_URL` | `http://photo-ai:8080` | Адрес сервиса. **Пустое значение = сервис выключен**: кнопка «🤖 ИИ» скрыта, `POST /api/entries/:id/photo/enhance-ai` отвечает `503`, приложение при этом полностью работоспособно | +| `PHOTO_AI_MAX_PIXELS` | `4000000` | Максимум пикселей входного изображения, вход большего размера уменьшается | +| `PHOTO_AI_SOFT_MAX_RETRIES` | `60` | Сколько раз задание ждёт недоступный сервис, не увеличивая счётчик попыток; после исчерпания — честная ошибка | +| `PHOTO_AI_SOFT_BACKOFF_MS` | `10000` | Первая пауза перед мягким повтором | +| `PHOTO_AI_SOFT_BACKOFF_MAX_MS` | `300000` | Потолок паузы (задержка растёт вдвое) | + +Порт `8081` на `127.0.0.1` — только loopback хоста, наружу ничего не публикуется (хостовый `8080` занят +`text-corrector`). Ручные проверки сервиса: + +```bash +curl http://127.0.0.1:8081/health +curl -F "image=@photo.jpg" -F "scale=2" http://127.0.0.1:8081/enhance -o out.jpg +``` + +Состояние сервиса и воркера — в `GET /api/photo-jobs/status` (admin): `service` отдаёт health фото-сервиса +(`configured`, `reachable`, `latency_ms`, `error`), `ai_configured` — задан ли `PHOTO_AI_URL`, +`worker` — состояние очереди и конфигурация воркера. + ## Публичный доступ через Tailscale Стек не требует внешнего IP и проброса портов: контейнер `tailscale` запускается с `network_mode: host`, входит в вашу tailnet-сеть и через **Serve** открывает приложение внутри tailnet, а через **Funnel** — в публичном интернете. diff --git a/docker-compose.yml b/docker-compose.yml index b6d1bea..706866d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -191,6 +191,8 @@ services: # ИИ-улучшение фотографий (Real-ESRGAN x2, CPU). # Поднимается вместе со стеком; если не нужен — PHOTO_AI_URL пустой в .env. + # Порт 8081 пробрасывается только на loopback хоста — наружу ничего не публикуется, + # хостовый 8080 уже занят text-corrector. Ручные проверки: curl http://127.0.0.1:8081/health photo-ai: build: ./photo-ai container_name: photo-ai @@ -201,6 +203,8 @@ services: TZ: Europe/Moscow volumes: - photo-ai-models:/models + ports: + - "127.0.0.1:8081:8080" volumes: