Files
tmc-test-task/README.md
Oscar 9cb07e2f8e upd
2026-06-05 11:26:12 +03:00

6.0 KiB
Raw Permalink Blame History

TMC Test Task

Fullstack-приложение для управления коллекцией элементов с двухпанельным интерфейсом, виртуальным скроллом и drag & drop.

Задеплоенное приложение: https://test-1.koptilnya.xyz/


Что это

Интерфейс с двумя панелями: слева — 1 000 000 предсгенерированных элементов, справа — выбранные. Элементы можно перемещать между панелями, искать, добавлять новые и менять порядок через drag & drop.


Стек

Слой Технологии
Backend Node.js 22, Express 4, TypeScript 5
Frontend Nuxt 4 (SPA), Vue 3, TypeScript 6
UI @nuxt/ui, vue-draggable-next, SCSS
Инфраструктура Docker, Traefik, Let's Encrypt
CI/CD Gitea Actions

Архитектура бэкенда

Сервер запускается на порту 1337. API задокументировано через Swagger: /api/docs.

Эндпоинты (/api/items)

Метод Путь Описание
GET / Невыбранные элементы (пагинация, поиск)
GET /selected Выбранные элементы в заданном порядке
POST /add Добавить элемент в очередь на создание
POST /add-value Создать элемент с произвольным значением
POST /select Переместить элемент в правую панель
POST /deselect Вернуть элемент в левую панель
PUT /reorder Изменить порядок выбранного элемента

Хранилище

In-memory хранилище с 1 000 000 предсгенерированных элементов (случайные строки 612 символов). Выбранные элементы хранятся в Set для O(1)-поиска и в упорядоченном массиве для drag & drop. Новые элементы получают ID начиная с 1 000 001.

Очередь с батчингом

Ключевая часть бэкенда — класс RequestQueue (src/services/queue.ts):

  • Добавление элементов — батч сбрасывается каждые 10 секунд или при достижении 100 операций
  • Select / Deselect — применяются немедленно для мгновенного отклика UI
  • Reorder — батч сбрасывается каждую 1 секунду
  • Дедупликация — повторные операции над одним элементом отбрасываются

Такое разделение позволяет держать UI отзывчивым для частых пользовательских действий и одновременно снижать нагрузку на запись.


Архитектура фронтенда

SPA на Nuxt 4, SSR отключён. Сервер отдаётся через Nginx.

Панели

Левая панель (LeftPanel.vue) — невыбранные элементы:

  • Виртуальный скролл (высота строки 47px, overscan 5 элементов)
  • Infinite scroll для подгрузки страниц
  • Поиск с дебаунсом 300 мс
  • Модальное окно добавления нового элемента

Правая панель (RightPanel.vue) — выбранные элементы:

  • Те же параметры виртуализации
  • Drag & drop через vue-draggable-next
  • Автоскролл во время перетаскивания (зона 80px, скорость до 12px/тик)
  • После отпускания — синхронизация нового порядка с бэкендом

Состояние

Composable useItems.ts управляет всем состоянием: загрузка, пагинация, поиск, операции select/deselect/reorder. API-клиент генерируется автоматически из OpenAPI-спеки бэкенда (npm run gen:api).


CI/CD и деплой

Пайплайны описаны в .gitea/workflows/ и запускаются при пуше в main.

deploy-backend.yml

Триггер: изменения в /backend/**

  1. Checkout через SSH
  2. docker build -t tmc-backend ./backend
  3. Остановка старого контейнера
  4. Запуск нового в сети traefik с env PATH_PREFIX=/test-1
  5. Traefik автоматически получает Let's Encrypt сертификат и проксирует на api.koptilnya.xyz

deploy-frontend.yml

Триггер: изменения в /frontend/**

  1. Checkout через SSH
  2. docker build с build-arg API_BASE_URL (URL бэкенда)
  3. Остановка старого контейнера
  4. Запуск нового; Traefik проксирует на test-1.koptilnya.xyz

Оба сервиса живут в одной Docker-сети traefik. Traefik роутит запросы по хосту и управляет TLS.


Локальный запуск

# Backend
cd backend
pnpm install
pnpm dev          # :1337

# Frontend
cd frontend
pnpm install
pnpm dev          # :3000

По умолчанию фронт обращается к http://localhost:1337. Переопределить через NUXT_PUBLIC_API_BASE.

# Обновить API-клиент после изменений в бэкенде
cd frontend
pnpm gen:api      # требует запущенного бэкенда на :1337