Skip to content

Repository files navigation

TaskHub

REST API для управления задачами с поддержкой двусторонней интеграции с внешними системами на примере Service Desk.

Стек

  • Node.js 24
  • TypeScript
  • Express
  • Prisma
  • PostgreSQL 17
  • Docker / Docker Compose

Запуск через Docker Compose

Требования

  • Docker
  • Docker Compose

Создать .env на основе .env.example.

Пример:

DATABASE_URL="postgresql://taskhub:taskhub@localhost:334/taskhub?schema=public"
SERVICE_DESK_INTEGRATION_TOKEN="replace-with-service-desk-token"
SERVICE_DESK_BASE_URL="https://service-desk.example/api"

SERVICE_DESK_BASE_URL должен содержать адрес REST API внешнего Service Desk. Если внешний Service Desk отсутствует, основное API Task Hub продолжает работать независимо от него.

Запуск приложения и PostgreSQL

docker compose up --build

После запуска API доступен по адресу:

http://localhost:333

PostgreSQL доступен на локальном порту:

localhost:334

Остановка

docker compose down

Данные PostgreSQL сохраняются в Docker volume и не удаляются при обычном docker compose down.

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

Требования

  • Node.js 24+
  • PostgreSQL

Установить зависимости:

npm ci

Создать .env на основе .env.example.

Применить миграции:

npx prisma migrate deploy

Сгенерировать Prisma Client:

npx prisma generate

Запустить приложение:

npm run dev

Проверка типов:

npm run typecheck

Сборка:

npm run build

API задач

Основные endpoints:

POST   /tasks
GET    /tasks
GET    /tasks/:id
PATCH  /tasks/:id
PUT    /tasks/:id
DELETE /tasks/:id

Создание задачи

POST /tasks

Обязательные поля:

  • title
  • priority

status по умолчанию имеет значение todo.

Допустимые статусы:

  • todo
  • in_progress
  • done

Допустимые приоритеты:

  • low
  • medium
  • high

due_date передаётся в формате ISO date-time с обязательным timezone.

Например:

{
  "title": "Подготовить отчёт",
  "priority": "high",
  "due_date": "2026-08-03T18:00:00+03:00"
}

При создании задачи due_date не может находиться в прошлом.

Успешный запрос возвращает 201 Created и идентификатор созданной задачи:

{
  "id": "5da25b33-fb3d-4402-915a-ad1f02eeee03"
}

Получение списка задач

GET /tasks

Поддерживаются параметры:

  • offset
  • limit
  • status
  • sort=due_date|priority
  • order=asc|desc

Пагинация, фильтрация и сортировка могут использоваться одновременно.

Пример:

GET /tasks?offset=0&limit=10&status=todo&sort=priority&order=desc

PATCH и PUT

PATCH /tasks/:id выполняет частичное изменение задачи --- обновляются только переданные поля.

PUT /tasks/:id полностью заменяет изменяемые данные задачи.

Для PUT обязательны:

  • title
  • status
  • priority

Если due_date при PUT отсутствует, значение устанавливается в null.

При изменении существующей задачи через PATCH или PUT допускается установка due_date в прошлом.

Удаление

DELETE /tasks/:id реализован как soft delete.

Запись физически остаётся в PostgreSQL, а поле deleted_at получает время удаления.

Удалённые задачи:

  • не возвращаются в GET /tasks;
  • возвращают 404 Not Found при запросе по ID.

HTTP-коды

Основные используемые коды:

Код Значение


200 успешное получение или изменение 201 задача создана 204 задача удалена 400 некорректный формат входных данных 401 ошибка авторизации интеграции 404 задача не найдена 409 конфликт состояния интеграции 422 нарушение бизнес-правила 500 внутренняя ошибка сервера

Некорректный формат данных возвращает 400 Bad Request.

Например:

  • неизвестный status;
  • неизвестный priority;
  • некорректный UUID;
  • некорректный формат due_date;
  • отсутствие timezone в due_date.

Корректно сформированный запрос, нарушающий бизнес-правило создания задачи с датой исполнения в прошлом, возвращает 422 Unprocessable Entity.

Интеграция с Service Desk

Реализована двусторонняя интеграция Task Hub с внешним Service Desk.

Service Desk → Task Hub

Внешняя система отправляет события через:

POST /integrations/service-desk/webhook

Webhook защищён Bearer-токеном:

Authorization: Bearer <SERVICE_DESK_INTEGRATION_TOKEN>

Пример события:

{
  "event": "ticket.created",
  "ticket": {
    "id": "SD-14852",
    "subject": "Не работает выгрузка отчёта",
    "state": "new",
    "urgency": 1,
    "due_date": "2026-08-03T18:00:00+03:00"
  }
}

Данные Service Desk преобразуются в модель Task Hub.

Соответствие статусов:

Service Desk Task Hub


new todo in_progress in_progress resolved done closed done

Соответствие приоритетов:

Service Desk Task Hub


1 high 2 medium 3 low

При первом событии для внешнего тикета создаётся новая задача.

Связь между задачей Task Hub и объектом внешней системы сохраняется в IntegrationBinding.

Повторное событие с тем же идентификатором внешнего объекта обновляет уже существующую задачу вместо создания дубликата.

Task Hub → Service Desk

Если задача связана с тикетом Service Desk через IntegrationBinding, изменение задачи через:

PATCH /tasks/:id
PUT   /tasks/:id

инициирует исходящий HTTP-запрос во внешний Service Desk:

PATCH {SERVICE_DESK_BASE_URL}/tickets/{external_entity_id}

Task Hub преобразует свою модель обратно в модель Service Desk.

Соответствие статусов:

Task Hub Service Desk


todo new in_progress in_progress done resolved

Соответствие приоритетов:

Task Hub Service Desk


high 1 medium 2 low 3

Для авторизации исходящего запроса используется Bearer-токен.

Если внешний Service Desk временно недоступен, изменение задачи в Task Hub сохраняется, а ошибка синхронизации записывается в лог.

Входящий webhook обновляет задачу непосредственно через integration-модуль и не запускает обратную синхронизацию в тот же Service Desk. Это предотвращает цикл повторных изменений между системами.

OpenAPI

Описание API находится в:

openapi.json

Спецификация содержит описание endpoints, параметров, моделей запросов и ответов, а также HTTP-кодов.

Postman

Готовая коллекция находится в:

postman/TaskHub.postman_collection.json

Коллекция содержит последовательный сценарий проверки API, включая:

  • создание задачи;
  • валидацию;
  • пагинацию, фильтрацию и сортировку;
  • получение задачи;
  • PATCH;
  • PUT;
  • soft delete;
  • проверку удалённой задачи;
  • входящее создание задачи через Service Desk webhook;
  • обновление задачи повторным событием Service Desk;
  • проверку авторизации webhook.

При создании задачи её id автоматически сохраняется в переменную taskId и используется последующими запросами.

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

src/
├── index.ts
├── db.ts
├── task/
│   ├── task.route.ts
│   └── task.service.ts
└── integration/
    ├── integration.service.ts
    └── service-desk/
        ├── service-desk.route.ts
        ├── service-desk.service.ts
        ├── service-desk.mapper.ts
        └── service-desk.client.ts

task.route.ts отвечает за HTTP-слой и проверку формата входных данных.

task.service.ts содержит операции над задачами и работу с Prisma.

integration.service.ts является общей точкой запуска синхронизации с внешними системами.

Модуль service-desk разделён на:

  • service-desk.route.ts --- входящий webhook;
  • service-desk.service.ts --- сценарии синхронизации и работа со связями;
  • service-desk.mapper.ts --- преобразование моделей Task Hub и Service Desk;
  • service-desk.client.ts --- исходящие HTTP-запросы в Service Desk.

Связь внутренних задач с объектами внешних систем хранится отдельно в модели IntegrationBinding, поэтому модель Task не зависит от конкретной внешней системы.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages