Commit Graph

282 Commits

Author SHA1 Message Date
820e8865a5 refactor(chat): фаза 6 — общий useMessagePolling, TaskChat на общий MessageBubble (слоты под task-специфику) и groupByDate, виртуализация мессенджера + автоскролл только у низа, TaskChat без RHF/zod (локальный стейт), анимация появления последнего сообщения; TaskChat 2023→1754 строк 2026-09-08 21:58:03 +03:00
aa8bf6ac33 feat(ui): фаза 5 — Тема 2.0 во всех базовых ui/* (select/textarea/tabs/popover/tooltip/dropdown-menu/checkbox/dialog на рецептах и токенах), data-density/control-variant/button-radius на html, консолидация размеров кнопок (44 места → size=sm), max-h+скролл диалогов 2026-09-08 21:23:25 +03:00
113ed458b6 feat(ui): фаза 4 — скелетоны PageSkeleton/ListSkeleton/CardSkeleton/FormSkeleton (мигрированы ~19 страниц вместо спиннеров), PageTransition на framer-motion (fade 150ms, MotionConfig reducedMotion=user, гейт skinComponents.motion.enabled), StaggerList для списков задач/диалогов/справочников 2026-09-08 21:10:08 +03:00
c4fdfe4eab refactor(ui): фаза 3 — единый AppShell (Sidebar не размонтируется при навигации), Layout стал тонким контейнером, SPA-навигация вместо location.href (уведомления, тосты, TableEditor), scroll restoration, плавное появление биллинг-оверлея 2026-09-08 20:48:28 +03:00
5d219ac794 fix(ui): фаза 2 — стабилизация данных: throttle+invalidate вместо refetch-пачек на SSE, единый useUnreadBadges (polling только при обрыве SSE), TaskChat polling только при потере SSE, one-shot guard в safeLazy, плавная смена скина (transition цветов) 2026-09-08 20:38:01 +03:00
7a98bf7bf3 feat(ui): фаза 1 — шкалы motion/z-index в токенах, boot-скрипт темы в index.html, рецепты компонентов (design-system/recipes.ts) для button/input/badge/card/table, layout.taskDetail в скине, удалён мёртвый CSS (.btn-compact и др.) и зависимость tw-animate-css 2026-09-08 20:26:14 +03:00
c8d7f9b336 fix(auth-ui): тихий фейл 400 при смене пароля, валидация, пароль при создании
PLAN-user-password-fixes.md (инцидент 2026-09-07, user 55/56):
- toServiceError в client auth.service: BadRequestError → {success:false, error}
- полная клиентская валидация пароля в ProfilePasswordRow
- опциональный пароль при создании пользователя (UserModal + POST /api/users)
- onError с toast в PollCard/ReactionBar (этап 4)
- 11 новых тестов (129/129)
2026-09-08 16:42:59 +03:00
0b2319b11d feat(automations): ctx.tasks.sendMessage — сообщение в чат задачи от Системы; userId в triggerData status_changed 2026-09-08 16:31:59 +03:00
163351b345 docs(log): шаги 0.3, 0.6, 0.14 — подтверждены на проде, этап E закрыт 2026-09-08 15:15:17 +03:00
a5ab294608 ci(deploy): compose из инфра-репо напрямую через -f (чистый git status на сервере)
Шаг 0.14 (доработка): без cp в /opt/crm — docker compose -f
/opt/crm-infra/docker-compose.prod.yml --project-directory /opt/crm
2026-09-08 15:02:33 +03:00
0fb3b54b66 ci(deploy): прод-compose из приватного инфра-репо iistwin-infra (шаг 0.14)
Убраны git stash кастомизаций и восстановление compose из бэкапа вне репо.
Источник правды для прод-compose: iisultanov/iistwin-infra (приватный),
клонируется в /opt/crm-infra deploy-ключом при каждом деплое
2026-09-08 14:27:29 +03:00
03871b76a2 ci(deploy): deploy-пользователь вместо root, DEPLOY_HOST в секретах; fix jsdom для Node 20
Шаг 0.3 плана production-готовности:
- deploy.yml: username deploy, host из секрета DEPLOY_HOST
- сервер: пользователь deploy (группа docker), /opt/crm переписан на deploy
- SSH_KEY = ключ deploy (не root)
- pr-check fix: jsdom ^26 (jsdom 30 несовместим с Node 20 CI-раннера)
2026-09-08 14:05:07 +03:00
06bfd2aa75 feat(auth): шаг 0.6 — TTL access 15 минут + хэширование refresh/reset-токенов в БД
Some checks are pending
Branch Check / Type Check & Build (push) Waiting to run
- server/utils/jwt.ts: дефолт JWT_ACCESS_EXPIRES 30d → 15m (env сохранено), hashToken() (sha256 hex)
- migrations/0082_token_hashes.sql: *_hash колонки в user_sessions/users, DELETE FROM user_sessions (глобальный разлогин), legacy plain-колонки сохранены, но не пишутся
- storage: lookup/отзыв сессий и reset-токенов по хэшу; markSessionReplaced по id сессии
- auth.service: ротация в rotateFamilySession(), grace-period ротирует активную сессию вместо возврата plain-токена
- auth.core.routes: forgot/reset-password пишут/ищут sha256-хэш, plain только в письме
- tests/token-security.test.ts: 12 тестов (выпуск по хэшу, ротация, reuse detection, grace, reset)
- .env.example, swagger, IMPLEMENTATION_LOG.md обновлены

Проверки: npm run check чисто, vitest 118/118, lint 0 errors, build собирается
2026-09-08 11:35:32 +03:00
639036c87e security(rls): ENABLE_RLS обязателен в production (fatal без флага)
Шаг 0.1 (завершение) плана production-готовности:
- прод: ENABLE_RLS=true включён, 31 таблица ENABLE+FORCE проверена
- код: fatal при NODE_ENV=production без ENABLE_RLS
- .env.example + DOCKER.md: пометки об обязательности
2026-09-08 10:55:58 +03:00
12e797eb1a fix(rls): startup-проверка schema drift — array_agg name[] приходит строкой
Шаг 0.1 (подготовка) плана production-готовности:
pg-драйвер возвращает name[] как "{a,b,c}" (нет парсера OID 1003) —
проверка находила 0 таблиц и запуск с ENABLE_RLS=true всегда падал.
Поддержаны оба формата. Проверено на копии прод-БД: app стартует,
31 таблица ENABLE+FORCE, login/search/sync работают
2026-09-08 10:28:44 +03:00
b3da3e334f docs(log): шаг 0.4 подтверждён на проде (docker inspect) 2026-09-08 10:16:05 +03:00
87fe0f03f5 chore(compose): ресурсные лимиты для всех сервисов
Шаг 0.4 плана production-готовности (подтверждён пользователем):
- mem_limit/cpus/pids_limit по docker stats прода + запас
- все 4 compose-файла + серверный бэкап .bak.serverfix
- config валиден везде
2026-09-08 10:01:23 +03:00
42e5ace655 feat(notifications+a11y): центр уведомлений /notifications, skip-link, axe smoke
Шаг 2.5 плана production-готовности:
- страница уведомлений (фильтр, догрузка, прочтение) + ссылки из dropdown/мобильного меню
- общие хелперы notification-utils (дедупликация dropdown)
- skip-link + id=content на main
- axe smoke-тесты (0 critical/serious), исправлены 2 нарушения в новой странице
- 3 новых теста (107/107), i18n отложен по решению пользователя
2026-09-08 09:32:36 +03:00
6e7270d2ee refactor(form-editor): разбиение FormEditor.tsx (4975→337) — шаг 2.2 завершён
Шаг 2.2 (часть 4) плана production-готовности:
- 14 файлов в components/form-editor/ (контроллер, FieldCard, вкладки, диалоги)
- сверка 13/15 блоков байт-в-байт, расхождения объяснены
- warnings 566→542, check/build чисто, vitest 104/104

Итог 2.2: Chat 2696→639, TaskDetail 3278→804, Bots 3928→163, FormEditor 4975→337
2026-09-08 08:57:55 +03:00
95d03aeeff refactor(bots): разбиение Bots.tsx (3928→163) — механическая экстракция
Шаг 2.2 (часть 3) плана production-готовности:
- 16 файлов в components/bots/ (типы, контроллер, диалоги, вкладки, секции)
- сверка 28/28 сегментов дословно, порядок хуков 1:1
- check/lint/build чисто, vitest 104/104
2026-09-08 08:12:35 +03:00
52b583c671 refactor(task-detail): разбиение TaskDetail.tsx (3278→804) — механическая экстракция
Шаг 2.2 (часть 2) плана production-готовности:
- 14 файлов в components/task-detail/ (типы, вкладки, диалоги, шапка, таб-бар,
  мобильный accordion, хук условий полей)
- посимвольная сверка: все несовпадения объяснены (export-префиксы/импорты/пропсы)
- check/lint/build чисто, vitest 104/104
2026-09-08 06:53:25 +03:00
6bbedad291 refactor(chat): разбиение Chat.tsx (2696→639) — механическая экстракция без смены поведения
Шаг 2.2 (часть 1) плана production-готовности:
- 8 файлов в components/chat/: типы, утилиты, MessageBubble, диалоги,
  TypingIndicator, useChatController (логика перенесена дословно)
- неизменность доказана посимвольной сверкой с оригиналом
- check/lint/build чисто, vitest 104/104
2026-09-08 01:29:37 +03:00
75ec0f25bb chore(repo): дедуп formatAmount, пины образов, дефолты MinIO, доля any
Шаги 2.3 и 2.4 плана production-готовности:
- lib/format.ts formatAmount вместо 2 локальных копий
- пины тегов minio/rclone/ollama/traccar/pgvector + osrm по digest
- minioadmin без дефолта (env required, dev-подсказки в .env.example)
- 28 any в auto-transitions/shared типизированы из @shared/schema
- удалён _tablefield_orig.txt
- check/lint/build чисто, vitest 104/104
2026-09-08 01:07:55 +03:00
5750a3106f security(auth): anti-enumeration, returnTo-валидация, лимит попыток логина
Шаг 2.1 плана production-готовности:
- единый 401 при любом отказе логина + constant-time bcrypt
- forgot-password: идентичный ответ независимо от существования email
- sanitizeReturnTo (open redirect fix в /api/documents/generate-link)
- 10 неудачных попыток → блок 15 мин (in-memory, аудит)
- DATABASE_SSL_REJECT_UNAUTHORIZED env (дефолт true)
- доделка 0.7: статичные тексты в llm-providers/rag/finance-di2
- 8 новых тестов (104/104)
2026-09-08 00:46:57 +03:00
0828003141 security(deps): npm audit fix — dompurify 3.4.15, tar, tiptap (60→46, 0 critical)
Внеплановый шаг после 1.6: совместимые обновления по npm audit.
Остаток 46 (2 high, 43 moderate) — требует breaking changes, разбор отдельно
2026-09-08 00:31:47 +03:00
b1a3461c33 feat(onboarding+modules): чеклист новой org, MedSchedule за флагом, удаление Capacitor
Шаги 1.9 и 1.10 плана production-готовности:
- OnboardingChecklist (форма→коллеги→задача) на главной, фикс EmptyState в Tasks
- organizations.medschedule_enabled (миграция 0081), гейт модуля по флагу
- Capacitor-стек удалён (5 зависимостей, push-notifications.ts, capacitor.config)
- check/lint/build чисто, vitest 96/96
2026-09-08 00:27:17 +03:00
960f28d3de docs(backups): стратегия бэкапов PostgreSQL (RPO/RTO), проверка восстановления
Шаг 1.8 плана production-готовности: дамп 2026-09-07 03:00 восстановлен
в scratch-БД без ошибок (125 таблиц), процедура задокументирована в DOCKER.md
2026-09-08 00:15:27 +03:00
abd5d4b65f chore(ci+logs): eslint в CI, npm audit, logger с уровнями, retention error_logs
Шаги 1.6 и 1.7 плана production-готовности:
- eslint flat-config (баг-ловушки, легаси warn), lint блокирующий в pr-check
- npm audit --audit-level=high в CI (отчёт)
- фикс реального бага: условный useRef в TaskTitleInline
- server/utils/logger.ts (LOG_LEVEL/LOG_FORMAT) в 5 горячих местах
- error_logs retention 30 дней (worker), redactSensitive в captureErrorLog
- 0 errors lint, vitest 96/96
2026-09-08 00:11:38 +03:00
6eaca79cc6 perf(ui): виртуализация реестра задач и чата (@tanstack/react-virtual)
Шаг 1.3 плана production-готовности:
- TaskRegistry: виртуальные строки с динамической высотой, порог 30
- TaskChat: виртуальные сообщения, порог 100, автоскролл/scrollToMessage
- выделение/сортировка/поиск/inline-редакторы работают по полному массиву
- check/build чисто, vitest 96/96
2026-09-07 23:46:42 +03:00
667a321341 perf(sse+tasks): индекс SSE-соединений, getTaskTree на recursive CTE, батчи N+1
Шаги 1.4 и 1.5 плана production-готовности:
- SseConnectionIndex (byOrg/byUser), один heartbeat-таймер, протокол SSE не тронут
- getTaskTree/getTaskParentChain: WITH RECURSIVE CTE (было 1+2N запросов)
- sync initial/delta: Promise.all по формам
- embedding queue/reindex: батч-предзагрузки вместо поштучных запросов
- 8 новых тестов (96/96)
2026-09-07 23:29:21 +03:00
9528a8123a perf(directories): SQL-фильтрация/пагинация справочников, N+1, лимиты column-values
Шаги 1.1 и 1.2 плана production-готовности:
- getDataTableRowsPaged: фильтры/поиск/сортировка/пагинация в SQL (values->>N)
- getDataTablesWithAccess и list_directories без N+1
- /column-values: limit (дефолт 20, кап 1000), убраны debug-логи с горячего пути
- tree-режим и legacy-путь TableEditor сохранены 1:1
- 10 новых тестов (88/88)
2026-09-07 23:09:15 +03:00
701af06290 feat(billing): PaymentProvider + Mock, self-service пополнение баланса
Шаг 0.13 плана production-готовности:
- интерфейс PaymentProvider (createPayment/verifyWebhook/getPaymentStatus)
- MockPaymentProvider с настоящим webhook (sha256-подпись, идемпотентность)
- applySucceededPayment: транзакция с guard, credit, авто-снятие billingBlocked
- роуты payments + confirm-test (только mock) + публичный webhook
- Billing.tsx: Пополнить/Подтвердить (тест), фикс setLocation в рендере
- миграция 0080 billing_payments, 13 новых тестов (78/78)
2026-09-07 22:45:30 +03:00
23c49adcd1 perf(access): TTL-кэш getAccessibleTaskIds с точечной инвалидацией
Шаг 0.12 плана production-готовности:
- accessibleTasksCache (45с) per userId:orgId внутри storage-метода
- инвалидация в storage-слое: роли, делегирования, form access,
  assignees, createTask/updateTask, appRole пользователя
- 9 новых юнит-тестов (65/65 зелёные)
2026-09-07 22:27:33 +03:00
82d577c797 docs(log): регрессия поиска после 0.10/0.9 пройдена (прод) 2026-09-07 22:11:27 +03:00
a27766a38b perf(tasks): лимиты/пагинация /api/tasks и дельта-синк в SQL
Шаги 0.10 и 0.9 плана production-готовности:
- дефолт limit 50, кап 200; списковые ответы без description
- курсорная пагинация (updated_at, id) + nextCursor
- since-фильтр дельта-синка перенесён в SQL (updated_at >= since)
- embedding pre-count через COUNT(*) вместо выгрузки миллиона строк
- 11 новых юнит-тестов (56/56 зелёные)

Инвариант поиска сохранён: search-ветка /api/tasks?search= не изменена
2026-09-07 22:01:25 +03:00
0a60c6abf1 docs(log): шаги 0.5/0.2 — разбор инцидента с досрочным деплоем Dockerfile
Прод восстановлен (5426c70): app healthy, uid=1001(app), health 200,
миграция 0079 применена, trgm-индексы на месте
2026-09-07 21:45:51 +03:00
5426c70f1f fix(deploy): vite.config импортируется по вычисляемому URL — esbuild без --splitting
esbuild без --splitting инлайнит статический import('../vite.config') в бандл
и поднимает его импорты (vite, @vitejs/plugin-react) на верхний уровень —
prod падал с ERR_MODULE_NOT_FOUND при старте. Динамический import по
вычисляемому URL esbuild не анализирует и оставляет рантайму (dev-only).

Проверено: dist/index.js содержит 0 статических импортов vite, бандл стартует,
подключается к БД и доходит до инициализации таблиц
2026-09-07 21:33:58 +03:00
9fc6ad1bd1 fix(deploy): prod-бандл без devDependencies — динамический импорт vite в setupVite
Шаги 0.5/0.2 плана production-готовности (досрочно ушли с a54ec77):
- Dockerfile: npm ci --omit=dev вместо копирования node_modules из builder,
  пользователь app (USER app), HOME=/tmp, npm_config_cache=/tmp/.npm
- docker/Dockerfile.documents: USER app, HOME=/tmp
- server/vite.ts: vite и vite.config импортируются динамически внутри setupVite —
  иначе prod-бандл падал с ERR_MODULE_NOT_FOUND (@vitejs/plugin-react)
- package.json: vitest/supertest/@types/* (16 пакетов) перенесены
  в devDependencies — именно vitest в dependencies тянул vite/esbuild/tsx в прод-образ

Проверено локально: образ собирается, uid=1001(app), /app/data доступна на запись,
бандл стартует без devDeps (падает только на отсутствии JWT-секретов окружения)
2026-09-07 21:22:27 +03:00
a54ec775dd perf(db): миграция 0079 — pg_trgm GIN-индексы поиска и горячие индексы
Шаг 0.11 плана production-готовности:
- триграммные GIN на tasks.title/description (ilike '%…%' без смены семантики)
- tasks(organization_id, updated_at DESC), tasks(form_id, updated_at)
- field_history(task_id), users(email)

Проверено на 100k задач: Seq Scan 77.5ms → Bitmap Index Scan 4.8ms (~16x)
2026-09-07 21:09:50 +03:00
cf6f937108 security(users): SafeUser — passwordHash/токены не уходят клиенту
Шаг 0.8 плана production-готовности:
- shared/schema.ts: тип SafeUser + safeUserColumns
- getUsersByOrganization, listUsers, searchUsers, getUsersByRole,
  getUsersByOrgRoleId, documents getUser — без чувствительных колонок
- sync (initial/delta), автоматизации ctx.users.list, MCP list_users — sanitized
2026-09-07 21:03:00 +03:00
26419f395f security(errors): 5xx без утечки err.message клиенту
Шаг 0.7 плана production-готовности:
- глобальный error handler: нейтральный текст + код инцидента, детали в лог
- route-уровень: зачистка err.message/String(err) в 5xx по 12 файлам,
  оригинальные ошибки логируются с контекстом
2026-09-07 20:42:39 +03:00
b3d7923b23 test(ci): скрипт test, тесты в pr-check, фикс sporadic 403 в trackUserActivity
Шаг A3 (из 1.6) плана production-готовности:
- package.json: скрипт "test": "vitest run"
- pr-check.yml: шаг Tests после type check
- server/utils/userActivity.ts: try/catch в trackUserActivity — синхронная
  ошибка трекинга активности больше не срывает authenticateToken (ложный 403
  на первом запросе пользователя за минуту)
- tests/auto-transitions.test.ts: 3 устаревших теста обновлены под намеренное
  поведение (forward-only position, синк assignee, аудит вместо системного сообщения)

45/45 тестов зелёные, npm run check чисто.
2026-09-07 20:28:44 +03:00
76a0d859a5 chore(docs): add implementation log
Шаги 0.0 и 0.15 плана доведения до production-готовности:
сквозной журнал исполнения плана с шапкой-контекстом и инвариантом поиска.
2026-09-07 20:13:37 +03:00
a2b231d876 fix(history): московское время и ФИО вместо user ID в истории задачи 2026-09-07 16:55:51 +03:00
9faef8fcf5 SDD: отчёт task 5 — фиксированные цвета графиков DI2; выбор колонок доходов/расходов уже реализован 2026-09-07 00:56:16 +03:00
134b825da5 DI2: фиксированные цвета серий графиков — не зависят от скина/темы
По решению владельца цвета серий (категории, контрагенты, классы просрочки,
прибыль/убыток) должны быть стабильными: одинаковый цвет = одни и те же данные.
Возвращены исходные палитры DI2 (до рестайла 968010c):
- income/expenses-overview, salary: 20 цветов hex (#2563eb, #e11d48, ...)
- calculations: income/expense/profit/tax/toPay/cumulative + HIGHLIGHT_COLORS
- debt: OVERDUE_COLORS по классам (cls0..cls365plus), ДЗ/ПДЗ, фолбэк #94a3b8
- planning: PIE_COLORS hsl, paid/debt, totalDebt/totalOverdue, прогноз/факт, легенда
- profitability: income/expense/profit (hsl из оригинала)
- income-categories: TAG_COLORS (8 hex)
- dashboard: палитра из исходной темы DI2 (hsl(210 85% 42%) и т.д.), долг hsl(0 72% 51%)

Не тронуты: цвета текста/осей/сеток/тултипов/фонов (семантические токены скина).
2026-09-07 00:55:32 +03:00
d36a13aa36 SDD: леджер и отчёт этапа 4 (проверки + документация di2-restyle) 2026-09-07 00:28:13 +03:00
ffa8d83fa7 SDD: леджер — этап 3 принят 2026-09-07 00:17:23 +03:00
47b785edcd DI2 restyle: отчёт этапа 3 (визуальный рестайл) 2026-09-07 00:06:47 +03:00
968010c411 DI2: визуальный рестайл — графики и цвета по токенам скина, таблицы по UI Style Guide, мобильная нижняя панель, переключатель темы
- recharts: все hex/hsl палитры → var(--chart-1..5), семантика → var(--info/success/warning/destructive)
- палитровые Tailwind-классы (red/blue/green/amber и т.д.) → семантические классы через color-mix (Tailwind v3 не генерирует alpha для var-цветов)
- ui/table.tsx: шапка по гайду (bg-muted/50, text-[12px]), строки h-[var(--table-row-height,40px)], ячейки px-3, hover muted 40%
- lib/field-styles.ts (порт из основного клиента) + fieldControlClass в column-filter
- app-sidebar: брендинг «ОмегаПром / Финансы», компактные отступы
- bottom-nav.tsx: мобильная нижняя панель (Обзор/Разделы/Настройки) по референсу FinanceBottomNav, safe-area-bottom в index.css
- переключатель темы Sun/Moon в хедере (useDi2Theme), SidebarTrigger скрыт на мобильном
- заголовки страниц text-lg font-semibold
2026-09-07 00:04:49 +03:00