Реализация целевого дизайна (docs/architecture/target-design.md): - src/: модульная структура (config, logger, errors, http, core: auth/history/jobs/engines/comfy/codex/images) - Очередь заданий: FIFO, кониурентность codex=1 / comfy-upscale=2, MAX_QUEUED_JOBS=5, JSONL-журнал и восстановление после рестарта (running → interrupted, upscale requeue) - Персистентные сессии (sha256-хеши токенов), scrypt-хеш пароля, rate-limit входа, Origin-проверка, magic-byte валидация аплоадов - Атомарная запись манифеста истории, каскадное удаление, URL с фактическим расширением + 302-алиас /image.jpg, миграция history/ → data/history - Даунскейл sharp до 1024px перед Codex + масштабирование области референса - SSE /api/events с фолбэком на polling, фронтенд переведён на ES-модули (public/js/) - Тесты node:test + supertest (27), CI workflow test.yml, Docker/деплой: том kadr-data - Документация: README, docs/architecture/
14 KiB
System Design Baseline
Вердикт: READY
Артефакт: docs/architecture/system-design-baseline.md
Статус: 1.0, подтверждён по рабочей копии main@1c36a21 (рабочее дерево чистое), дата наблюдения 2026-08-26.
Язык: русский (проект и владелец русскоязычные).
Этот документ — источник истины по архитектуро-формирующим требованиям и ограничениям проекта «Кадр». Он не описывает решение (см. docs/architecture/target-design.md). Каждый материал параметр имеет владельца, источник, дату и триггер пересмотра.
1. Идентичность и бизнес-контекст
| Параметр | Значение | Источник |
|---|---|---|
| Продукт | «Кадр» — локальный AI-фоторедактор: редактирование фотографии по текстовому промпту (Codex CLI) + 4× апскейл (ComfyUI RealESRGAN) | README.md |
| Владелец/оператор | Один человек («владелец»), доступ только ему | README.md разд. «Авторизация» |
| Развёртывание | Один контейнер Docker на хосте Portainer (kadr-photo-editor, порт 3000), LAN |
deploy/docker-compose.yml, README.md |
| Окружение | Node.js 20+, Express 4, Codex CLI (OpenAI), ComfyUI http://192.168.31.240:8188, прокси http://192.168.31.240:4067 для исходящего к OpenAI |
package.json, server.js, deploy/docker-compose.yml |
| Читатели документа | Владелец; ИИ-агент, реализующий целевую архитектуру | — |
Критические сценарии (джорни):
- Владелец загружает фото → видит превью → редактирует промптом (1–3 мин, фон) → результат в истории.
- Владелец увеличивает разрешение версии (несколько секунд) → результат в истории.
- Владелец возвращается к любой версии истории (дерево), продолжает правки с неё.
- Владелец удаляет версию каскадно (с потомками).
- Владелец получает подсказки промптов (из истории / от Codex).
Горизонт решений: один пользователь, LAN, один хост, один контейнер, горизонт планирования 6–12 месяцев. Выход за эти рамки — триггер пересмотра (см. реестр).
2. Область и не-цели
В области: редактирование по промпту, апскейл 4×, история версий с деревом и каскадным удалением, авторизация одного владельца, референс с областью, подсказки промптов, мультизагрузка, деплой Docker/Portainer, CI на Gitea Actions.
Вне области (не-цели): многопользовательность; публичный интернет-доступ; мобильные приложения; пакетная обработка; генерация изображений «с нуля»; онлайн-редактор с кистями/слоями; платёжные интеграции; облачный хостинг. Всё перечисленное — кандидаты на отдельное решение, не для текущего горизонта.
3. Драйверы и ограничения (реестр)
Легенда: Крит. — DRIVER / SUPPORTING / INFORMATIONAL; Статус — CONFIRMED (из кода/документов), ASSUMED, UNKNOWN.
| # | Тема | Параметр | Крит. | Статус | Значение/мера | Источник и владелец | Триггер пересмотра |
|---|---|---|---|---|---|---|---|
| D1 | Бизнес | Один владелец, закрытый доступ | DRIVER | CONFIRMED | Ровно один логин (APP_USER/APP_PASSWORD), cookie-сессия kadr_session, 7 суток, sliding |
server.js:119-218, README.md |
Появление второго пользователя |
| D2 | Бизнес | Редактирование по промпту — ядро продукта | DRIVER | CONFIRMED | Обработка 60–82 с на 512×512, 1–3 мин на больших; узкое место — вызов image-модели | README.md разд. «Тест скорости» |
Смена движка редактирования |
| D3 | Бизнес | История версий — непрерывная ветка правок | DRIVER | CONFIRMED | Дерево по parentId, до 50 версий, каскадное удаление, переживает перезапуск |
server.js:820-900, 1765-1826, README.md |
Рост лимита истории |
| D4 | Demand | Нагрузка | SUPPORTING | CONFIRMED | Один пользователь, 1 редактирование + 1 апскейл одновременно; опрос статуса каждые 3 с; лимит аплоада 25 МБ, промпт ≤ 1000 симв. | server.js:24, 1876, README.md |
Параллельные правки разных фотографий |
| D5 | Demand | Объём данных | SUPPORTING | CONFIRMED | ≤ 50 версий × ≤ 25 МБ исходник; реальные файлы 1–6 МБ | history/history.json, server.js:34 |
Лимит 50 перестаёт устраивать |
| D6 | Качество | Время ответа API | SUPPORTING | CONFIRMED | POST /api/edit, /api/upscale, /api/suggest отвечают мгновенно (< 1 с) с jobId; тяжёлая работа фоном |
README.md стр. 25-29 |
— |
| D7 | Качество | Завершение редактирования | DRIVER | CONFIRMED | Codex: таймаут 420 с; ComfyUI: таймаут 300 с; сбои отображаются в UI, задание помечается error |
server.js:22, 29, 465-541, 1489-1534 |
— |
| D8 | Данные | Переживание перезапуска | DRIVER | CONFIRMED | История переживает перезапуск; сессии и задания — нет (память, sessions/jobs Map); .codex-jobs очищается при старте |
server.js:141-153, 2259, README.md стр. 98 |
Цель перепроектирования: переживание заданий |
| D9 | Данные | Согласованность истории | DRIVER | CONFIRMED | Один писатель (historyWriteChain), полная перезапись history.json при каждом изменении, файлы history/<uuid>.<ext> |
server.js:830-868 |
Переход на SQLite при > 500 версий |
| D10 | Безопасность | Аутентификация | DRIVER | CONFIRMED | Сравнение пароля — constantTimeEqual по открытому значению env-переменной; без хеширования, без rate-limit на вход |
server.js:155-159, 1641 |
Редизайн: хешировать (scrypt) |
| D11 | Безопасность | Уязвимости | SUPPORTING | CONFIRMED | Только UUID в путях (JOB_ID_PATTERN); MIME+расширение проверяются, но без проверки сигнатуры файла; SameSite=Lax; без Secure (HTTP LAN); без Origin-проверки |
server.js:234-267, 2116 |
Появление внешнего доступа |
| D12 | Операции | Деплой | SUPPORTING | CONFIRMED | Образ node:22-bookworm-slim (glibc обязателен для Codex); том kadr-history; секреты APP_USER/APP_PASSWORD из Gitea; Codex через прокси :4067 |
Dockerfile, deploy/docker-compose.yml, .gitea/workflows/*.yml |
Смена хостинга |
| D13 | Операции | Наблюдаемость | SUPPORTING | CONFIRMED | Только лог в stdout (уровни debug/info/warn/error), без requestId, без метрик | server.js:50-79, README.md разд. «Логирование» |
— |
| D14 | Экономика | Стоимость | INFORMATIONAL | CONFIRMED | Стоимость = токены Codex (ChatGPT API), редактирование ~15–52k токенов/операция | README.md разд. «Тест скорости» |
Смена тарифа |
| D15 | Эволюция | Скорость редактирования | SUPPORTING | CONFIRMED | Время растёт с разрешением исходника (1200×800 медленнее 512×512); замерено: CODEX_REASONING_EFFORT=low даёт ~17% |
README.md разд. «Тест скорости» |
Внедрение предварительного даунскейла |
| D16 | Ограничение | Внешние сервисы | DRIVER | CONFIRMED | ComfyUI API (upload/workflow/history/view), модель sdxl_turbo.safetensors, upscale RealESRGAN_x4plus.safetensors; Codex CLI codex exec с флагами -i, -o last_message.txt, --ephemeral, bypass sandbox |
server.js:15-17, 397-411, README.md |
Обновление ComfyUI/Codex |
| D17 | Ограничение | Исходящий egress | DRIVER | CONFIRMED | Прямой выход к OpenAI блокируется (403 cf-ray HEL); обязателен прокси http://192.168.31.240:4067, NO_PROXY для внутренних адресов |
deploy/docker-compose.yml:20-27, README.md п.5 разд. Portainer |
Смена сети/провайдера |
4. Ключевые сценарии (source/stimulus/environment/artifact/response/measure)
- Редактирование с сохранением в историю. Источник: владелец. Стимул:
POST /api/editс фото/sourceId+ промпт. Окружение: один контейнер, Codex доступен. Артефакт: сервис/api/edit. Реакция: ответ < 1 с сjobId; ≤ 420 с — новая версияkind=editв истории; мера: версия видна вGET /api/historyи поjob.status=done. - Апскейл. Стимул:
POST /api/upscale. Реакция: ≤ 300 с — версияkind=upscale(PNG), исходник масштабируется до ≤ 2048 px до подачи в ComfyUI. Мера:job.status=done. - Переживание перезапуска (цель). Стимул: перезапуск контейнера во время
running-задания. Реакция (целевая): задание помечается прерванным с понятной ошибкой; очередь-задания и сессии переживают рестарт; история не теряется. Мера: после рестартаGET /api/jobs/:id/statusвозвращаетinterrupted/error, а не 404; сессия владельца жива. - Безопасный вход. Стимул: 10 неудачных попыток логина за 15 мин с одного IP. Реакция: блокировка с
Retry-After; пароль не хранится в открытом виде. Мера: rate-limit срабатывает, в логах нет паролей.
5. Допущения и неизвестные
- ASSUMED: ComfyUI и Codex доступны из сети владельца (рабочее состояние подтверждается историей и
.bench/). - ASSUMED: целевой читатель (ИИ-агент) реализует сервер на Node.js/Express и фронтенд без сборки (vanilla ES-модули) — см. решения в target-design.md.
- UNKNOWN: точные объёмы будущего использования (нет метрик) — не влияет: горизонтальный запас огромен для одного пользователя.
- UNKNOWN: SLA внешних сервисов (Codex/ComfyUI) — обрабатываются таймаутами и понятными ошибками.
6. Триггеры пересмотра baseline
- Второй пользователь или внешний доступ (D1, D11).
- Лимит истории 50 или объём > 500 версий (D3, D5, D9).
- Параллельные правки разных фотографий (D4).
- Смена движка редактирования (D2, D16).
- Смена хостинга/сети (D12, D17).
- Переход на SQLite или другое хранилище (D9).
System Design Baseline
Checklist: 5/5 complete
Incomplete: None — все пункты чек-листа (объём и назначение; реестр доказательств; драйверы; написание; валидация) выполнены; изменены только документы docs/architecture/*.md, код не трогался.