Работы по сайту, CRM, ботам и интеграциям пошагово и постоянно сохраняются на этой странице.
Последнее обновление: Записей: 36
Интерфейс CRM
Запущены скролл сайдбара, сворачиваемое меню и fluid-раскладка CRM
Выполнено
В сайдбаре CRM верхний и нижний блоки теперь закреплены, само меню прокручивается отдельно, а активный пункт автоматически попадает в видимую область. На desktop меню сворачивается до иконок и запоминает состояние; на планшете открывается полный drawer. Dashboard и таблицы используют всю доступную ширину до 1600px.
Scrollbar сайдбара 6px: скрыт в покое, 18% белого при hover меню и 30% при hover ползунка
В светлых областях нативный тёмный scrollbar 15%; горизонтальные скроллы таблиц, board и stepper — 6px
Меню 232px сворачивается в 72px icon-режим, подписи скрываются, tooltip и localStorage сохраняются
Нижний блок RESTORIA CRM закреплён, прокручивается только nav, активные Настройки автоматически видимы
Контент fluid с боковыми отступами 24px и максимумом 1600px только на очень широком экране
Ширина dashboard/таблиц и отсутствие horizontal overflow проверены на desktop 1280/1440/1920 и планшете 900px
Успешны production build CRM и read-only browser acceptance 34/34 локально и в production
Все телефонные поля переведены на единый формат +998
Выполнено
Сайт и CRM теперь используют один общий PhoneInput, а Staff-приложение — нативный компонент с теми же правилами. Префикс не удаляется, пользователь вводит 9 цифр, разные форматы вставки приводятся к +998XXXXXXXXX, а неполный телефон нельзя отправить. Также опубликован новый подписанный Staff APK.
Исправлены все формы сайта: callback, квиз и оценка по фото
Телефоны CRM в login, заказах, клиентах, лидах, звонках, мастерах, курьерах, филиалах, сотрудниках, чатах и Настройках используют shared-компонент
Нормализуются +998, 998, локальные 9 цифр, пробелы/скобки/дефисы и формат 00998
Обязательное пустое и любое частичное значение блокирует submit; подсказки доступны на RU/UZ
Аудит кода подтвердил отсутствие raw web/mobile phone inputs и блокирует будущую регрессию в CI
Запущены современный центр управления и контроль Telegram auto-lead
Выполнено
Настройки стали единым поисковым центром управления организацией и workflow, сайтом, каналами, зашифрованными интеграциями, AI, скриптами, правами команды, филиалами, каталогом и исполнителями. SUPERADMIN аудируемо включает или выключает auto-lead для private-чатов Telegram; по умолчанию и в production он OFF, а группы и импорт истории всегда исключены.
Production-toggle явно сохранён в OFF, изменение записано в аудит
Только новый входящий private-чат Telegram может создать лид при включённом toggle
Группа Telegram, исходящее сообщение и импорт истории никогда не создают лид
40/40 прежних Telegram-import лидов переведены в ARCHIVED, активных 0, данные не удалены
Settings UI показывает реальные значения: 40 архивных, 0 ожидающих и импорт всегда OFF
Поиск и 11 карточек управления объединяют возможности CRM в единый каталог
Production browser acceptance Settings прошёл на 1440/1280/900 px без overflow и runtime-ошибок
CRM-чаты и лиды
Остановлены авто-лиды из чатов и запущен полноценный CRM-мессенджер
Выполнено
Входящие Telegram-чаты в production не создают auto-lead, а импорт истории защищён от этого всегда: лид явно создаёт менеджер, а site/quiz и курьерские заявки продолжают работать. В CRM-чате доступны реальная отправка ответа и файла, AI-черновик, шаблоны, emoji, статусы доставки, повтор ошибки, поиск, сброс unread и адаптивная панель клиента. Заказ из чата атомарно связывает диалог с клиентом.
Входящее и импортированное сообщение не создаёт лид; «Создать лид» — отдельное управляемое действие
40 прежних авто-лидов отмечены CHAT_IMPORT и переведены в ARCHIVED с аудитом; активных лидов 0
Legacy-лиды CHAT_IMPORT не конвертируются скрыто по телефону нового заказа
Ответ менеджера и реальный PNG доставлены в Telegram; sending/delivered/failed и retry сохраняются в БД
Работают AI-черновик, шаблоны из Скриптов, emoji, Enter/Shift+Enter и серверный поиск внутри диалога
Unread сбрасывается при открытии; есть управляемый автоскролл и кнопка перехода к последним сообщениям
Правая панель не обрезается; видны быстрые действия заказа, лида, карточки клиента и звонка
Production-цепочка Telegram→CRM→ответ→вложение→AI→заказ прошла 14/14, browser 1440/1280/900 и 53 suite/409 тест успешны
Клиентский Telegram-бот
Запущены второй номер, реальный вызов курьера и отзывы
Выполнено
После подтверждения основного контакта бот предлагает дополнительный телефон и ищет заказы по всем связанным номерам. Вызов курьера по геолокации, дате, настроенному временному слоту и комментарию создаёт реальную заявку CRM и точку маршрута. Отзывы с оценкой 1–5 и текстом поступают на модерацию; прежние временные заглушки удалены.
Форматы +998, 998, локальные 9 цифр и номер с пробелами приводятся к единому +998XXXXXXXXX
Код мобильного оператора проверяется, подтверждённый второй номер сохраняется как verified-канал клиента
«Мои заказы» находит по связанным телефонам даже заказ, оставшийся на старой дублирующей карточке клиента
Если заказов и второго номера нет, бот предлагает тот же поток добавления телефона
Для курьера работают Telegram-геолокация или ручная точка карты, Сегодня/Завтра, 4 действующих слота и необязательный комментарий
Для неизвестного телефона атомарно создаются клиент и лид, pickup-заказ, точка/задача курьера, уведомление менеджера и аудит
Route insertion запускается существующим алгоритмом; клиент получает от бота подтверждение даты и слота
Текстовый отзыв с 1–5 звёздами поступает в CRM-модерацию неопубликованным
Успешны 51 backend suite/389 тестов, 27 focused-тестов, restore snapshot, production read-only и Telegram getMe, изолированный compiled-API E2E 17/17
Заказы и контроль
Запущен безопасный контролируемый откат статуса заказа
Выполнено
Администратор теперь возвращает ошибочный статус только с обязательной причиной и после предварительного просмотра последствий. Система в одной контролируемой транзакции обрабатывает чек, начисления мастеров, производственный этап, курьерские задачи и уведомление клиента; каждое действие записывается в аудит с автором и причиной.
ADMIN и SUPERADMIN имеют право всегда; доступ MANAGER отдельно включает только владелец системы
Для причины «Другое» обязателен текст, а все последствия показываются на RU/UZ до подтверждения
Невыплаченное начисление мастера возвращается в «Ожидается»; выплаченная сумма не меняется и получает отметку аванса
Чек не отзывается, производственные фото сохраняются, а этап переоткрывается тому же мастеру
Гарантийный возврат создаёт отдельный флаг и бесплатную для клиента доработку
Обычный откат не уведомляет клиента; гарантийный брак — единственное исключение
В ленте автор, направление и причина видны со знаком «⟲»; полный audit-log сохраняется
Успешны 49 backend suites/362 теста, изолированный restore БД+медиа, dry-run миграции, production API и browser acceptance на 1440px
Безопасность и интеграции
Ключи интеграций перенесены в централизованное зашифрованное хранилище
Выполнено
Запущено единое управление секретами ботов, Telegram, AI, Instagram и будущих внешних провайдеров. Каждое значение отдельно шифруется AES-256-GCM; браузер видит только маску, а само значение не попадает в логи, аудит или ответы API. Superadmin может безопасно заменить ключ и проверить подключение в Настройках.
Проведена полная инвентаризация действующих env и legacy DB-хранилищ без раскрытия значений
10 действующих credentials перенесены в центральную зашифрованную таблицу; остаток plaintext в legacy site, channel и Telegram session равен 0
Master key находится вне БД и Git в ограниченном файле; env хранит только ссылку на этот файл
Кэш секретов инвалидируется при изменении или удалении; повторный запуск миграции безопасно завершается no-op
«Интеграции и ключи» доступны только SUPERADMIN; настроенные значения показаны исключительно как ****1234
Для ADMIN secret API отвечает 403; Telegram API ID/hash, Instagram verify token и общий site-text API не раскрывают значения
Реальные connection tests основного клиентского бота, бота сотрудников и Gemini успешны
Успешно: 47 backend suites/350 тестов, все production-сборки, dependency audit 0, acceptance 19/19 и browser smoke 26/26
Заказы и производство
Запущены заказы с несколькими предметами, отдельными услугами и фото
Выполнено
Каждая пара обуви, сумка или другой физический предмет внутри одного заказа теперь учитывается отдельно. Ручная приёмка и AI-черновик привязывают к каждому предмету собственные услуги, описание и фото; карточка заказа поддерживает редактирование по правам, пересчёт суммы и скидки, бирки формата 1/N и обязательное внутреннее фото каждого предмета на производственном этапе.
В ручной приёмке можно добавить до 50 физических предметов; у каждого собственные услуги каталога, описание и цена
AI-черновик сохраняет количество физических предметов, разворачивает формулировки вроде «2 штуки» в отдельные предметы и отправляет неоднозначный результат на проверку человеком
Миграция ввела обязательную связь каждой услуги с OrderItem, backfill старых записей и уникальную позицию предмета внутри заказа
В карточке заказа клиент, срок, заметка, скидка, предметы, фото и услуги редактируются только в пределах соответствующих permissions
Каталожная цена по умолчанию остаётся canonical; ручное изменение цены доступно только с правом price:override
После изменения предметов или услуг пересчитываются итог, процентная скидка, остаток и переплата
Опасное изменение или удаление услуги блокируется, если производственный этап уже начат или закрыт
Staff-бот требует отдельное внутреннее фото каждого предмета этапа и завершает этап после загрузки последнего обязательного фото
PDF-бирки содержат номер предмета 1/N; тест подтвердил автоматическую раскладку заказа из 9 предметов на две страницы
Итоговая проверка: 44 backend suites и 332 теста, все production-сборки, restore 48 таблиц/1925 media, dry-run миграции и HTTPS acceptance 19/19 успешны
Мобильное приложение
Исправлен запуск Android APK и опубликован release 1.0.3
В работе
Найдена точная причина, по которой прежний APK устанавливался, но не открывался: launcher-класс в Android manifest не совпадал с Kotlin package. Восстановлен единый canonical package, новый подписанный release опубликован в production и прошёл чистую установку и launcher-тест в Android runtime. Запись будет завершена после подтверждения новой версии владельцем на физическом телефоне.
Manifest старого 1.0.2 (3) вызывал uz.restoria.staff.MainActivity, но DEX-класс находился в пакете uz.restoria.restoria_staff; Android launcher не мог найти класс
MainActivity перенесён в canonical package uz.restoria.staff, а manifest указывает полное имя launcher-класса
В release-процедуру добавлен fail-closed контроль совпадения launcher в manifest и фактического класса внутри DEX
Flutter analyze и test успешны; release APK Restoria Staff 1.0.3 (4) подписан release-ключом и прошёл проверку zipalign
Успешно проверены application ID, version, min/target SDK, launcher в manifest и наличие класса в DEX
В Android 15 runtime выполнена чистая установка; LAUNCHER intent открыл MainActivity, подтверждены процесс и foreground activity, отрисован экран входа Flutter
В logcat для Restoria нет FATAL EXCEPTION, ClassNotFoundException, Unable to instantiate activity или ANR
Production metadata и download отвечают HTTP 200: restoria-staff-1.0.3-4.apk, 49 058 337 байт; SHA-256 и файл побайтно совпадают с локальным release
Открыта проверка на физическом телефоне: владелец устанавливает из CRM именно 1.0.3 (4) и подтверждает запуск
Созданы ролевые инструкции для всех сотрудников и постоянная ссылка APK
Выполнено
В CRM создан раздел «Инструкции и APK» для всех авторизованных сотрудников. Владелец и администратор могут выбрать любую должность, остальные сотрудники видят начало работы, ежедневный процесс и правила безопасности своей роли. Здесь же доступна актуальная версия Restoria Staff APK, подписанная настоящим release-ключом.
Написаны отдельные инструкции RU/UZ для SUPERADMIN, ADMIN, MANAGER, RECEPTIONIST, MASTER_INTERNAL, MASTER_EXTERNAL, QC, COURIER и CASHIER
Инструкции соответствуют реальным permissions и процессам CRM/mobile: сотруднику не обещаются закрытые телефон, цены или системные действия
Страница доступна каждому сотруднику с JWT независимо от business-permission; админы выбирают любую роль, остальные видят только свою
Добавлен режим печати инструкции или сохранения в PDF из браузера
В мобильный Профиль добавлены «Инструкция по моей роли», «Скачать обновление APK» и фактическая версия package
Исправлено старое несоответствие экрана приёмки: фото с камеры после проверки доступа сохраняется как BEFORE + INTERNAL и не смешивается с клиентским контуром
При обрыве сети на загрузке фото уже созданный заказ не дублируется: приложение даёт точное предупреждение, а в карточке заказа CRM можно повторно добавить фото приёмки
Metadata APK отдаёт версию, размер, SHA-256, дату, минимальный Android и release notes RU/UZ; download защищён лимитом 5/мин и nosniff
Manifest открывается только для canonical имени restoria-staff-x.y.z-code.apk, совпадающих version/size, обычного файла без symlink
npm run release:mobile-apk в дальнейшем одной процедурой выполняет pub get, analyze, test, release build, проверку подписи и атомарное обновление файла/manifest CRM
Для Play Console заново собран release AAB 1.0.2+3 размером 41,4 МБ и проверено наличие release-подписи
Успешно: 37 backend suites, 279/279 тестов, Flutter analyze/test, все production-сборки и npm production audit 0
Новый атомарный snapshot с APK: 343 DB-объекта и 12 media; retention удалил предыдущий snapshot того же дня и сохранил 2 нужных snapshot
Production deploy завершён: read-only acceptance через реальный HTTPS 19/19, API/CRM/сайт/оба бота online в PM2
Browser smoke 82/82: 8 активных ролевых аккаунтов, границы разрешений/запретов, все 9 инструкций в selector владельца, просмотр ADMIN, кнопка APK и public-страницы подтверждены
APK полностью скачан из production: Content-Type/filename/48 723 453 байта и SHA-256 точно совпали с локальным release
Внешние stock-медиа удалены, создан ZIP-импорт собственных материалов
Выполнено
По запросу владельца прежние фото и видео Pexels удалены с главной страницы и из assets проекта. Вместо них в настройках CRM создан безопасный импорт собственных фото и видео Restoria одним ZIP-архивом с предварительным просмотром, редактированием подписей RU/UZ и порядка, а также отдельной ручной публикацией каждого материала.
Удалены прежние 6 stock-фото, 1 видео, постер и код статического каталога; пока собственный контент не опубликован, пустой раздел и ссылка на него в меню не показываются
Строго проверяются лимиты ZIP: 45 МБ сжатого файла, 80 записей, 20 медиа, 80 МБ общего распакованного объёма и отдельные ограничения фото/видео
В простом режиме достаточно поместить JPEG, PNG, WebP, MP4 и WebM в ZIP; безопасная подпись RU/UZ формируется из имени файла
Необязательный manifest.json задаёт точные подписи и описания RU/UZ, порядок и постер видео; дубликаты, несовпадение типа и лишние файлы не импортируются
Изображения проверяются по сигнатуре и decode, затем перекодируются в WebP до 2000 px без EXIF; видео принимается только с настоящей сигнатурой MP4/WebM
Каждый импорт создаётся скрытым с isPublished=false; публичный API отдаёт только вручную подтверждённые безопасные поля и HTTPS media URL
Записи импорта и audit log сохраняются в одной DB-транзакции; при ошибке все новые файлы автоматически удаляются
Изменение и удаление закрыты правом settings.system; удаление может затронуть только UUID-файлы собственного каталога site-media
Перед миграцией создан атомарный snapshot: 339 DB-объектов и 9 media; применена миграция №16, schema-diff равен нулю
Успешно: 36 backend suites, 275/275 тестов, production-сборки backend, CRM и сайта, npm production audit 0
Первая production-приёмка обнаружила отсутствующий путь в public allowlist site-media и неверное ожидание автоподписи в тесте; открыт только public site-media, временная тестовая запись и файл полностью удалены
Финальная приёмка через реальный HTTPS 4/4: ZIP-импорт, скрытая модерация, ручная публикация, WebP 200, cleanup БД и файла; после теста остаток записей/файлов site-media равен 0/0
Read-only production acceptance 15/15 и browser smoke SUPERADMIN/public 24/24; API, CRM, сайт и оба бота online в PM2
Контент основного сайта
История: лицензионные медиа были добавлены, затем удалены
Выполнено
Эта запись сохраняет историю фото и видео Pexels, временно добавленных 2026-07-21. Следующим поручением владелец попросил удалить внешний контент и создать ZIP-импорт собственных материалов; assets и раздел удалены, новый результат зафиксирован записью выше.
Контент со случайных сайтов и из Instagram не копировался: использованы только материалы Pexels с понятными условиями повторного использования
Страницы источников, авторы и лицензия Pexels проверены 2026-07-21 и видны пользователю
Загруженные файлы проверены по сигнатурам JPEG/MP4; изображения перекодированы в WebP без EXIF-метаданных
6 WebP-фото занимают около 731 КиБ; видео — 4,2 МБ, постер — 24 КБ; фото используют lazy-load, видео — preload=metadata
Добавлены заголовки, пояснения и accessibility-тексты alt/aria на RU и UZ
Референсы не попали в галерею «реальных работ Restoria»; публикация настоящих пар до/после остаётся только через CRM
Рехост фото и видео из Instagram без разрешения запрещён; в дальнейшем они добавляются только с разрешением автора или официальным embed
Усилены границы уведомлений, заказов, интеграций и полномочий
Выполнено
Финальный аудит чтения и записи API/CRM выявил дополнительные строгие границы для уведомлений dashboard, приватности заказа, профиля курьера, делегирования полномочий сотрудников и публичных callback Instagram. Исправления опираются на серверные permissions, а UI больше не показывает и не вызывает действия без действующего права.
API уведомлений отдаёт только канал DASHBOARD и безопасные поля без recipient; аудитории admin, manager и receptionist разделены
Текст WebSocket-уведомления не транслируется: только код события уходит в комнату соответствующего permission
Колокольчик CRM без dashboard.view скрыт и не вызывает API уведомлений
Мастер получает только заказ, назначенный напрямую, через stage или task; ОТК — только заказы своей очереди и истории задач ОТК
Для мастера и ОТК сервер удаляет телефон/каналы/адрес клиента, delivery-details, payments и клиентские цены
Order search, карточка, allowed transitions, этапы и event feed используют одну row-level проверку; финансовые события не попадают в feed без права
Excel-экспорт привязан к reports.finance, PDF — к receipts.print, payment-link — к orders.finance, каталог и прайс — к price.view
Карточка заказа открывается после загрузки прав и показывает только разрешённые finance/status/assign/courier/payment/discount/document блоки
Приёмщик сохраняет список курьеров и заявки доставки; создание, редактирование и деактивация профиля курьера требуют routes.manage
Точка доставки проверяется DTO и service по существующему заказу, ISO-дате, слоту, координатам, stage и активному курьеру; завершённая точка не редактируется
Делегированный users.manage не может выдать роль или permission выше собственных; защищены SUPERADMIN и последний активный владелец
Instagram POST webhook закрыт без HMAC raw-body; public manual/mock fallback удалён, OAuth state хранится хешем, живёт 10 минут и одноразовый
Сервисные ключи PBX, ботов и tracking используют общее constant-time сравнение
Новый и сменяемый пароль валидируется как 8–128 символов на DTO и service; существующие логин и пароль не менялись
Успешно: 34 backend suites, 265/265; все production-сборки, Prisma validate, npm audit 0 и Flutter format/analyze/test
Production RBAC API-приёмка 19/19: проверены поля notification, scope/redaction мастера, 403 приёмщика и unsigned webhook без записи в БД
Общая read-only production-приёмка 17/17: работают API/DB health, HTTPS и fail-closed интеграции
Browser smoke через реальный HTTPS 73/73: разрешённые и запрещённые страницы 8 активных ролей, persistence resize заказа и 3 публичные страницы
API, CRM, сайт и оба бота online в PM2; milestone виден в публичном отчёте, код отправлен в удалённую Git-ветку
Roadmap P1–P3
Завершены мобильный рабочий поток, порядок маршрута и удобство таблицы
Выполнено
Мастера получили в мобильном приложении приём этапа, возврат с причиной, безопасное внутреннее фото из камеры или галереи, завершение только после фото и просмотр собственного заработка. CRM атомарно сохраняет drag-and-drop порядок внутри маршрута, а ширина колонок заказов меняется мышью, касанием или клавиатурой и сохраняется в браузере. Одновременно закрыто несоответствие ownership, позволявшее сотруднику другой роли обратиться к действию чужой задачи или курьерской точки.
Действия этапа в мобильном приложении используют существующую role/owner-защиту backend и безопасное декодирование/перекодирование изображений
Экран заработка за месяц или всё время получает только профиль мастера из JWT; границы дат строго проверяются как полные локальные дни
Production API задан по HTTPS; release-manifest запрещает cleartext-трафик и не содержит ненужного разрешения геолокации
Android scaffold, Gradle wrapper и pubspec.lock добавлены в Git для воспроизводимой сборки; local property, key и keystore остаются секретными
Release AAB проверен с настоящим non-debug upload-key; при неполной настройке signing release-сборка завершается fail-closed
Внутренний порядок маршрута принимает только полный актуальный список UUID и записывает все sortOrder одной транзакцией
Точки TO_MASTER/FROM_MASTER включены в алгоритм и карту; завершение продолжает нужный stage и не переводит заказ ошибочно в статус «выдан»
Callback внешнего мастера сделан retry-safe: открытая задача не дублируется, этап повторно не уходит в ОТК, а при ошибке callback статус точки компенсируется
Ручной перенос не отменяет выбранного курьера повторным запуском алгоритма; старый и новый списки последовательно нормализуются
Действия курьера защищены по роли, владельцу точки, статусу, причине и сумме; чужой маршрут и action закрыты для остальных ролей tasks.my
Колонки заказов меняются в пределах 64–480 px мышью/клавиатурой, повреждённые значения storage очищаются, доступен reset
Успешно: 27 backend suites, 240/240; Flutter format/analyze/test, все production-сборки, Prisma validate и npm audit 0
Production read-only acceptance 17/17: подтверждены API health, БД, HTTPS, auth и fail-closed интеграции
Browser smoke через реальные HTTPS-домены 24/24: 20 страниц CRM, resize→reload persistence и 3 публичные страницы
Production API мастера: earnings 200, телефон клиента скрыт; чужие route/action дают 403, неполный order отклонён 400 без записи в БД
API, CRM, сайт и оба бота online в PM2; новый milestone виден в публичном отчёте, commit отправлен в удалённую Git-ветку
Production-приёмка
Доступная без внешних данных часть roadmap передана в production
Выполнено
Сайт, CRM, API, боты, media CMS, зависимости и fail-closed интеграции прошли единый финальный цикл приёмки. Старые acceptance/smoke-скрипты, записывавшие данные в production, заменены read-only проверками, устаревшие заявления о mock и открытых LAN-портах исправлены. Оставшиеся пункты требуют только внешних credentials, реальных медиа или sudo-доступа владельца.
Успешно: 230/230 backend-тестов, четыре production-сборки и npm audit 0
Read-only production acceptance: 17/17; тестовые user, lead, order, payment или call не создавались
Read-only browser smoke через реальные HTTPS-домены: 20 страниц CRM и 3 публичные, успешно 23/23
CRM→API→public media E2E: PNG→WebP, настоящий MP4, публичная выдача и полный cleanup БД/файлов
Два старых 4×4 тестовых PNG без связи с БД перемещены в восстанавливаемую Корзину; БД и каталог галереи чисты
Prisma 6.19.3 работает в production: health, 15 migrations и нулевой schema-diff
Финальный atomic snapshot: 339 объектов БД и 9 медиа; изолированный restore: 45 таблиц и 9/9 медиа
API, CRM, сайт и оба бота online в PM2; публичный сайт, отчёт и CRM отвечают HTTP 200
3000/3001/3002 только на 127.0.0.1; payment mock/webhook 503, PBX без ключа 403, delivery 503 без изменения БД
Все commits кода и отчёта отправлены в удалённую Git-ветку
Слой данных
Prisma контролируемо обновлена с 5.22 до 6.19.3
Выполнено
Prisma ORM и Client переведены на единую версию 6.19.3. Официальный список breaking changes проверен по schema и коду, CLI-конфигурация перенесена в отдельный prisma.config.ts. К production-БД не применялось неизвестных изменений schema.
Node 22.14 и TypeScript 5.6 соответствуют требованиям Prisma 6
Не найдены Bytes-поля, старый NotFoundError, middleware $use и неявные PostgreSQL many-to-many связи
Prisma Client 6.19.3 успешно сгенерирован, schema валидна
Все 15 миграций production-БД актуальны, schema-diff: “No difference detected”
prisma.config.ts детерминированно задаёт root .env, schema, migration path и seed-команду
Пройдены 230 backend-тестов, все production-сборки и аудит с 0 уязвимостей
Prisma 7 одновременно меняет ESM, output клиента, PostgreSQL driver adapter и pool-семантику, поэтому вынесена в отдельную архитектурную миграцию
Безопасность интеграций
Delivery, АТС и платёжные адаптеры переведены в честный fail-closed режим
Выполнено
Пока нет договора и проверенного реального адаптера, production больше не создаёт фиктивный трек Yandex, запись «звонок отвечен» или результат упрощённого webhook Payme/Click/Uzum. CRM явно показывает состояние интеграции; собственные курьеры, подтверждённые вручную звонки и наличный/терминальный поток продолжают работать.
DELIVERY_PROVIDER в production установлен в disabled; mock возможен только в test/development
Один токен Yandex без реального API-вызова не может перевести задачу в статус «в пути»
Payme, Click и Uzum сохранены в CRM, показаны как «не подключены» и недоступны для выбора
Упрощённые методы invoice/webhook реальных платежей удалены и закрыты до contract-тестов
Ключ АТС отделён от bot-key, имеет минимум 32 символа и принимается только через заголовок X-PBX-Key
Click-to-call без АТС больше не создаёт ложный ANSWERED; открывается набор номера устройства
Безопасность credentials
Безопасно ротированы production JWT и ключ внутреннего сервиса
Выполнено
Строгая проверка после deploy обнаружила, что в старой конфигурации сохранился шаблон ключей, характерный для development. Без публикации значений они были атомарно заменены отдельными криптографически случайными ключами; все сервисы одновременно перезагружены с новой конфигурацией. Ради безопасности прежние сессии CRM аннулированы, логин и пароль не менялись.
JWT и ключ bot-service раздельно созданы из 48 криптографически случайных байт
Замена выполнена через временный файл и atomic rename без вывода значений в терминал
Validator теперь отклоняет шаблоны local/development/sample/test и совпадение двух ключей
Проверено без показа значений, что активный процесс API загрузил именно обновлённые ключи
API, CRM, сайт и оба Telegram-бота online; health и публичные страницы отвечают 200
Основной сайт и CMS
Сайт дополнен, в CRM добавлено безопасное управление реальными медиа
Выполнено
На главную добавлены направления услуг, советы по уходу, удобная навигация, блок видео реальных работ, SEO structured data и улучшения accessibility. CRM теперь управляет текстами RU/UZ, парами «до/после», их порядком и видео процесса. Пока подтверждённых реальных медиа нет, сайт честно помечает текущую графику как иллюстрацию.
Главная дополнена 6 направлениями услуг, 4 советами по уходу и понятными CTA
В CRM создано управление двуязычными текстами сайта, галереей и видео
Изображения галереи декодируются на сервере, очищаются от EXIF/метаданных, уменьшаются до 2000 px и переводятся в WebP
Видео процесса принимается только по сигнатуре MP4/WebM, под UUID-именем и с лимитом 30 МБ
Для публикации финального фото заказа требуется реальное фото «до» и клиентский уровень видимости медиа
Проверены URL, координаты карты, lazy-loading, skip-link для клавиатуры, JSON-LD и двуязычные alt/aria-тексты
Успешно пройдены 222 backend-теста, все production-сборки и аудит зависимостей; уязвимостей: 0
Безопасность зависимостей
Next.js и NestJS контролируемо обновлены до безопасных версий
Выполнено
Сайт и CRM переведены на Next.js 15.5.20, API — на NestJS 11.1.28 и линию Express 5. Каждое breaking-изменение адаптировано в коде; аудит framework, upload и транзитивных пакетов доведён до нуля уязвимостей.
Исходный npm audit: 29 записей; итоговый аудит: 0
Все публичные страницы адаптированы к async API route-параметров Next.js
Peer-пакеты NestJS объединены в единый dependency-граф 11.1.28
Установлена production-линия Multer 2.2 и Express 5
Срок JWT проверяется в строгом формате и преобразуется в безопасное числовое значение
Успешно пройдены 216 backend-тестов и production-сборки всех четырёх приложений
Границы сервера
Закрыты внутренние порты и усилена production-конфигурация
Выполнено
API, CRM и сайт теперь слушают только loopback-интерфейс сервера; единственным внешним входом остаётся Nginx. PM2 детерминированно загружает root env и принудительно задаёт production, а backend не запускается с опасной конфигурацией.
Порты 3000, 3001 и 3002 слушают только адрес 127.0.0.1
NODE_ENV=production проверен у всех пяти сервисов PM2
Удалены development-fallback для JWT и внутренних сервисных ключей
Production CORS сокращён до официальных HTTPS-origin
Публичный домен возвращает 404 для внутренних stage-медиа и 200 для галереи
CSP/headers и границы uploads для Nginx готовы в Git; применение требует root-доступ сервера
Резервное копирование
Внедрены атомарный backup БД и медиа и реальный restore-тест
Выполнено
Ежедневная задача systemd объединяет PostgreSQL и локальные медиа в единый версионированный snapshot, проверяет SHA-256 и применяет daily/weekly/monthly retention. Еженедельный тест восстанавливает копию в отдельную временную БД и каталог.
В реальном snapshot сохранены 339 объектов БД и 11 медиафайлов
Изолированный тест успешно восстановил 45 таблиц и все 11 медиафайлов
Сервисы systemd backup и restore повторно проверены с exit-code 0
Добавлены lock от параллельного запуска и атомарный staging
Offsite не запускается без полного набора данных; готовый поток шифрует AES-256 и проверяет обратную расшифровку
Безопасность входа
Безопасно восстановлен SUPERADMIN-доступ владельца системы
Выполнено
После подтверждения владельца для единственной активной учётной записи SUPERADMIN установлен новый сильный временный пароль. Секретные данные не добавлялись в отчёт или код.
Сброс применён только к одной точно найденной активной учётной записи SUPERADMIN
Операция записана в audit-log сервера без секретных данных
Новые данные успешно проверены через реальный API входа
Безопасность медиа
Все потоки загрузки файлов переведены на единые безопасные правила
Выполнено
Медиа этапов, клиентов, галереи, Telegram и звонков больше не доверяют присланному имени или MIME. Сервер проверяет тип по содержимому, создаёт безопасное UUID-имя и применяет строгий лимит размера.
Изображения принимаются только с сигнатурой JPEG, PNG или WebP
Фото этапа может загрузить только назначенный мастер или администратор
Аудио проверяется и хранится только как MP3, WAV, OGG или M4A
Записи АТС загружаются только с отдельно разрешённого HTTPS-хоста, без redirect и до 50 МБ
Документы Telegram хранятся в неисполняемом бинарном формате, исходное имя — только для безопасного отображения
Локальный snapshot БД+медиа, retention и реальный restore-тест завершены. Осталось включить AES-256 offsite-копию после получения внешнего S3 endpoint, bucket, access key и отдельного пароля шифрования.
БД и медиа включены в единый атомарный цикл копирования
Дневное/недельное/месячное хранение работает
Тест восстановления выполнен без воздействия на production-данные
Данные доступа к внешнему storage пока отсутствуют на сервере
Безопасность сервера
Усиление портов, security-заголовков и production-конфигурации
В работе
Loopback-порты, строгая production env-проверка и граница публичных медиа уже работают. Конфигурация CSP и дополнительных заголовков Nginx готова, но для установки в `/etc/nginx` требуется root-доступ.
Состояние портов и firewall повторно проверено
API, CRM и сайт переведены на loopback и прошли smoke-тест
Конфигурация Nginx подготовлена с rollback через Git
Осталось: установка с root, nginx -t, reload и smoke-тест заголовков
Технический долг
Контролируемое обновление framework и библиотек
Выполнено
Next.js, NestJS, Express, Multer и транзитивные пакеты обновлены до безопасных линий; миграция Prisma 6.19.3 завершена со schema-diff. Уязвимостей в аудите не осталось. Prisma 7 вынесена в безопасный отдельный цикл из-за архитектурных изменений ESM и driver-adapter.
29 записей аудита сокращены до 0
Миграция Next.js и NestJS завершена с тестами и build
Миграция Prisma 6.19.3 завершена: 15 актуальных migrations и нулевой schema-diff
Зафиксирована безопасная граница отдельной архитектурной миграции Prisma 7
Основной сайт
Наполнение, развитие и улучшение удобства Restoria.uz
В работе
База контента, UX, SEO и CMS сайта завершена. Безопасный технический поток галереи и видео готов; оставшийся контентный этап — загрузить через CRM реальные фото «до/после» и видео процесса с разрешением клиентов.
Дополнены услуги, цены, сроки, уход, CTA Telegram/телефон и путь к отслеживанию
Готово управление RU/UZ-контентом, WebP-галереей, video poster и видео процесса из CRM
Проверены SEO structured data, lazy-loading и основные требования accessibility
Осталось: загрузить одобренные владельцем реальные медиа с описанием и категорией
Интеграции
Подключение реальных платёжных провайдеров и АТС по договору
В работе
Fail-closed защита production завершена: неподключённые Payme, Click, Uzum, Yandex Delivery и АТС не создают фиктивный результат. Оставшийся внешний этап — договоры с выбранными провайдерами, официальные credentials, sandbox и contract-тесты подписи/idempotency.
Неподключённые провайдеры видны в CRM, но недоступны для выбора
Production mock/fallback для payments, delivery и АТС закрыт
Осталось: официальные credentials провайдеров и sandbox contract-тесты
Защита данных
Защищены realtime-канал и границы публичного API
Выполнено
WebSocket CRM проверяет JWT, активность аккаунта и индивидуальные права. События отправляются только в соответствующие permission-room. Публичный endpoint текстов возвращает только необходимые сайту поля org.
WebSocket без токена не получает события
Событие звонка получают только роли с правом просмотра клиентов
Настройки AI и каналов исключены из public texts
Демо-отзывы и внутренние поля review исключены из публичного ответа
Безопасность Telegram
Защищены идентификация ботов и поток медиа
Выполнено
Клиентский и служебный боты теперь связывают профиль только через собственный Telegram-контакт пользователя. Существующая привязка не заменяется незаметно. Фото больше не сохраняются URL-адресом с токеном Telegram — они проверяются и копируются в хранилище Restoria.
Владелец контакта проверяется по Telegram user ID
К endpoint дохода сотрудника добавлена проверка сервисного ключа
Задачи и точки курьера проверяются на принадлежность исполнителю
Закрытие многоэтапной работы через бот без обязательного фото заблокировано
Тип изображения определяется по реальной сигнатуре байтов, а не имени файла
Безопасность платежей
Тестовые онлайн-платежи закрыты в production
Выполнено
Mock-ссылка и вебхуки без проверенной подписи больше не могут подтверждать оплату в production. До полноценных тестов подписи и транзакций реальных адаптеров Payme/Click система остаётся fail-closed.
Production PAYMENT_PROVIDER переведён в disabled
Mock-режим работает только вне production
В CRM провайдеры видны как неподключённые и заблокированы; Telegram-бот не выдаёт ссылку
С сайта удалены несоответствующие обещания Payme/Click
Безопасность входа
Закрыт публичный вход демо-администратора
Выполнено
С формы входа CRM удалены готовые телефон и пароль. Старый публично известный аккаунт отключён, его прежние сессии стали недействительными, а пароль заменён невосстанавливаемым значением.
Перед блокировкой проверено наличие другого активного SUPERADMIN
Старый демо-вход возвращает 401 на уровне API
Production-данные входа удалены из seed- и тестовых скриптов
Операция записана в серверный audit-log
Прозрачность
Создан постоянный отчёт о развитии
Выполнено
Внедрён постоянный двуязычный журнал, в котором по шагам сохраняются безопасные изменения сайта, CRM и интеграций.
Источник отчёта сохраняется в истории Git
Страница работает на русском и узбекском языках
Секреты и инструкции по эксплуатации уязвимостей не публикуются
Безопасное начало
Начат контролируемый процесс изменений
Выполнено
Остановлена автоматическая отправка непроверенного кода. До изменений созданы снимки базы данных и локальных медиа, работа вынесена в отдельную ветку.
Production-сервисы продолжили работать
Резервная копия БД успешно создана
Снимок медиа проверен контрольной суммой
Аудит
Проведён полный read-only аудит сайта и CRM
Выполнено
Проверены архитектура, API, конвейер заказов, боты, платежи, маршруты, резервное копирование, SEO и конфигурация сервера. Исправления расставлены по уровню риска.