გორა.
AI-translation · draft (awaiting native review)
Jusmila — каталог мото-запчастей в Литве

ტექნიკური სტეკი

Монорепо на Turborepo, NestJS API, Next.js витрина, Vite-админка, скрейпер на chrome-devtools MCP.

~4 წუთი წასაკითხი · 800 სიტყვა

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.

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

Jusmila — стек и архитектура · hiregora.com