Архитектура
Схема системы и ключевые архитектурные решения
Общая схема
Browser (React 19, TanStack Query, Zustand, Tailwind CSS v4)
↓
Next.js 16 App Router (Vercel, Node runtime)
├── src/app/(app)/... защищённые страницы
├── src/app/api/... HTTP API (Route Handlers)
├── src/lib/auth/ Better Auth + RBAC
├── src/lib/ai/ AIService (multi-provider + fallback chain)
└── src/lib/storage/ S3-клиент (R2 / MinIO)
↓
PostgreSQL (Prisma 6) + Redis (BullMQ) + R2/MinIO (S3 API)Бэкенда как отдельного сервиса нет: API routes Next.js = бэкенд. Тяжёлая фоновая работа (скачивание видео Safebot) вынесена в BullMQ-воркер на Railway.
Ключевые решения
- Единый Next.js — App Router рендерит страницы, Route Handlers дают HTTP API. Один деплой, один кодстайл.
- RBAC в 4 уровня — подробно в разделе Auth и RBAC:
src/proxy.ts(middleware) — проверка сессии;- server-side redirect по ролям (
lib/auth/redirects.ts); withPermission("key", handler)— guard каждого API endpoint;usePermissions()/<Can>— скрытие элементов UI.
- Динамические permissions — базовая матрица
ROLE_PERMISSIONS_MATRIX+ точечные overrides в таблицеUserPermission. - Invitation-only — публичная регистрация заблокирована, вход в систему только по приглашению.
- AI Service — мультипровайдер (Gemini → OpenAI → Template), ключи хранятся в БД, управление через admin UI
/ai, бюджет-лимиты. Прямые вызовы AI-SDK запрещены — толькоaiService.generate(). - Уведомления в 3 канала — in-app (polling 30s), browser push (Web Push API + Service Worker), Telegram (bot). WebSockets сознательно не используются.
- Multi-tenancy — все данные привязаны к
companyId. Клиент видит только свою компанию, Monster Team — всех клиентов. Скоуп решается вlib/api/scope.ts. - Soft delete — физическое удаление запрещено: везде
deletedAt(+deletedByдля задач). - Reference Categories — database-driven — категории референсов лежат в таблице (не enum) и управляются через REST API.
- Единая пагинация — хук
usePagination+ компонент<Pagination />, состояние в URL.
Поток данных на примере задачи
POST /api/tasks→withPermission("task.create")→ Zod-валидация тела → Prisma-транзакция (Task + TaskSelectedItem + TaskStyle + TaskInteriorType + Material).auditLog()пишет действие вAuditLog.notifyMonsterTeam()рассылает уведомления команде (in-app + push + telegram).- Клиентский кэш обновляется через TanStack Query invalidation.
Файлы: presigned upload
Файлы не проходят через сервер приложения:
Клиент → POST /api/uploads/presign → presigned PUT URL (S3)
Клиент → PUT файл напрямую в R2/MinIO
Клиент → POST /api/uploads/confirm/[fileId] → запись FileObject в БДПравила кода (жёсткие)
- Нельзя вызывать AI напрямую — только
lib/ai/service.ts. - Нельзя хардкодить цвета — только CSS-переменные (8 тем!). См. Стилизация.
- Нельзя писать кастомные кнопки — только UI Kit (
@/components/ui). - Нельзя хардкодить permissions и категории референсов.
- Нельзя удалять физически — только soft delete.
- Каждая фича/фикс — запись в
CHANGELOG.md([Unreleased]); рефакторинги и стили не записываются.