Skip to content

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

WorkOrderHub

MVP для создания, назначения и выполнения выездных заявок. Оператор управляет заявками, а команда видит только свою работу и меняет ее статус.

Возможности

Оператор

  • входит по телефону, паролю и демонстрационному 2FA-коду;
  • видит все заявки и список выездных команд;
  • создает и редактирует заявки;
  • назначает или меняет исполнителя;
  • без перезагрузки страницы получает изменения статуса заявки.

Выездная команда

  • видит только заявки, назначенные ее учетной записи;
  • не может получить чужую заявку подменой UUID в запросе;
  • переводит свою заявку new -> in_progress -> completed;
  • без перезагрузки страницы получает новую назначенную заявку.

Технологии

React, TypeScript, Vite, Node.js, Express, TypeORM, PostgreSQL, Redis, RabbitMQ и Socket.IO. Приложения запускаются через npm, инфраструктура — через Docker Compose.

Требования

  • Node.js 22;
  • npm;
  • Docker Desktop с Docker Compose.

Порты: 5173, 3000, 5432, 6379, 5672, 15672.

Быстрый запуск

1. Запустить инфраструктуру

docker compose up -d
docker compose ps

RabbitMQ Management UI http://localhost:15672:

  • логин: fieldops;
  • пароль: fieldops.

2. настройка backend

cd backend
npm ci

Скопируйте пример локальной конфигурации:

Copy-Item .env.example .env

Затем примените миграции и добавьте тестовых пользователей:

npm run db:setup

Команда применяет миграции TypeORM и добавляет тестовые данные.

3. Запустить backend

В каталоге backend:

npm run dev

Backend http://localhost:3000.

Проверка состояния:

GET http://localhost:3000/health

ответ:

{
  "status": "ok",
  "database": "connected",
  "redis": "connected",
  "rabbitmq": "connected"
}

4. Запустить frontend

В отдельном терминале:

cd frontend
npm ci
npm run dev

Откройте адрес, который напечатает Vite, обычно http://localhost:5173.

Vite проксирует /api и /socket.io на backend, поэтому для локальной разработки отдельная настройка CORS не нужна.

Как работает демонстрационный 2FA

  1. Backend проверяет телефон и хеш пароля.
  2. Шестизначный код сохраняется в Redis на 5 минут и печатается в терминале: [2FA] +10000000001: 123456.
  3. После проверки кода создается Redis-сессия на 24 часа.
  4. Браузер получает HttpOnly cookie с идентификатором сессии.

Основной сценарий

  1. Оператор входит в систему.
  2. Оператор создает заявку в статусе new.
  3. Оператор назначает пользователя с ролью team.
  4. Backend сохраняет изменение в PostgreSQL.
  5. Publisher отправляет событие order.assigned в RabbitMQ.
  6. Consumer получает событие и подтверждает его обработку через ack.
  7. Socket.IO отправляет событие только в комнату назначенного пользователя.
  8. Frontend команды повторно запрашивает свой список заявок.
  9. Команда переводит заявку в in_progress, затем в completed.
  10. После каждого изменения backend публикует order.statusChanged.
  11. Consumer передает событие в Socket.IO-комнату роли operator.
  12. Frontend оператора повторно запрашивает заявки, и таблица обновляется без ручной перезагрузки.

RabbitMQ не подключается к браузеру. Он отвечает за асинхронную передачу события внутри backend, а Socket.IO — за push-уведомление от backend к уже подключенному браузеру.

Авторизация и защита чужих заявок

Каждый защищенный HTTP-запрос проходит через middleware authenticate:

  1. middleware читает идентификатор сессии из cookie;
  2. получает сессию из Redis;
  3. загружает текущего пользователя и его роль из PostgreSQL;
  4. записывает пользователя в request.currentUser;
  5. следующий middleware или route handler проверяет роль и выполняет операцию.

Операторские операции дополнительно защищены проверкой роли operator. При чтении или изменении статуса командой запрос к PostgreSQL сразу фильтруется одновременно по UUID заявки и UUID текущего исполнителя. Для чужой заявки возвращается 404, чтобы не раскрывать факт ее существования.

Ограничения существуют не только в интерфейсе: ключевые проверки выполняются backend-ом.

Модель данных

  • Role (id, name, code)operator или team.
  • User (uuid, fullName, phone, passwordHash, role) — пользователь системы.
  • Order (uuid, executor, executionDate, address, status, description) — заявка; исполнитель необязателен.

Одна роль связана с несколькими пользователями, один team-пользователь — с несколькими заявками. Статусы: new, in_progress, completed.

REST API

Аутентификация:

  • POST /api/auth/login, POST /api/auth/verify-2fa;
  • GET /api/auth/me, POST /api/auth/logout.

Заявки:

  • GET /api/orders, GET /api/orders/:uuid — оператор видит все, команда только свои;
  • POST /api/orders, PATCH /api/orders/:uuid — только оператор;
  • PATCH /api/orders/:uuid/status — только назначенная команда.

Команды: GET /api/teams — только оператор.

Структура проекта

WorkOrderHub/
  backend/
    src/
      auth/
      database/
      entities/
      messaging/
      middleware/
      routes/
      services/
      verification/
      websocket/
  frontend/
    src/
  docker-compose.yml

команды

Backend

В каталоге backend:

Команда Назначение
npm run dev Запуск с автоматическим перезапуском при изменении исходников
npm run typecheck Проверка TypeScript без генерации JavaScript
npm run build Компиляция в backend/dist
npm start Запуск ранее собранного backend/dist/server.js
npm run db:migrate Применить непримененные миграции
npm run db:migrate:revert Откатить последнюю миграцию
npm run db:seed Создать или обновить тестовые роли и пользователей
npm run db:setup Последовательно выполнить migration и seed
npm run verify:realtime Проверить основной REST/RabbitMQ/Socket.IO сценарий

Для npm run verify:realtime должны работать инфраструктура и backend. Скрипт проверяет авторизацию, адресные уведомления и смену статусов, затем удаляет тестовые данные.

Frontend

В каталоге frontend:

Команда Назначение
npm run dev Запуск Vite dev server
npm run typecheck Проверка TypeScript
npm run build TypeScript-проверка и production-сборка в frontend/dist
npm run preview Локальный просмотр production-сборки

env backend

Основные значения уже имеют локальные defaults. Файл .env не должен попадать в Git.

Переменная Значение по умолчанию Назначение
PORT 3000 Порт backend
DB_HOST localhost PostgreSQL host
DB_PORT 5432 PostgreSQL port
DB_NAME fieldops Имя базы данных
DB_USER fieldops Пользователь PostgreSQL
DB_PASSWORD fieldops Пароль PostgreSQL
DB_LOGGING false Логирование SQL TypeORM
REDIS_URL redis://localhost:6379 Подключение к Redis
RABBITMQ_URL amqp://fieldops:fieldops@localhost:5672 Подключение к RabbitMQ
RABBITMQ_ORDER_EVENTS_QUEUE fieldops.order-events Имя очереди событий заявок
TWO_FACTOR_TTL_SECONDS 300 Срок жизни 2FA challenge
SESSION_TTL_SECONDS 86400 Срок жизни сессии
SESSION_COOKIE_NAME fieldops_session Имя session cookie
NODE_ENV development В production включает флаг Secure у cookie

Frontend обычно не требует .env. Если Socket.IO находится не на origin frontend и не проксируется Vite, адрес можно задать через VITE_SOCKET_URL.

Ограничения

  • 2FA выводится в консоль; регистрации, восстановления пароля и OAuth нет.
  • Backend работает одним процессом; RabbitMQ использует одну очередь и не переподключается автоматически.
  • Нет rate limiting, CSRF-токенов и аудита.
  • Team — пользователь с ролью team, отдельной сущности команды нет.
  • Статусы меняются только вперед; realtime-событие запускает повторный REST-запрос.
  • Ручное тестирование UI и пограничных случаев еще требуется.

PostgreSQL и RabbitMQ не объединены общей транзакцией. Если запись в БД прошла, а публикация события — нет, realtime-уведомление потеряется. Transactional Outbox для MVP намеренно не реализован.

Остановка окружения

Остановить контейнеры, сохранив данные в volumes:

docker compose down

Удаление volumes уничтожит локальную базу и состояния инфраструктуры, поэтому используйте такой вариант только если действительно нужен полный сброс:

docker compose down -v

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages