Architecture Overview (Frontend Application)
This is technical documentation for developers. If you're a business user, see the Getting Started guide instead.
Этот документ описывает техническую архитектуру фронтенд-приложения hanc-app-frontend – клиентской части платформы для голосовых диалогов.
Приложение построено на Next.js (React) и предоставляет веб-интерфейс для настройки и мониторинга AI-агентов (телефонных ассистентов). Ниже подробно рассмотрены компоненты, модули и интеграции фронтенда.
1. Введение
Frontend представляет собой Single Page Application (SPA) на базе Next.js, выполняемое в браузере с поддержкой серверного рендеринга (SSR). Клиентское приложение является центральной точкой взаимодействия пользователей с платформой.
1.1 Назначение
Frontend предоставляет интерфейс для:
- создания и настройки AI-агентов;
- управления базой знаний агентов;
- просмотра истории и аналитики звонков;
- управления подпиской и биллингом;
- привязки и управления телефонными номерами.
Пользователи (конечные клиенты и агентства) используют фронтенд для конфигурирования платформы и контроля работы AI-агентов.
1.2 Типы пользователей
Приложением пользуются два основных типа пользователей:
1.2.1 Конечный клиент
Пользователь, управляющий собственным аккаунтом и AI-агентом.
Доступные возможности:
- настройка AI-агента;
- загрузка базы знаний;
- просмотр истории и аналитики звонков;
- управление подпиской;
- привязка телефонных номеров.
1.2.2 Агентство
Пользователь с расширенными правами, управляющий несколькими клиентскими аккаунтами (Customer) или рабочими пространствами (Workspace).
Дополнительно доступны разделы:
- Agency — управление клиентскими аккаунтами;
- Team — управление пользователями и ролями внутри организации.
2. Внешние интеграции и границы доверия
Фронтенд выступает посредником между пользователем и внешними сервисами. Каждый сервис образует отдельную границу доверия, требующую явной аутентификации и контроля доступа.
2.1 HANC Core API
Фронтенд взаимодействует с HANC Core API через RESTful HTTP(S) запросы.
Конфигурация:
- Базовый URL API задаётся через
NEXT_PUBLIC_API_URL(например,https://api.hanc.me/v1/...) - Версия API явно включена в URL (
/v1) - Транспорт: HTTPS
- Аутентификация: Firebase ID Token (Bearer Token)
Реализация:
- Все запросы требуют действительный Firebase ID Token или API-ключ
- Взаимодействие реализовано через Axios с автоматическим добавлением
Authorizationзаголовка - При ошибках авторизации фронтенд инициирует повторный вход пользователя
Полное описание всех API-endpoints можно увидеть на https://api.hanc.me/api-docs, а также в JSON формате на https://api.hanc.me/api-docs/json
Trust boundary: HANC Backend.
2.2 Firebase Authentication
Firebase используется для аутентификации пользователей на стороне фронтенд.
Функции:
- Поддерживаемые методы: OAuth (Google), e-mail/password
- Конфигурация передаётся через
NEXT_PUBLIC_FIREBASE_* - Firebase выдаёт ID-токены, которые используются для доступа к Core API
- Firebase применен на серверной стороне фронта (Firebase Admin SDK) для управления сессиями
Границы доверия:
- Firebase отвечает за выдачу и валидацию пользовательских удостоверений
- Backend проверяет подпись и срок действия ID-токенов
- Для SSR используется Firebase Admin SDK и Session Cookies
При SSR-запросах фронтенд применяет Firebase Session Cookies для определения статуса аутентификации.
Trust boundary: инфраструктура Google Firebase.
2.3 Voice Service / LiveKit
Фронтенд напрямую интегрируется с LiveKit Client SDK для установления и управления голосовой WebRTC-сессией между пользователем и AI-агентом.
Функции:
- Перед подключением приложение запрашивает внешний Voice Service по публичному URL
NEXT_PUBLIC_VOICE_SERVICE_URLдля получения временного токена и параметров подключения - Фронтенд вызывает POST
/create-room-token - Полученный токен используется для подключения к WebRTC-комнате через LiveKit SDK
- Сам медиапоток проходит напрямую между фронтенд и LiveKit Server
Trust boundary: Voice Service и LiveKit.
2.4 Stripe
Stripe используется для обработки платежей и управления подписками.
Функции фронтенд:
- инициирует создание Checkout Session и Billing Portal через внешний API;
- перенаправляет пользователя на хостинг Stripe;
- отображает результат операции после возврата пользователя.
Фронтенд не интегрируется со Stripe SDK и не обрабатывает платёжные данные.
Границы доверия:
- Создание Checkout Session и Billing Portal выполняется через Core API
- Webhooks Stripe обрабатываются исключительно серверной частью
- Все чувствительные платёжные операции находятся вне фронтенд
Trust boundary: Stripe и Core API.
2.5 Twilio
Фронтенд не взаимодействует с Twilio API напрямую.
Функции фронтенд:
- UI для ввода Twilio Account SID и Auth Token;
- управление телефонными номерами и их статусом.
Границы доверия:
- Все операции с Twilio (верификация, импорт, покупка номеров, SIP-настройки) выполняются через Core API
- Отправка и проверка SMS-кодов выполняется сервером
- Импорт и покупка телефонных номеров происходят через API
- SID и Auth Token не сохраняются на стороне фронтенд
Trust boundary: Twilio Verification и Voice Services.
2.6 PostHog Analytics
Фронтенд использует PostHog JavaScript SDK для сбора аналитики.
- Ключ передаётся через
NEXT_PUBLIC_POSTHOG_KEY - Аналитика не влияет на бизнес-логику платформы
Trust boundary: внешняя система телеметрии.
3. Структура кодовой базы
3.1 Файловая структура
Проект организован по функциональным директориям, что облегчает навигацию по коду. Ниже приведены основные папки и файлы:
3.1.1 Директория маршрутов src/app/
(auth)/– группы маршрутов для аутентификации (страницы логина, регистрации, восстановления пароля и т.д.). Например,login/page.tsx(экран входа) иsignup/page.tsx(экран регистрации) подключают соответствующие компоненты экранов из директории screens/Auth.(...panel)/– защищённые маршруты основного приложения (панель пользователя). Здесь организованы разделы по функциям:agency/,dashboard/,settings/и др., каждый со своей страницей (page.tsx) и иногда вложеннымlayout.tsx.(public)/– открытые маршруты (например, демо-страницы).
3.1.2 Компоненты экранов src/screens/
Здесь находятся реализации UI для маршрутов:
Auth/– экраны аутентификации. Например,LoginScreen/(компоненты для страницы входа) иRegisterScreen/(компоненты для страницы регистрации, включая файлRegisterScreen.tsxс логикой формы иserver.tsс серверными действиями регистрации).- Другие экраны – например,
Dashboard/(экран панели управления),KnowledgeBase/(экран базы знаний) и т.д., часто разбиты на подкомпоненты (components/) и вспомогательные суб-модули (например,stores/для состояния илиproviders/для контекста).
3.1.3 UI-компоненты src/components/
ui/– базовые UI-компоненты (кнопки, поля ввода, модальные окна, таблицы, иконки и др.).features/– более сложные функциональные компоненты или обёртки (например,ReactQueryWrapperдля инициализации React Query, Notifications для уведомлений).widgets/– специализированные виджеты для страниц (например,UserProfileInfo/,CreateAgentModal/,TeamTable/и другие компоненты, отображающие бизнес-данные в UI).
3.1.4 API-клиент src/gen/
Автоматически сгенерированный клиент API на TypeScript c помощью Swagger:
http-client.ts– базовый HTTP-клиент, класс HttpClient на основе Axios для выполнения запросов. Он создаёт экземпляр Axios с указанным базовымURL(например, из переменной окруженияNEXT_PUBLIC_API_URL) и предоставляет метод request() для отправки запросов.- Файлы API-клиентов по сущностям:
User.ts,Agency.ts,Voice.tsи др. – в каждом определён класс (например, UserApi или аналогичный), наследующийHttpClient. Эти классы содержат методы для вызова конкретных REST-эндпоинтов. Например, вUser.tsметод userControllerCreateUserV1 выполняет POST запрос на/v1/userдля регистрации пользователя.
3.1.5 React Query hooks src/queries/
Для каждой сущности определены функции, использующие @tanstack/react-query. Например, user.ts содержит hooks для получения/изменения данных пользователя, вызывающие методы из сгенерированного API-клиента пользователя. Это абстракция над прямыми вызовами API, упрощающая повторное использование логики запросов.
3.1.6 Утилиты src/utils/
validations/– схемы и функции валидации форм. Здесь определены схемы Zod для различных форм (например, LoginFormSchema, SignUpFormSchema и др.), а также составные валидаторы (e-mail, пароль, капча и т.д.).firebase/– настройки Firebase для аутентификации:firebaseConfig.ts(конфигурация Firebase SDK на клиенте),firebaseAdminConfig.ts(инициализацияadmin SDKна сервере), и вспомогательные функции (firebaseServer.tsсодержит методы интеграцииFirebaseс бекендом).- прочие утилиты – например, форматирование данных, путя, clsx.
3.1.7 Прочие директории
public/– статические файлы (изображения, аудио и пр.).- Корневые файлы конфигурации –
next.config.ts,docker-compose.yml,.env.example(пример переменных окружения, где заданURL APIи ключиFirebase), и т.д.
3.2 Маршруты приложения
Фронтенд реализован на Next.js 15 с использованием App Router (директория src/app). Код организован по функциональным зонам, используя группировку маршрутов (route groups) и вложенные layout'ы. Это позволяет разделять области приложения, управлять доступом (публичные vs защищённые страницы) и переиспользовать UI-шаблоны.
3.2.1 (auth) – публичная область для аутентификации
Доступна без входа:
/login– страница входа пользователя (с поддержкой email/пароля и Google OAuth)./signup– регистрация нового пользователя./forgot-password– восстановление пароля (форма ввода email для сброса).
Эти страницы используют общий минималистичный layout. После успешного входа пользователь перенаправляется на /dashboard. Если уже авторизован и пытается зайти на /login/, /signup, middleware перенаправит его на Dashboard.
3.2.2 (public) – публичные страницы
Не требуют авторизации:
/demo-agent-call/[id]– демо-страница звонка агенту. Позволяет любому посетителю поговорить с демонстрационным агентом веб-клиента. Пользователь нажимает кнопку звонка и инициирует WebRTC-соединение с агентом. Эта страница предназначена для маркетинговых/демонстрационных целей, чтобы потенциальные клиенты могли опробовать голосового агента.
3.2.3 (...panel) – защищённые страницы
Доступны после авторизации.
Overview (/dashboard)
Главная панель после входа в систему. Здесь отображается сводная информация:
Ключевые метрики и статистика: общее число звонков, суммарное время разговоров, средняя длительность.
Dashboard обычно использует общий layout для защищённых разделов (панель навигации слева, шапка). Имеется кнопка создания нового агента Create Voice Agent, в зависимости от кол-ва созданных агентов и тарифа, может быть неактивна. Вызывает POST /v1/agent.
Использует два источника информации: GET /v1/call/general-metrics, GET /v1/call/daily-metrics
Voice Agents (/voice-agents)
Раздел управления голосовыми AI-агентами. Этот раздел по сути является главным, он позволяет пользователю максимально кастомизировать агента для своих нужд, включает в себя огромный перечень настроек, которые будут представлены ниже
Имеется кнопка создания нового агента Create Voice Agent, в зависимости от кол-ва созданных агентов и тарифа, может быть неактивна. Вызывает POST /v1/agent. У существующих агентов есть отображение привязан ли номер к outbound и/или inbound и две настройки: Delete (DELETE /v1/agent/:agent_id) и Duplicate (POST /v1/agent). Под каждого нового агента также вызывается POST /v1/llm с новым экземпляром языковой модели.
-
Детали агента: страница
/voice-agents/[agentID]– редактирование настроек агента. Cодержит:-
Overview: Раздел с быстрыми действиями и управлением агента, содержит:
- Quick Actions: быстрые действия, такие как:
- Talk to agent: инициирует разговор с агентом через web, вызывая
POST /v1/create-room-token, создавая комнату и подключая пользователя и voice сервис агента (см. раздел 7) - Demo: редиректит на
/demo-agent-call/:agent_idгде подключатеся агент в режиме demo. - Call Agent: чтобы протестировать телефонный звонок, нужно привязать номер телефона, тогда при нажатии
POST /v1/configuration/frontend/listпользователь получит из dropdown два номера телефона, позвонив на которые, он сможет проверить работоспособность агента, только уже по сотовой связи. - Copy AgentID: скопировать ID агента.
- Talk to agent: инициирует разговор с агентом через web, вызывая
- Мanagement: доп функции, такие как
Duplicate(дублирование агента),Rename(переименовать агента),Delete(удалить агента), а такжеRequest feature(перенаправит на локальную почту с котактом hanc для предложения какой нибудь новой функциональности)
- Quick Actions: быстрые действия, такие как:
-
Dashboard: Показывает кол-во звонков из
GET /v1/call/daily-metrics/:agent_id, есть возможность мгновенного обновления информации и выбора временного периода, также содержит информацию о настроениях пользователей изGET /v1/sentiment-stats/:agent_id. -
Model: настройка модели, содержит такие разделы:
-
Knowledge base: ривязка базы знаний (документов) к агенту. Здесь можно выбрать одну или несколько Knowledge Base, которые агент будет использовать для ответа на вопросы.
-
First message: первое сообщение, которое скажет агент при начале разговора.
-
Agent prompt: детальная установка, как вести себя агенту, содержит
RichTextEditorиз библиотекиmantine.Все изменения патчатся через
PATCH /v1/llm/:agent_id.
-
-
Settings: Общие настройки, содержит такие настройки как:
-
Language: первоночальный приветственный язык, дальше агент переключается на язык собеседника.
-
Voice: выбор голоса(тональность, гендер, практические зоны применения).
-
End call after silence: автоматическое завершение звонка после таймаута, если в ответ тишина.
-
End-Call timeout (sec): таймаут в секундах, если ecas включена.
-
Reminder timeout (sec): время после которого повторно будет произнесено сообщение, в случае тишины.
-
Max reminders: кол-во сообщений, которое агент может сказать в случает тишины.
-
Sentiment analysis: анализ настроения звонящего, трекается как
positive | neutral | negative. -
Call summary: генерирует краткий обзор диалога с ключевыми фразами и действиями.
Все изменения патчатся через
PATCH /v1/agent/:agent_id.
-
-
Tools: дополнительные инструменты:
- Call Forwrding: функция переадресации вызовов, содержит такие настройки как:
-
Name: имя аккаунт менеджера.
-
Forwarding number: номер на который будет переадресация.
-
Global condition: глобальное условие, в случает которого, переадресовать вызов.
После сохранения:
POST /v1/agent/:agent_id/tools. Номер появится во вкладке Tools с возможностью сделать неактивным, изменить (PATCH /v1/agent/:agent_id/tools/:_id), удалить (DELETE /v1/agent/:agent_id/tools/:_id).
-
- Book Appointment: планировать и записывать клиентов с помощью планировкщика Cal.com
-
Name: название планировщика консультаций,
-
General Condition: условие, при котором клиент будет записан.
-
API KEY:
APIключ с cal.com. -
Event ID: идентификатор события.
-
Book an appointment: если выключено, то проверяет досутпность записи.
-
Confirmation: по дефолту no confirmation, есть выбор с подтверждением по SMS.
После сохранения:
POST /v1/agent/:agent_id/tools. Инструмент появится во вкладке Tools с возможностью сделать неактивным, изменить (PATCH /v1/agent/:agent_id/tools/:_id), удалить (DELETE /v1/agent/:agent_id/tools/:_id).
-
- API tool RAG: интсрумент позволяет подключиться к стороннему
APIдля подключения базы знаний в реальном времени.-
Name: имя инструмента.
-
General Condition: условие при котором будет запрос на стороннее
API. -
Execution message: сообщение будет произносится, пока идёт запрос к
API. -
API Endpoint URL: ендпоинт на который будет запрос.
-
Включает в себя также все настройки запроса(Method, Headers, Query Params, с определённой схемой Body(JSON)), а также позволяет задать request таймаут.
После сохранения:
POST /v1/agent/:agent_id/tools. Инструмент появится во вкладке Tools с возможностью сделать неактивным, изменить (PATCH /v1/agent/:agent_id/tools/:_id), удалить (DELETE /v1/agent/:agent_id/tools/:_id).
-
- Call Forwrding: функция переадресации вызовов, содержит такие настройки как:
-
Actions: вкладка содержит в себе набор кастомных переменных, в которые будут помещены значения взятые из диалога, а также набора действий, которые можно активировать и взаимодействовать с переменными.
-
Retrieval Variables: извлкаемые переменные, содержит набор:
-
Text, Number, Boolean, Selector: имя и описание, по которому
Llmбудет выделять значение в переменную.После сохранения:
PATCH /v1/agent/:agent_id. Инструмент появится во вкладке Actions/Retrieval Variables с возможностью изменить (PATCH /v1/agent/:agent_id), удалить (PATCH /v1/agent/:agent_id).
-
-
Actions: действия, которые можно выполнить:
- Send Email: отправка на
Emailнужных данных:- Name: название action.
- Subject: тема сообщения.
- Message: описание
Llmданных, которые нужно, чтобыLlmизвлекла. Есть возможность добавлять помимо заготовленных, кастомные переменные, которые можно создать в разделе Retrieval Variables. - General Condition: условие при котором сообщение будет отправлено на email, если поле пустое, сообщение будет отправляться всегда.
- Receipient Email(s): имейл(ы) получаетелей.
- Send SMS: аналогично отправке
Email, только нетsubjectи отправка производится поSMS. - API Call: отправка данных посредстов
API:- Name: название
action. - Condition: условие для отправки, если пусто, то отправка всегда.
- API Endpoint URL: конечный
endpointдля отправки. - Набор методов, параметров, заголовков для отправки.
- Name: название
После сохранения:
POST /v1/agent/:agent_id/actions. Инструмент появится во вкладке Actions с возможностью сделать неактивным, изменить (PATCH /v1/agent/:agent_id/actions/:_id), удалить (DELETE /v1/agent/:agent_id/actions/:_id). - Send Email: отправка на
-
-
Widgets: вкладка содержит информацию о том, как быстро встроить виджет с агентом в любое приложени:
- Floating Widget: флоатинг виджет , который встаривается посредсвом
<script><script/>, предусматривает обьязательный атрибутagent-id, находится за пределами layout tree. - Inline Widget: встраивается непросредственно в контент страницы, также через
<script><script/>, также треует agent-id.
- Floating Widget: флоатинг виджет , который встаривается посредсвом
-
Call Logs: содержит детальные логи, есть фукционал экспорта логов в
xlsx.- Имеет большое кол-во метрик:
StartTimeDurationDirectionSourceFromToSentimentVariablesStatus
- Имеет большое кол-во метрик:
-
Call Logs (/call-logs)
Логи о всех звонках, от всех агентов за определённое время, содержит такую статистику:
- Agent Name: имя агента.
- Start time: время начала звонка.
- Duration: длительность звонка.
- Direction: входящий или исходящий.
- Source: источник.
- From: номер телефона звонящего.
- To: принимающий номер.
- Sentiment: настроение.
- Variables: возможные переменные, см. Voice Agents -> Actions -> Retrieval Variables.
- Status: статус звонка.
Интерфейс позволяет выбрать временной отрезок, а также мгновенно обновить информацию (GET /v1/call/list).
Также имеется возможность экспорта логов в формате xlsx
Knowledge Base (/knowledge-base)
Раздел управления базами знаний для агентов. Позволяет загружать и хранить файлы, которые будут использоваться агентом в режиме Retrieval-Augmented Generation.
- Список Knowledge Bases: страница отображает все базы знаний, доступные текущему пользователю. Каждая запись включает название список файлов.
- Создание новой KB: кнопка Create открывает форму, где пользователь указывает название, промпт и прикрепляет файл для загрузки. Промпт позволяет чётко указать в каких случаях следует обращаться к KB. Отправка формы:
POST /v1/knowledge-base. - Удаление:
DELETE /v1/knowledge-base/:_id. - Редактирование позволяет изменить информацию или файл, или добавить ещё файлы (
POST /v1/knowledge-base/:_id/files). Удаление файла:DELETE /v1/knowledge-base/:_id/file.
Phone Numbers (/phone-numbers)
Управление подключёнными телефонными номерами. Раздел позволяет пользователю подключать, просматривать и удалять номера телефонов, используемые агентами. Отображает все активные номера, подключенные к текущей рабочей области, указаны:
- Номер телефона (в международном формате)
- Тип
- Агент на входящий звонок
- Агент на исходящий звонок
Если подписки нет, доступна только кнопка Upgrade now, которая делает редирект на /settings/billing.
Если есть, появляется возможность импортировать номера из Twililo, для этого должен быть настроена интеграция с Twilio, купить номер Buy number, а также Connect SIP Trunk, что бы так законектиться нужно написать на email.
Integrations (/integrations)
Настройка внешних интеграций:
- Позволяет интегрировать Twilio через Twilio SID, Twilio Auth Token.
Сохранение:
POST /v1/integration/twilio. - Также есть возможность сгенерировать токен для использования API HANC
Team (/team)
Управление командой и приглашениями. Раздел позволяет управлять участниками команды, привязанной к рабочей области. Список участников c полями Name, Email
Кнопка Invite User открывает модальное окно, в котором вводится Email.
Отправка формы: POST /v1/workspace/invite-member.
Удаление члена команды: DELETE /v1/workspace/remove-member/:member_id.
Agency (/agency)
Управление агентством (для клиентов с multi-workspace). Содержит два реаздела:
-
Customers: есть доступ только с планом Agency, есть кнопка Create Customer, которая вызывает модальное окно:
- Name: наименование, которое будет отображаться. Primary contact: основной контакт
- Full Name: полное имя.
- Email: имейл.
- Set initial password: задать изначальный пароль.
Отправка:
POST /v1/customer. -
Account: собственно аккаунт агентства, имеет два раздела:
-
Profile:
- Информация о агенстве.
- Invite Team Member: добавить по Email нового участника (
POST /v1/agency/invite-user).
-
Domain:
- Возможность привязать CNAME record из домена, после привязки DNS вручную будет активирован SSL-сертификат и включен white-label, пользователю надо сообщить на Email для завершения установки (
PATCH /v1/agency).
- Возможность привязать CNAME record из домена, после привязки DNS вручную будет активирован SSL-сертификат и включен white-label, пользователю надо сообщить на Email для завершения установки (
-
Settings (/settings)
Редактируемая информация о пользователе + Plan & Billing.
-
/settings/profile— раздел с основной информацией:Full Name,E-mail,Phone Number,Password,Language,Time Zone. Любое изменение:POST /v1/user. -
/settings/billingраздел с возможностью Downgrade/Upgrade Plan- Иноформация о текущем плане.
- Кол-во оставшихся кредитов.
- Разные планы и их подробное сравнение.
При клике на Subscribe:
PUT /v1/stripe/subscriptions→ редирект на Stripe Checkout. При подписке добавляется кнопка Manage billing info (POST /v1/stripe/billing-portal), а также в самом низу кнопка Cancel subscription.
4. Управление состоянием
4.1 Zustand
Для управления глабльным состоянием UI используется Zustand (директория src/store). Состояние включает:
- Данные пользователя (user)
- Статус подписки
- WebRTC состояния
- Управление модальными окнами
- Выбор текущего агента или Workspace
4.2 React Query
Для управления запросами используется React Query (@tanstack/react-query):
- Кэширует запросы
- Обновляет данные по триггерам
- Управляет статусами: isLoading, isError, isSuccess
Более подробно можно ознакомиться в разделе 6.
5. UI: формы и валидация
В приложении активно используются управляемые формы с библиотекой Mantine и схемами валидации Zod. Для каждой формы создаётся объект form через хук useForm из @mantine/form, где передаются initialValues и параметр validate: zodResolver(schema). Например, для логина используется LoginFormSchema , а для создания клиента CreateCustomerFormSchema. Библиотека Zod позволяет определить строгие правила: .string().email({message:'Invalid email'}) или .string().min(6,{message:'...'}), автоматически показывая ошибки под полями, как показано в документации Mantine.
5.1 Валидационные схемы
В папке src/utils/validations/ лежат файлы-схемы, в которых с помощью Zod описаны правила валидации для форм (имена полей, форматов, длины). Эти схемы импортируются в компоненты форм и передаются в zodResolver.
5.2 Примеры форм
Форма регистрации (RegisterScreen) включает поля имейл, пароль и пр., подключена к схеме RegisterFormSchema; модальные формы (например, CreateCustomerModal, InviteTeamMemberModal) аналогично используют useForm с zodResolver и Mantine-компоненты. После валидации события сабмита обрабатываются вызовом функций-мутаций.
5.3 Ошибки и уведомления
При невалидном вводе пользователю отображается сообщение, заданное в схеме Zod. При успешной отправке/ошибке запроса вызывается общая система уведомлений (Notification), которая выводит toast об успехе или неудаче.
6. Data Flow (от UI до HTTP-client)
Когда пользователь заполняет форму и отправляет её, верхний уровень React-компонент инициирует цепочку событий, которая заканчивается HTTP-запросом к бекенд API. Пример с регистрацией пользователя:
6.1 Инициирование в компоненте
При сабмите формы в RegisterScreen вызывается обработчик onSubmit. Обработчик собирает данные формы (имя, email, пароль ) и запускает мутацию регистрации пользователя. Для работы с серверными операциями используется React Query – например, хук useRegisterUserQuery() возвращает функцию mutateAsync для регистрации. Компонент вызывает registerUserQuery.mutateAsync(formData), передавая данные формы.
6.2 Вызов функции запроса (React Query)
Внутри useRegisterUserQuery определена функция mutationFn, которая и выполняет запрос к API. Она использует сгенерированный API-клиент пользователя. В начале этого файла создаётся экземпляр класса User (сгенерированного в src/gen/User.ts) с указанием базового URL сервера API (из process.env.NEXT_PUBLIC_API_URL). Таким образом, объект user знает куда обращаться (например, `` в продакшене). Затем mutationFn вызывает метод клиента API: user.userControllerCreateUserV1(data), где data – это объект с полями из формы, приведенный к требуемому DTO (например, CreateUserDto). Этот метод – часть сгенерированного класса User – отвечает за вызов эндпоинта регистрации.
6.3 Работа сгенерированного API-клиента
Метод userControllerCreateUserV1 внутри класса User уже предопределён генератором. Согласно спецификации Swagger, он выполняет POST-запрос на URL /v1/user (относительно базового URL) с переданными данными пользователя в теле запроса. Запрос помечен как secure (требует авторизации, в данном случае, возможно, JWT-токен если пользователь уже авторизован, либо не используется для открытой регистрации). Этот метод под капотом вызывает универсальный метод request() базового класса HttpClient.
6.4 HTTP-клиент (низший уровень)
HttpClient.request() формирует и отправляет HTTP-запрос с помощью Axios. Он объединяет базовый URL и путь (/v1/user), устанавливает заголовки (например, Content-Type: application/json), сериализует тело запроса и вызывает axios.instance.request(...). Если в конфигурации клиента указан флаг secure, то перед запросом может быть выполнен securityWorker – функция, которая добавляет данные авторизации (например, токен) к параметрам запроса. В случае регистрации новый пользователь ещё не имеет токена, поэтому запрос отправляется без авторизационного заголовка.
6.5 Взаимодействие с бекендом
Запрос достигает бекенда (URL и метод совпадают с задокументированными в Swagger). Бекенд создает нового пользователя и возвращает ответ (например, данные созданного пользователя). HTTP-клиент получает ответ (Axios возвращает промис с AxiosResponse).
6.6 Обработка результата на фронтенде
После успешного выполнения мутации registerUserQuery компонент регистрации продолжает логику: например, в нашем проекте сразу после создания пользователя выполняется дополнительный шаг – аутентификация нового пользователя.
Если регистрация происходила через Google, у приложения уже есть userCredential от Firebase. Если же регистрация обычная (почта/пароль), то фронтенд самостоятельно логинит пользователя в Firebase с помощью signInWithEmailAndPassword, сразу после создания аккаунта на бекенде, создаётся связанная учётная запись в Firebase Auth и выполняется вход.
Далее вызывается функция authUserOnServer(userCredential), которая связывает клиентскую авторизацию с серверной сессией. Этот метод (определён в firebaseServer.ts) берёт токен, полученный от Firebase для вошедшего пользователя, и отправляет запрос на внутренний API Next.js: POST /api/auth/login с заголовком Authorization: Bearer <firebase_id_token>. В нашем приложении этот внутренний маршрут проверяет токен через Firebase Admin SDK и устанавливает сессионную куку чтобы пользователь считался залогиненным в системе. Если ответ от /api/auth/login вернул статус 200 , фронтенд считает пользователя авторизованным.
6.7 Завершение потока
После успешной регистрации и логина редирект пользователя на защищённую страницу (вызовом router.push(toHome()). В случае ошибок на любом этапе – они обрабатываются: например, если запрос к /v1/user или /api/auth/login завершился неудачно, в UI отображается уведомление об ошибке.
6.8 Аналогичный поток для входа
Аналогичный поток реализован и для входа в систему, при сабмите формы логина компонент вызывает signInWithEmailAndPassword (Firebase) с введёнными данными. Если Firebase аутентификация успешна, вызывается authUserOnServer(userCred) для создания серверной сессии таким же образом. После успешного ответа от /api/auth/login происходит переход на домашнюю страницу панели. Если вход не удался (неверные креденшлы), компонент устанавливает ошибки в форме и показывает уведомление об ошибке входа.
6.9 Резюме
Таким образом, верхние слои (React-компоненты и хуки) инициируют сетевые запросы, но не работают с URL напрямую. Они пользуются сгенерированными методами API-клиента, инкапсулирующими детали запросов. Эти методы, в свою очередь, используют общий HttpClient на базе Axios для отправки HTTP-запросов к внешнему бекенд API. Дополнительно, для интеграции с Firebase применяются функции, отправляющие запросы к внутренним API-роутам Next.js (например, для установки аутентификационных cookie после входа) – это позволяет координировать глобальное состояние аутентификации между фронтендом и бекендом.
6.10 Middleware
Финальную стадию в нашем случае завершает проверка из src/middleware.ts. На каждый запрос по защищённым роутам мы проверяем валидность токена сессии через firebase-admin. В случае неудачи, cookie удаляется и пользователя редиректит на /login.
7. Ключевые сценарии: работа с аудио-звонками
Одна из ключевых возможностей - организация прямого звонка к агенту, как со стороны телефонной сети, так и через браузер. Фронтенд обеспечивает подключение по WebRTC и передачу аудио.
7.1 Сценарий 1: Тестовый вызов через веб (WebRTC call)
Это функция доступна пользователю с любыми привелегиями, позвонить своему агенту напрямую через браузер, минуя телефонную сеть, например, чтобы протестировать сценарий или продемонстрировать работу агента. На странице агента Demo. При нажатии запускается специальный хук useLiveKitCall. Последовательность такая:
7.1.1 Инициация комнаты
Хук создает новый объект LiveKit Room используя клиентскую библиотеку livekit-client. Это локальный объект конференции.
7.1.2 Настройка слушателей событий
Перед подключением хук регистрирует обработчики на события комнаты – например, RoomEvent.ParticipantConnected, ParticipantDisconnected, появление новых аудио-треков и другие. Обработчик ParticipantConnected проверяет identity, и если обнаруживает агента, устанавливает флаг agentConnected = true и начинает воспроизводить аудио приветствия агента (когда трек агента придет).
7.1.3 Запрос токена комнаты
Фронтенд обращается к Voice сервису, на отдельном домене (NEXT_PUBLIC_VOICE_SERVICE_URL). Выполняется POST /create-room-token с телом JSON: { metadata: { agent_id: <ID агента>, timestamp: <Date.now()> } }. Этот запрос говорит голосовому сервису, что нужен токен для комнаты, где будет задействован данный агент.
7.1.4 Получение ответа
Voice сервис возвращает объект с параметрами конференции:
- url – адрес LiveKit сервера (например,
wss://voice.hanc.ai– WebSocket URL для соединения). - token – JWT токен доступа, позволяющий подключиться к комнате. В токен заложена identity текущего пользователя.
- room_name – уникальное имя сессии (сгенерировано сервисом).
- user_identity – identity звонящего, чтобы фронтенд знал, как он идентифицирован на сервере.
В локальное состояние сохраняется room_name и user_identity.
7.1.5 Подключение к LiveKit
Вызывается await room.connect(url, token), устанавливается WebRTC соединение с LiveKit сервером. Браузер при этом запросит у пользователя доступ к микрофону (если не был дан ранее).
7.1.6 Публикация аудио
После успешного подключения статус звонка меняется на 'connected'. Хук вызывает enableMicrophone(room) – это включает захват аудио с микрофона пользователя и публикует его как аудио-трек в комнату. Теперь пользователь говорит – его голос отправляется на сервер LiveKit в эту комнату.
7.1.7 Подключение AI-агента
Параллельно, Voice сервис добавляет в эту же комнату второго участника - агента. Есть два возможных пути:
- Если агент привязан к телефонному номеру, сервис мог бы инициировать исходящий звонок через Twilio (Call API) на SIP-транк LiveKit с коннектом в эту комнату. Но для Web-вызова Twilio не нужен, Voice сервис сам запускает экземпляр AI-бота.
Voice сервис использует LiveKit Server SDK или специальный компонент (Media Gateway), чтобы подключить виртуального участника в комнату. Этот участник представляет агента: он получает аудио пользователя, прогоняет через модуль Speech-To-Text (STT), затем через LLM (ядро диалоговой логики), результат пропускает через Text-To-Speech (TTS) – и публикует сгенерированное аудио обратно в комнату. Такой pipeline обеспечивает двусторонний разговор в режиме реального времени.
- Альтернативный вариант – Twilio SIP: Twilio звонок -> SIP -> LiveKit.
Когда агент подключается к комнате, фронтенд получает событие ParticipantConnected. Хук useLiveKitCall уже умеет определять, что подключился именно агент. Тогда UI устанавливает флаг agentConnected, и может, например, отобразить статус "Agent joined" и начать ожидать аудио от агента. Когда агент публикует свой аудио-трек (синтезированная речь), фронтенд через LiveKit SDK получит событие об новом треке и начнет его воспроизводить (возможно, автоматически, или потребует клик "Unmute audio" из-за автоплей политики браузеров).
7.1.8 Диалог
Пользователь и агент обмениваются репликами. Голос пользователя идет к боту, бот отвечает – его ответ звучит на фронтенде. Продолжительность звонка может быть неограниченной, ограничена лишь балансом минут.
7.1.9 Завершение звонка
Пользователь может нажать end call, либо если агент закрывает соединение, вызов завершается. Хук обрабатывает RoomEvent.Disconnected, удаляет слушателей, освобождает ресурсы микрофона. Фронтенд UI возвращается в исходное состояние.
7.2 Сценарий 2: Входящий/исходящий звонок через телефонную сеть
Здесь фронтенд не участвует непосредственно в аудио, но обеспечивает инфраструктурные моменты:
7.2.1 Входящий звонок
Когда внешний абонент звонит на приобретенный Twilio номер, Twilio через SIP-транк направляет звонок в LiveKit комнату. Архитектура: для каждого телефонного звонка бэкенд создаёт уникальную LiveKit-комнату, подключает агента, подключает голос звонящего (через Twilio SIP Gateway) к этой же комнате. В итоге, реальный звонящий общается с виртуальным агентом. Фронтенд в этом процессе непосредственно не задействован: он не узнает о звонке в реальном времени. После завершения звонка, запись попадает в Call Logs.
7.2.2 Исходящий звонок
На данный момент возможность outbound call не реализована.
7.3 LiveKit Security
Фронтенд получает токен только на свою роль, он не знает токен агента – его создает Voice сервис самостоятельно. Токены одноразовые и короткоживущие. Коммуникация идет по зашифрованному WebRTC. Таким образом, фронтенд не имеет доступа к аудио агента вне комнаты и доверяет Voice сервису подключать правильного бота.
7.4 Итог
Пользователь фронтенда может в любой момент протестировать своего голосового агента через веб-интерфейс. Это упрощает отладку диалогов, не требуя реального звонка. Все сложности не видны пользователю, фронтенд же обеспечивает качественное соединение. Если что-то идет не так, фронтенд может разорвать сессию и показать сообщение об ошибке.
8. Разработка и отладка
8.1 Локальный запуск
Чтобы запустить приложение локально, обязательно нужно иметь файл .env со всеми нужными переменными, после можно запускать npm run dev, если нужно протестировать окружение - docker compose build -> docker compose up.
8.2 Логирование
Для выявления потенциальных ошибок предусмотрен ряд console.log(...), перед этим мы делаем проверку на isDev() NODE_ENV !== 'production', таким образом мы отлавливаем ошибки в Dev и исключаем попадание ненужных console.log в production.
8.3 API-документация
Для полного ознакомления с Hanc Core API, описано swagger'ом https://api.hanc.me/api-docs, а также https://api.hanc.me/api-docs/json в JSON формате. Здесь полностью описаны все существующие endpoints, method, headers, body, успешные и неуспешные варианты response, а также название операции и типизация для этой операции.