# CLAUDE.md Инструкции для Claude Code при работе в этом репозитории. ## Что это **TeleWave** — сервис онлайн-каналов: зритель смотрит сетку каналов, видео отдаётся из хранилища на сервере. Админ ведёт библиотеку, каналы и эфир. Работает: аутентификация, роли, пользователи; библиотека шоу/серий, коллекции, жанры, метаданные (TMDb/OMDb), обработка медиа (ffmpeg → HLS), изображения; группы, шаблон сетки, стыки и заставки, автосборка по профилям, обмен конфигурацией файлом и запросом к ИИ; генерация ленты, live-раздача, публичный просмотр, статистика хранилища. Telegram-бот сознательно не делаем. Конвенции — зеркало [`PnvPanel`](../PnvPanel) (тот же автор). **Спеки в `docs/` — источник правды по домену**, читай до правок в подсистеме: [`tv-scheduler-architecture.md`](docs/tv-scheduler-architecture.md) (группы, сетка, слоты, стыки, заставки, планировщик, автосборка, обмен конфигурацией — главный документ), [`media-storage-and-streaming.md`](docs/media-storage-and-streaming.md) и [`server-storage-setup.md`](docs/server-storage-setup.md) (медиа и хранилище). ## Стек - **Backend**: C# / .NET 10, ASP.NET Core Minimal API, Clean Architecture, CQRS через [`LiteCqrs.Net`](https://github.com/mrleo1nid/LiteCqrs.Net) (лёгкая альтернатива MediatR с явным разделением Command/Query — см. README). EF Core 10 + Npgsql, Identity + JWT, FluentValidation, Serilog, маппинг DTO вручную, OpenAPI + Scalar UI, тесты на xUnit + NSubstitute. - **Frontend**: React 19 + Vite + TS, TanStack Query/Router (файловые роуты, `routeTree.gen.ts` генерируется сборкой), shadcn поверх Radix + Tailwind v4, Zustand (только auth+тема), react-hook-form + zod, i18n (ru/en), pnpm + oxlint + prettier. ## Архитектура — жёсткие правила Зависимости строго внутрь: **Api → Infrastructure → Application → Domain**. - **Domain** — без внешних зависимостей: `Auth`, `Library` (шоу, серии, коллекции, жанры), `Media`, `Images`, `Programming` (группы, сетка, слоты, стыки + **чистый планировщик** `Planning`), `Broadcast` (каналы, заставки, лента, live), `Settings`. Модель rich: приватные сеттеры, фабрики `Create`, поведенческие методы. - **Application** — CQRS-хендлеры, DTO, валидаторы и **порты** (`Common/Interfaces/I*.cs`: `IAppDbContext`, `ICurrentUser`, `IMediaProcessor`, `IMediaStorage`, `IBumperRenderer`, `IMetadataProvider`, `IImageStore`, `IStorageInspector`, …). Зависит только от Domain: никаких `Npgsql`/`AspNetCore.Identity`, лишь их абстракции. - **Infrastructure** — реализации портов: EF Core (`AppDbContext : IdentityDbContext`, конфигурации в `Persistence/Configurations`), ffmpeg, хранилище, метаданные, идемпотентный `DbInitializer` (роли + админ из env `AdminSeed__*`). - **Api** — Minimal API (`Endpoints/*Endpoints.cs`), middleware, DI composition root (`Program.cs`). Обязательно: - Команды меняют состояние в транзакции (`UnitOfWorkBehavior`); запросы только читают. Диспетчер — `ISender`/`ICommandHandler`/`IQueryHandler`, регистрация в `Application/DependencyInjection.cs`. - Управляемые ошибки — через `Result` (`Common/Models/Result.cs`), не исключениями. Валидация — FluentValidation через `ValidationBehavior`; хендлер не перепроверяет формат ввода. - Всё I/O асинхронно, `CancellationToken` пробрасывается до EF/HTTP; никаких `.Result`/`.Wait()`. Nullable включён, `TreatWarningsAsErrors=true` — предупреждения анализаторов не игнорировать. - Планировщик (`Domain/Programming/Planning`) — **чистая функция**: вход `PlanningInput`, никаких запросов и часов внутри. Данные под него собирает `Application/Programming/Planning`. ## Инварианты, которые легко нарушить - **Роли** динамические, у пользователя ровно одна; системные `admin`/`user` (`IsSystem=true`) не переименовать и не удалить, последнего админа не разжаловать, себя не заблокировать и не удалить. - **Регистрация открытая** (создаётся `user`, токены сразу); понадобится модерация — заводи отдельный флаг, не переиспользуй `IsBlocked`. Вход по `UserName`, email и SMTP не используются; JWT access + refresh в httpOnly `tw_refresh_token` с обнаружением повторного использования отозванного. - **Правка сетки эфир не двигает**: команда поднимает ревизию шаблона, ленту пересобирает применение. - **Вещательные сутки** начинаются с `Channel.DayStartTime` (06:00), сетка задаётся во времени канала (`UtcOffsetMinutes`), в базе всё в UTC. ## Единый контейнер - Один образ: Api раздаёт REST (`/api`) и статику SPA из `wwwroot` (fallback на `index.html`), один origin, база API — относительный `/api`. Multi-stage Dockerfile: node → dotnet sdk → aspnet. Отдельный nginx для статики не вводить — ломает требование единого контейнера. - Работает под непривилегированным `USER $APP_UID` (uid/gid 1654) **статически**, без gosu/entrypoint: права на bind-mount выставляет оператор (`docs/server-storage-setup.md` § 5). Data Protection не заводим — шифруемых секретов на диске нет. **TLS внешний**: `app` отдаёт HTTP и доверяет `X-Forwarded-*`. - docker-compose: только `app`. PostgreSQL внешний (`ConnectionStrings__Default` из `.env`), база и пользователь создаются заранее вручную; миграции применяются на старте (`ApplyMigrationsAsync`), прав `CREATEDB` не нужно. ## Соглашения по коду - `Command`/`Query` + `Handler`/`Validator` (где есть что проверить), папки по фичам (`Auth/Login/`, `Programming/Templates/Generate/`, …). Один публичный тип на файл = имя файла, кроме `Body` — тела Api-эндпоинтов, не совпадающие с командой 1:1 (`sealed record` внизу файла эндпоинта). DTO — суффикс `Dto`. - Ошибки — каталоги `XErrors` (`Error.Validation/NotFound/Conflict/…`), наружу единым `application/problem+json` (`Api/Common/ResultExtensions.cs`). Секреты не логировать. - **Изображения — только через реестр `Image`** (`Domain/Images`, галерея `features/admin/images`): картинка хранится как `ImageId` и выбирается контролом `ImageGallery`/`GalleryBrowser`, своих файловых полей не заводить. Файлы — `images/{id}{ext}`, отдаются по `/api/images/{id}`. - Фронт: фича = папка в `src/features/admin/<фича>`; тексты только через i18n, ключи в `ru.ts` и `en.ts`. - Комментарии объясняют **почему**, а не что; по-русски, как в существующем коде. ## Команды Backend (из `backend/`): ```bash dotnet build dotnet csharpier format . # только через dotnet (версия из .config/); в CI — csharpier check dotnet test tests/TeleWave.Domain.Tests # по одному проекту: MSBuild не берёт несколько dotnet test tests/TeleWave.Application.Tests dotnet test tests/TeleWave.Integration.Tests # Testcontainers; без Docker пропускаются dotnet run --project src/TeleWave.Api dotnet ef migrations add --project src/TeleWave.Infrastructure --startup-project src/TeleWave.Api ``` Frontend (из `frontend/`): ```bash pnpm install && pnpm dev pnpm lint && pnpm typecheck && pnpm build pnpm format && pnpm format:check # CI падает на неотформатированном — прогоняй после правок ``` Инфраструктура: `docker compose up -d --build` (только api; postgres внешний). Окружение — Windows, оболочка **PowerShell**; для POSIX-скриптов есть Bash-инструмент. ## Рабочие принципы - Базовый срез реализован — правь по месту. Новые крупные направления с непринятыми решениями (многоэкземплярность, транскод-профили, подсистемы) — после сверки; неоднозначность — вопрос. - Меняешь поведение подсистемы — обнови её спеку в `docs/` тем же заходом. - Соблюдай границы слоёв — главный инвариант проекта. - Правь файлы через Edit/Write: пользователь видит дифф, а harness не даст перезаписать файл вслепую. Скриптом на python — только при крайней необходимости (массовая механическая замена), и сказать явно. - Не коммить и не пуши без явной просьбы; отвечай пользователю на русском.