Commit Graph

224 Commits

Author SHA1 Message Date
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
ba0d3ac85a SDD: леджер — этап 2 принят 2026-09-06 23:42:08 +03:00
0834356286 DI2: отчёт по этапу 2 (мост тем/скинов iistwin) 2026-09-06 23:33:18 +03:00
b4579340ff DI2: мост тем/скинов iistwin — Di2ThemeProvider, общий generated-skins.css, токены в формате основного клиента
- vite.config.di2.ts + client-di2/tsconfig.json: алиас @skin → client/src/themes (tokens, skins, generated-skins.css — единый источник токенов)
- client-di2/src/themes/Di2ThemeProvider.tsx: порт логики ThemeProvider основного клиента (localStorage['skin'] → скин организации → 'iistwin'; runtime CSS кастомных скинов через generateSkinCss; data-skin/data-theme/class light|dark на <html>; контекст с toggleTheme). Ключи 'skin'/'theme' общие с iistwin — выбор синхронизируется между приложениями
- main.tsx: обёртка <Di2ThemeProvider> вокруг App
- tailwind.config.ts DI2: цвета hsl(var(--x) / <alpha-value>) → var(--x), радиусы → var(--radius) с каскадом calc, добавлены warning/success/info
- client-di2/src/index.css: собственные HSL-тройки заменены fallback-блоками основного клиента (до гидратации), первой строкой @import общего generated-skins.css; DI2-only переменные (--card-border, --popover-border, *-border, --button-outline, --badge-outline) переопределены через color-mix от семантических токенов; удалены --opaque-button-border-intensity и hsl(from ...) relative-color вычисления; жёсткие --font-sans: Open Sans убраны (шрифты эмитит generateSkinCss из токенов скина)
- client-di2/index.html: lang=ru, viewport без maximum-scale + interactive-widget=resizes-content, мега-link Google Fonts (23 семейства) заменён на Inter + DM Mono как в основном клиенте
- Alpha-модификаторы семантических цветов (166 мест: bg-muted/50, border-primary/30 и т.п.) заменены на arbitrary values color-mix(in oklab, var(--x) NN%, transparent) — Tailwind v3 молча не генерирует /NN для var()-цветов; палитровые цвета (green-500/10 и т.п.) не тронуты
2026-09-06 23:31:43 +03:00
10a2c1a629 SDD: леджер — этап 1 принят 2026-09-06 22:04:22 +03:00
4c260f2aaf DI2: отчёт по этапу 1 (удаление старого модуля финансов) 2026-09-06 21:55:21 +03:00
66f4e5ef53 DI2: удаление старого модуля финансов — пул БД и аудит перенесены в finance-di2
- server/finance/ (19 файлов) удалён: пул финансовой БД перенесён в server/finance-di2/db-core.ts, db-client.ts теперь реэкспортирует из ./db-core
- Шедулер аудита перенесён в server/finance-di2/audit-scheduler.ts (03:00 МСК, setInterval, без node-cron), запуск из registerDi2Routes с защитой от повторного старта
- История аудита DI2 теперь персистентна: загрузка/запись data/finance-audit-history.json в audit-agent.ts (порт из удалённого server/finance/audit.ts)
- registerFinanceRoutes убран из server/routes/index.ts, registerDi2Routes остался на его месте
- Клиент: удалены pages/finance/, components/finance/, hooks/useFinance.ts, hooks/useColumnLabels.ts, lib/format.ts, pages/salary.tsx; роуты /finance*, /income*, /expenses*, /salary и lazy-импорты убраны из App.tsx
- Навигация: «Финансы» в Sidebar и MobileBottomNav ведёт на субапп /di2 (внешняя ссылка), гейт moduleAccess.finance + finance.view сохранён
- index.css: удалён блок .hover-elevate/.hover-elevate-2/.active-elevate (использовался только старым модульным DataTable)
- FinanceBottomNav.tsx сохранён как референс для этапа 3 (.superpowers/sdd/di2-restyle/FinanceBottomNav.reference.tsx)
- Не тронуто: client-di2/, сиды прав finance.view/manage, finance_access (module-access)
2026-09-06 21:53:47 +03:00
6bcf4dfdfc DI2: навигация сайдбара на wouter Link — base /di2 подставляется в href и клики не покидают субапп (прямые <a href> уводили на iistwin) 2026-09-06 20:53:44 +03:00
f993c4d1f0 DI2: дашборд клиента ходил на старый /api/dashboard (fetch-шаблон не был переименован) — CRM-формат ломал рендер. Фикс URL на /api/di2/dashboard 2026-09-06 20:40:42 +03:00