Files
PnvPanel/CLAUDE.md
T
Leonid Pershin 24d9ea1099
CI / Backend (build + test) (push) Successful in 1m14s
CI / Frontend (lint + typecheck + build) (push) Successful in 30s
Implement role and user management enhancements
- Added MaxIpLimit to roles, allowing for the configuration of simultaneous IP limits for users.
- Updated role creation and update commands to include MaxIpLimit, ensuring proper handling in the application logic.
- Enhanced user management by introducing a DELETE endpoint for user accounts, with appropriate checks to prevent self-deletion.
- Updated documentation to reflect changes in role and user management, clarifying the new IP limit functionality and user deletion process.
- Adjusted related tests to cover new functionality and ensure robust validation of role and user management features.
2026-07-13 07:18:13 +03:00

172 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# CLAUDE.md
Инструкции для Claude Code при работе в этом репозитории.
## Что это
**PnvPanel** — self-service портал для VPN-конфигураций (VLESS/VMess/Trojan/Shadowsocks): пользователи
сами создают конфиги, админ управляет серверами и пользователями. Есть **Telegram-бот** (ссылка на
сайт, просмотр конфигов, passwordless-вход). Бэкенд оркестрирует панели **3x-ui** через
[`ThreeXui.Net`](https://github.com/mrleo1nid/ThreeXui.Net) и хранит проекцию домена в PostgreSQL.
Живые обновления — SignalR. Поставка — **единый Docker-образ** (фронт+бек+бот) + PostgreSQL в compose.
> Собрано и покрыто тестами, единый образ и compose-стек проверены живьём. Осознанно не реализовано:
> тарифы, лимиты трафика/срока на конфиг, полное самообслуживание в боте — см. [tech-stack.md](docs/tech-stack.md).
## Документация (single source of truth)
Прежде чем менять архитектуру или добавлять фичу — свериться с [`docs/`](docs/README.md):
[Vision](docs/vision.md) · [Architecture](docs/architecture.md) · [Domain Model](docs/domain-model.md) ·
[Tech Stack](docs/tech-stack.md) · [Backend Conventions](docs/backend-conventions.md) ·
[Frontend](docs/frontend.md) · [Telegram Bot](docs/telegram-bot.md) · [API Design](docs/api-design.md)
**Держи доки в синхроне с кодом.** Меняешь контракт/архитектуру — обнови соответствующий док в том же изменении.
## Стек
- **Backend**: C# / .NET 10, ASP.NET Core Web API, Clean Architecture, CQRS (**собственный тонкий
диспетчер**, без MediatR), EF Core 10 + Npgsql, ASP.NET Core Identity + JWT, SignalR, FluentValidation,
Serilog. Маппинг DTO вручную (`FromDomain(...)`, без Mapster). OpenAPI — нативный
`Microsoft.AspNetCore.OpenApi` + Scalar UI (без Swashbuckle).
- **Frontend**: React 19 + Vite + TS, TanStack Query/Router, shadcn-стиль поверх Radix + Tailwind v4,
Zustand (только auth), react-hook-form + zod, @microsoft/signalr. pnpm, oxlint.
- **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.
Обязательно:
- Команды меняют состояние в транзакции (`UnitOfWorkBehavior`); запросы только читают (`AsNoTracking` +
проекция в DTO). Диспетчер — **собственный** (`ISender`/`ICommandHandler`/`IQueryHandler`, DI-регистрация).
- Управляемые ошибки — через `Result<T>`, не исключениями (исключения только для исключительного).
- Валидация — FluentValidation через `ValidationBehavior`; хендлер не перепроверяет формат ввода.
- Всё I/O асинхронно, `CancellationToken` пробрасывается до EF/HTTP. Никаких `.Result`/`.Wait()`.
- Nullable reference types включены; предупреждения анализаторов не игнорировать.
## Интеграция с 3x-ui
- Только через порт `IXuiPanelGateway`. Один `BaseAddress` в `ThreeXui.Net`, а нод много → гейтвей
держит **клиента per-node** (кэш по `NodeId`) из расшифрованных `NodeCredentials`. Детали —
[architecture.md](docs/architecture.md#интеграция-с-3x-ui-threexuinet).
- Пароли нод **шифруются at-rest** (`ISecretProtector`), расшифровка только в Infrastructure, никогда в логах/ответах.
- Недоступность ноды → `Result.Failure`/`NodeStatus.Offline`, не 500 наружу.
- Операции идемпотентны; при частичном сбое (клиент создан в панели, упала БД) — компенсация.
## Домен: роли, активация, конфиги
Полная модель — [domain-model.md](docs/domain-model.md). Ключевые инварианты:
- **Роли динамические** (`AppRole`), квоты на роли (не на `Plan`): `MaxConfigs` (число конфигов),
`MaxIpLimit` (лимит одновременных IP клиента в 3x-ui, `limitIp`); -1 = без лимита. У пользователя
ровно одна роль; `admin` — без лимитов. Системные роли `admin`/`user` не удалять/переименовывать.
Понижение роли — грандфазеринг (лишние конфиги не отзываются, новые блокируются до входа в квоту).
- **`limitIp`** выставляется автоматически по `MaxIpLimit` роли при создании клиента (`Create`/`Rotate`);
панель не даёт настраивать его per-конфиг и не трогает уже созданных клиентов при смене роли/квоты.
- **Активация**: новый пользователь `IsActivated=false`, роль `user`; конфиги создаёт только
активированный. `ActivationRequest` одобряет админ на сайте или в Telegram — одними командами.
- **Инбаунды по ролям** (`Inbound.AllowedRoles`, M:N): создание конфига проверяет активацию + квоту роли
(в транзакции — гонки параллельных созданий) + `AllowedRoles` + включённость ноды.
- **Блокировка** (`AppUser.IsBlocked`): вход запрещён + все конфиги `Disabled` в 3x-ui; в `AuditLog`.
- **Удаление пользователя** — свой аккаунт (`DELETE /api/auth/me`) или админом
(`DELETE /api/admin/users/{id}`, себя удалить нельзя): отзыв всех конфигов в 3x-ui, затем `AppUser`;
админский путь дополнительно пишет `AuditLog` (`UserDeleted`) и шлёт Telegram-DM.
- **Аудит**: значимые действия (активация/блок/роль/отзыв/ноды/инбаунды/удаление) — `AuditLog`
(append-only, источник Web/Telegram/System).
- **Ротация конфига** (`Rotate()`) — новый UUID/ссылка, квоту не тратит. **Бот read-only** по конфигам.
- **Подписка**: агрегированная (`AppUser.SubscriptionToken`) + по конфигу. API без версионирования
(`/api`, без `v1`); подписка отдаёт `Subscription-Userinfo`.
- **Вход по `UserName`** (email не используется). Восстановление пароля — через привязанный Telegram
(self-service) либо сбросом админом (`ResetUserPasswordCommand`); без привязки UI настойчиво предлагает привязать.
- **Сидинг**: идемпотентный `DbInitializer` создаёт системные роли + админа из env
(`AdminSeed__Username`/`Password`); каталог `ClientApp` — из [`seed/client-apps.json`](seed/client-apps.json).
Источник примера env — [`.env.example`](.env.example), обновляй при новых настройках. Секреты — только через env.
`Telegram__AdminTelegramUserIds` — отдельно от сидинга, не пишется в БД, читается напрямую из `TelegramOptions`.
## Telegram-бот
Детали флоу — [telegram-bot.md](docs/telegram-bot.md).
- Presentation-адаптер, не бизнес-слой: `TelegramBotHostedService` (long polling) вызывает **те же**
CQRS-команды через `ISender`. `Telegram.Bot` не проникает в Application/Domain — только `Api/Telegram/`.
- Passwordless-вход выпускает те же JWT/refresh, что и обычный логин; требует привязки Telegram (с сайта)
либо регистрации прямо из бота (`RegisterViaTelegramCommand` — логин `@username`/id, пароль генерируется
и приходит в чат один раз). Новый аккаунт — роль `user`, `IsActivated=false`, активация как обычно.
- Токены привязки/входа — короткоживущие одноразовые. `Telegram:BotToken` — секрет, не логировать.
Без токена бот просто не стартует — панель работает и без него.
## Единый контейнер
- Один образ: 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-контейнер для статики без явной просьбы — ломает требование единого контейнера.
- **TLS — внешний**; `app` отдаёт HTTP + доверяет `X-Forwarded-*` (`ForwardedHeaders`). Свой nginx/Caddy не добавляй.
- Миграции применяются авто на старте. CI (GitHub Actions) — только build/test, без деплоя.
## Соглашения по коду
Полный список — [backend-conventions.md](docs/backend-conventions.md). Кратко:
- `<Verb><Noun>Command`/`<Get|List><Noun>Query` + `Handler`/`Validator` (валидатор — где есть что
проверить). DTO — суффикс `Dto`; тела запросов Api — `Body`; тела ответов без Application DTO —
`ResponseDto`. Application — по фичам (feature folders).
- Один публичный тип на файл = имя файла (кроме `Body`/`ResponseDto` в файле эндпоинтов).
Async-методы — суффикс `Async` + `CancellationToken`.
- Секреты не логировать; логи — Serilog. Явного обогащения `UserId`/`NodeId`/`ConfigId`/`CorrelationId`
пока нет — не полагайся на него при расследовании.
- Ошибки API — единый `application/problem+json`.
## Команды
Backend (из `backend/`):
```bash
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/`):
```bash
pnpm install
pnpm dev
pnpm build
pnpm lint && pnpm typecheck
pnpm gen:api # типы из OpenAPI-схемы бэкенда
```
Инфраструктура:
```bash
docker compose up -d # api + postgres (+ web)
```
> Окружение: Windows, основная оболочка — **PowerShell**. Для POSIX-скриптов есть Bash-инструмент.
## Ключевые решения
См. [tech-stack.md](docs/tech-stack.md#ключевые-решения-по-домену-и-поведению): CQRS — собственный
диспетчер (не MediatR); одна роль на пользователя; секреты нод — ASP.NET Data Protection; тарифы `Plan`
не реализованы; i18n — RU+EN (react-i18next); Telegram — long polling, только привязка (не signup);
история трафика — простая таблица + TTL; логирование — Serilog.
## Рабочие принципы
- Не начинай крупную реализацию без сверки с доками и, при неоднозначности, без вопроса пользователю.
- Соблюдай границы слоёв — главный инвариант проекта. Нарушение = ошибка ревью.
- Обновляй документацию вместе с кодом. Не коммить и не пуши без явной просьбы.
- Отвечай пользователю на русском.