b1d5fbefcba02fdc6c19fef85cec1c4e82fc8dea
4
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
928784ba48 |
fix(135): from-lineup advanced overrides can express "clear to none"
PR Gates / CI image pin matches docker/ci (pull_request) Successful in 12s
Build ErsatzTV Image / Formatting (changed .cs conform to .editorconfig) (pull_request) Successful in 17s
PR Gates / Docs update reminder (pull_request) Successful in 19s
PR Gates / decisions lifecycle (pull_request) Successful in 22s
Build ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (pull_request) Successful in 13m44s
Build ErsatzTV Image / Functional E2E (curl contracts) (pull_request) Successful in 17m8s
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 20m34s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 20m41s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped
CreateChannelFromLineupHandler resolved every advanced override with advanced.X ?? template.X, so null always meant INHERIT and a channel could not drop a template-set watermark / filler / preferred language. Add an optional typed `clear` enum list to CreateChannelFromLineupAdvancedOptions: omitted/null still inherits (byte-stable for existing clients), a field named in `clear` is forced to none. Set+clear of the same field is a 422. The enum (CreateChannelFromLineupClearField) lives in ErsatzTV.Core so the OpenAPI string-enum scan renders it as a string enum, matching every sibling advanced-options enum. Handler resolves clearable fields once via ResolveClearable and validates set/clear conflicts via ValidateClear; reference validation skips existence checks for cleared (null) refs. SPA: the shared advancedOptions model re-adds a real "None" option to the five id selects (watermark + fillers) in both the Channel Builder and the Auto-Tune DetailPanel, routed through a CLEAR overrides sentinel that applyOverridesToRequest folds into advanced.clear (never leaking onto the wire as a field value). The backend enum also covers the preferred audio/subtitle language strings for machine clients; the SPA text inputs keep "empty = inherit" (tri-state deferred). Docs: api-conventions.md §2, spa-conventions.md §11, decisions.md record api.from-lineup-clear-to-none; v1.json + generated TS regenerated. fixes #135 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
cf834d8b60 |
security(#283): sniff artwork content type from bytes, remove serve-side ?contentType= reflection
Build ErsatzTV Image / Docs update reminder (pull_request) Successful in 5s
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 8m33s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 10m40s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped
S4 stored-XSS + S9 upload-size DoS from the #197 cold API review. The artwork path trusted client-supplied content types at both ends: upload validated only the declared multipart Content-Type (never decoded the bytes), and serving reflected a client `?contentType=` straight into the response Content-Type on unauthenticated GET sinks (/iptv/logos, /artwork/watermarks). Chain: upload <script> bytes as image/png -> GET ...?contentType=text/html serves them as HTML in-origin. nosniff (#279) does not help because the server explicitly declares text/html. - Upload: derive the content type from the bytes via SkiaSharp SKCodec (header-only, no decode -> no decompression-bomb path); reject non-images 422. New ErsatzTV.Core/Images/ImageContentTypes as the single allow-list source. Dropped the untrusted declared Content-Type from the UploadArtwork command. - Serve: removed the ?contentType= reflection structurally -- dropped ContentType from GetCachedImagePath and the [FromQuery] binding on GetImage/GetWatermark; the handler always sniffs the file, defaulting application/octet-stream. ArtworkContentTypeModel.UrlWithContentType is now the bare path; SPA previews no longer append the query. - Defense-in-depth: channel-logo / watermark {path, contentType} DTOs run through ArtworkContentTypeModel.Sanitized(), blanking non-allow-listed types on write. - S9: Kestrel MaxRequestBodySize from ETV_MAXIMUM_UPLOAD_MB rejects oversized bodies during read (controller file.Length check kept as friendly-error backstop). Both serve sinks are IgnoreApi, so no OpenAPI change. Tests: byte-sniff accept/ reject, Sanitized() allow-list, Location no longer carries ?contentType=. Docs: api-conventions §4a + decisions.md 2026-07-12. Refs #283 #197 #66 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
cf36c30997 |
fix(api): rework from-lineup to generated playlist design + review fixes (#63)
Adversarial review found the previous multi-flood design non-viable:
PlayoutModeSchedulerFlood never yields to a following Dynamic-start
schedule item, so only the first item ever played, and grouping
media items into a Collection silently dropped the requested order.
Redesign:
- Single-item lineup: one ProgramScheduleItemFlood referencing the
target directly (media item / collection / smart / multi / rerun /
playlist); no generated collection or playlist. Response PlaylistId
is null.
- Multi-item lineup (>= 2): one generated IsSystem Playlist in a
get-or-created IsSystem PlaylistGroup ("Channel Lineups"), one
PlaylistItem per entry in lineup order with PlayAll=true, referenced
by a single Flood schedule item. Rerun collections and playlists are
rejected (422) in multi lineups (PlaylistItem/CollectionKey lack the
fields to enumerate them).
Review fixes:
- Normalize + strict-validate MediaType<->CollectionType pairs once up
front (422 on mismatch / wrong id / not exactly one id).
- Reject MultiCollection with non-Shuffle order (mirrors
PlayoutModeMustBeValid), Mirror playout source, all via 422.
- OnDemand parity: queue TimeShiftOnDemandPlayout post-commit.
- De-collide generated ProgramSchedule and Playlist names against their
unique indexes instead of leaking a UNIQUE-constraint DbUpdateException.
- Generic 422 on save failure + ILogger; AnyAsync existence checks;
Either/Validation unwrap via Match; XML doc on request DTO + endpoint.
- Response model: ChannelId, PlaylistId (nullable), ProgramScheduleId,
PlayoutId (CollectionId removed). Regenerated OpenAPI v1.json + v1.d.ts.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
||
|
|
6857a0d191 | feat(api): composite create-channel endpoint (#63) |