# CLAUDE.md Инструкции для Claude Code при работе в этом репозитории. ## Что это **TeleWave** — сервис онлайн-каналов: пользователи смотрят сетку каналов, видео отдаётся из хранилища на сервере. Админ управляет каналами и пользователями. > Текущее состояние — **рабочий вертикальный срез**: вход/регистрация, роли, пользователи, админка; > библиотека шоу/серий, загрузка и обработка медиа (ffmpeg → HLS-сегменты), реестр изображений, > метаданные (TMDb/OMDb), каталог каналов, планировщик эфира (реклама, ТВ-заставки-переходы, > weekly-override'ы) и live-раздача HLS с публичным просмотром сетки. Telegram-бот сознательно > не делаем. Дальнейшие крупные направления (напр. многоэкземплярное развёртывание, новые доменные > фичи) — по сверке с пользователем. Архитектура и код-конвенции — прямое зеркало [`D:\Github\PnvPanel`](../PnvPanel) (тот же автор, тот же стек), но домен урезан под текущий объём фичи. ## Стек - **Backend**: C# / .NET 10, ASP.NET Core Web API (Minimal API), Clean Architecture, CQRS через [`LiteCqrs.Net`](https://github.com/mrleo1nid/LiteCqrs.Net) (NuGet-пакет, лёгкая CQRS-библиотека, альтернатива MediatR с явным разделением Command/Query — см. её README). EF Core 10 + Npgsql, ASP.NET Core Identity + JWT (access + refresh), FluentValidation, Serilog. Маппинг DTO вручную. OpenAPI — нативный `Microsoft.AspNetCore.OpenApi` + Scalar UI. - **Frontend**: React 19 + Vite + TS, TanStack Query/Router, shadcn-стиль поверх Radix + Tailwind v4, Zustand (только auth+тема), react-hook-form + zod. pnpm, oxlint. - **Инфра**: единый Docker-образ (API + статика SPA); PostgreSQL — **внешний сервер**, не в compose (в отличие от PnvPanel). ## Архитектура — жёсткие правила Слои и направление зависимостей: **Api → Infrastructure → Application → Domain** (внутрь). - **Domain** — без внешних зависимостей. Сейчас единственная сущность — `Auth/RefreshToken` (rich model: приватные сеттеры, фабричный `Issue(...)`, поведенческий `Revoke(...)`). - **Application** — CQRS-хендлеры, DTO, валидаторы, **порты** (интерфейсы: `IAppDbContext`, `ICurrentUser`, `IIdentityService`, `IJwtTokenService`, `IRefreshTokenService`, `IRoleService`). Зависит только от Domain — никаких `Npgsql`/`AspNetCore.Identity`, только их абстракции. - **Infrastructure** — реализации портов: EF Core (`AppDbContext : IdentityDbContext`), `IdentityService`/`RoleService`/`JwtTokenService`/`RefreshTokenService`, идемпотентный `DbInitializer` (сидинг ролей + админа). - **Api** — Minimal API эндпоинты (`Endpoints/*Endpoints.cs`), middleware, DI composition root (`Program.cs`). Обязательно: - Команды меняют состояние в транзакции (`UnitOfWorkBehavior`); запросы только читают. Диспетчер — из `LiteCqrs.Net` (`ISender`/`ICommandHandler`/`IQueryHandler`), регистрация через `AddLiteCqrs(...)` в `TeleWave.Application/DependencyInjection.cs`. - Управляемые ошибки — через `Result` (`Common/Models/Result.cs`), не исключениями. - Валидация — FluentValidation через `ValidationBehavior`; хендлер не перепроверяет формат ввода. - Всё I/O асинхронно, `CancellationToken` пробрасывается до EF/HTTP. Никаких `.Result`/`.Wait()`. - Nullable reference types включены; `Directory.Build.props` включает `TreatWarningsAsErrors=true` — предупреждения анализаторов не игнорировать. ## Домен: роли, пользователи, аутентификация Полноценной доменной модели (каналы/видео) пока нет — см. заметку в начале файла. Текущие инварианты: - **Роли** (`AppRole : IdentityRole`, `Infrastructure/Identity/`) — динамические, без квот (в отличие от PnvPanel: тут нет `MaxIpLimit`/`BillingEnabled` — при появлении доменных фич квоты/лимиты добавляются на `AppRole`/`AppUser` по мере необходимости, не заранее). У пользователя ровно одна роль. Системные роли `admin`/`user` (`IsSystem=true`) нельзя переименовать/удалить — проверяется в `RoleService`. Смену роли (`ChangeUserRoleCommand`) запрещено делать так, чтобы не осталось ни одного `admin` (`RoleErrors.CannotRemoveLastAdmin`). - **Регистрация** — открытая, без гейта активации (в отличие от PnvPanel): `RegisterCommandHandler` создаёт пользователя с ролью `user` и сразу выдаёт токены (auto-login). Если в будущем понадобится модерация новых пользователей — добавлять отдельным полем/флагом по аналогии с `IsActivated` в PnvPanel, не переиспользовать `IsBlocked`. - **Блокировка** (`AppUser.IsBlocked`) — админ блокирует/разблокирует пользователя; вход запрещён (`LoginCommandHandler`/`RefreshCommandHandler` проверяют флаг). Админ не может заблокировать/удалить самого себя (`UserErrors.CannotBlockSelf`/`CannotDeleteSelf`). - **Вход по `UserName`** (email не используется, SMTP не нужен). JWT — access (короткий TTL) + refresh (httpOnly cookie `tw_refresh_token`, `SameSite=Strict`, ротация с обнаружением повторного использования отозванного токена — см. `RefreshTokenService.RotateAsync`). - **Сидинг**: идемпотентный `DbInitializer` создаёт системные роли + админа из env (`AdminSeed__Username`/`Password`) при старте. Источник примера env — `.env.example`. ## Единый контейнер - Один образ: Api раздаёт REST (`/api`) и статику SPA из `wwwroot` (fallback на `index.html`). Один origin, база API — относительный `/api`. - Multi-stage Dockerfile: node (фронт) → dotnet sdk (publish + копирование в `wwwroot`) → aspnet runtime. Никакого gosu/root-drop и Data Protection key-ring — секретов на диске пока нет (JWT-ключ — обычная конфигурация, не шифруемый секрет at-rest); если появятся зашифрованные данные (например API-ключи внешних сервисов) — добавлять Data Protection по образцу PnvPanel. - docker-compose: только `app` — PostgreSQL живёт вне compose (внешний сервер/хост, адрес и креды — в `ConnectionStrings__Default` из `.env`; база и пользователь на нём создаются заранее вручную, приложение их не сидит). Миграции применяются авто на старте (`ApplyMigrationsAsync` в `Program.cs`) — прав `CREATEDB` не требуется, только DDL/DML в уже существующей базе. - Не вводи отдельный nginx-контейнер для статики — ломает требование единого контейнера. - **TLS — внешний**; `app` отдаёт HTTP + доверяет `X-Forwarded-*` (`ForwardedHeaders`). ## Соглашения по коду - `Command`/`Query` + `Handler`/`Validator` (валидатор — где есть что проверить). Папки — по фичам (`Auth/Login/`, `Admin/Roles/CreateRole/`, ...). - DTO — суффикс `Dto`; тела запросов Api-эндпоинтов, не совпадающие с командой 1:1, — `Body` (`sealed record` внизу файла эндпоинта). - Один публичный тип на файл = имя файла (кроме `Body` в файле эндпоинтов). - Ошибки — статические каталоги `XErrors` (`AuthErrors`, `RoleErrors`, `UserErrors`) с `Error.Validation/NotFound/Conflict/Unauthorized/Forbidden(...)`. - Секреты не логировать; логи — Serilog. - Ошибки API — единый `application/problem+json` (см. `Api/Common/ResultExtensions.cs`). - **Изображения — через общий реестр `Image`** (домен `Domain/Images`, галерея `features/admin/images`). Любой новый функционал, где загружается или выбирается картинка, должен использовать контрол `ImageGallery`/`GalleryBrowser` (пикер по категориям `ImageCategory`) и хранить ссылку на `ImageId`, а не заводить своё файловое поле. Автоматически полученные картинки (например постер из метадаты) тоже регистрируются как `Image`. Файлы лежат под `images/{id}{ext}`, отдаются по `/api/images/{id}`. ## Команды Backend (из `backend/`): ```bash dotnet build # Юнит-тесты (по одному проекту за вызов — MSBuild не принимает несколько): dotnet test tests/TeleWave.Domain.Tests dotnet test tests/TeleWave.Application.Tests # Интеграционные тесты (Testcontainers-Postgres) — нужен запущенный Docker; без него пропускаются: dotnet test tests/TeleWave.Integration.Tests dotnet run --project src/TeleWave.Api dotnet ef migrations add --project src/TeleWave.Infrastructure --startup-project src/TeleWave.Api dotnet ef database update --project src/TeleWave.Infrastructure --startup-project src/TeleWave.Api ``` Frontend (из `frontend/`): ```bash pnpm install pnpm dev pnpm build pnpm lint && pnpm typecheck ``` Инфраструктура: ```bash docker compose up -d --build # только api; postgres — внешний, см. ConnectionStrings__Default ``` > Окружение: Windows, основная оболочка — **PowerShell**. Для POSIX-скриптов есть Bash-инструмент. ## Рабочие принципы - Базовый вертикальный срез (каналы, планировщик, раздача HLS, обработка медиа) уже реализован — правь его по месту. Но новые крупные направления с непринятыми архитектурными решениями (напр. многоэкземплярное развёртывание, транскод-профили, новые доменные подсистемы) начинай только после сверки с пользователем. При неоднозначности — вопрос пользователю, не предположение. - Соблюдай границы слоёв — главный инвариант проекта, как и в PnvPanel. - Не коммить и не пуши без явной просьбы. - Отвечай пользователю на русском.