mirror of
https://github.com/snapotter-hq/SnapOtter.git
synced 2026-08-03 07:46:42 +02:00
All 181 docs markdown files translated into 20 languages (apps/docs/<locale>/**). Companion to the i18n code PR; admin-merged because the file count exceeds GitHub's per-PR CI trigger limit. Validated by pnpm i18n:check (all surfaces, 0 stale/missing) and a clean all-locale docs build.
124 lines
14 KiB
Markdown
124 lines
14 KiB
Markdown
---
|
||
description: "Структура монорепозитория, архитектура приложений и пакетов, жизненный цикл запроса и потребление ресурсов SnapOtter."
|
||
i18n_source_hash: 9e8f80499a37
|
||
i18n_provenance: human
|
||
i18n_output_hash: c47f9f3041c4
|
||
---
|
||
|
||
# Архитектура {#architecture}
|
||
|
||
SnapOtter представляет собой монорепозиторий, управляемый с помощью рабочих пространств pnpm и Turborepo. Он развёртывается как стек из 3 контейнеров Docker Compose: образ приложения SnapOtter, PostgreSQL 17 и Redis 8.
|
||
|
||
## Структура проекта {#project-structure}
|
||
|
||
```
|
||
snapotter/
|
||
├── apps/
|
||
│ ├── api/ # Fastify backend
|
||
│ ├── web/ # React + Vite frontend
|
||
│ └── docs/ # This VitePress site
|
||
├── packages/
|
||
│ ├── image-engine/ # Sharp-based image operations
|
||
│ ├── media-engine/ # FFmpeg spawn + progress parsing
|
||
│ ├── doc-engine/ # qpdf, LibreOffice, ghostscript wrappers
|
||
│ ├── ai/ # Python AI model bridge
|
||
│ └── shared/ # Types, constants, i18n
|
||
└── docker/ # Dockerfile and Compose config
|
||
```
|
||
|
||
## Пакеты {#packages}
|
||
|
||
### `@snapotter/image-engine` {#snapotter-image-engine}
|
||
|
||
Основная библиотека обработки изображений, построенная на [Sharp](https://sharp.pixelplumbing.com/). Она выполняет все операции без AI: изменение размера, обрезку, поворот, отражение, преобразование, сжатие, удаление метаданных и цветовые корректировки (яркость, контраст, насыщенность, оттенки серого, сепия, инверсия, цветовые каналы).
|
||
|
||
У этого пакета нет сетевых зависимостей, и он работает полностью в рамках процесса.
|
||
|
||
### `@snapotter/ai` {#snapotter-ai}
|
||
|
||
Прослойка, которая вызывает Python-скрипты для ML-операций. При первом использовании прослойка запускает постоянный процесс-диспетчер Python, который предварительно импортирует тяжёлые библиотеки (PIL, NumPy, MediaPipe, rembg), чтобы последующие вызовы AI не несли накладных расходов на импорт. Если диспетчер ещё не готов, прослойка возвращается к порождению нового подпроцесса Python на каждый запрос.
|
||
|
||
**Модели не загружаются заранее.** Каждый скрипт инструмента загружает веса своей модели с диска в момент запроса и освобождает их после завершения запроса. Полный профиль памяти см. в разделе [Потребление ресурсов](#resource-footprint).
|
||
|
||
Поддерживаемые операции: удаление фона (rembg/BiRefNet), апскейлинг (RealESRGAN), размытие лиц (MediaPipe), улучшение лиц (GFPGAN/CodeFormer), стирание объектов (LaMa ONNX), OCR (PaddleOCR/Tesseract), колоризация (DDColor), удаление шума, удаление эффекта красных глаз, реставрация фотографий, создание фото на паспорт, исправление прозрачности (HR-маттинг BiRefNet) и изменение размера с учётом содержимого (Go-бинарник caire).
|
||
|
||
Python-скрипты находятся в `packages/ai/python/`. Образ Docker предварительно загружает все веса моделей во время сборки, поэтому контейнер полностью работает офлайн.
|
||
|
||
### `@snapotter/shared` {#snapotter-shared}
|
||
|
||
Общие типы TypeScript, константы (такие как `APP_VERSION` и определения инструментов) и строки перевода i18n, используемые как фронтендом, так и бэкендом.
|
||
|
||
## Приложения {#applications}
|
||
|
||
### API (`apps/api`) {#api-apps-api}
|
||
|
||
Сервер Fastify v5, предоставляющий 241 маршрут инструментов в пяти модальностях (image, video, audio, PDF, file), который обрабатывает:
|
||
- Загрузку файлов, управление временным рабочим пространством и постоянное хранение файлов
|
||
- Библиотеку файлов пользователя с цепочками версий (таблица `user_files`), каждый обработанный результат ссылается на исходный файл и записывает, какой инструмент был применён, с автоматически генерируемыми миниатюрами для страницы Files
|
||
- Выполнение инструментов (направляет каждый запрос инструмента в движок изображений или прослойку AI)
|
||
- Оркестрацию конвейеров (последовательное объединение нескольких инструментов в цепочку)
|
||
- Пакетную обработку с управлением параллелизмом через очереди задач BullMQ (пулы: image, media, ai, docs, system)
|
||
- Аутентификацию пользователей, RBAC (роли admin/user с полным набором прав), управление API-ключами и ограничение частоты запросов
|
||
- Управление командами, CRUD только для администратора; пользователи назначаются в команду через поле `team` в их профиле
|
||
- Настройки времени выполнения, хранилище «ключ-значение» в таблице `settings`, которое управляет `disabledTools`, `enableExperimentalTools`, `loginAttemptLimit` и другими эксплуатационными параметрами без повторного развёртывания
|
||
- Пользовательский брендинг и предпочтения времени выполнения через настройки, хранящиеся в базе данных
|
||
- Документацию Scalar/OpenAPI по адресу `/api/docs`
|
||
- Отдачу собранного фронтенда как SPA в продакшене
|
||
|
||
Ключевые зависимости: Fastify, Drizzle ORM (pg-core, node-postgres), Sharp, BullMQ, ioredis, Zod для валидации.
|
||
|
||
Сервер обрабатывает корректное завершение работы по SIGTERM/SIGINT: он сливает HTTP-соединения, останавливает воркеры BullMQ, завершает диспетчер Python и закрывает соединение с базой данных.
|
||
|
||
### Web (`apps/web`) {#web-apps-web}
|
||
|
||
Одностраничное приложение на React 19, собранное с помощью Vite. Использует Zustand для управления состоянием, Tailwind CSS v4 для стилизации и Lucide для значков. Обменивается данными с API по REST и SSE (для отслеживания прогресса).
|
||
|
||
В число страниц входят: рабочее пространство инструментов, страница Files для управления постоянными загрузками и результатами, конструктор автоматизации/конвейеров и панель настроек администратора.
|
||
|
||
Собранный фронтенд отдаётся бэкендом Fastify в продакшене, поэтому в контейнере Docker нет отдельного веб-сервера.
|
||
|
||
### Docs (`apps/docs`) {#docs-apps-docs}
|
||
|
||
Этот сайт на VitePress. Развёртывается на Cloudflare Pages автоматически при пуше в `main`.
|
||
|
||
## Как проходит запрос {#how-a-request-flows}
|
||
|
||
1. Пользователь выбирает инструмент в веб-интерфейсе и загружает файл.
|
||
2. Фронтенд отправляет multipart-POST на `/api/v1/tools/:section/:toolId` с файлом и настройками.
|
||
3. Маршрут API проверяет входные данные с помощью Zod, затем запускает обработку.
|
||
4. Для стандартных инструментов задача ставится в соответствующий пул BullMQ (image, media или docs в зависимости от модальности). Внутрипроцессный воркер BullMQ автоматически ориентирует изображение на основе метаданных EXIF, выполняет функцию обработки инструмента и возвращает результат.
|
||
5. Для AI-инструментов прослойка TypeScript отправляет запрос постоянному диспетчеру Python (или в качестве запасного варианта порождает новый подпроцесс), ждёт его завершения и читает выходной файл.
|
||
6. Прогресс задачи сохраняется в таблицу `jobs` в PostgreSQL, поэтому состояние переживает перезапуски контейнера. Обновления в реальном времени доставляются через SSE по адресу `/api/v1/jobs/:jobId/progress`.
|
||
7. API возвращает `jobId` и `downloadUrl`. Пользователь скачивает обработанный файл из `/api/v1/download/:jobId/:filename`.
|
||
|
||
Для конвейеров API подаёт вывод каждого шага как входные данные для следующего, выполняя их последовательно.
|
||
|
||
Для пакетной обработки API использует потоки BullMQ с дочерними задачами на каждый шаг и возвращает ZIP-файл со всеми обработанными файлами.
|
||
|
||
## Потребление ресурсов {#resource-footprint}
|
||
|
||
SnapOtter спроектирован для низкого потребления памяти в простое. Ничего не загружается заранее и не держится «прогретым» при запуске.
|
||
|
||
### В простое {#at-idle}
|
||
|
||
Процесс Node.js/Fastify, PostgreSQL и Redis запущены. Типичное потребление ОЗУ в простое составляет **~200-300 МБ** по всем трём контейнерам (процесс Node.js, Postgres и Redis). Нет процесса Python, нет весов моделей в памяти.
|
||
|
||
### Что запускается и когда {#what-starts-and-when}
|
||
|
||
| Компонент | Запускается когда | Память во время активности |
|
||
|-----------|-------------|---------------------|
|
||
| Сервер Fastify + Postgres + Redis | Запуск контейнера | ~200-300 МБ суммарно |
|
||
| Воркеры BullMQ | Запуск контейнера (внутрипроцессно) | Один воркер на пул (image, media, ai, docs, system) |
|
||
| Диспетчер Python | Первый запрос AI-инструмента | Интерпретатор Python + предварительно импортированные библиотеки (PIL, NumPy, MediaPipe, rembg), без весов моделей |
|
||
| Веса AI-моделей | Во время запроса конкретного инструмента | Загружаются с диска, освобождаются по завершении запроса |
|
||
|
||
### Загрузка моделей {#model-loading}
|
||
|
||
Все файлы весов моделей (суммарно несколько ГБ) постоянно находятся на диске в `/opt/models/`. Каждый скрипт AI-инструмента загружает в память только свою(-и) модель(-и) на время запроса, затем освобождает их. Некоторые скрипты явно вызывают `del model` и `torch.cuda.empty_cache()` после инференса, чтобы память возвращалась немедленно.
|
||
|
||
Между запросами кэша моделей нет. Запуск одного и того же AI-инструмента подряд перезагружает модель каждый раз. Это удерживает память в простое около нуля ценой задержки на загрузку модели при каждом AI-запросе.
|
||
|
||
### Холодный старт первого AI-запроса {#first-ai-request-cold-start}
|
||
|
||
Диспетчер Python не запущен при старте контейнера. Первый AI-запрос запускает две вещи параллельно: диспетчер начинает прогреваться в фоне, а сам запрос откатывается к разовому порождению подпроцесса Python. Как только диспетчер сигнализирует о готовности, все последующие AI-запросы используют его напрямую и обходят затраты на порождение подпроцесса.
|