Qadam Roadmap
проектdocs/architecture/api-routes.md

Карта API-маршрутов

Обновлён 14 апр. 2026 г., 17:16 · 0 комментариев

Карта API-маршрутов

Паспорт документа

Base prefix: /api/v1/


Авторизация и токены

Web-клиент использует httpOnly cookies. Дополнительно JwtAuthGuard умеет читать Bearer token из Authorization header, что используется для non-web клиентов и служебных сценариев.

CookieСодержимоеTTLPath
qadam_atAccess JWT {sub, email, type, tokenType:'access'}15 мин/
qadam_rtRefresh JWT {tokenType:'refresh', jti}7 дней/

JwtAuthGuard применён глобально (APP_GUARD). Сначала читает Bearer token из Authorization, если его нет — fallback на cookie qadam_at. Затем проверяет подпись и tokenType === 'access'. Маршруты с @Public() пропускают проверку.

Роли проверяются в контроллерах через assertSeller(user) / assertBuyer(user) и аналогичные проверки по типу пользователя, без отдельного role-guard слоя. При несоответствии бросается ForbiddenException.

Ротация токена: POST /auth/refresh читает qadam_rt из cookie или refreshToken из body, верифицирует его, проверяет jti, генерирует оба новых токена и для web-клиента заново устанавливает cookies.

qadam_rt теперь intentionally доступна всему web runtime по path=/, чтобы server-side proxy и server components могли читать refresh cookie при полной перезагрузке страницы. При выдаче новой пары токенов backend дополнительно чистит legacy qadam_rt с path=/api/v1/auth, чтобы не оставлять дублирующую refresh-cookie после миграции.


OpenAPI и Swagger

Backend теперь публикует машиночитаемый и человекочитаемый API-контур:

НазначениеURLДоступ
Swagger UI/api/docsВнутренний инженерный доступ
OpenAPI JSON/api/openapi.jsonВнутренний инженерный доступ

Именно OpenAPI JSON должен использоваться как источник истины для frontend codegen, contract review и дальнейшего выноса frontend в отдельный репозиторий. docs/architecture/api-routes.md остаётся обзорной картой маршрутов, но не заменяет машиночитаемый контракт.

Дополнительно в репозитории зафиксирован versioned artifact:

  • apps/api/openapi/openapi.json — канонический snapshot контракта для CI и frontend codegen;
  • apps/web/src/shared/api/generated/openapi.d.ts — generated TypeScript contract для web;
  • pnpm sync:api-contract — обновить artifact и generated types;
  • pnpm check:api-contract — проверить drift между backend-кодом, artifact и generated types.

Health

МетодPathAuthОписание
GET/healthPublicБыстрый health check API
GET/health/livePublicLiveness probe процесса API
GET/health/readyPublicReadiness probe с проверкой PostgreSQL и Redis
GET/metricsPublic route, loopback-only runtime accessPrometheus-compatible runtime/process/http metrics

GET /metrics публикуется в OpenAPI как часть system API, но runtime сознательно режет его на 403, если запрос пришёл не с loopback-адреса сервера. Это инженерный endpoint для локального scrape и operational диагностики, а не публичный маршрут для браузера.


Auth

МетодPathAuthCookiesОписание
POST/auth/registerPublicweb: ← sets qadam_at + qadam_rt; mobile: ← returns both in bodyРегистрация (email/phone, пароль, тип аккаунта)
POST/auth/check-availabilityPublicn/aПроверка доступности email и/или телефона перед регистрацией
POST/auth/register/buyerPublicweb: ← sets qadam_at + qadam_rt; mobile: ← returns both in bodyЕдиная регистрация buyer с онбордингом
POST/auth/register/sellerPublicweb: ← sets qadam_at + qadam_rt; mobile: ← returns both in bodyЕдиная регистрация seller с профилем, адресами и направлениями
POST/auth/loginPublicweb: ← sets qadam_at + qadam_rt; mobile: ← returns both in bodyВход по email/phone + пароль
GET/auth/meqadam_at или Bearer→ reads access tokenТекущий пользователь
POST/auth/refreshPublicweb: → reads qadam_rt, mobile: → reads refreshToken bodyРотация токенов
POST/auth/logoutqadam_at или Bearerweb: ← clears both cookiesВыход, очистка сессии
POST/auth/forgot-passwordPublicn/aНачать восстановление пароля
POST/auth/verify-reset-codePublicn/aПроверить SMS-код для сброса пароля
POST/auth/reset-passwordPublicweb: ← sets qadam_at + qadam_rt; mobile: ← returns both in bodyСбросить пароль и открыть сессию
POST/auth/change-passwordqadam_at или Bearer→ reads access tokenСменить пароль из авторизованного кабинета
POST/auth/add-buyer-roleqadam_at или Bearer→ reads access tokenДобавить buyer-профиль к seller/seller_staff аккаунту

Buyer (роль: BUYER)

МетодPathAuthОписание
GET/me/profileqadam_at или BearerПолучить профиль покупателя
POST/me/profileqadam_at или BearerСоздать профиль
PATCH/me/profileqadam_at или BearerОбновить account-данные buyer-профиля
PUT/me/profileqadam_at или BearerLegacy alias для PATCH
GET/me/childrenqadam_at или BearerПолучить список детей parent-buyer
POST/me/childrenqadam_at или BearerДобавить ребёнка
PATCH/me/children/:studentIdqadam_at или BearerОбновить ребёнка
DELETE/me/children/:studentIdqadam_at или BearerУдалить ребёнка
PATCH/me/interestsqadam_at или BearerПолностью заменить интересы student-buyer
POST/leadsqadam_at или BearerОставить заявку на товар
GET/me/leadsqadam_at или BearerМои заявки
GET/me/reviewsqadam_at или BearerМои отзывы
POST/me/reviewsqadam_at или BearerНаписать отзыв

Review flow больше не подразумевает немедленную публикацию: новые отзывы создаются в PENDING, GET /me/reviews публикует status и moderationNote, а публичные страницы и агрегаты учитывают только PUBLISHED.


Seller (роль: SELLER)

МетодPathAuthОписание
GET/seller/profileqadam_at или BearerПрофиль продавца
POST/seller/profileqadam_at или BearerСоздать профиль
PATCH/seller/profileqadam_at или BearerОбновить профиль
PUT/seller/profileqadam_at или BearerLegacy alias для PATCH
GET/seller/addressesqadam_at или BearerСписок адресов продавца
POST/seller/addressesqadam_at или BearerДобавить адрес продавца
PATCH/seller/addresses/:addressIdqadam_at или BearerОбновить адрес продавца
DELETE/seller/addresses/:addressIdqadam_at или BearerУдалить адрес продавца
PATCH/seller/addresses/:addressId/set-primaryqadam_at или BearerСделать адрес основным
GET/seller/notification-settingsqadam_at или BearerПолучить настройки уведомлений продавца
PATCH/seller/notification-settingsqadam_at или BearerОбновить настройки уведомлений продавца
GET/seller/telegram/connect-linkqadam_at или BearerПолучить deep link для запуска seller Telegram onboarding flow
POST/seller/telegram/verifyqadam_at или BearerПривязать Telegram по верификационному коду
DELETE/seller/telegramqadam_at или BearerОтвязать Telegram
GET/seller/itemsqadam_at или BearerСписок своих товаров
GET/seller/items/:idqadam_at или BearerТовар по ID
POST/seller/itemsqadam_at или BearerСоздать товар
PUT/seller/items/:idqadam_at или BearerОбновить товар
POST/seller/items/:id/submitqadam_at или BearerОтправить на модерацию
POST/seller/items/:id/withdrawqadam_at или BearerОтозвать pending-товар с модерации обратно в draft
POST/seller/items/:id/archiveqadam_at или BearerАрхивировать товар
DELETE/seller/items/:idqadam_at или BearerУдалить товар
GET/seller/leadsqadam_at или BearerЗаявки на мои товары; доступно owner и SELLER_STAFF
PUT/seller/leads/:id/statusqadam_at или BearerИзменить статус заявки (CREATED / CONTACTED / ENROLLED / REJECTED); доступно owner и SELLER_STAFF
GET/seller/reviewsqadam_at или BearerОтзывы по товарам продавца
PATCH/seller/reviews/:id/replyqadam_at или BearerОставить или обновить ответ продавца на отзыв
POST/seller/reviews/:id/complaintqadam_at или BearerПодать жалобу на опубликованный отзыв и отправить его на повторную модерацию
GET/seller/staffqadam_at или BearerСписок сотрудников
POST/seller/staffqadam_at или BearerДобавить сотрудника
PUT/seller/staff/:idqadam_at или BearerОбновить сотрудника
DELETE/seller/staff/:idqadam_at или BearerУдалить сотрудника

Для PATCH /seller/profile backend возвращает специфичные PHONE_TAKEN и EMAIL_TAKEN, если contact phone/email конфликтуют с другим аккаунтом. GET /seller/telegram/connect-link отдаёт готовый deep link с коротким signed token; frontend больше не должен собирать bot URL вручную. POST /seller/telegram/verify и DELETE /seller/telegram возвращают { success, username }, где username может быть null.

Seller item status-модель теперь включает DRAFT, PENDING, ACTIVE, REJECTED. GET /seller/items и GET /seller/items/:id дополнительно публикуют latestModerationRecord, чтобы frontend мог показать последнюю seller-visible причину/комментарий модерации. PUT /seller/items/:id больше не разрешён для PENDING айтемов: seller должен сначала вызвать POST /seller/items/:id/withdraw.

Seller review surface теперь публикуется отдельно: GET /seller/reviews отдаёт список отзывов по айтемам продавца со статусом, sellerReply, sellerReplyAt, canReply, canEditReply и activeComplaint. POST /seller/reviews/:id/complaint доступен только для PUBLISHED-отзывов, переводит отзыв в PENDING_MODERATION и временно убирает его из public aggregates, а PATCH /seller/reviews/:id/reply разрешён только в течение 48 часов после последнего ответа продавца.

Seller notification baseline теперь публикует GET/PATCH /seller/notification-settings с полями notifyNewLeadTelegram, notifyNewLeadEmail, notifyStatusChangeTelegram, telegramConnected, telegramUsername, sellerEmail. После создания лида LeadService сначала пытается Telegram-доставку продавцу, а при её отказе/невозможности и включённом notifyNewLeadEmail переходит на SMTP-capable email fallback; если SMTP не настроен на конкретном окружении, backend фиксирует SKIPPED, но не ломает создание лида. sellerEmail теперь резолвится из seller profile email с fallback на account email. Для owner-facing status-change notifications notifyStatusChangeTelegram больше не является пустым флагом: если статус меняет активный SELLER_STAFF, backend шлёт owner-уведомление по выбранным статусам из SELLER_STATUS_CHANGE_NOTIFY_STATUSES и при недоступном Telegram так же переходит на email fallback.


Catalog (Public)

МетодPathAuthОписание
GET/catalog/itemsPublicСписок опубликованных товаров с фильтрами
GET/catalog/items/:slugPublicТовар по slug (трекается событие view_item)
GET/catalog/items/:slug/reviewsPublicОтзывы на товар
GET/subjectsPublicПубличный alias справочника направлений
GET/catalog/subjectsPublicКатегории / предметы
GET/catalog/locationsPublicЛокации
GET/sellers/:idPublicПубличный профиль seller

GET /sellers/:id теперь публикует не только витринные поля seller и список опубликованных айтемов, но и явный seo block (title, description, canonicalUrl, openGraph, jsonLd) для SSR/metadata слоя. Профиль отдаётся только для ACTIVE seller-аккаунтов; несуществующий или непубличный seller должен давать 404.


Admin (роль: ADMIN)

МетодPathAuthОписание
GET/admin/statsqadam_at или BearerСтатистика дашборда
GET/admin/leadsqadam_at или BearerВсе заявки в системе
GET/admin/sellersqadam_at или BearerПагинированный список seller-аккаунтов для admin-операций
PATCH/admin/sellers/:sellerId/statusqadam_at или BearerИзменить account status seller-профиля
GET/admin/moderation/itemsqadam_at или BearerТовары на модерации
GET/admin/moderation/items/:idqadam_at или BearerТовар для проверки
POST/admin/moderation/items/:id/approveqadam_at или BearerОдобрить товар (опциональный комментарий)
POST/admin/moderation/items/:id/rejectqadam_at или BearerОтклонить товар (причина обязательна)
GET/admin/moderation/reviewsqadam_at или BearerОчередь отзывов на модерацию
GET/admin/moderation/reviews/:idqadam_at или BearerОтзыв для модерации
PATCH/admin/moderation/reviews/:idqadam_at или BearerПринять moderation decision по отзыву
POST/catalog/subjectsqadam_at или BearerСоздать категорию
PUT/catalog/subjects/:idqadam_at или BearerОбновить категорию
DELETE/catalog/subjects/:idqadam_at или BearerУдалить категорию
POST/catalog/locationsqadam_at или BearerСоздать локацию
PUT/catalog/locations/:idqadam_at или BearerОбновить локацию
DELETE/catalog/locations/:idqadam_at или BearerУдалить локацию

GET /admin/sellers принимает фильтры search, status, sellerType, page, limit и возвращает seller summary с displayName, accountStatus, contact fields и item counters (totalItems, pendingItems, activeItems), чтобы admin мог работать со статусами без ручного знания sellerId.

После PATCH /admin/sellers/:sellerId/status seller-facing защищённые маршруты больше не маскируют статус аккаунта под generic token error: backend отдаёт явные 403 ACCOUNT_UNDER_REVIEW или 403 ACCOUNT_BLOCKED, если seller пытается идти в защищённый контур со старым access token.

Review moderation теперь живёт на отдельном backend-срезе: ReviewStatus включает PENDING, PUBLISHED, REJECTED, PENDING_MODERATION; admin принимает решение через PATCH /admin/moderation/reviews/:id, catalog/public seller aggregates считают только PUBLISHED, а seller complaint flow переводит спорный отзыв обратно в PENDING_MODERATION.


Uploads

МетодPathAuthОписание
POST/upload/imageqadam_at или BearerЗагрузить JPG/PNG/WebP до 5 МБ и получить публичный URL
GET/uploads/images/:fileNamePublicПубличная выдача уже загруженного изображения

Текущий production уже переведён на S3-compatible storage DigitalOcean Spaces, поэтому новые upload'ы обычно возвращают абсолютный публичный URL. Локальный /uploads/images/<fileName> остаётся compatibility/fallback-вариантом для OBJECT_STORAGE_DRIVER=local и старых локальных файлов. Canonical transport contract для upload endpoint остаётся тем же: { url: string }, где url является готовым публичным URL.


Tracking

МетодPathAuthОписание
POST/trackPublicОтправить событие трекинга. Если пользователь авторизован — привязывается к нему, иначе по IP. Cookie qadam_cid используется как client ID.

Admin moderation endpoints теперь принимают решение только по PENDING айтемам. Если айтем уже ушёл в ACTIVE или REJECTED, backend возвращает конфликт статуса, а GET /admin/moderation/items/:id публикует latestModerationRecord и moderationHistory.