Files
TeleWave/CLAUDE.md
T

12 KiB
Raw Blame History

CLAUDE.md

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

Что это

TeleWave — сервис онлайн-каналов: пользователи смотрят сетку каналов, видео отдаётся из хранилища на сервере. Админ управляет каналами и пользователями.

Текущее состояние — рабочий вертикальный срез: вход/регистрация, роли, пользователи, админка; библиотека шоу/серий, загрузка и обработка медиа (ffmpeg → HLS-сегменты), реестр изображений, метаданные (TMDb/OMDb), каталог каналов, планировщик эфира (реклама, ТВ-заставки-переходы, weekly-override'ы) и live-раздача HLS с публичным просмотром сетки. Telegram-бот сознательно не делаем. Дальнейшие крупные направления (напр. многоэкземплярное развёртывание, новые доменные фичи) — по сверке с пользователем.

Архитектура и код-конвенции — прямое зеркало D:\Github\PnvPanel (тот же автор, тот же стек), но домен урезан под текущий объём фичи.

Стек

  • Backend: C# / .NET 10, ASP.NET Core Web API (Minimal API), Clean Architecture, CQRS через 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<AppUser, AppRole, Guid>), 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<T> (Common/Models/Result.cs), не исключениями.
  • Валидация — FluentValidation через ValidationBehavior; хендлер не перепроверяет формат ввода.
  • Всё I/O асинхронно, CancellationToken пробрасывается до EF/HTTP. Никаких .Result/.Wait().
  • Nullable reference types включены; Directory.Build.props включает TreatWarningsAsErrors=true — предупреждения анализаторов не игнорировать.

Домен: роли, пользователи, аутентификация

Полноценной доменной модели (каналы/видео) пока нет — см. заметку в начале файла. Текущие инварианты:

  • Роли (AppRole : IdentityRole<Guid>, 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).

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

  • <Verb><Noun>Command/<Get|List><Noun>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/):

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 <Name> --project src/TeleWave.Infrastructure --startup-project src/TeleWave.Api
dotnet ef database update --project src/TeleWave.Infrastructure --startup-project src/TeleWave.Api

Frontend (из frontend/):

pnpm install
pnpm dev
pnpm build
pnpm lint && pnpm typecheck

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

docker compose up -d --build   # только api; postgres — внешний, см. ConnectionStrings__Default

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

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

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