Продукт студии
BeautyKit
Онлайн-запись для салонов и частных мастеров: один шаблон на много салонов, своё приложение у каждого, свободное время считает сервер. Живое демо, которое можно потрогать.
- постоянных серверов
- 0
- проверки базы данных
- 863
- машинного времени на новый салон
- < 1 мин

Система онлайн-записи для салонов красоты, домашних студий и частных мастеров. Клиент открывает обычную ссылку на телефоне, выбирает услугу, мастера и реально свободное время — и записывается без регистрации. Владелец ведёт записи, клиентов, расписание и деньги в кабинете с того же телефона.
Главное в ней не экраны, а то, что один шаблон обслуживает много салонов: новый собирается из одного файла описания и фотографий, без правки кода приложения и без влияния на уже работающие салоны.
Ссылка «Открыть сайт» выше ведёт в салон LUNA Beauty, каким его видят клиенты. Рядом на том же коде работает студия NOIR Studio — другой город, один мастер, тёмная тема и адрес, который виден только после подтверждённой записи.
А по этим двум ссылкам вы получаете собственную копию салона на сутки и можете менять в ней что угодно: цены, услуги, фотографии, тему. Первая открывает кабинет владельца сразу, без пароля. Вторая — клиентскую часть той же копии: записывайтесь и смотрите, как запись появляется в кабинете. Остальные посетители всё это время видят исходный салон.
Попробовать кабинет ↗Клиент своей копии ↗
Салоны работают в демонстрационном режиме: уведомления никому не уходят, названия и телефоны вымышлены.

Двойная запись невозможна не в коде, а в базе
Занятость мастеров, кабинетов и ручных блокировок лежит в одной таблице с ограничением EXCLUDE в PostgreSQL. Два пересекающихся интервала на один ресурс база просто не примет.
Очевидный путь — проверить занятость запросом и потом вставить запись. Он работает ровно до того момента, когда два клиента нажимают «Записаться» на последний слот в одну секунду: оба запроса видят свободно, оба вставляют. Проверка в коде не может это закрыть, потому что между проверкой и вставкой всегда есть промежуток. Ограничение в базе закрывает, и закрывает один раз для всех способов записи — клиентского, ручного из кабинета и переноса.
Свободное время считает сервер, а не телефон
Клиент получает готовый список слотов. В расчёте участвуют график салона и график мастера, исключения и отпуск, ручные блокировки, буферы до и после процедуры, занятость физических ресурсов, часовой пояс салона и переходы на летнее время.
Считать это на устройстве проще и быстрее — и неверно. Телефон не знает о чужих записях, сделанных секунду назад, а его часы и часовой пояс принадлежат клиенту, не салону. Клиент в другом городе видел бы чужое время и записывался бы в занятый слот.
Цена в записи — снимок, а не ссылка на услугу
Запись хранит стоимость и длительность на момент оформления. Владелец поднимает цену на маникюр — уже оформленные записи не меняются.
Ссылка на услугу выглядит правильнее нормализованно, но переписывает прошлое: клиент приходит, а в кабинете у него цена сегодняшнего прайса. Отсюда же правило в финансах: будущая стоимость записей никогда не называется выручкой. Деньги появляются в отчёте, когда их получили, а не когда записались.
Адрес домашней студии виден только после подтверждённой записи
У салона с витриной адрес на главной. У частного мастера, который принимает дома, — район, а точный адрес приходит вместе с подтверждением.
Это не настройка ради настройки: для домашней студии публичный адрес — вопрос безопасности, а не удобства. Поэтому режим показа адреса задаётся салоном, а сервер не отдаёт точный адрес в публичных данных вовсе — его невозможно достать, минуя интерфейс.
Доступ клиента — токен, и в базе лежит только его хэш
Регистрации нет. Ссылка «Моя запись» содержит криптографически случайный токен, который открывает ровно одну запись и её переписку.
В базе хранится не сам токен, а его хэш: утечка таблицы не даёт доступа к записям. Область токена ограничена одной записью — по нему нельзя посмотреть соседнюю, даже если угадать идентификатор.
У каждого салона своё приложение, хотя сборка одна
Салон можно поставить на экран «Домой» — со своим названием, иконкой и цветами. При этом сборка frontend одна на все салоны.
Разные приложения из одной сборки — это отдельная работа: у каждого салона свой манифест, свои иконки, своя область действия у service worker и свои кеши. Иначе установка второго салона подменяет первый, а ссылки ведут в чужую оболочку.
Демо, которое можно сломать
Обычная демонстрация показывает экраны и запрещает их трогать, потому что иначе следующий посетитель увидит чужие правки. Здесь правки идут в личную копию, остальные видят исходный салон, копия удаляется автоматически. Это дороже, чем видео с экрана, и это единственный способ дать потрогать систему, в которой важна именно работа с данными.
AI-помощник отвечает только по данным салона
Помощник у клиента и помощник у владельца — разные наборы серверных инструментов. Цены, свободное время, мастера и адрес он берёт вызовами в базу, а не придумывает.
Отдельно оговорено, что недоступность модели не ломает запись: если ключей нет или ответ не пришёл, приложение честно сообщает, что помощник недоступен, и запись работает дальше. В обоих демо-салонах помощник как раз выключен — ключей там нет, и приложение это прямо говорит.
Что умеет

Клиент, без регистрации. Главная салона, услуги с ценами и длительностью, мастера, портфолио с полноэкранным просмотром, отзывы, FAQ, график и контакты. Запись в четыре шага: услуга, мастер, дата и время, имя и телефон — шаг с мастером пропускается, если выбора нет. Страница «Моя запись» по защищённой ссылке: перенос и отмена по правилам салона, добавление в календарь файлом .ics, напоминание push там, где оно действительно работает. Чат с салоном с фотографиями и системными событиями вроде «Запись перенесена с 13:00 на 15:30». Светлая и тёмная тема, работа без сети с честными сообщениями, установка как приложение.
Кабинет владельца. Экран «Сегодня» с ближайшим клиентом, полученными за день деньгами и расписанием. Календарь день-неделя-месяц по всем мастерам или по одному, ручная запись с той же проверкой конфликтов, перенос, статусы визита, блокировки времени. Карточка клиента с историей, средним чеком, будущими записями и внутренними заметками, которых клиент не видит никогда. Управление услугами, ценами, буферами, мастерами, графиком, портфолио, отзывами и настройками салона. Финансы по реальным платежам: оплата, частичная оплата, предоплата, возврат, способы оплаты, график по дням. Чат с клиентами в реальном времени, push о новых записях, роли владельца, администратора и мастера.
Публикация салона. Описание салона — один файл с проверкой схемы, ссылок, изображений и контраста цветов. Команды публикации занимают меньше минуты машинного времени; сам контент — названия, услуги, фотографии, график — занимает часы, и в документации проекта это сказано прямым текстом, а не спрятано за обещанием «салон за шесть минут». Повторная публикация не трогает записи, клиентов, платежи, переписку и правки владельца.
Как устроено
Постоянного сервера на Node.js нет вовсе. Статика раздаётся с обычного веб-сервера, вся серверная логика живёт в SQL-функциях, триггерах, четырёх Edge Functions и расписании внутри базы. Это меньше движущихся частей и меньше мест, где что-то может молча упасть ночью.
Один проект базы на все салоны: идентификатор салона во всех таблицах, составные внешние ключи, политики доступа на каждой таблице и проверка членства на сервере. Публичные функции доступны анониму и не отдают персональных данных; функции кабинета требуют входа и членства; служебные доступны только с ключом сервиса.
Уведомления идут через очередь с арендой задачи, повторами и защитой от дублей, а не отправляются прямо в обработчике запроса: иначе сбой почты или push ломает саму запись.
Серверная часть — 48 миграций и около 13 тысяч строк SQL, интерфейс — около 48 тысяч строк TypeScript.
Что проверено, а что нет
Проверки базы — 863 из 863: конкурентная запись, ресурсы, буферы, многодневная занятость, атомарный перенос, идемпотентность повторного запроса, изоляция салонов друг от друга, область действия токена, историческая цена, платежи, очередь уведомлений, приватность адреса, повторная публикация, песочницы. Модульные проверки — 1046, сквозные в браузере — 88 на iPhone, Android и настольном браузере, включая конфликт двух клиентов за последний слот.
Честно о том, что не проверялось: вызов настоящей языковой модели (ключей нет), реальная доставка push на устройство, установка на физический телефон, настоящий платёжный провайдер — платежи отмечаются в кабинете руками, — а также почта и SMS: они не реализованы.
React 19 · TypeScript · Vite · Supabase · PostgreSQL 17 · Edge Functions · PWA · Web Push · Playwright · pgTAP