Tech stack
Монорепо на Turborepo, NestJS API, Next.js витрина, Vite-админка, скрейпер на chrome-devtools MCP.
TL;DR
Монорепо на Turborepo с тремя приложениями: API (NestJS + TypeORM + PostgreSQL 16 + Redis + Socket.IO), публичная витрина (Next.js 14 + next-intl + TanStack Query), админка (Vite 6 + React 18 + Leaflet). Скрейпинг каталога — chrome-devtools MCP вместо Puppeteer. Хостинг — отдельный VPS ARM64.
Стек и архитектура
Монорепо
Турборепо собирает три приложения и общие пакеты:
auto-parts/
├── apps/
│ ├── api/ NestJS — REST + WebSocket
│ ├── web/ Next.js — публичная витрина
│ └── admin/ Vite + React SPA — внутренняя админка
├── packages/
│ ├── shared/ общие типы DTO
│ └── ui/ shared React-компоненты (для admin)
├── scraper/ chrome-devtools MCP скрейпер
└── deploy/ docker-compose, nginx-конфиги
Turborepo даёт кэш сборок (CI собирает только изменённое приложение), turbo dev запускает все три параллельно для локальной разработки.
API (NestJS)
NestJS 10 с TypeORM:
- Аутентификация:
@nestjs/jwt+@nestjs/passport+ bcrypt. Cookie-based для админки, Bearer-токены для будущих интеграций. - Хранилище: PostgreSQL 16 через TypeORM. Основные entity —
Product,Category,Fitment,Order,OrderItem,Customer,DeliveryZone,Inventory,Supplier. - Кэш: Redis через
@nestjs/cache-manager+@keyv/redis. Кэшируется фильтрованный каталог, fitment-таблицы, гео-привязки. - Real-time:
@nestjs/platform-socket.ioс@socket.io/redis-adapter— горизонтальное масштабирование, синхронизация остатков между админкой и витриной без перезагрузки. - Документация:
@nestjs/swaggerгенерирует OpenAPI-спеку на /api/docs. - Защита:
@nestjs/throttlerдля rate-limiting (особенно на эндпоинты поиска), глобальные guards для проверки JWT и ролей.
Витрина (Next.js)
Next.js 14 с App Router. Ключевые модули:
- next-intl — мультиязычность (LT / RU / EN). Каждый язык — отдельный URL-префикс (
/lt,/ru,/en), статически рендерится для SEO. - TanStack Query (React Query) — серверный кэш для каталога, корзины, статусов заказов. Refetch на фокус окна, рефреш через WebSocket-инвалидацию.
- Framer Motion — анимации перехода между состояниями (загрузка карточки, добавление в корзину).
- Zustand — клиентское состояние корзины и фильтров каталога.
- Tailwind CSS v4 — стилизация, без отдельной дизайн-системы; компоненты пишутся по месту.
Каталог рендерится в смешанном режиме: списки категорий — статически (ISR), карточки товаров — на сервере с инвалидацией по таймеру или событию из админки.
Админка (Vite SPA)
Vite 6 + React 18. Тяжёлая SPA — много экранов, частые перерисовки, поэтому Vite-режим даёт быстрый hot-reload без задержек, в отличие от Next.js dev-сборки.
- Leaflet — карты Литвы для зон доставки. Используется без внешних API (OpenStreetMap-тайлы).
- jsPDF + jspdf-autotable — экспорт накладных и отчётов прямо в браузере, без серверного rendering.
- react-quill-new — rich-text для описаний товаров (HTML с поддержкой картинок).
- rrweb-player — встроенный плеер для session replay из жалоб клиентов.
- Recharts — графики аналитики (продажи, конверсия).
- react-router-dom 7 — клиентский роутинг, file-based не используется.
- Zustand — общий store, без Redux.
Аутентификация — общая с API через JWT + cookie на поддомене admin-* (или basic-auth для совсем закрытых эндпоинтов).
Скрейпер
Отдельная папка scraper/ со скриптами импорта данных. Подход — chrome-devtools MCP, а не Puppeteer или Playwright:
scraper/
├── README.md
├── scripts/
│ ├── import-mototex.ts
│ ├── import-anjese.ts
│ └── ...
└── data/ кэш промежуточных результатов
MCP (Model Context Protocol) запускает реальный браузер и управляет им из Node-процесса. С точки зрения сайта-источника — это обычный пользователь в Chrome, поэтому работает на сайтах с агрессивной анти-бот защитой.
Цикл импорта: открыть список каталога → перебрать страницы → на каждой карточке вытащить артикул, цены, фото, fitment-таблицу → нормализовать → отправить в API → API создаёт или обновляет товар. Прогресс летит в админку через WebSocket, оператор видит ход импорта в реальном времени.
Хостинг
- VPS ARM64. Отдельный сервер, юзер
parts. ARM выбран из-за дешевизны и более низкого энергопотребления — для каталога без heavy-compute это не критично. - nginx — reverse-proxy, TLS-терминатор, статика. Виртуальные хосты для www-витрины и админки на разных поддоменах.
- Docker compose — все сервисы (api, web, admin, postgres, redis) в одном файле. Деплой —
docker compose pull && docker compose up -d. - GitHub Actions — CI: lint, type-check, build → push образа в GHCR. Smart path-фильтр пересобирает только изменённые приложения.
Криптоплатежи
В API подключены bitcoinjs-lib и bip32 — это инфраструктура для приёма криптоплатежей (BTC, ETH через дочерние ключи). Деривация адресов BIP32: один master-key на платформу, дочерние адреса под каждый заказ — это даёт отслеживание входящих транзакций без раскрытия master-key.
Это пока опционально на витрине, но архитектура есть; для русскоязычного сегмента в Литве это может стать каналом расчётов когда евро-эквайринг неудобен.
Что бы я переписал
При новой итерации:
- Прайс-аттестация. Сейчас прайсы импортируются как есть. Можно добавить слой нормализации с историей: «эта деталь у поставщика X стоила Y, теперь Z, разница 30%, не подсунули ли подделку?»
- CDN для фото. Сейчас картинки идут через nginx с диска. При росте каталога — стоит переключить на S3/R2 + CDN.
- Полнотекстовый поиск. TypeORM
LIKEтянет на 10 000 SKU, но при 50 000+ нужен Postgres FTS или Meilisearch.
Сейчас платформа работает в проде, заказы идут, импорт каталога автоматизирован. Дальнейшие итерации зависят от роста ассортимента и трафика.