9.3 KiB
Frontend
SPA на React 19 + Vite + TypeScript. Общается с бэком по REST (JWT Bearer) и получает живые обновления по SignalR. Типы API генерируются из OpenAPI-схемы бэкенда.
Раздача из единого контейнера. В проде собранный фронт (
dist/) кладётся вwwwrootASP.NET Core и раздаётся тем же приложением (SPA-fallback наindex.html). Фронт и бек — один origin, база API — относительный/api, SignalR —/hubs/panel. В dev Vite-сервер проксирует/apiи/hubsна бэкенд. Детали упаковки — architecture.md.
Стек
| Задача | Выбор |
|---|---|
| Сборка/dev | Vite |
| Язык | TypeScript (strict) |
| Данные с сервера | TanStack Query |
| Роутинг | TanStack Router (типобезопасный) |
| UI-компоненты | shadcn/ui + Tailwind CSS v4 |
| Иконки | lucide-react |
| Клиентский стейт | Zustand (auth, тема) |
| Формы | react-hook-form + zod |
| Realtime | @microsoft/signalr |
| Графики | Recharts |
| QR-коды | qrcode.react |
| Типы API | openapi-typescript / orval (codegen) |
| i18n | react-i18next (RU + EN) |
| Пакетный менеджер | pnpm |
Структура
frontend/
src/
app/ # провайдеры (Query, Router, Auth, Theme), корневой layout
routes/ # маршруты TanStack Router (login, dashboard, configs, instructions, admin/*)
features/
auth/ # формы, хуки useLogin/useRegister, стор авторизации
configs/ # список/создание/редактирование/детали конфигов, QR, подписка
instructions/ # страница инструкций + каталог приложений по ОС
nodes/ # (admin) управление нодами
apps/ # (admin) CRUD каталога приложений
admin/ # пользователи, роли, аудит, статистика
theme/ # провайдер темы (light/dark/system) + переключатель
shared/
api/ # http-клиент (fetch + JWT/refresh), сгенерированные типы, query-хуки
realtime/ # инициализация SignalR, подписки → инвалидация Query-кэша
ui/ # обёртки над shadcn/ui, общие компоненты
lib/ # утилиты, форматирование (байты, даты)
config/ # env, константы
styles/ # tailwind, темы
index.html
vite.config.ts
package.json
Дизайн / UX
- Тема оформления: светлая и тёмная (переключатель в шапке; вариант «системная»). Реализация —
Tailwind
dark(класс наhtml) + shadcn/ui; выбор сохраняется (localStorage). - Современный и чистый вид: shadcn/ui + Tailwind, адаптивность, аккуратная типографика.
- Дашборд пользователя: карточки конфигов (протокол, локация, трафик прогресс-баром, срок, статус), быстрые действия (копировать ссылку, показать QR, перевыпустить, отозвать) + карточка «Общая подписка» (агрегированная ссылка/QR со всеми конфигами). Для неактивированного — экран «запросить активацию».
- Создание конфига: выбор локации/inbound (по
DisplayName) + метка + лимит устройств → мгновенная выдача ссылки + QR + ссылка на страницу инструкций. - Страница инструкций (
/instructions): общие шаги «как импортировать ссылку/QR» + каталог приложений (GET /api/apps), сгруппированный по ОС; клик по приложению открывает ссылку на скачивание. Данные ведёт админ (каталогClientApp). - Редактирование конфига: изменить метку и лимит устройств.
- Настройки аккаунта: смена пароля, привязка/отвязка Telegram, удаление аккаунта (с подтверждением).
- Админка: таблицы (TanStack Table) с пагинацией/фильтрами для нод, пользователей, конфигов, ролей, каталога приложений, журнала аудита; очередь запросов активации; графики трафика (Recharts). Блокировка пользователя — с подтверждением (гасит VPN). Управление приложениями (название, ссылка, ОС, вкл/выкл).
- Состояния: скелетоны при загрузке, аккуратные пустые состояния и toasts на ошибки/успех.
Работа с API
- HTTP-клиент оборачивает
fetch: подставляет access-token, при 401 — прозрачно обновляет через refresh-cookie и повторяет запрос; при неуспехе — разлогин. - Все запросы/мутации — через TanStack Query (ключи по фичам, инвалидация после мутаций).
- Типы ответов/запросов — из codegen по OpenAPI (никакого ручного дублирования DTO).
Авторизация на клиенте
-
Access-token — в памяти (не в localStorage), кладётся в
Authorization: Bearer. -
Refresh-token — httpOnly Secure cookie (JS не читает), ротация на сервере.
-
Стор авторизации (Zustand) хранит профиль/роли/
isActivated; guard-маршруты по роли (adminvs обычный) и по активации (неактивированного ведём на экран «запросить активацию»). -
Вход — по username (email не используется). «Забыли пароль?» ведёт: при привязанном Telegram — восстановление через бота; иначе — подсказка обратиться к админу.
-
Баннер привязки Telegram: пока Telegram не привязан, показываем настойчивый, но не блокирующий баннер/напоминание — это единственный self-service способ восстановить доступ. Настройки: смена пароля.
Вход и привязка через Telegram
- «Войти через Telegram»:
POST /api/auth/telegram/login-request→ показать deep-link/QR на бота, затем ждать подтверждения (поллингGET …/login-request/{id}или событие SignalR). ПриApproved— сохранить access, refresh уже в cookie, редирект в панель. - «Привязать Telegram» (в настройках, для вошедшего):
POST /api/auth/telegram/link-token→ показать deep-link/QR; статус привязки обновить по факту (поллинг/SignalR). Отвязка —unlink. - QR для deep-link —
qrcode.react.
Realtime
- Одно SignalR-подключение к
/hubs/panelс JWT; реконнект с бэкоффом. - Обработчики событий (
configTrafficUpdated,configStatusChanged,nodeStatusChanged) точечно обновляют/инвалидируют кэш TanStack Query — UI обновляется без перезагрузки.
Скрипты (ожидаемые)
pnpm dev # dev-сервер Vite
pnpm build # прод-сборка
pnpm preview # предпросмотр сборки
pnpm lint # ESLint
pnpm typecheck # tsc --noEmit
pnpm gen:api # генерация типов из OpenAPI-схемы бэкенда