Files
PnvPanel/CLAUDE.md
T

15 KiB
Raw Blame History

CLAUDE.md

Инструкции для Claude Code при работе в этом репозитории.

Что это

PnvPanel — self-service портал для VPN-конфигураций. Пользователи сами создают себе конфиги (VLESS/VMess/Trojan/Shadowsocks), админ управляет серверами и пользователями. Есть Telegram-бот (ссылка на сайт, просмотр конфигов, passwordless-вход через привязку Telegram). Бэкенд оркестрирует панели 3x-ui через библиотеку ThreeXui.Net и хранит свою проекцию домена в PostgreSQL. Живые обновления — по SignalR. Приложение (фронт + бек + бот) поставляется единым Docker-образом; PostgreSQL — отдельным контейнером в compose.

Статус: проектирование. Код ещё не написан. Актуальны только документация и этот файл. При старте реализации следуй docs/roadmap.md (этапы M0…M6).

Документация (single source of truth)

Прежде чем менять архитектуру или добавлять фичу — свериться с docs/:

Держи доки в синхроне с кодом. Меняешь контракт/архитектуру — обнови соответствующий док в том же изменении.

Стек

  • Backend: C# / .NET 10, ASP.NET Core Web API, Clean Architecture, CQRS (собственный тонкий диспетчер, без MediatR), EF Core 10 + Npgsql (PostgreSQL), ASP.NET Core Identity + JWT, SignalR, FluentValidation, Mapster, Serilog (логирование).
  • Frontend: React 19 + Vite + TypeScript, TanStack Query/Router, shadcn/ui + Tailwind CSS v4, Zustand, react-hook-form + zod, @microsoft/signalr, Recharts. Пакетный менеджер — pnpm.
  • Telegram: Telegram.Bot, бот как BackgroundService в процессе Api (long polling).
  • Инфра: единый Docker-образ (API + бот + статика SPA) + PostgreSQL в docker-compose.

Архитектура — жёсткие правила

Слои и направление зависимостей: Api → Infrastructure → Application → Domain (внутрь).

  • Domain — без внешних зависимостей. Rich model: инварианты в сущностях (приватные сеттеры, фабричные методы, поведенческие методы). Никаких EF/HTTP/Identity здесь.
  • Application — CQRS-хендлеры, DTO, валидаторы, порты (интерфейсы). Зависит только от Domain. Никаких Npgsql/SignalR/ThreeXui.Net — только их интерфейсы (IAppDbContext, IXuiPanelGateway, IRealtimeNotifier, ISecretProtector, ICurrentUser, ...).
  • Infrastructure — реализации портов: EF Core, Identity/JWT, XuiPanelGateway, SignalR-пуш, фоновые сервисы, шифрование секретов.
  • Api — Minimal API эндпоинты (по фичам), SignalR-хабы, middleware, DI composition root.

Обязательно:

  • CQRS: команды меняют состояние и идут в транзакции (UnitOfWorkBehavior); запросы только читают (AsNoTracking + проекция в DTO). Диспетчер — собственный (ISender/ICommandHandler/ IQueryHandler, регистрация хендлеров через DI), без внешних CQRS-библиотек.
  • Управляемые ошибки — через Result<T>, не исключениями. Исключения — только для исключительного.
  • Валидация — FluentValidation через ValidationBehavior; хендлер не перепроверяет формат ввода.
  • Всё I/O асинхронно, CancellationToken пробрасывается до EF/HTTP. Никаких .Result/.Wait().
  • Nullable reference types включены; предупреждения анализаторов не игнорировать.

Интеграция с 3x-ui

  • Только через порт IXuiPanelGateway. ThreeXui.Net регистрируется на один BaseAddress, а нод много → гейтвей держит клиента per-node (кэш по NodeId), создавая его из расшифрованных NodeCredentials. Детали — в architecture.md.
  • Пароли нод шифруются at-rest (ISecretProtector), расшифровка только внутри Infrastructure, никогда не в логах/ответах API.
  • Недоступность ноды → Result.Failure/NodeStatus.Offline, не 500 наружу.
  • Операции с 3x-ui идемпотентны; при частичном сбое (клиент создан в панели, но упала БД) — компенсация.

Роли, активация, сидинг

  • Роли динамические: AppRole : IdentityRole<Guid> + поле MaxConfigs (квота на число конфигов). Квота — на роли, а не на Plan. У пользователя ровно одна роль; квота = MaxConfigs его роли (admin — без лимита). Системные роли (admin/user) не удалять/переименовывать.
  • Активация: новый пользователь IsActivated = false, роль user. Конфиги может создавать только активированный. ActivationRequest (с комментарием заявителя) одобряет админ на сайте или в Telegram — одними и теми же командами (ApproveActivationCommand/RejectActivationCommand).
  • Инбаунды по ролям: Inbound.AllowedRoles (M:N). При создании конфига доменный инвариант проверяет: активирован + под квотой роли + роль входит в AllowedRoles инбаунда + нода включена. Проверку квоты делать в транзакции (гонки параллельных созданий).
  • Понижение роли — грандфазеринг: смена на меньшую квоту разрешена; лишние конфиги не отзываем, но новые нельзя до входа в квоту.
  • Блокировка (AppUser.IsBlocked): вход запрещён + все конфиги Disabled (отключить клиентов в 3x-ui); разблокировка — обратно. Действие в AuditLog.
  • Аудит: значимые действия (активация, блок, смена роли, отзыв, ноды/инбаунды) писать в AuditLog (append-only, источник Web/Telegram/System).
  • Подписка: агрегированная на юзера (AppUser.SubscriptionToken, все активные конфиги) + по конфигу.
  • Ротация конфига (Rotate()): новый UUID/ссылка, квоту не тратит. Бот в MVP — read-only по конфигам.
  • Конфиг: пользователь задаёт метку (Label) и лимит устройств (DeviceLimitlimitIp в 3x-ui, 0=без лимита), может редактировать.
  • Самоудаление аккаунта (DELETE /api/auth/me): отзыв всех конфигов + удаление данных, аудит анонимизируется.
  • API без версионирования в MVP (/api без v1). Подписка отдаёт Subscription-Userinfo.
  • Вход — по UserName (email в системе не используется вовсе; SMTP не нужен). Восстановление пароля: через привязанный Telegram (self-service), без привязки — сброс админом (ResetUserPasswordCommand). Пока Telegram не привязан — UI настойчиво предлагает его привязать.
  • Сидинг из env: идемпотентный DbInitializer на старте создаёт системные роли и учётку админа (username/пароль/Telegram id) из переменных окружения. Единый источник примера — .env.example; при добавлении новой настройки обновляй и его. Секреты (пароль админа, JWT-ключ, BotToken) — только через env/secret-store.
  • Telegram id админов (AdminSeed__TelegramUserIds) авторизуют админ-действия в боте и получают уведомления о запросах активации.

Telegram-бот

  • Бот — presentation-адаптер, не бизнес-слой. Хостится в процессе Api (TelegramBotHostedService, long polling). Хендлеры апдейтов вызывают те же CQRS-команды/запросы через собственный ISender (GetMyConfigsQuery, LinkTelegramCommand, ApproveTelegramLoginCommand, ...).
  • Telegram.Bot не проникает в Application/Domain — только в Api/Telegram/.
  • Passwordless-вход выпускает те же JWT/refresh, что и обычный логин. Требует привязки Telegram (в MVP — только привязка существующего аккаунта, регистрация из бота — backlog).
  • Токены привязки/входа: короткоживущие, одноразовые, высокоэнтропийные. Telegram:BotToken — секрет, не логировать. Панель должна работать и без бота (если токен не задан — бот просто не стартует).
  • Детали флоу — telegram-bot.md.

Единый контейнер

  • Один образ приложения: Api раздаёт REST (/api), SignalR (/hubs), хостит бота и статику SPA из wwwroot (fallback на index.html). Фронт и бек — один origin, база API — относительный /api.
  • Multi-stage Dockerfile: node (сборка фронта) → dotnet sdk (publish + копирование в wwwroot) → aspnet runtime.
  • docker-compose: app (единый образ) + db (PostgreSQL). В dev — Vite-прокси /api,/hubs на бэк.
  • Не вводи отдельный nginx-контейнер для статики без явной просьбы — это ломает требование единого контейнера.

Соглашения по коду

Полный список — в backend-conventions.md. Кратко:

  • Команды <Verb><Noun>Command, запросы <Get/List><Noun>Query, + Handler/Validator. DTO — суффикс Dto.
  • Application организована по фичам (feature folders) внутри слоёв.
  • Один публичный тип на файл, имя файла = имя типа. Async-методы — суффикс Async + CancellationToken.
  • Секреты не логировать; логи структурные (Serilog) с UserId/NodeId/ConfigId/CorrelationId.
  • Ошибки API — единый ProblemDetails.

Команды (ожидаемые — появятся по мере создания проектов)

Backend (из backend/):

dotnet build
dotnet test
dotnet run --project src/PnvPanel.Api
dotnet ef migrations add <Name> --project src/PnvPanel.Infrastructure --startup-project src/PnvPanel.Api
dotnet ef database update --project src/PnvPanel.Infrastructure --startup-project src/PnvPanel.Api
dotnet format

Frontend (из frontend/):

pnpm install
pnpm dev
pnpm build
pnpm lint && pnpm typecheck
pnpm gen:api        # типы из OpenAPI-схемы бэкенда

Инфраструктура:

docker compose up -d        # api + postgres (+ web)

Окружение: Windows, основная оболочка — PowerShell. Для POSIX-скриптов есть Bash-инструмент. Пути — с учётом Windows.

Принятые решения (зафиксированы)

Ключевые развилки закрыты — см. tech-stack.md: CQRS — собственный диспетчер (не MediatR); одна роль на пользователя; секреты нод — ASP.NET Data Protection; тарифы Planbacklog (в MVP без лимитов трафика/срока); i18n — RU+EN (react-i18next); Telegram — long polling, только привязка (не signup); история трафика — простая таблица + TTL; логирование — Serilog.

Рабочие принципы

  • Не начинай крупную реализацию без сверки с доками и, при неоднозначности, без вопроса пользователю.
  • Соблюдай границы слоёв — это главный инвариант проекта. Нарушение = ошибка ревью.
  • Обновляй документацию вместе с кодом. Не коммить и не пуши без явной просьбы.
  • Отвечай пользователю на русском (язык общения в проекте — русский).