6.0 KiB
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 предсгенерированных элементов (случайные строки 6–12 символов). Выбранные элементы хранятся в 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/**
- Checkout через SSH
docker build -t tmc-backend ./backend- Остановка старого контейнера
- Запуск нового в сети
traefikс envPATH_PREFIX=/test-1 - Traefik автоматически получает Let's Encrypt сертификат и проксирует на
api.koptilnya.xyz
deploy-frontend.yml
Триггер: изменения в /frontend/**
- Checkout через SSH
docker buildс build-argAPI_BASE_URL(URL бэкенда)- Остановка старого контейнера
- Запуск нового; 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