- INSTRUCTIONS_TEXT: добавлено требование выйти из встречи, а не завершать её
- vpn.db.ts: findAllActiveVpnTasks для поиска активных VPN-задач
- vpn.service.ts: checkVpnRoomsValidity — периодическая проверка комнат, concurrency=5
- vpn-bot.service.ts: findVpnBotConversationId и notifyVpnRoomExpired
- server/index.ts: worker каждые 6 часов, уведомление пользователю о протухшей ссылке
- validateTelemostRoom: таймаут 3 сек + кэш 10 мин
- createVpnTask: транзакция + batch insert field values
- prepareVpnSubscription: валидация и создание подписки в фоне при получении ссылки
- sendVpnBotMessage: кэш botId, Promise.all для enrich/members/orgMap
- processPlatformSelection: одно сообщение с файлом и подпиской вместо двух
- Chat.tsx: нажатая кнопка становится зелёной галочкой, остальные disabled
- Добавлена ветка 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 нужно добавить отдельно
- Добавлены active_chat_* поля в user_presence и endpoint /api/devices/active-chat
- Сервер не шлёт Web Push/Pushy, если получатель смотрит в тот же чат на видимой вкладке
- Клиент сообщает серверу и Service Worker об активном чате из Chat.tsx и TaskChat.tsx
- TaskChat: входящие сообщения автоматически отмечаются прочитанными при видимой вкладке
- SW: подавляет foreground/system push, если активный чат совпадает
- /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.
- Добавлено поле 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.
- queryClient: refreshSession возвращает reason (network/unauthorized);
при network-ошибке не делаем logout, а бросаем network_error_during_refresh.
- useAuth: checkAuth и refreshUser не сбрасывают сессию при network-ошибке
во время refresh, переводят в офлайн-режим.
- auth.service: remember берётся из сессии, race tolerance 5 минут.
- access token lifetime унифицирован до 30 дней по умолчанию, cookie maxAge
теперь совпадает с JWT expiry (было 15 минут fallback).
- Add client/src/themes/ with tokens, built-in skins (iistwin, twenty, omegaprom)
- Add ThemeProvider with data-skin/data-theme and runtime CSS for custom skins
- Add npm run skins:generate script for build-time CSS generation
- Add organization_skins table, migration and CRUD API
- Add activeSkinId to organizations and expose it in /api/auth/me
- Add skin switcher in /profile and admin skin editor in /settings/appearance
- Adapt index.css hardcoded colors to skin tokens
- Update AGENTS.md and Obsidian notes
- number-поля теперь рендерятся как text + inputMode=numeric,
чтобы браузер не округлял значения через input type=number
- пробелы при вставке в number-поля удаляются
- бэкенд нормализует значения number-полей в строку перед сохранением
- добавлен хелпер normalizeFieldValueForStorage
Closes: искажение расчётного счёта и других длинных числовых полей