Real-time дашборд аналитики использования AI

Когда движок синхронизации стал тянуть данные использования из каждого аккаунта в единую базу, следующим шагом было сделать эти данные полезными без необходимости писать SQL-запросы. Клиенту нужен был дашборд — что-то, что можно открыть в течение дня и видеть, что происходит по всем аккаунтам в реальном времени: кто чем пользуется, сколько это стоит и куда уходят деньги. Но помимо простого мониторинга, нужно было ещё привязывать использование к конкретным клиентским проектам для биллинга. AI-инструмент для разработки не знает, над каким проектом вы работаете — он просто логирует, что вы сделали запрос. Связать эти запросы с биллингуемой работой — это и было настоящей задачей.

Стек

  • Next.js 14
  • React 18
  • Prisma
  • PostgreSQL

обзор и производительность

Дашборд открывается на мультиаккаунтном обзоре: каждый аккаунт показывает связанный email, текущий биллинговый период, общую сумму расходов и разбивку по моделям. Ниже — набор графиков, визуализирующих тренды использования во времени: суточные кривые стоимости, круговые диаграммы распределения по моделям и составной столбчатый график, показывающий, как тратиться смещаются между быстрыми и премиум-моделями в течение недели. Графики сделаны на Recharts и подкреплены Zustand для управления клиентским состоянием. Данные грузятся быстро на первом рендере, потому что тяжёлая агрегация происходит на сервере: запросы Prisma тянут предрасчитанные сводки из материализованных представлений PostgreSQL, а не из сырых таблиц событий. Эта оптимизация пришла позже в проекте — изначально дашборд запрашивал таблицу событий напрямую, и по мере того как датасет рос за пару сотен тысяч строк, время загрузки страницы поползло с менее чем секунды почти до четырёх. Материализованные представления для сводок биллингового периода вернули его обратно ниже секунды, обновление — почасовое cron-задание.

реал-тайм-слой

Реал-тайм-компонент работает на Server-Sent Events. Когда движок синхронизации пишет новые события использования в базу, он триггерит SSE-рассылку всем подключённым клиентам дашборда. Браузер получает эти события и обновляет графики и итоги без перезагрузки страницы. Чтобы SSE надёжно работал через Nginx, потребовалась специфическая конфигурация прокси, которая неочевидна из документации — нужны длинные read-таймауты, отключённая буферизация прокси и заголовок `X-Accel-Buffering: no`, чтобы Nginx не держал поток ответа в памяти. Без этих настроек SSE-соединение молча буферизовалось на 30–60 секунд и потом выгружало пачку событий разом, превращая «реал-тайм» опыт в скорее отложенный повтор. На стороне клиента SSE-соединения молча обрываются, когда ноутбук уходит в сон или переключает сети. Нативный EventSource браузера не всегда красиво справляется с переподключением, поэтому мы добавили механизм heartbeat: сервер шлёт пинг каждые 15 секунд, и если клиент не получает его в течение 30 секунд, он сносит соединение и переподключается. Просто, но это убрало жалобы в духе «дашборд выглядит замороженным».

рабочие сессии и доступ

Трекинг рабочих сессий — фича, которая превратила это из инструмента мониторинга в инструмент биллинга. Идея простая: группировать события использования в рабочие сессии — непрерывные блоки активности, разделённые паузами — и позволять пользователю присваивать каждой сессии клиентский проект. На практике определение того, что считается «сессией», потребовало эвристик. Мы остановились на 30-минутном промежутке бездействия как границе сессии: если использования нет в течение 30 минут, следующее событие начинает новую сессию. Это было настроено под реальные паттерны работы клиента — они обычно переключают проекты во время естественных перерывов (обед, встречи), и 30-минутный промежуток разумно ловит эту границу. Каждая сессия показывает длительность, общую стоимость, использованные модели и количество запросов. Пользователь тегирует сессии именами проектов из выпадающего списка, и система генерирует отчёты о расходах по проектам в конце каждого биллингового цикла. Накатить это на данные, изначально собранные без какого-либо понятия о сессиях, означало бэкфиллить — алгоритм ретроактивно прогоняется по историческим событиям, что потребовало аккуратной обработки, чтобы избежать перепроцессинга уже размеченных сессий.

Доступ защищён JWT-аутентификацией, токены генерируются и валидируются библиотекой jose. Токен включает роль пользователя, и слой API проверяет права на каждый запрос. Сейчас есть единственная админская роль, но структура поддерживает добавление read-only-просмотрщиков или скоупа по аккаунтам позже. Фронтенд — React 18 на Next.js 14, с Zustand, управляющим всем от активного фильтра аккаунта до выбранного диапазона дат и буфера live-событий SSE. Хранение состояния в Zustand вместо разбросанного по локальным state компонентов сильно упростило реализацию реал-тайм-обновлений — одно обновление стора пропагируется во все графики и итоговые компоненты, которые от него зависят.

результаты

Дашборд теперь даёт клиенту полную видимость их расходов на AI-инструменты по каждому аккаунту, в реальном времени, с возможностью отследить расходы до конкретных клиентских проектов. Вывод: реал-тайм-фичи обманчиво дороги в сложности. SSE проще WebSocket на бумаге, но операционные детали — конфигурация Nginx, логика переподключения, heartbeat, буферизация — складываются в нетривиальный объём инфраструктурной работы. Если данным действительно не нужно быть живыми, polling с коротким интервалом почти всегда лучший выбор. В этом случае клиент смотрит на дашборд во время активной работы и хочет видеть, как расходы тикают по мере работы — реал-тайм здесь действительно важен, поэтому сложность была оправдана.