Commit Graph

57 Commits

Author SHA1 Message Date
bde9c2a641 feat(gps): левая панель-дерево объектов вместо select'ов; fix(csp): разрешён api-maps.yandex.ru для JS API 2.1 2026-08-25 14:59:09 +03:00
104b3ca422 feat: модуль GPS-мониторинга (карта Яндекс.Карт, Traccar, геозоны, маршруты, группы) 2026-08-24 19:30:11 +03:00
c0e2ddd9c8 Исправлено дублирование сообщений в task-чате: защита от двойного Enter/клика по кнопке отправки 2026-08-24 16:53:25 +03:00
8b713e66c0 Исправлено дублирование сообщений в task-чате: защита от двойного Enter/клика по кнопке отправки 2026-08-24 16:50:05 +03:00
a4eaf65915 Отслеживание просмотров задач: task_views, POST /api/tasks/:id/view и обновление /api/home/recent 2026-08-24 15:40:30 +03:00
e93e39383f Исправлено сохранение настроек кнопки (buttonActionType, buttonUrl, buttonFormId) в bulkSaveFormFields 2026-08-21 11:42:43 +03:00
281f8da995 fix: rate limiting по пользователям из JWT + cleanup WebPush подписок и cooldown sync-push 2026-08-20 14:17:13 +03:00
5ff2aaa023 fix: RAG embedding_queue ON CONFLICT + пересоздание finance views
- enqueueEmbedding: onConflictDoNothing без предиката не находил
  частичный уникальный индекс embedding_queue_pending_entity_idx
  (WHERE processed_at IS NULL) — каждая постановка в очередь индексации
  падала. Добавлен where: isNull(processedAt).
- finance ensureViews: CREATE OR REPLACE VIEW не умеет менять тип столбца
  (stavkands text↔varchar во внешней БД omegaprom) — заменено на
  DROP VIEW IF EXISTS + CREATE VIEW (зависимых объектов нет).
2026-08-06 15:10:38 +03:00
47869971e6 feat: объединение страниц профиля — /profile рендерит карточку пользователя
- /profile теперь обёртка MyProfilePage над UserProfilePage (один экран для своего и чужого профиля)
- self-вкладки из ProfileSettings перенесены: Уведомления, Offline, Интерфейс, Безопасность (components/profile/*)
- ProfileSettings.tsx удалён
- админ видит вкладки Уведомления и Offline на чужой карточке с данными целевого пользователя
  (userId-параметр в GET/POST /api/subscriptions, PATCH/DELETE по владельцу, GET /api/sync/config?userId=; проверка canManageUser)
- Роль/Активен/Статус disabled для не-админа (сервер их и раньше игнорировал)
- пункт меню «Настройки профиля» переименован в «Мой профиль»
2026-08-05 11:48:07 +03:00
667bddeac9 feat: замещение пользователей (substitution) — заместители по умолчанию и по формам, контур ознакомления, inbox reasons
- таблица user_delegations (миграция 0067) + колонки delegated_from_user_id/pending_review в task_assignees
- delegation.service: резолвер с цепочкой и защитой от циклов, bulk-до-назначение чанками, чистка истёкших замещений
- центральный хук в addTaskAssignee: заместитель добавляется при любом назначении (ручном, автопереходе, user-поле, MCP)
- контур ознакомления: после перехода отсутствующий остаётся в исполнителях с pending_review, снимается кнопкой «Ознакомлен»
- inbox reasons delegation/delegation_review + фильтр «Замещение» на главной
- блок «Замещение» в профиле пользователя, бейдж «за <имя>» и баннер ознакомления в карточке задачи
2026-08-05 10:44:55 +03:00
d562735eca fix(TaskDetail): фиксы per-field редактирования по тестам прода
- server: rich-text/company/task разрешены в PATCH field-values (была 400, поля не сохранялись)
- user-поле: дропдаун открывается сразу при редактировании, текущее значение всегда в списке
- select.tsx: viewport max-h-60 вместо высоты триггера (список показывал 1 пункт)
- плотность строк: min-h 28/36px, leading-5, read-only системные строки — py-1 как раньше
- вход в редактирование: double-click/double-tap (одиночный клик — только карандаш)
- textarea/table/task/company: компактная ширина редакторов, h-7
- сброс pendingAutofillRef при отмене; нативный select не отменяется по blur
2026-08-03 22:20:30 +03:00
9a387c5db0 fix: бесконечный ретрай upload 400, скобки в именах файлов, тост batch-create
- useOfflineSync: tryUploadFile/trySendAttachmentMessage бросали ошибку без
  кода статуса — постоянная 400 не распознавалась regex \b400\b и вложение
  ретраилось бесконечно (спам POST /api/upload каждые ~30 сек).
- validateFilename: разрешены скобки () в именах (типично для фото с
  телефонов; сохраняемое имя всё равно генерируется), в текст 400 добавлено
  само имя файла для диагностики.
- BatchCreateDialog: тост ошибки показывает текст сервера (из BadRequestError
  или JSON в message 5xx) вместо общего «Ошибка создания задач».
2026-08-03 15:25:11 +03:00
1d5321ed0c feat: чанкинг batch-create >100 задач + журнал ошибок API
B: BatchCreateDialog режет список на чанки ≤100 (лимит сервера), каждый
чанк со своим idempotency-key (requestId-N) — повтор после ошибки не
создаёт дублей; 10-минутный лок армируется только при полном успехе,
а не до запроса (раньше блокировал даже после 400).

D: таблица error_logs (миграция 0066), захват ответов >=400 в request
middleware (без 401/health/events, дедуп 60с), admin API GET/DELETE
/api/error-logs и вкладка «Журнал ошибок» в Настройках.
2026-08-03 14:55:29 +03:00
1bb6a53226 feat: закрытые задачи в глобальном поиске зачёркнуты + бейдж статуса
searchTasksByKeyword теперь возвращает isCompleted и через leftJoin
formStatuses — statusName/statusColor/statusIsFinal. В HomeGlobalSearch
закрытые задачи (isCompleted || statusIsFinal) зачёркнуты и приглушены,
у каждой задачи цветной бейдж текущего статуса.
2026-08-03 13:29:36 +03:00
4ace1369ca fix: согласующие перехода — только авторизация, без автоназначения
assigneeUserId/assigneeConditions перехода раньше и давали право нажать
кнопку, и молча переназначали задачу на согласующего; кликнувший также
всегда добавлялся в task_assignees (список «Ответственный»).
Теперь ответственный при переходе всегда сохраняется, кликнувший в
ответственные не добавляется (доступ трекается через recordTransitionAccess).
В FormEditor добавлена подсказка о семантике блока «Согласующие».
2026-08-03 11:18:14 +03:00
b4bded2ffb feat: триггер schedule для автоматизаций — ежедневный запуск по МСК
- планировщик server/workers/automation-scheduler.ts (тик 60 сек, все организации)
- защита от дублей: automations.last_scheduled_run_at (миграция 0065), catch-up в пределах суток
- UI: выбор триггера 'По расписанию' и времени запуска в Automations.tsx
- MCP: create_automation/update_automation принимают trigger 'schedule'
- triggerData: { trigger: 'schedule', scheduledAt }
2026-07-24 13:52:03 +03:00
c152a670ae fix: числовая сортировка date/datetime/number полей в реестре задач
Сортировка кастомных полей шла лексикографически по отображаемой строке:
даты «ДД.ММ.ГГГГ» сортировались по дню («01.12.2025» < «02.01.2024»),
числа как строки («10» < «9»). Теперь для field_<id> типов date/datetime/number
сравнение идёт по сырому значению (timestamp/число), пустые — всегда в конец.
2026-07-24 12:03:16 +03:00
59079992dc Фикс предпросмотра файлов с абсолютными self-URL
- extractFileKey понимает абсолютные URL (по pathname) — превью работает для уже записанных файлов
- normalizeFileUrl: абсолютные self-URL нормализуются в относительные при записи file-полей (REST field-values, MCP update_task_fields/upload-инструменты, bot-api, create task)
2026-07-24 11:10:55 +03:00
b981614095 MCP: инструмент update_task_fields — обновление кастомных полей задачи
- Merge-семантика как POST /api/tasks/:id/field-values: upsert/delete, поля типа task пересоздают связи
- Аудит field.changed с актором ключа (бот + metadata.source='mcp'), side-эффекты: автопереходы, заголовок, кэши, SSE
- Доступен только в режиме full, как update_task
2026-07-23 16:31:45 +03:00
affbdeee1e Upload-тикеты: загрузка файлов через REST без знания API-ключа
- MCP create_upload_ticket → короткоживущий uploadUrl (10 мин, in-memory)
- POST /api/upload/ticket/:token — тот же пайплайн (processUploadRequest вынесен из /api/upload), авторизация тикетом, проверки скоупов
- get_api_guide: флоу без ключа — ticket → curl → привязка по fileUrl
2026-07-23 15:53:17 +03:00
af119d1cdc MCP: удалён base64-вариант загрузки файлов — только REST upload + привязка по fileUrl
- upload_file удалён; upload_task_file/upload_message_file/upload_directory_file/upload_table_row_file принимают только fileUrl (+fileName)
- descriptions указывают на REST POST /api/upload и get_api_guide
- server/services/file-upload.service.ts удалён (без base64 не используется)
2026-07-22 16:22:26 +03:00
b3818852df UI: API/MCP-доступ в карточке бота, legacy-ключи в Settings, бот и канал в истории (этап 3)
- ApiScopesEditor — общий редактор скоупов (режим + формы/справочники)
- Bots.tsx: секция «Доступ к API и MCP» при создании и в настройках бота, показ ключа один раз, перевыпуск с подтверждением
- Settings.tsx: управление ключами убрано, read-only список legacy-ключей
- TaskHistory/TaskDetail: иконка бота + бейдж канала MCP/API у записей аудита
- GET /api/bots/:id возвращает apiKey
2026-07-22 15:35:34 +03:00
8cfd49fd9f Атрибуция от бота в MCP/REST + REST API по ключу (этап 2)
- MCP: getActor (владелец ключа + бот), аудит changedByName=бот/label ключа, botId, metadata.source='mcp'; аудит для update_task/update_task_status
- sendTaskMessage: botId (messageType 'bot'), починен путь isBotToken без req.user
- authenticateTokenOrApiKey: JWT или X-Api-Key с белым списком endpoint'ов (/api/upload, messages, field-values, create task), проверки скоупов, аудит source='api'
- Лимиты файлов через env: UPLOAD_IMAGE_MAX_MB=25, UPLOAD_DOC_MAX_MB=100, UPLOAD_MAX_MB=100
- MCP upload-инструменты: параметр fileUrl для привязки уже загруженного файла; новый инструмент get_api_guide
2026-07-22 15:18:04 +03:00
68153caef2 API-ключи привязаны к ботам 1:1 (этап 1)
- Миграция 0064: organization_api_keys.bot_id (unique partial), task_audit_log.bot_id, file_uploads.bot_id
- POST/PUT /api/bots: apiAccess {enabled, mode, formIds, tableIds} — ключ создаётся/обновляется/деактивируется вместе с ботом, сырой ключ показывается один раз
- POST /api/bots/:id/api-key/regenerate; GET /api/bots возвращает apiKey summary
2026-07-22 14:42:32 +03:00
1fae441a09 Безопасность: bot-токены выделены в отдельный тип bot_login (этап 0)
- POST /api/bot/auth/login выдаёт bot_login/bot_login_refresh (audience workflow-bots, TTL 30d/90d)
- authenticateBot принимает bot_login и bot_service
- authenticateToken/authenticateFileToken отклоняют bot-токены (401) — закрыта коллизия bot.id с user.id
2026-07-22 14:32:11 +03:00
72de9a25fb fix: шаблон названия резолвит массив id справочника в отображаемые значения 2026-07-22 14:04:39 +03:00
936bce2ba9 MCP: загрузка файлов (base64) в file-поля, комментарии, справочники и таб-таблицы задач
- server/services/file-upload.service.ts: uploadFileFromBase64 (whitelist расширений, magic bytes, лимиты 10/50 МБ, S3/MinIO или локальный диск, запись file_uploads)
- Инструменты (write/full): upload_file, upload_task_file, upload_message_file, upload_directory_file, upload_table_row_file
- Проверки доступа isFormAllowed/isTableAllowed
2026-07-22 11:28:45 +03:00
25f84ee532 MCP: напоминания, уведомления, inbox, агрегации, update_form, дерево задач, поля пользователей, DaData — этап 3
- read: list_task_reminders, list_notifications, get_inbox, aggregate_tasks, get_task_tree, get_user_field_values, dadata_suggest
- write: set_task_reminder, send_notification, mark_notifications_read
- full: delete_task_reminder, update_form, set_user_field_value
2026-07-21 17:30:50 +03:00
05e09fa08b MCP: сообщения задач, исполнители, документы, файлы, аудит — этап 2
- list_task_messages, get_task_assignees, list_document_templates, get_task_file, get_task_audit_log (read)
- send_task_message, generate_document (write); set_task_assignees (full)
- Логика POST /api/tasks/:id/messages вынесена в server/services/task-message.service.ts (переиспользуется route и MCP)
- get_task_file: нативный S3 presigned URL или локальный путь с пояснением
2026-07-21 17:19:22 +03:00
002ce152be MCP: инструменты справочников (data tables) — этап 1
- 15 инструментов: list/get_directory, list_directory_rows (flat/tree), get_directory_row, get_directory_column_values (read); create/bulk_create_directory_row (write); create/update/delete_directory, update/delete/move_directory_row, link/unlink_directories (full)
- Объектные проверки isTableAllowed по scopes.tableIds
- Логика дерева строк вынесена в server/utils/data-table-tree.ts (переиспользуется REST-эндпоинтом и MCP)
- bulkCreateDataTableRows: поддержка parentId и position
2026-07-21 17:02:04 +03:00
a7733b4a03 Права доступа API-ключей: скоупы read/write/full и выборочный доступ к формам и справочникам
- organization_api_keys.scopes (jsonb, NULL = полный доступ для legacy-ключей)
- MCP: фильтрация инструментов по режиму и проверка formIds в handler'ах
- PATCH /api/mcp-keys/:id, валидация скоупов в server/utils/api-key.ts
- UI Settings: диалог создания/редактирования ключа с режимом и мультивыбором форм/справочников
2026-07-21 16:12:55 +03:00
08b0979ca2 feat: группа без детей с пустым отображением становится конечной строкой в дереве 2026-07-21 15:17:38 +03:00
9e1612c4c3 fix: отображение последнего заполненного значения в дереве справочника 2026-07-21 15:00:17 +03:00
9edd90211f feat: привязка подсправочника к строкам родителя (parentRowIds) 2026-07-21 10:43:28 +03:00
7cf5ffff9a feat: многоуровневое дерево справочника и настраиваемое отображение колонок 2026-07-20 21:59:36 +03:00
aea0e9c3fe feat(vpn): монитор актуальности Яндекс Телемост + инструкция про выход из встречи
- INSTRUCTIONS_TEXT: добавлено требование выйти из встречи, а не завершать её
- vpn.db.ts: findAllActiveVpnTasks для поиска активных VPN-задач
- vpn.service.ts: checkVpnRoomsValidity — периодическая проверка комнат, concurrency=5
- vpn-bot.service.ts: findVpnBotConversationId и notifyVpnRoomExpired
- server/index.ts: worker каждые 6 часов, уведомление пользователю о протухшей ссылке
2026-07-17 13:08:24 +03:00
9f1c2e5b45 perf(vpn-bot): фоновая подготовка подписки, оптимизация запросов, анимация кнопок
- validateTelemostRoom: таймаут 3 сек + кэш 10 мин
- createVpnTask: транзакция + batch insert field values
- prepareVpnSubscription: валидация и создание подписки в фоне при получении ссылки
- sendVpnBotMessage: кэш botId, Promise.all для enrich/members/orgMap
- processPlatformSelection: одно сообщение с файлом и подпиской вместо двух
- Chat.tsx: нажатая кнопка становится зелёной галочкой, остальные disabled
2026-07-17 12:38:33 +03:00
6bb89b4cf8 feat(chat): typing indicator для ботов, кнопки ботов в мессенджере, VPN-бот с кнопками
- Добавлены SSE-события conv_typing_started/stopped и их обработка в Chat.tsx
- AI-бот публикует typing events перед/после генерации ответа
- Боты могут отправлять сообщения с кнопками через /api/bot/conversations/:id/messages
- Добавлен callback /api/messenger/messages/:id/callback для нажатий кнопок
- VPN-бот использует кнопки Android/iOS вместо текстового prompt
- Оптимистичное обновление unread count, ускоренный reportActiveChat, polling fallback,
  логирование SSE, push suppression для bot_direct
2026-07-17 11:25:25 +03:00
586a591773 fix(vpn): восстановление VPN-модуля после затирания chat/push fix'ом
- Добавлена ветка bot.type === 'vpn' в messenger.messages.routes.ts
- Добавлен полный модуль server/vpn/ (routes, service, db, bot, config)
- Подключены VPN-маршруты в server/routes/index.ts
- Добавлена раздача /apps статики и COPY в Dockerfile
- apps/ пока содержит .gitkeep и README; APK/IPA нужно добавить отдельно
2026-07-17 09:32:43 +03:00
0a5f4516ef fix(push.routes): req.user!.userId -> req.user!.id
У объекта пользователя из authenticateToken поле id, а не userId.

Это чинит 500 на /api/devices/active-chat и других device-эндпоинтах.
2026-07-16 20:42:55 +03:00
7672dd98df fix(chat): подавлять push-уведомления при открытом активном чате и автоотметка прочтения в task chat
- Добавлены active_chat_* поля в user_presence и endpoint /api/devices/active-chat

- Сервер не шлёт Web Push/Pushy, если получатель смотрит в тот же чат на видимой вкладке

- Клиент сообщает серверу и Service Worker об активном чате из Chat.tsx и TaskChat.tsx

- TaskChat: входящие сообщения автоматически отмечаются прочитанными при видимой вкладке

- SW: подавляет foreground/system push, если активный чат совпадает
2026-07-16 18:59:00 +03:00
2b682d5ad3 fix: allow app admins to access MedSchedule UI and API 2026-07-15 11:10:46 +03:00
9c6eaa5dcf feat: подсправочники — отображение в форме задачи, фильтрация и отображение значений 2026-07-15 10:56:25 +03:00
f560facabd fix: MedSchedule ESM import for Router and data table links resolution 2026-07-15 09:58:07 +03:00
f519ce95db feat: MedSchedule модуль — семейный медицинский помощник с Telegram/MAX ботом, n8n OCR и /med UI 2026-07-15 09:42:55 +03:00
0da6c04790 fix(permissions): restore admin access after page reload
- /api/auth/me now returns app-role permissions (was empty after reload).
- client hasPermission/hasAnyPermission now bypass admin so UI queries are not disabled when permissions array is stale.
2026-07-13 16:16:46 +03:00
d91dd2bd92 feat(directories): import/export hierarchical data tables with template button
- server/storage/data-tables-core.storage.ts: support parentId in sync operations
- server/routes/data-tables-sync.routes.ts: validate parent references and cycles for hierarchical rows
- client/TableEditor.tsx: export/import _id/_parent_id/_position, add Download Template button
2026-07-12 23:29:35 +03:00
09861fc966 feat: настраиваемая колонка отображения в дереве справочника
- Добавлено поле data_tables.tree_display_column_index.
- Backend /api/directories/:id/rows/tree возвращает displayColumnIndex и
  формирует label для row-узлов на основе выбранной колонки.
- В редакторе справочника добавлен селектор «Колонка для отображения в дереве».
- DirectoryRowTree, DirectoryColumnTree, DirectoryTreeSelect,
  DirectoryTreeMultiSelect используют настроенную колонку.
- TableFieldSelect / TableFieldMultiSelect используют
  treeDisplayColumnIndex справочника как fallback, если в поле формы
  не задан displayColumn.
- Миграция 0057 + запись в _journal.json.
2026-07-12 23:07:34 +03:00
782731674b Древовидные справочники: выбор колонки для дерева (treeColumnIndex) и API /rows/tree 2026-07-12 21:59:38 +03:00
ec08f3222d Древовидные справочники: parent_id, drag&drop, древовидный выбор в полях table 2026-07-12 20:55:30 +03:00