Как я в 14 лет собирал автоматическую ИИ-видеостудию: разбор Edge-архитектуры и борьба с абузерами
Пока в сети спорят, заменит ли нейросеть программистов и монтажеров, я решил автоматизировать то, что меня всегда дико бесило — рутинный монтаж видео. Склеить куски, наложить субтитры, подогнать озвучку в условном редакторе — это скучно и долго.
Я решил написать систему, которая делает это автоматически. Передаешь текстовый сценарий, бэкенд нарезает его на сцены, генерирует голос через API озвучки, формирует видеоряд и собирает финальный MP4 на лету через облачный рендеринг по JSON-манифестам.
Ниже — честный разбор того, как это устроено изнутри, почему пришлось пересесть на Edge Runtime и как первая же волна знакомых тестеров чуть не вылила весь мой бюджет на API.
Сервер без задержек: убираем Cold Starts
Изначально я смотрел в сторону стандартного Node.js, но для быстрой генерации ответов критически важен минимальный TTFB (время до получения первого байта). Обычные серверные функции при редких запросах засыпают, а их «холодный старт» занимает от 1.5 до 3 секунд. Для пользователя это выглядит так, будто сайт завис.
Решение — полный перевод ИИ-роутов на Edge Runtime (работает на легковесном движке V8). Функции выполняются на ближайших к пользователю дата-центрах, из-за чего холодный старт упал практически до нуля.
Чтобы выжать максимальную скорость, пришлось переписать логику запросов. Вместо последовательного ожидания через await (когда одна операция ждет завершения другой), я запустил независимые сетевые вызовы к базе данных параллельно:
TypeScript
// Обработчик на Edge Runtime export const config = { runtime: 'edge' }; export default async function handler(req: Request) { const { userId, prompt, configId } = await req.json(); // Запросы в базу идут одновременно, а не друг за другом const [userProfile, systemConfig] = await Promise.all([ fetch(`${process.env.DATABASE_URL}/rest/v1/profiles?id=eq.${userId}`, { headers: { Authorization: `Bearer ${process.env.DB_ANON_KEY}` } }).then(res => res.json()), fetch(`${process.env.DATABASE_URL}/rest/v1/configs?id=eq.${configId}`, { headers: { Authorization: `Bearer ${process.env.DB_ANON_KEY}` } }).then(res => res.json()) ]); // Дальше формируем манифест для рендеринга... return new Response(JSON.stringify({ success: true })); }
База данных: хронология SQL-миграций
В качестве бэкенда и БД я взял связку облачного Postgres. Архитектуру таблиц строил поэтапно, фиксируя изменения через миграции.
Этап 1: Базовая схема. Связал системных пользователей со своими профилями и таблицей генерации видео через внешние ключи (FOREIGN KEY). При удалении юзера его генерации каскадно удаляются, чтобы не захламлять базу.
Этап 2: Система поддержки. Добавил таблицу support_tickets, чтобы пользователи могли отправлять репорты об ошибках генерации напрямую из интерфейса.
Этап 3: Оптимизация под нагрузкой. Когда запросов стало много, база начала просаживаться по IOPS при поиске. Чтобы запросы к тикетам и истории видео не сканировали всю таблицу целиком, я накатил составные B-Tree индексы:
SQL
-- Оптимизируем поиск открытых тикетов конкретного юзера CREATE INDEX idx_support_tickets_user_status ON public.support_tickets (user_id, status); -- Ускоряем вывод истории генераций от новых к старым CREATE INDEX idx_video_generations_user_created ON public.video_generations (user_id, created_at DESC);
Битва с абузерами: как я защищал баланс API
Самый жесткий урок я получил, когда открыл проект для первых тестов среди знакомых и в локальных чатах. Генерация видео — штука дорогая. Каждый запрос к нейросетям за текст, голос и рендер кадра стоит реальных денег (списывается с моего личного баланса API). Естественно, тут же нашлись умники, которые решили потестить систему на прочность и начали слать запросы пачками через скрипты.
Пришлось оперативно разворачивать трехэтапный анти-фрод контур:
Валидация через Zod: Отсек пустые запросы и спам-сообщения. Текст промпта должен быть строго от 10 до 500 символов.
Ограничение на бесплатные тесты: Не более 5 генераций в час на одного человека.
Фингерпринтинг на уровне Middleware: Просто банить по IP бесполезно. Теперь на уровне промежуточного слоя (Middleware) собираются хэши заголовков браузера (User-Agent, язык, экран) и IP-адрес, превращаясь в SHA-256 хэш сессии. Если система видит, что с одного "цифрового отпечатка" создают кучу новых аккаунтов в обход лимитов, функция сразу отдает HTTP 429 Too Many Requests, даже не нагружая основную базу данных.
Для защиты внутренних механик авторизацию в итоге завязал на Telegram OAuth по Deep Links. Бот анализирует метаданные аккаунта (возраст, активность), полностью отсекая авто-рег фермы ботов, созданные пять минут назад.
Вывод
Время простых и глупых оберток над ChatGPT прошло — они никому не интересны. Выживают только проекты, которые автоматизируют сложные цепочки процессов (как видеопроизводство) и имеют под капотом нормальную архитектуру, защищенную от взлома и абуза.
Проект пока сугубо некоммерческий, пилю ради опыта и обкатки технологий.
Буду рад здоровой критике кода и архитектуры в комментариях! Как бы вы оптимизировали работу с очередями тяжелых задач в Postgres?