чат с агентомспроситьAI и агенты7 файловИнфраструктура4 файлаРазработка6 файловОбо мне5 файлов
TrainerPad - CRM для тренеров, которая работает без сети и сама разруливает конфликты синхронизации
  • офлайн
  • синхронизация
  • IndexedDB

TrainerPad - CRM для тренеров, которая работает без сети и сама разруливает конфликты синхронизации

Персональные тренеры ведут клиентов и тренировки из зала, где интернета либо нет, либо он рвётся. Обычный PWA с кэшем эту проблему не решает: как только два устройства расходятся в офлайне, данные нужно мирить, а «последний записавший прав» здесь означает потерянную тренировку.

Написал продукт один, с нуля: 626 коммитов за полгода, около 67 000 строк TypeScript. React с Vite и Zustand, Dexie поверх IndexedDB на клиенте, Express с Prisma и PostgreSQL на сервере.

Движок синхронизации

Ядро - собственный движок на 646 строк. Очередь изменений живёт в IndexedDB со своими правилами слияния: создание и следующая за ним правка схлопываются в создание, правка и удаление - в удаление, иначе сервер получает операции над записью, которой у него ещё нет. Цикл синхронизации идёт каждые 30 секунд и запускается мгновенно при возврате на вкладку. Два открытых окна браузера разводит блокировка через navigator.locks - без неё они наперегонки отправляют одну и ту же очередь. Догрузка с сервера идёт с нахлёстом в две секунды и приоритетом локальных изменений: нахлёст закрывает дыру от рассинхрона часов, приоритет гарантирует, что свежая правка из зала не затрётся ответом сервера. Баланс клиента пересчитывается локально, а не принимается от сервера на веру.

До этого стоял готовый PowerSync. Сравнил его с RxDB, ElectricSQL и Turso и мигрировал на связку Dexie с обычным REST, сняв около 6300 строк чужого рантайма. Это не борьба с библиотекой ради принципа: набор конфликтов в тренерском сценарии оказался узким и специфичным, и описать его своими правилами вышло дешевле, чем гнуть общее решение под себя.

Как это выглядит в жизни: однажды пришлось восстанавливать 154 упражнения пользователя из его же локальной базы после сбоя. Для таких случаев в продукте есть принудительный ре-синк и экспорт локальной базы в Telegram.

Всё остальное тоже один

Продукт живёт в проде на своём сервере: HTTPS с security-заголовками, рядом админка и самостоятельный мониторинг ошибок на self-hosted GlitchTip.

Поисковикам одностраничное приложение отдаётся заранее отрисованным: пререндер вынесен в CI - Chrome на раннере обходит публичные маршруты и кладёт готовый HTML со структурированными данными. Семантическое ядро описано прямо в коде, рядом с маршрутами.

Блог пишется полуавтоматически: по расписанию headless-агент готовит черновики статей и обложки, я вычитываю и публикую. То есть один человек ведёт продукт, инфраструктуру, поисковую оптимизацию и контент-машину разом.