15 KiB
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/:
- Vision · Architecture · Domain Model
- Tech Stack (ADR) · Backend Conventions
- Frontend · Telegram Bot · API Design · Roadmap
Держи доки в синхроне с кодом. Меняешь контракт/архитектуру — обнови соответствующий док в том же изменении.
Стек
- 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) и лимит устройств (DeviceLimit→limitIpв 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; тарифы Plan — backlog (в MVP без лимитов трафика/срока);
i18n — RU+EN (react-i18next); Telegram — long polling, только привязка (не signup);
история трафика — простая таблица + TTL; логирование — Serilog.
Рабочие принципы
- Не начинай крупную реализацию без сверки с доками и, при неоднозначности, без вопроса пользователю.
- Соблюдай границы слоёв — это главный инвариант проекта. Нарушение = ошибка ревью.
- Обновляй документацию вместе с кодом. Не коммить и не пуши без явной просьбы.
- Отвечай пользователю на русском (язык общения в проекте — русский).