89 lines
7.5 KiB
Markdown
89 lines
7.5 KiB
Markdown
# Ревью TeleWave — план исправлений
|
||
|
||
Статус чекбоксов: `[ ]` не сделано · `[x]` исправлено · `[~]` в работе.
|
||
|
||
Дата ревью: 2026-07-25. Область: backend (C#/.NET 10), frontend (React 19), инфра.
|
||
Сборка чистая, 144 теста зелёные, typecheck чистый.
|
||
|
||
---
|
||
|
||
## 🔴 Критичное
|
||
|
||
- [x] **C1. Утечка слота семафора → медиа-обработка навсегда встаёт**
|
||
`MediaProcessingBackgroundService.cs:52` — слот берётся до `ClaimNextAsync`; бросок из claim
|
||
уводит во внешний `catch` без `Release()`. После N ошибок диспетчер зависает навсегда.
|
||
_Fix: try/finally вокруг claim либо Release() в ветке ошибки._
|
||
|
||
- [x] **C2. Слабый JWT-ключ по умолчанию без fail-fast**
|
||
`appsettings.json:8` + `DependencyInjection.cs:65` — placeholder-ключ, нет проверки на
|
||
переопределение и длину; тот же ключ у stream-токенов. Забыли → обход авторизации.
|
||
_Fix: на старте кидать, если ключ = placeholder или < 32 байт._
|
||
|
||
- [x] **C3. Инъекция/поломка ffmpeg-фильтра из пользовательских подписей**
|
||
`FfmpegBumperRenderer.cs:306` — `EscapeText` не экранирует `,` `;` `[` `]` `%` перевод строки.
|
||
Подпись с запятой рвёт filtergraph; `[`/`]` — инъекция звеньев. Валидатор проверяет только длину.
|
||
_Fix: экранировать полный набор метасимволов filtergraph._
|
||
|
||
---
|
||
|
||
## 🟠 Среднее
|
||
|
||
### Безопасность
|
||
- [ ] **M1. Cookie без `Secure` за TLS-прокси** — `AuthEndpoints.cs:207`, `StreamingEndpoints.cs:63`.
|
||
- [ ] **M2. Ключи TMDb/OMDb в логах** — `TmdbMetadataProvider.cs:27`, `OmdbMetadataProvider.cs:25` (`System.Net.Http` не приглушён в Serilog).
|
||
- [ ] **M3. Stream-токен неотзывной, TTL 6ч, игнорирует userId/блокировку** — `StreamTokenService.cs:15`, `StreamingEndpoints.cs:98,123`.
|
||
- [ ] **M4. Rate-limiter глобальный (не партиционирован), только на `/api/auth`** — `Program.cs:66`.
|
||
|
||
### Архитектура / транзакции
|
||
- [ ] **M5. `ExecuteDeleteAsync` ломает границу UnitOfWork + файлы удаляются до коммита** — `DeleteShowMediaCommandHandler.cs:36`, `ClearAllMediaCommandHandler.cs`, `DeleteAllShowsCommandHandler.cs`.
|
||
- [ ] **M6. Query с побочным эффектом на ФС** — `RenderBumperPreviewQueryHandler.cs:18` (рендер файлов под видом запроса).
|
||
- [ ] **M7. Файловый/HTTP I/O до коммита** — Delete/Register/RefreshEpisodes хендлеры (orphan-файлы при откате).
|
||
|
||
### Планировщик / медиа
|
||
- [ ] **M8. Нет таймаута на ffmpeg/ffprobe** — `ProcessRunner.cs:63` (зависший процесс держит слот).
|
||
- [ ] **M9. Гонка при конкурентной генерации расписания канала** — `ScheduleGenerator.cs:62` (тик + regenerate → дубли записей).
|
||
- [ ] **M10. Рендер заставок синхронно внутри тика планировщика** — `ScheduleBumperResolver.cs:174`.
|
||
- [ ] **M11. Override через полночь не работает** — `SchedulePlannerModels.cs:40` (`EndMinute <= StartMinute` → пустое окно).
|
||
|
||
### Фронтенд
|
||
- [ ] **M12. Молчаливое проглатывание ошибки → вечный скелетон** — `AirPage.tsx:43`.
|
||
- [ ] **M13. Повторный 401 после refresh не разлогинивает** — `client.ts:80`.
|
||
- [ ] **M14. Клиентская пагинация поверх усечённого ответа** — `ShowDetail.tsx:42` (`pageSize:500`), `ChannelDetail.tsx:32` (`pageSize:100`).
|
||
- [ ] **M15. Дедуп загрузок только по имени файла** — `upload-store.ts:143`.
|
||
|
||
---
|
||
|
||
## 🟡 Низкое / прочее
|
||
|
||
- [ ] **L1. Синхронный I/O без CancellationToken в портах удаления** (`IMediaStorage`/`IImageStore`/`IBumperTemplateStorage`).
|
||
- [ ] **L2. `ValidationBehavior` вызывает `Validate` вместо `ValidateAsync`** — `ValidationBehavior.cs:24`.
|
||
- [ ] **L3. Инвариант «Single = 1 серия» проверяется в хендлере, а не в агрегате** — `Show.AddEpisode`.
|
||
- [ ] **L4. Двойной `SaveChanges` в командном пути ScheduleGenerator** — `ScheduleGenerator.cs:79,132`.
|
||
- [ ] **L5. HLS-плеер не восстанавливается после fatal network/media error** — `ChannelPlayer.tsx:106`.
|
||
- [ ] **L6. Бесконечная повторная регистрация «падающего» файла из inbox** — `InboxScannerBackgroundService.cs:92`.
|
||
- [ ] **L7. Возможное переполнение int в весах шоу** — `SchedulePlanner.cs:256,265`.
|
||
- [ ] **L8. OpenAPI/Scalar мапятся всегда, без гейта по окружению** — `Program.cs:108`.
|
||
- [ ] **L9. `AllowedHosts: "*"` и dev-креды БД в appsettings.json**.
|
||
- [ ] **L10. SSRF-поверхность в ImageDownloader** (без allowlist схемы/хоста) — `ImageDownloader.cs:16`.
|
||
- [ ] **L11. Документация (CLAUDE.md/README) отстала**: заявлено «каталог каналов и раздача видео не реализованы», хотя реализованы.
|
||
- [ ] **L12. Пробелы в тестах**: фоновые сервисы, ScheduleGenerator-оркестрация, media-конвейер, эндпоинты без тестов.
|
||
|
||
---
|
||
|
||
## Журнал исправлений
|
||
|
||
### 2026-07-25 — критические (C1–C3)
|
||
|
||
- **C1** — `MediaProcessingBackgroundService.cs`: `ClaimNextAsync` обёрнут в `try/catch`, слот
|
||
освобождается (`slots.Release()`) при любом сбое захвата перед `throw`. Теперь транзиентная
|
||
ошибка БД не «съедает» слот семафора — диспетчер не зависает.
|
||
- **C2** — `Infrastructure/DependencyInjection.cs`: после загрузки `JwtOptions` добавлен fail-fast:
|
||
старт падает с понятным сообщением, если `Jwt:SigningKey` короче 32 байт или содержит `change-me`
|
||
(значение-заглушка). Тот же ключ подписывает stream-токены, поэтому это закрывает и их.
|
||
- **C3** — `FfmpegBumperRenderer.cs`: подписи «Сейчас/Далее» (`NowLabel`/`NextLabel`) переведены с
|
||
инлайнового `text=` на `textfile=` (`nowlabel.txt`/`nextlabel.txt`, `expansion=none`), как уже
|
||
сделано для названий шоу. Метасимволы filtergraph в подписи больше не ломают/не инъектируют
|
||
цепочку. Неиспользуемый `EscapeText` удалён. Файлы чистятся в `finally`.
|
||
|
||
Проверка: `dotnet build` — 0 warnings/0 errors (при `TreatWarningsAsErrors`); тесты 95 + 49 зелёные.
|