Updated the grid import/export process to allow for the inclusion of groups and collections directly from the configuration file. This enhancement ensures that missing groups and collections are created during import, improving the flexibility of the grid management system. Adjusted related classes and methods to accommodate these changes, ensuring a cohesive integration. Enhanced documentation and user prompts to clarify the new functionality and its usage.
13 KiB
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 cookietw_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. Контейнер работает под непривилегированным пользователем образа (USER $APP_UID, uid/gid 1654) — но статически, без gosu/entrypoint-скриптов и подгонки uid на старте: права на bind-mount хранилища выставляет оператор на хосте (см.docs/server-storage-setup.md§ 5). 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
# Форматирование — csharpier, версия закреплена в .config/dotnet-tools.json (один раз: dotnet tool restore).
# Глобально установленный csharpier может быть другой версии — вызывать только через dotnet:
dotnet csharpier format .
dotnet csharpier check .
# Юнит-тесты (по одному проекту за вызов — 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, обработка медиа) уже реализован — правь его по месту. Но новые крупные направления с непринятыми архитектурными решениями (напр. многоэкземплярное развёртывание, транскод-профили, новые доменные подсистемы) начинай только после сверки с пользователем. При неоднозначности — вопрос пользователю, не предположение.
- Соблюдай границы слоёв — главный инвариант проекта
- Не коммить и не пуши без явной просьбы.
- Отвечай пользователю на русском.