# ThaiForTravel Mini App Backend

Отдельный backend для Telegram Mini App. WordPress остаётся источником правды по экскурсиям и трансферам, а backend отвечает за Telegram-аутентификацию, кэш каталога, избранное, заявки, планы отдыха, напоминания и доставку событий менеджерам.

## Контур

```text
WordPress + ACF
  -> плагин tft-miniapp-api
  -> нормализованный REST JSON
  -> Mini App Backend + ежедневный снимок каталога в Redis
  -> PostgreSQL + Redis (AOF)
  -> Telegram Bot + группа менеджеров
  -> CRM adapter
```

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

1. Создайте `.env` из `.env.example`.
2. Поднимите PostgreSQL и Redis:

```bash
docker compose up -d
```

3. Установите зависимости и примените миграцию:

```bash
npm install
npm run prisma:generate
npm run prisma:migrate:dev
```

4. Запустите API и worker:

```bash
npm run dev
npm run dev:worker
```

## Аутентификация Mini App

Клиент передаёт исходную строку `Telegram.WebApp.initData` в заголовке:

```text
Authorization: tma <initData>
```

Backend проверяет HMAC, срок `auth_date` и создаёт или обновляет локальную запись Telegram-пользователя. Поля пользователя из тела запроса не считаются доверенными.

## Основные маршруты

- `GET /health/live`
- `GET /health/ready`
- `GET /v1/catalog/tours`
- `GET /v1/catalog/tours/:id`
- `GET /v1/catalog/transfers`
- `GET /v1/catalog/cities`
- `GET /v1/catalog/filters`
- `GET /v1/me`
- `GET|POST|DELETE /v1/favorites`
- `GET|POST /v1/applications`
- `GET /v1/applications/:id`
- `GET|POST|PATCH /v1/plans`
- `POST|DELETE /v1/plans/:id/items`
- `GET /v1/plans/:id/recommendations`
- `POST /v1/plans/:id/submit`
- `GET|POST|DELETE /v1/reminders`
- `POST /v1/chat/open`
- `GET /admin/` — защищённая панель пользователей, заявок, оплат и чатов

Для быстрой витрины Mini App использует `GET /v1/catalog/tours?view=card`: ответ содержит одну уменьшенную обложку, цены, категории и варианты программ. Полная галерея, программа и условия загружаются через `GET /v1/catalog/tours/:id` только после открытия карточки.

Worker обновляет снимок каталога сразу после запуска и затем раз в `CATALOG_REFRESH_INTERVAL_SECONDS` (по умолчанию раз в сутки). Снимок живёт 48 часов, поэтому при кратковременной недоступности WordPress приложение продолжает работать с предыдущей версией. Принудительное безопасное обновление доступно через `POST /v1/internal/catalog/refresh` с заголовком `x-tft-sync-secret`.

Полный контракт находится в `openapi.yaml`.

Инструкция развёртывания находится в `docs/deployment.md`.
Определения показателей админки находятся в `docs/admin-dashboard.md`.

## Гарантии

- повторная отправка заявки защищена `Idempotency-Key`;
- заявка и outbox-сообщения создаются в одной транзакции;
- Telegram и CRM доставляются асинхронно с повторными попытками;
- двухдневная экскурсия занимает оба дня плана;
- несколько услуг в один день разрешены;
- рекомендации всегда фильтруются по выбранному направлению;
- публичный каталог не раскрывает внутренние ACF-ключи;
- HTML из WordPress очищается в плагине до передачи Mini App.
