BR 2.6 — Photo Studio Service
Репозиторий
nearbyErp/ai-photo-studio-backend (Go), ветка feature/transform-mode
Контекст
Рабочий прототип на Go. Auth — JWKS / local HS256 + UserInfo enrichment (см. ADR-021). Polza интеграция переведена на async polling (см. ADR-022).
Прогресс на 2026-05-08
Реализованы и закоммичены: enricher, optional iss/aud, Polza async:true, polling 15 мин, smart fallback, IDOR fix на SSE, atomic quota для user-presets, no-block UX на студии, live-history с уведомлениями. Остаётся: introspection-валидатор после получения service-token, Gateway-rewrite на проде, reconciliation worker для долгих Polza-задач.
Задачи
1. Добавить IntrospectionValidator
- Создать файл
internal/auth/introspection.go - Реализовать
IntrospectionValidator— вызываетPOST {AUTH_SERVICE_URL}/internal/auth/validateс заголовкомX-Service-Token: {AUTH_SERVICE_TOKEN} - Обновить структуру
Claims(internal/auth/claims.go): добавить полеScope(опциональный,scope.type+scope.legal_entity_ids/scope.store_idsиз ответа validate) - Реализовать retry + timeout: 3 попытки, timeout 500 мс на запрос
- При ошибке вызова Auth Service возвращать
503 Service Unavailable(не 401 — это разные ситуации)
2. Redis-кэш для introspection-ответов
- При успешном ответе
/internal/auth/validateкэшировать результат в Redis по ключуintrospect:{sha256_of_token}с TTL 60 сек - При cache-hit — не вызывать Auth Service, брать данные из Redis
- Использовать существующий Redis-клиент из
deps(Redis уже используется для очереди заданий)
3. Переменная AUTH_MODE в конфигурации
- Добавить env-переменную
AUTH_MODE(допустимые значения:introspection,jwks) - При
AUTH_MODE=introspection— инициализироватьIntrospectionValidatorвместоJWKSValidator - Дополнительные переменные для introspection:
AUTH_SERVICE_URL— URL Auth Service (напримерhttp://auth-service:3001)AUTH_SERVICE_TOKEN— service token дляX-Service-Tokenheader
- Обновить
.env.exampleс новыми переменными - В
docker-compose.yml(иdocker-compose.prod.yml) задатьAUTH_MODE=introspectionдля production-окружения
4. Выровнять URL-префикс для API Gateway
Это изменение затрагивает фронтенд Photo Studio
При смене URL-пути с
/v1/на/api/v1/gensvc/существующий фронт (если есть) сломается. Координировать с командой фронтенда.
- Вариант A принят (2026-05-07): API Gateway rewrite
{gateway}/api/v1/gensvc/*→{service}:8080/v1/*. Код сервиса не меняется, фронт продолжает работать на/v1/...относительные пути. - Зафиксировать в Gateway-конфиге маршрут
/api/v1/gensvc/*с rewrite rule - Прописать в
Overview.md(этого сервиса) URL-маппинг для сторонних потребителей (Admin BFF, фронт-клиенты): «вызывайте через Gateway, не напрямую» -
Вариант B (изменение роутера)— отклонён: обусловливает риск ломки фронта без выгоды
Решение по URL-выравниванию
Gateway rewrite (Вариант A). Внутренний роутер
router.goостаётся на/v1/..., наружу торчит только/api/v1/gensvc/.... Зафиксировано 2026-05-07.
5. Обновить api-spec.yaml
- Изменить
servers[].url— добавить prod-URL через Gateway ({gateway}/api/v1/gensvc) - Обновить security scheme: добавить описание introspection (сейчас описан только JWKS/HS256)
- Синхронизировать пути в спеке с выбранным вариантом URL (A или B из п.4)
6. Обновить фронт (если есть standalone UI)
- Проверить — есть ли standalone фронтенд Photo Studio (
ai-photo-studio-frontendили аналог) - Если есть — обновить базовый URL с
https://ai-photo-studio.nirbi.ru/v1на{gateway}/api/v1/gensvc - Добавить redirect на ERP login (
{erp-admin}/login) если нет валидного JWT-токена
Порядок деплоя
- User Service деплоим с новыми ключами
gensvc.*в каталоге - Выпускаем service-token (см. Auth Service)
- Деплоим Photo Studio Service с
AUTH_MODE=introspection - Проверяем:
POST /internal/auth/validateвозвращает правильные permissions - Smoke test: создать задание с тестовым токеном пользователя с
gensvc.*
Связанные файлы в репозитории
| Файл | Что менять |
|---|---|
internal/auth/claims.go | Добавить поле Scope в struct Claims |
internal/auth/jwks.go | Оставить как есть (legacy JWKS для dev/fallback) |
internal/auth/introspection.go | Создать новый — IntrospectionValidator |
internal/httpserver/router.go | Опционально: URL-prefix если выбран Вариант B |
docs/api-spec.yaml | Обновить servers + security description |
.env.example | Добавить AUTH_MODE, AUTH_SERVICE_URL, AUTH_SERVICE_TOKEN |
Сделано (2026-05-08)
Помимо плана выше реализовано в ходе интеграции:
Auth (см. ADR-021)
-
optional iss/aud— envJWT_ISSUER/JWT_AUDIENCEenforce-ятся только если непустые -
UserInfoFetcher— обогащение Claims черезGET /api/v1/auth/meпосле валидации подписи (in-process кеш, 1024 entries cap, TTL 60 сек) -
RequirePermissionmiddleware подключён ко всем чувствительным роутам
Polza (см. ADR-022)
-
async: trueв payload POST/v1/media— устранён connection reset - Polling
GET /v1/media/{id}каждые 3 сек, deadline 15 мин -
isFallbackableErrorклассификатор — fallback только на Polza-side failures -
WORKER_CONCURRENCY=5,WORKER_JOB_TIMEOUT_SEC=1080,WORKER_MAX_RETRIES=1 - User-friendly error message при таймауте
Безопасность (после code review)
-
StreamJob— owner check (IDOR fix) -
MaxBytesReaderна всех multipart endpoints -
InsertWithQuota— атомарная вставка user-preset с проверкой 50-лимита -
validateSizeCount— убрана мёртвая ветка -
SetWriteDeadline(0)на SSE-стриме (раньше резалось через 120с) - Worker panic-guard на
WORKER_MAX_RETRIES<1
Roadmap (после получения service-token от ERP)
-
IntrospectionValidator(см. п. 1-3 выше) - Gateway rewrite в проде (см. п. 4)
- Reconciliation worker для Polza > 15 мин: сохранять
polza_task_idв БД, фоновый цикл подтягивает результаты после polling-таймаута. См. ADR-022 § Roadmap. - Provider abstraction — единый
Providerinterface, переключательAI_PROVIDERдля A/B на fal.ai / OpenRouter / прямой OpenAI
Связанные контракты
- Photo Studio Service Overview
- Photo Studio Service API
- Frontend Specs: AI Студия
- Frontend: Экран генерации
- Frontend: История
- Frontend: Админка пресетов
- validate
- ADR-021 — Go + JWT validation
- ADR-022 — Polza async polling