Маркетплейс / EdTech

Repetitor.my — маркетплейс репетиторов

Repetitor.my — заказная разработка полноценного B2C-маркетплейса репетиторов для Москвы, Санкт-Петербурга, Екатеринбурга, Нижнего Новгорода и Казани. Сервис объединяет каталог преподавателей, кабинеты ученика и репетитора, расписание и онлайн-запись.

Обсудить похожий проект
Архитектура Repetitor.my с каталогом репетиторов, личными кабинетами и онлайн-записью

Задача

Разработать мультигородской маркетплейс, где ученик находит репетитора и записывается на занятие, а преподаватель управляет профилем и расписанием.

Решение

Как реализовали проект

Единое приложение на Next.js обслуживает все города без копирования кода. Поддомен определяет город и переводит запрос на общий динамический маршрут, PostgreSQL хранит профили, предметы, цены, расписание, заявки и контент, а отдельный Python-сервис подготавливает автоматический импорт объявлений. Проект разбит на 10 принимаемых этапов с прозрачным журналом прогресса.

Состав работ

Что вошло в решение

  • 01Мультигородской каталог на единой кодовой базе
  • 02Личные кабинеты ученика, репетитора и администратора
  • 03Онлайн-запись со слотами на 30, 60 и 90 минут
  • 04Ранжирование преподавателей по реальным метрикам
  • 05Управление городами, категориями и контентом
  • 06Интеграционный контур импорта объявлений Avito

Инфраструктура

Архитектура решения

  1. 01

    Next.js 16 с App Router обслуживает публичный каталог, личные кабинеты и административный интерфейс из одной кодовой базы.

  2. 02

    Прокси-слой определяет город по Host и делает rewrite на общий маршрут /[city]/; локально та же логика проверяется через параметр ?city= без wildcard-DNS.

  3. 03

    PostgreSQL и Drizzle ORM описывают 18 таблиц с индексами под реальные запросы и составными ключами для связей многие-ко-многим.

  4. 04

    Единая таблица accounts отвечает за роли admin, tutor и student, а профильные данные вынесены в связанные сущности.

  5. 05

    Python-сервис скрапинга запускается по systemd-таймеру и передаёт данные через защищённый API-контракт импорта.

  6. 06

    Демо работает в отдельном LXC-контейнере; приложение и Cloudflare Tunnel разделены на независимые systemd-юниты, чтобы рестарт приложения не менял публичную ссылку.

Возможности

Функционал

01

Мультигородской каталог

Одна кодовая база обслуживает пять городов. Город определяется по поддомену, а список городов редактируется в админке без изменения маршрутизации вручную.

02

Профили и личные кабинеты

Репетитор управляет предметами, классами, ценами, медиа и доступностью, ученик сохраняет данные и создаёт заявки, администратор контролирует справочники и контент.

03

Онлайн-запись на занятия

Сетка 8:00–23:00 работает с шагом 30 минут. Бронь на 30, 60 или 90 минут занимает последовательные слоты и не может выйти за конец рабочего дня.

04

Умное хранение расписания

По умолчанию время доступно, а в базу записываются только закрытые интервалы. Пустая база сразу означает полностью открытое расписание без стартовой миграции.

05

Ранжирование по реальным метрикам

Карточки сортируются по онлайн-статусу, заявкам за день, лайкам и дате последнего поста. Каждый показатель служит детерминированным тайбрейком предыдущего.

06

Импорт объявлений

Отдельный Python-сервис обходит матрицу город × категория, собирает доступные данные и отправляет их в приложение через bearer-защищённый endpoint.

07

Постепенное наполнение контента

Вопросы, ответы и фотографии получают заранее рассчитанные даты публикации и появляются постепенно при чтении, без отдельной постоянно работающей cron-задачи.

08

Администрирование и контроль

Админка управляет городами, категориями, рекламой и настройками сайта, а GET /api/health позволяет подключить внешний мониторинг доступности.

Стек проекта

Технологии

Next.js 16
TypeScript
React 19
Tailwind CSS 4
Drizzle ORM
PostgreSQL
Zod
Vitest
Python
Playwright
Cloudflare

Защита данных

Безопасность

  • Пароли хешируются bcryptjs, формы и API проходят Zod-валидацию, а сессионные cookies получают secure-флаг.
  • Тип и размер файла проверяются единообразно во всех точках загрузки, включая массовое добавление и рекламные креативы.
  • Дата записи проверяется не только по формату, но и на календарную корректность; несуществующие даты отклоняются.
  • Вопросы и отзывы проходят кириллица-only проверку, которая блокирует транслит, но сохраняет цифры и пунктуацию.
  • Импорт данных использует отдельный bearer-токен вместо пользовательской cookie-сессии.

Живые данные

Интеграции

Avito import APIScrapling / StealthyFetcherPlaywrightPostgreSQLNodemailer / SMTPCloudflare TunnelUptime monitoring

UX / UI

Дизайн-процесс

Интерфейс строится вокруг короткого пути ученика: выбрать город и предмет, сравнить понятные карточки, открыть профиль и забронировать свободное время. Для репетитора сложные данные разделены на последовательные настройки профиля, цен и расписания. Адаптивная сетка, явные состояния слотов и единые формы уменьшают число ошибок на мобильных устройствах.

Инженерная работа

Нетривиальные решения

Пять городов без пяти приложений

Поддоменная маршрутизация отделена от страниц: новый город добавляется в базе и сразу использует общий набор компонентов и бизнес-правил.

Расписание без лишних данных

Инверсная модель хранит только закрытые слоты, поэтому база остаётся компактной, а новое расписание сразу доступно без генерации тысяч записей.

Avito блокирует лёгкие запросы

WAF возвращал 403 обычному HTTP-клиенту, поэтому интеграционный сервис использует полноценный headless-браузер. OCR номера телефона намеренно не реализован как обход антибот-защиты.

Органический рост без фоновой джобы

Контент публикуется по заранее рассчитанным датам, а запрос показывает только записи с createdAt не позднее текущего времени.

Повторный аудит перед приёмкой

Отдельная проверка нашла и закрыла шесть реальных ошибок безопасности и граничных случаев в загрузках, датах и валидации.

Честная граница демо и продакшна

Демо развёрнуто и мониторится, но реальный wildcard-домен, SSL, нагрузка и бэкапы не объявлены проверенными без доступа к инфраструктуре клиента.

Результат

Создана функциональная платформа и демо-стенд: закрыто около 96% сметы из 10 этапов, реализованы ключевые пользовательские сценарии, админка, тесты и инфраструктура. Боевой хостинг, реальный wildcard-домен и нагрузочное тестирование честно оставлены отдельным этапом после получения доступа от заказчика.

Следующий кейс

TV-PORTAL / Damba Relax — Wi-Fi и ТВ-киоск

Смотреть кейс →