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.
161 lines
13 KiB
Markdown
161 lines
13 KiB
Markdown
# 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<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. Контейнер работает под непривилегированным пользователем образа (`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/`):
|
||
```bash
|
||
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/`):
|
||
```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, обработка медиа) уже реализован —
|
||
правь его по месту. Но новые крупные направления с непринятыми архитектурными решениями (напр.
|
||
многоэкземплярное развёртывание, транскод-профили, новые доменные подсистемы) начинай только после
|
||
сверки с пользователем. При неоднозначности — вопрос пользователю, не предположение.
|
||
- Соблюдай границы слоёв — главный инвариант проекта
|
||
- Не коммить и не пуши без явной просьбы.
|
||
- Отвечай пользователю на русском.
|