This commit is contained in:
Oscar
2026-06-05 11:26:12 +03:00
parent bdd58583d9
commit 9cb07e2f8e

131
README.md Normal file
View File

@@ -0,0 +1,131 @@
# TMC Test Task
Fullstack-приложение для управления коллекцией элементов с двухпанельным интерфейсом, виртуальным скроллом и drag & drop.
**Задеплоенное приложение:** [https://test-1.koptilnya.xyz/](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.
---
## Локальный запуск
```bash
# Backend
cd backend
pnpm install
pnpm dev # :1337
# Frontend
cd frontend
pnpm install
pnpm dev # :3000
```
По умолчанию фронт обращается к `http://localhost:1337`. Переопределить через `NUXT_PUBLIC_API_BASE`.
```bash
# Обновить API-клиент после изменений в бэкенде
cd frontend
pnpm gen:api # требует запущенного бэкенда на :1337
```