Виджет чата --- Next.js (`@mailoo/chat`)

Обновлено: Oct 12, 2026Раздел: Интеграции

Предоставьте чат на сайте из хоста Next.js без размещения API-ключа в браузере. Используйте @mailoo/chat для BFF-маршрутов и опционального плавающего виджета. Контракт API и заметки по WebSocket: chat-widget{.interpreted-text role="doc"}, chat-websocket-production{.interpreted-text role="doc"}.

Поток

  1. Браузер вызывает только маршруты того же домена вашего приложения Next.js:
  • POST /api/v1/webhooks/chat/submit --- тело { "content": "...", "sessionPublicId?": "..." }
  • GET /api/v1/webhooks/chat/messages?sessionPublicId=... --- опциональный поллинг (since поддерживается)
  • GET /api/v1/webhooks/chat/status --- опциональный статус интеграции
  1. Обработчик маршрута пересылает в Mailoo с X-API-Key из серверного окружения (createChatSubmitHandler / createChatMessagesHandler / createChatStatusHandler из @mailoo/chat/routes). Опционально внедрите senderEmail / senderName через resolveSender, чтобы в панели управления отображалась личность посетителя вместо Guest.
  2. Опционально: виджет открывает WebSocket к API. Хост определяется во время выполнения на сервере (без NEXT_PUBLIC_* для origin API):
  • Порядок по умолчанию: MAILOO_CHAT_WS_ORIGIN → MAILOO_CHAT_INTEGRATION_API → API_BASE_URL (getMailooChatWebSocketOrigin из @mailoo/chat)
  • Установите MAILOO_CHAT_WS_ORIGIN, когда видимое браузеру имя хоста API отличается от URL, по которому Node-сервер обращается к API

Интерфейс пакета

// app/api/v1/webhooks/chat/submit/route.ts
import { createChatSubmitHandler } from '@mailoo/chat/routes'

export const POST = createChatSubmitHandler({
  resolveSender: async () => {
    const session = await auth()
    return session?.user?.email
      ? { email: session.user.email, name: session.user.name ?? undefined }
      : null
  },
})
// app/api/v1/webhooks/chat/messages/route.ts
import { createChatMessagesHandler } from '@mailoo/chat/routes'
export const GET = createChatMessagesHandler()
// app/api/v1/webhooks/chat/status/route.ts
import { createChatStatusHandler } from '@mailoo/chat/routes'
export const GET = createChatStatusHandler()

Headless-сессия (собственный UI) через @mailoo/chat/hooks:

import { useMailooChatSession } from '@mailoo/chat/hooks'

const chat = useMailooChatSession({
  apiOrigin,
  projectUid,
  integrationId,
  isAuthenticated,
  enabled: panelOpen,
})

Переменные окружения (только сервер)

MAILOO_CHAT_INTEGRATION_API=https://api.mailoo.app
MAILOO_CHAT_INTEGRATION_API_KEY=your-api-key-here
MAILOO_CHAT_INTEGRATION_PROJECT_UID=your-project-uid
MAILOO_CHAT_INTEGRATION_ID=your-jsbox-integration-id
MAILOO_CHAT_WELCOME_MESSAGE=Hello! How can we help?

# Необязательно: публичный API origin для ws/wss, если отличается от серверного URL API


# MAILOO_CHAT_WS_ORIGIN=https://api.mailoo.app

API-ключ должен иметь область chat.send-message (или FULL). Скопируйте projectUid и integrationId из раздела Подключение и настройки / фрагмента встраивания в панели управления.

Плавающий виджет

Импортируйте CSS оболочки (обязательно, чтобы FAB оставался fixed, даже если Tailwind не сканирует @mailoo/chat). В этом монорепо PostCSS надёжнее резолвит относительный путь:

@import '../../../../packages/chat/src/styles/widget.css';
/* опубликованный пакет: @import '@mailoo/chat/styles'; */

Рекомендуемый (дефолтный) путь: на сервере вызвать resolveMailooChatHostConfig и смонтировать MailooSiteChatShell. Brand, topics, position, welcome, privacy и operators всегда загружаются из JSBOX config.chatWidget:

import { resolveMailooChatHostConfig } from '@mailoo/chat'
import { MailooSiteChatShell } from '@mailoo/chat/client'

const chat = await resolveMailooChatHostConfig({
  locale,
  isAuthenticated: Boolean(session?.user?.email),
})

{chat ? (
  <MailooSiteChatShell
    chat={chat}
    unavailableAction={{
      type: 'mailto',
      email: 'support@example.com',
    }}
  />
) : null}

resolveMailooChatHostConfig возвращает null, если env MAILOO_CHAT_* неполный или widget-config не загрузился (HTTP-ошибки включая 429, сеть, невалидный ответ). Никогда не бросает --- страница остаётся HTTP 200, оболочку чата не монтируйте. Вызовите openMailooSiteChat() из любого client-компонента, чтобы открыть панель.

Кастомный UI: передайте widgetConfig в MailooSiteChatWidget сами (из fetchMailooChatWidgetConfig или BFF GET …/webhooks/chat/widget-config). Launcher --- фиксированный FAB (углы или middle-left / middle-right) с Heroicons.

Недоступность чата / лимит запросов

Пакет классифицирует сбои транспорта и сервиса при отправке и опросе сообщений (не показывает сырой текст API в UI):

  • rate_limited --- HTTP 429 (лимит запросов API-ключа в минуту)
  • unavailable --- сеть, BFF 502/**503** или другие 5xx

Ошибки валидации, закрытая сессия (409) и отсутствующая сессия (404, очищается автоматически) остаются обычными inline-ошибками --- панель недоступности для них не открывается.

resolveMailooChatHostConfig возвращает null (не монтируйте чат), если widget-config не загрузился. Так страница хоста остаётся доступной; панель недоступности работает только после монтирования виджета, когда send/poll падает.

Действие выбирает интегратор (email, страница поддержки или свой UI):

<MailooSiteChatShell
  chat={chat}
  unavailableAction={{ type: 'mailto', email: 'support@example.com' }}
  // или: { type: 'link', href: '/support', label: 'Связаться с нами' }
  // или: renderUnavailable={({ fault, labels, retry }) => ...}
/>

Без unavailableAction посетитель всё равно видит объяснение и кнопку Повторить (сброс fault и возобновление polling). Лучше передать контакт, чтобы посетитель не застревал. Headless: fault / onFault / retryAfterFault у useMailooChatSession.

Dogfood mailoo.app: сайт продукта монтирует MailooSiteChatShell в каждом locale layout, когда заданы MAILOO_CHAT_*, с unavailableAction → mailto:support@mailoo.app. Карточка Поддержка → Онлайн-чат открывает ту же управляемую панель. Без этих переменных карточка остаётся «Скоро», а launcher не показывается.

Консоль чата в панели управления

Для интеграций JSBOX Открыть консоль чата открывает интерфейс оператора для этой интеграции. Операторы используют сессию панели управления; посетители --- публичные вебхуки и WebSocket-пути выше.

Связанные материалы

  • chat-widget{.interpreted-text role="doc"} --- публичные вебхуки и области действия
  • chat-websocket-production{.interpreted-text role="doc"} --- заметки по продакшен WebSocket
  • nextjs-packages{.interpreted-text role="doc"} --- обзор пакетов
📚