This page was machine-translated. Spotted a mistake?Help improve it.
Skip to content

Архітектура

SnapOtter є монорепозиторієм, що керується робочими просторами pnpm та Turborepo. Він розгортається як стек Docker Compose з 3 контейнерів: образ застосунку SnapOtter, PostgreSQL 17 і Redis 8.

Структура проєкту

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

Пакети

@snapotter/image-engine

Основна бібліотека обробки зображень, побудована на Sharp. Вона обробляє всі не-AI операції: зміну розміру, обрізку, обертання, віддзеркалення, конвертацію, стиснення, видалення метаданих та коригування кольорів (яскравість, контраст, насиченість, відтінки сірого, сепія, інверсія, канали кольору).

Цей пакет не має мережевих залежностей і працює повністю в процесі.

@snapotter/ai

Проміжний шар, який викликає скрипти Python для ML-операцій. Під час першого використання цей шар запускає стійкий процес-диспетчер Python, який заздалегідь імпортує важкі бібліотеки (PIL, NumPy, MediaPipe, rembg), тож наступні AI-виклики пропускають накладні витрати на імпорт. Якщо диспетчер ще не готовий, шар повертається до породження свіжого підпроцесу Python на кожен запит.

Моделі не завантажуються заздалегідь. Скрипт кожного інструмента завантажує ваги своєї моделі з диска під час запиту і відкидає їх після завершення запиту. Дивіться Споживання ресурсів для повного профілю пам'яті.

Підтримувані операції: видалення фону (rembg/BiRefNet), збільшення роздільності (RealESRGAN), розмиття облич (MediaPipe), покращення облич (GFPGAN/CodeFormer), видалення об'єктів (LaMa ONNX), OCR (PaddleOCR/Tesseract), розфарбовування (DDColor), видалення шуму, видалення ефекту червоних очей, відновлення фото, генерація фото на паспорт, виправлення прозорості (BiRefNet HR-matting) та зміна розміру з урахуванням вмісту (двійковий файл Go caire).

Скрипти Python розташовані в packages/ai/python/. Образ Docker заздалегідь завантажує всі ваги моделей під час збірки, тож контейнер працює повністю офлайн.

@snapotter/shared

Спільні типи TypeScript, константи (як-от APP_VERSION та визначення інструментів) і рядки перекладів i18n, які використовуються і фронтендом, і бекендом.

Застосунки

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)

Односторінковий застосунок на React 19, зібраний за допомогою Vite. Використовує Zustand для керування станом, Tailwind CSS v4 для стилізації та Lucide для іконок. Взаємодіє з API через REST та SSE (для відстеження прогресу).

Сторінки включають робочий простір інструментів, сторінку Files для керування постійними завантаженнями та результатами, конструктор автоматизації/конвеєрів та панель адміністративних налаштувань.

Зібраний фронтенд обслуговується бекендом Fastify у продакшні, тож окремого вебсервера в контейнері Docker немає.

Docs (apps/docs)

Цей сайт VitePress. Розгортається на Cloudflare Pages автоматично під час відправлення в main.

Як проходить запит

  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-файл з усіма обробленими файлами.

Споживання ресурсів

SnapOtter розроблений для низького споживання пам'яті в стані спокою. Нічого не завантажується заздалегідь і не тримається «розігрітим» під час запуску.

У стані спокою

Процес Node.js/Fastify, PostgreSQL і Redis працюють. Типове споживання RAM у стані спокою становить ~200-300 МБ для всіх трьох контейнерів (процес Node.js, Postgres і Redis). Жодного процесу Python, жодних ваг моделей у пам'яті.

Що запускається і коли

КомпонентЗапускається колиПам'ять під час роботи
Сервер Fastify + Postgres + RedisЗапуск контейнера~200-300 МБ загалом
Воркери BullMQЗапуск контейнера (у процесі)Один воркер на пул (image, media, ai, docs, system)
Диспетчер PythonПерший запит AI-інструментаІнтерпретатор Python + заздалегідь імпортовані бібліотеки (PIL, NumPy, MediaPipe, rembg) - без ваг моделей
Ваги моделей AIПід час запиту конкретного інструментаЗавантажуються з диска, звільняються після завершення запиту

Завантаження моделей

Усі файли ваг моделей (загалом кілька ГБ) весь час лежать на диску в /opt/models/. Скрипт кожного AI-інструмента завантажує в пам'ять лише свою модель (моделі) на час запиту, а потім звільняє їх. Деякі скрипти явно викликають del model і torch.cuda.empty_cache() після виведення, щоб пам'ять повернулася негайно.

Кешу моделей між запитами немає. Виконання того самого AI-інструмента підряд щоразу перезавантажує модель. Це утримує споживання пам'яті в стані спокою близьким до нуля ціною затримки на завантаження моделі під час кожного AI-запиту.

Холодний старт першого AI-запиту

Диспетчер Python не працює під час запуску контейнера. Перший AI-запит запускає паралельно дві речі: диспетчер починає розігріватися у фоні, а сам запит повертається до одноразового породження підпроцесу Python. Щойно диспетчер сигналізує про готовність, усі наступні AI-запити використовують його безпосередньо й пропускають витрати на породження підпроцесу.