AI Photo Studio — Deployment Runbook
Гайд по подъёму ai-photo-studio (gensvc) на test VPS. Это сторонний продукт (не ERP-микросервис), интегрированный в админку франшизы как iframe-modal на ProductView/EditPage. См. ADR-021.
Архитектура
ai-photostudio-test.nirbi.ru → ai-photostudio-frontend (nginx static, React/Vite)
ai-photostudio-api-test.nirbi.ru → ai-photostudio-api (Go, gensvc :8080)
├── PostgreSQL (общий контейнер erp-postgres, БД gensvc_db)
├── Redis (общий erp-redis, DB index 2)
└── MinIO (общий erp-minio, buckets gensvc-{inputs,outputs,presets})
Iframe из https://erp-test.nirbi.ru/admin/catalog/products/{id} открывается в cross-origin режиме. Token приходит query-параметром ?erp_token=, parent-origin для postMessage — ?parent_origin=.
Pre-requirements (разовая настройка)
1. DNS A-records
В DNS-зоне nirbi.ru добавить:
ai-photostudio-test.nirbi.ru. A 185.152.93.77
ai-photostudio-api-test.nirbi.ru. A 185.152.93.77
Проверка: dig +short ai-photostudio-test.nirbi.ru → должен вернуть IP VPS.
DNS обязателен для certbot
Без A-records Let’s Encrypt не пройдёт http-01 challenge.
2. Клонирование репозиториев на VPS
VPS использует HTTPS-clone с Personal Access Token (как для других репо). Токен живёт в URL других origin’ов — переиспользуется.
ssh root@185.152.93.77
cd /root/erp
# Принять github.com host key (один раз)
ssh-keyscan -H github.com >> ~/.ssh/known_hosts
# Clone (PAT см. в любом другом erp-* репо: git remote -v)
git clone https://<PAT>@github.com/nearbyErp/ai-photo-studio-frontend.git
git clone https://<PAT>@github.com/nearbyErp/ai-photo-studio-backend.git3. Известные баги в Dockerfile коллеги (хотфиксы на VPS)
Пока коллега не сделает PR, на VPS нужны локальные правки:
| Файл | Проблема | Hotfix |
|---|---|---|
ai-photo-studio-backend/Dockerfile | FROM golang:1.22-alpine но go.mod требует Go 1.26 | sed -i 's|golang:1.22-alpine|golang:1.26-alpine|' ai-photo-studio-backend/Dockerfile |
ai-photo-studio-frontend/Dockerfile | FROM node:20-alpine + pnpm latest требует Node 22+ | sed -i 's|node:20-alpine|node:22-alpine|' ai-photo-studio-frontend/Dockerfile |
ai-photo-studio-frontend/Dockerfile | corepack prepare pnpm@latest ставит pnpm 11, который ломается на [ERR_PNPM_IGNORED_BUILDS] для esbuild | sed -i 's|pnpm@latest|pnpm@9.15.0|' ai-photo-studio-frontend/Dockerfile |
ai-photo-studio-frontend/pnpm-workspace.yaml | Содержит только onlyBuiltDependencies без packages — pnpm 9.15 фейлится с packages field missing or empty | Добавить packages:\n - .\n в начало файла |
Передать коллеге
Эти 4 хотфикса — временные. Создать PR в фотостудию с правильными версиями, чтобы убрать sed-патчи на VPS.
4. env-файл
/root/erp/erp-infrastructure/envs/ai-photostudio-api.env — копируется из .example с подстановкой реальных секретов.
cd /root/erp/erp-infrastructure
cp envs/ai-photostudio-api.env.example envs/ai-photostudio-api.env
# Подставить секреты из .env (POSTGRES_PASSWORD, REDIS_PASSWORD, MINIO_*)
# и значение JWT_LOCAL_SECRET = JWT_SECRET из envs/auth-service.env
sed -i \
-e "s|\${POSTGRES_PASSWORD}|<значение из .env>|g" \
-e "s|\${REDIS_PASSWORD}|<значение из .env>|g" \
-e "s|\${MINIO_ROOT_USER}|<значение>|g" \
-e "s|\${MINIO_ROOT_PASSWORD}|<значение>|g" \
-e "s|__SAME_AS_auth-service.env__app.jwt.secret__|<значение JWT_SECRET из auth-service.env>|" \
envs/ai-photostudio-api.env
# AI_API_KEY — реальный ключ Polza от бизнеса
nano envs/ai-photostudio-api.env # → AI_API_KEY=sk-...JWT_LOCAL_SECRET coordination
Тот же
JWT_SECRETнужно передать коллеге фотостудии — gensvc валидирует HS256 токены этим секретом. Безопасный канал передачи (не публикуем).
5. Создание БД (если postgres-volume уже инициализирован)
init-databases.sql срабатывает только при первом старте postgres. Для существующего volume — создать БД руками:
docker exec erp-postgres psql -U erp -d postgres -c 'CREATE DATABASE gensvc_db;'
docker exec erp-postgres psql -U erp -d postgres -c 'GRANT ALL PRIVILEGES ON DATABASE gensvc_db TO erp;'6. TLS-сертификаты (после DNS готов)
cd /root/erp/erp-infrastructure
docker compose run --rm certbot certonly \
--webroot -w /var/www/certbot \
-d ai-photostudio-test.nirbi.ru \
-d ai-photostudio-api-test.nirbi.ru \
--email <ваш email> --agree-tos --no-eff-emailПосле успеха — снять .disabled с nginx-конфига:
mv nginx/conf.d/ai-photostudio.conf.disabled nginx/conf.d/ai-photostudio.conf
docker exec erp-nginx nginx -s reload7. Pre-seed 288 webp пресетов
Без референс-картинок генерация падает («preset reference image not found»). Залить webp-файлы в bucket gensvc-presets:
# Вариант: через seed-presets binary (требует CSV + dir с webp)
docker compose run --rm ai-photostudio-api /app/seed-presets \
--presets-dir=/path/to/presets \
--csv=/path/to/preset-names-ru.csvSource присетов — у бизнеса/маркетинга
288 reference-фото — отдельная разовая работа. Пока её нет, AI-генерация будет валиться даже при работающем API ключе.
Регулярный деплой
Через /deploy-all:
docker compose build --no-cache ai-photostudio-api ai-photostudio-frontend
docker compose up -d --force-recreate ai-photostudio-api ai-photostudio-frontendHealth: оба контейнера healthy через 30 сек.
Verification
Smoke-test изнутри сети (без TLS)
# Backend healthz
docker exec ai-photostudio-api wget -qO- http://localhost:8080/healthz
# → 200 OK
# Frontend index
docker exec ai-photostudio-frontend wget -qO- http://localhost:80/index.html | head -10E2E с TLS
# Frontend SPA
curl -sI https://ai-photostudio-test.nirbi.ru/ | head -3
# API healthz
curl -sI https://ai-photostudio-api-test.nirbi.ru/healthz | head -3Из админки (полный e2e)
- Логин в
https://erp-test.nirbi.ru/admin/под аккаунтом с permissiongensvc.photo.create(системная роль «Администратор» имеет автоматически) - Открыть товар:
/admin/catalog/products/{any} - Над кнопкой «Заменить» — большая красная «Улучшить с помощью AI»
- Click → modal-overlay 1100×800 с iframe фотостудии (без header)
- Загрузить тест-фото → выбрать пресет → submit
- Дождаться
succeeded(~30с) - В ResultViewer кнопки «Применить к товару» (красная) + «Отмена»
- Click «Применить» → блок с лоадером «Применяем фото к товару» → модалка закрывается → новое фото товара видно в карточке
- POS-desktop этой же ТТ должен моментально подхватить новое фото через SSE (без 60с polling)
Troubleshooting
database "gensvc_db" does not exist
Postgres-volume инициализирован раньше, чем добавлен init-script. Создать БД руками (см. шаг 5).
using local HS256 auth — NOT for production
Это expected на test VPS — AUTH_MODE=local по решению из ADR-021. На прод поменять на JWKS когда auth-service отдаст endpoint.
permission denied: gensvc.photo.create
Юзер не имеет permission. Проверить:
docker exec erp-postgres psql -U erp -d user_db -c \
"SELECT r.name FROM roles r JOIN role_permissions rp ON rp.role_id=r.id WHERE rp.permission_key='gensvc.photo.create';"Должно показать «Администратор» как минимум. Если нет — миграция 030 не применилась.
400 preset reference image not found
В gensvc-presets bucket не залиты webp. Pre-seed (шаг 7).
nginx после reload падает
TLS-серты для ai-photostudio-доменов отсутствуют. Пока certbot не пройдёт — держать конфиг как .disabled.
postMessage не приходит в admin
Проверить что origin совпадает с VITE_PHOTO_STUDIO_URL в admin frontend. Console будет логировать игнорируемые сообщения.