RRestoria
Открытый журнал

Отчёт о развитии системы Restoria

Работы по сайту, 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
Сайт, CRM и Staff-приложение

Все телефонные поля переведены на единый формат +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
  • Успешны 53 backend suite/409 тестов, TypeScript 3/3, Flutter 4/4, analyze 0 issue, CRM/site build и audit 0 vulnerability
  • Production browser acceptance прошёл 24/24, общий read-only acceptance — 16/16
  • В production опубликован подписанный APK Restoria Staff 1.0.4+5 с проверкой SHA-256/download
Настройки CRM

Запущены современный центр управления и контроль 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 в единый каталог
  • Успешны 53 backend suite/409 тестов, backend/CRM build и audit 0 vulnerability
  • 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 можно повторно добавить фото приёмки
  • Release APK Restoria Staff 1.0.2+3: 48 723 453 байта; подпись проверена apksigner, Android Debug-подпись строго отклоняется
  • 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 МБ общего распакованного объёма и отдельные ограничения фото/видео
  • Отклоняются абсолютные пути, ../ traversal, backslash, symlink, лишняя глубина каталогов, длинные имена, зашифрованный/повреждённый ZIP, ZIP-bomb и неизвестные форматы
  • В простом режиме достаточно поместить 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
Безопасность и RBAC

Усилены границы уведомлений, заказов, интеграций и полномочий

Выполнено

Финальный аудит чтения и записи 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
  • Ключ АТС сравнивается constant-time, webhook защищены rate-limit
  • 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 и конфигурация сервера. Исправления расставлены по уровню риска.

  • Успешно пройдены 192 backend-теста
  • Все 15 миграций БД актуальны
  • Все production-сервисы находятся online
Вернуться на главную