Перейти к содержанию

Frontend

Frontend — административный веб-интерфейс DataHUB. Реализует личный кабинет администратора и оператора платформы: управление подключёнными системами, маршрутами, классами сообщений, журналом обмена, пользователями. Технологически — приложение Next.js (App Router, server-side rendering), реализованное на React 19 и TypeScript. Аутентификация — через NextAuth с делегацией к backend.

Эта статья описывает конфигурацию блока frontend. Архитектура самого frontend (структура src/app/, серверные компоненты, маршруты) — в Архитектура / Компоненты системы.

Структура блока

Каталог блока — /etc/datahub/frontend/:

/etc/datahub/frontend/
├── docker-compose.yml   # описание контейнера
├── .env.template        # шаблон переменных окружения
└── .env                 # реальные значения (создаётся при установке)

Это самый тонкий блок поставки — у него нет ни данных (./data), ни шаблонов, ни сертификатов, ни логов на хосте. Всё состояние — в JWT-куках браузера пользователя; вся бизнес-логика — на backend.

Файл docker-compose.yml:

services:
  frontend:
    image: registry.gitlab.com/datahub/v3/services/frontend:dev
    container_name: frontend
    hostname: frontend
    expose:
      - "3000"
    env_file:
      - .env
    restart: unless-stopped
    networks:
      - dh_network

Особенности:

  • expose: 3000 без ports — frontend виден только внутри dh_network. Внешние запросы приходят через nginx, который проксирует на http://frontend:3000.
  • restart: unless-stopped — в отличие от остальных блоков (restart: always), frontend перезапускается только при сбое, не при ручной остановке. Это нюанс политики Next.js-приложений.
  • Внутренний HEALTHCHECK (из Dockerfile) — каждые 30 сек запрашивает http://localhost:3000/healthz. При неготовности контейнер помечается unhealthy.

Образ

Образ собран из исходников Next.js приложения многоступенчатой сборкой:

  • Buildernode:22-slim, выполняет npm ci и npm run build (Next.js standalone output).
  • Runnernode:22-slim, копирует только .next/standalone, .next/static и public. Запускается под непривилегированным пользователем nextjs.

В runner-образе нет npm, исходников, node_modules — только минимальная среда для запуска. Размер образа порядка 200 МБ.

Конфигурация (.env)

Шаблон .env.template:

AUTH_SECRET={{FRONTEND_AUTH_SECRET}}

HOSTNAME=0.0.0.0
PORT=3000

BACKEND_API_PATH=http://backend:80
API_PROXY_PATH=http://frontend:3000

PASSWORD_MIN_LENGHT=12
PASSWORD_NUMBERS=true
PASSWORD_UPPERCASE_LOWERCASE=true
PASSWORD_SPECIAL_SYMBOLS="@#$%^&+=!"
PASSWORD_HELP="Пароль должен содержать хотя бы одну строчную букву, одну заглавную букву и одну цифру, а так же один из спецсимволов - @#$%^&+=!"
VERIFICATION_TOKEN_LENGTH=6
IS_2FA_ENABLED=true

# NODE_TLS_REJECT_UNAUTHORIZED=0 # отключает проверку TLS сертификатов
Переменная Назначение
AUTH_SECRET Секрет NextAuth для подписи session-cookie. Генерируется случайно (см. ниже), не должен совпадать с другими секретами платформы.
HOSTNAME, PORT На каком интерфейсе и порту слушает Next.js. По умолчанию 0.0.0.0:3000. Менять не нужно.
BACKEND_API_PATH URL backend внутри dh_network (для server-side вызовов). По умолчанию http://backend:80.
API_PROXY_PATH URL самого frontend (для случаев, когда frontend через себя проксирует браузерные API-вызовы). По умолчанию http://frontend:3000.
PASSWORD_MIN_LENGHT Минимальная длина пароля при регистрации (учитывается на стороне UI). Должна быть согласована с серверной политикой backend (PASSWORD_COMPLEXITY).
PASSWORD_NUMBERS, PASSWORD_UPPERCASE_LOWERCASE, PASSWORD_SPECIAL_SYMBOLS Требования к составу пароля. Согласовать с backend.
PASSWORD_HELP Текст-подсказка, который отображается пользователю при вводе пароля.
VERIFICATION_TOKEN_LENGTH Длина OTP-кода для подтверждения email/SMS. Согласовать с backend (EMAIL_VERIFICATION_TOKEN_LENGTH, PHONE_VERIFICATION_TOKEN_LENGTH).
IS_2FA_ENABLED Включена ли двухфакторная аутентификация (OTP по email или SMS при входе).
NODE_TLS_REJECT_UNAUTHORIZED Закомментировано. Раскомментировать =0 только для отладки, когда backend использует self-signed сертификат и frontend на server-side не может его проверить. На production-стенде должно быть выключено.

Генерация AUTH_SECRET

head -c 32 /dev/urandom | base64

Скопировать результат в AUTH_SECRET без кавычек. Менять AUTH_SECRET на работающей платформе означает разлогинить всех пользователей frontend (их session-cookie перестанут проходить проверку подписи).

Frontend взаимодействует с backend двумя способами:

  1. Server-side (Next.js Server Components, API routes) — прямые HTTP-вызовы на BACKEND_API_PATH=http://backend:80 внутри dh_network. Сертификаты не используются (HTTP, не HTTPS — трафик не выходит за пределы сети Docker).
  2. Client-side (браузер пользователя) — браузер обращается к https://lk.<your-domain>/api/..., эти запросы проходят через nginx → frontend (http://frontend:3000) → backend. То есть frontend выступает proxy для browser → backend через свой server-side endpoint.

Архитектурно это означает: backend никогда не выставлен напрямую в браузер пользователя, frontend знает все его эндпоинты и проксирует только то, что разрешено.

Аутентификация и сессии

Frontend использует NextAuth (v5 beta) в режиме credentials provider — пользователь вводит логин/пароль, NextAuth делегирует проверку backend'у через REST-вызов на ${BACKEND_API_PATH}/api/auth/..., получает JWT-токен, кладёт его в encrypted session-cookie на стороне браузера.

При каждом server-side вызове Next.js достаёт JWT из cookie, расшифровывает, передаёт в backend в заголовке Authorization: Bearer <token>.

Refresh-токен хранится отдельно и используется для тихого обновления access-токена при истечении срока.

Запуск и проверка

После заполнения .env:

cd /etc/datahub/frontend
sudo docker compose up -d

Просмотр лога старта:

sudo docker logs -f --tail 200 frontend

Ожидаемое сообщение — ▲ Next.js 16.x.x ... Ready in <время>.

Healthcheck:

sudo docker ps --filter name=frontend --format "table {{.Names}}\t{{.Status}}"

В колонке STATUS должно быть Up <время> (healthy).

Frontend наружу не выставлен — проверка через nginx:

curl -fkS https://lk.<your-domain>/healthz

Должен вернуть OK (это эндпоинт nginx, но при работающем frontend nginx гарантированно его проксирует).

Прямая проверка фронта — в браузере: https://lk.<your-domain>/. Должна открыться страница входа.

Логирование

Frontend пишет логи в stdout/stderr контейнера. Logback-аналога нет — стандартный console.log / console.error Next.js. Доступны через docker logs frontend и попадают в Docker json-file driver (ротация 100 МБ × 10 файлов).

Уровень логирования регулируется самим Next.js — для production-сборки выводятся только ошибки и предупреждения. Дебаг-лог Next.js можно включить переменной NEXT_DEBUG=1, но это шумно и оправдано только при диагностике.

Тонкие особенности

  • Standalone mode — образ собран с output: 'standalone' в next.config.ts. Это означает, что npm не нужен в runtime — стартует node server.js.
  • Sentry — в package.json есть @sentry/nextjs, но интеграция включается только при наличии SENTRY_DSN (в шаблоне .env не указан). На штатной поставке Sentry не используется.
  • Telemetry Next.js выключенаNEXT_TELEMETRY_DISABLED=1 (передаётся анонимная статистика разработчикам Next.js; отключено осознанно).

Что дальше