## 1. Безопасность JWT — критично **Файл:** `/server/utils/jwt.ts` - Убрать ВСЕ fallback-значения для секретов (`'access-secret-key'`, `'refresh-secret-key'` и т.д.). - При старте приложения, если `JWT_ACCESS_SECRET`, `JWT_REFRESH_SECRET` или `JWT_SUPERADMIN_SECRET` не заданы в `process.env` — вызывать `console.error('FATAL: JWT secrets not configured')` и `process.exit(1)`. - Разделить секреты: access, refresh, superadmin, bot — каждый свой ключ из env. - Сроки жизни токенов вынести в env с дефолтами: access=15m, refresh=7d, superadmin=1h, bot=1y. ## 2. SSE токен — перенести из query в заголовок **Файл:** `/server/routes.ts` (SSE endpoint) + фронт (`/client/src/...` где подключается EventSource) - На бэкенде: endpoint `/api/events` должен читать токен из заголовка `Authorization: Bearer `, а не из `req.query.token`. - На фронте: стандартный `EventSource` не умеет заголовки. Используй нативный `fetch` с `ReadableStream` для SSE или обёртку, которая передаёт токен в заголовке. **Не используй query-параметр для токена.** - Если фронт использует TanStack Query — создай кастомный хук `useSSE` на `fetch` + `ReadableStream.getReader()`. ## 3. Разбить routes.ts на модули **Файлы:** создать `/server/routes/` с файлами: - `auth.routes.ts` — логин, регистрация, refresh, logout, invite - `task.routes.ts` — задачи, статусы, поля, kanban, фильтры - `chat.routes.ts` — сообщения, комментарии, @mentions - `bot.routes.ts` — Telegram/Discord боты, вебхуки - `admin.routes.ts` — superadmin, биллинг, настройки - `automation.routes.ts` — автоматизации, VM sandbox (если есть) - `index.ts` — сборка всех роутов через `router.use('/api/auth', authRoutes)` и т.д. **Важно:** - Старый `/server/routes.ts` оставь как основной файл, но перенеси логику в модули и импортируй их. - Все существующие endpoint'ы (`/api/login`, `/api/tasks` и т.д.) должны продолжать работать. - Не меняй логику внутри handler'ов — только структурируй. ## 4. Транзакции для bulk-операций **Файлы:** `/server/routes/` (где сохраняются формы, статусы, поля) - Найди места, где в цикле `for` делаются множественные `db.insert()` или `db.update()` (например, сохранение полей формы, статусов kanban). - Обёртывай их в `await db.transaction(async (trx) => { ... })`. - Если одна операция в цикле падает — откатывай всю транзакцию. ## 5. Устранить N+1 в legacy endpoint'ах **Файл:** `/server/routes/task.routes.ts` (или в старом routes.ts если ещё не разбит) - Найди endpoint `/api/forms/:id/tasks` (legacy) и `/api/forms/:id/tasks/with-fields` (optimized). - Сделай так, чтобы legacy endpoint использовал тот же JOIN-подход, что и optimized, **или** пометь его deprecated и перенаправь на optimized. - Для `buildSystemFieldValues()` — замени отдельные запросы на `db.query.tasks.findMany({ with: { ... } })` с Drizzle relations. ## 6. Улучшить in-memory кэш (без Redis) **Файл:** `/server/utils/cache.ts` - Добавь TTL: `set(key, value, ttlMs)`. При `get` проверяй `Date.now() > createdAt + ttl`. - Добавь инвалидацию по префиксу: `invalidatePrefix('forms:')` удалит все ключи, начинающиеся с `forms:`. - Добавь `maxSize` (например, 1000 записей). При превышении — удаляй самые старые (LRU). - Добавь метод `clear()` для полной очистки. ## 7. Загрузка файлов — защита и потоки **Файл:** `/server/routes.ts` (multer конфигурация) - **Path Traversal:** замени `key.includes('..') || key.includes('/')` на строгую валидацию: только `a-zA-Z0-9_-`, расширение из белого списка (`jpg`, `jpeg`, `png`, `pdf`, `docx`, `xlsx`, `zip`). - **Mime-type:** проверяй `file.mimetype` против белого списка. Запрещай `application/octet-stream` без явного расширения. - **Размер:** лимит 10 МБ для изображений, 50 МБ для документов. - **Хранение:** для файлов > 5 МБ используй `diskStorage` в `/tmp/uploads`, а не `memoryStorage`. После обработки (S3 upload) — удаляй файл. Для маленьких файлов (< 5 МБ) можно оставить `memoryStorage`. - **Сканирование:** добавь базовую проверку magic bytes для изображений (чтобы `.exe` переименованный в `.jpg` не прошёл). ## 8. Вебхуки — retry логика (без Redis) **Файл:** `/server/routes/bot.routes.ts` (или где вызываются `fetch()` для вебхуков) - Замени голый `fetch().catch()` на функцию `sendWebhook(url, payload, retries = 3)`. - При неудаче (не 2xx или network error) — жди `2^attempt * 1000` мс (exponential backoff) и повторяй. - Если все 3 попытки провалились — логируй ошибку в БД (новая таблица `webhook_failures` или в `task_audit_log`). - **Ограничение:** не более 5 одновременных вебхуков — используй `p-limit` или самописный семафор (массив pending promises, max 5). ## 9. Исправить IDOR в сообщениях **Файл:** `/server/routes/chat.routes.ts` - Endpoint `/api/messages/:id` должен проверять, что сообщение принадлежит задаче, которая принадлежит организации текущего пользователя. - Добавь `AND eq(tasks.organization_id, req.user.organization_id)` в запрос. ## 10. Общий аудит (входы, экспорты, ошибки) **Файл:** `/server/utils/audit.ts` (создать) - Создай единую функцию `logAudit({ action, userId, organizationId, taskId, details, ip })`. - Добавь таблицу `system_audit_log` (id, action, user_id, org_id, task_id, details JSONB, ip, user_agent, created_at). - Логируй: - Успешный/неуспешный вход - Выход - Экспорт данных (Excel, CSV) - Изменение прав пользователя - Неудачные попытки аутентификации (rate limit сработал — тоже логируй) - Используй `db.insert()` без `await` (fire-and-forget), но оборачивай в `.catch()` чтобы не падало основное действие. ## 11. Индексы PostgreSQL **Файл:** `/shared/schema.ts` (Drizzle) или миграция `/server/migrations/` - Добавь через Drizzle индексы: - `tasks`: `organization_id` + `form_id`, `organization_id` + `current_status_id`, `organization_id` + `assigned_to` - `task_field_values`: `task_id` + `field_id` - `task_messages`: `task_id` + `created_at DESC` - `task_audit_log`: `task_id` + `created_at DESC` - `users`: `organization_id` + `role` - Сгенерируй SQL миграцию через `drizzle-kit generate` (или напиши raw SQL файл). ## 12. Улучшить rate limiting **Файл:** `/server/routes.ts` или `/server/index.ts` - API лимит: снизь с 5000/15min до **600/15min** для обычных пользователей. - SSE: отдельный лимит — **30 соединений/IP**. - Auth endpoints (`/api/login`, `/api/register`): **5 попыток/15min/IP** (строгий лимит против brute-force). - Используй `express-rate-limit` с `standardHeaders: true`, `legacyHeaders: false`. ## 13. VM Автоматизации — изоляция (без отдельного микросервиса) **Файл:** `/server/routes/automation.routes.ts` - Если в коде есть `vm.createContext` + `vm.Script` — оберни в `try/catch/finally`. - Добавь `cpuUsage` лимит через `process.cpuUsage()` перед и после. Если скрипт съел > 100ms CPU time — убивай. - Ограничь память: перед запуском проверяй `process.memoryUsage().heapUsed`. Если > 80% от лимита — отказывай в запуске. - **Запускай VM в `setImmediate` или `process.nextTick`** чтобы не блокировать текущий HTTP-ответ, если это возможно. Если нет — добавь комментарий `// TODO: вынести в Worker при масштабировании`.