Update scheduling parameters and refactor channel endpoints: extend HorizonDays to 7 and RetentionDays to 90 in appsettings.json. Consolidate channel-related endpoint logic by removing obsolete files and enhancing the ShowEndpoints with audience and genre management capabilities. Improve error handling and streamline command handlers for channel operations.
build / backend (push) Successful in 7m40s
build / frontend (push) Failing after 39s
tests / backend-tests (push) Successful in 6m9s

This commit is contained in:
Leonid Pershin
2026-07-26 13:32:13 +03:00
parent c4ef954dea
commit 66040a8841
272 changed files with 27944 additions and 8699 deletions
@@ -318,12 +318,6 @@ namespace TeleWave.Infrastructure.Migrations
b.Property<Guid>("Id")
.HasColumnType("uuid");
b.Property<int>("AdInsertion")
.HasColumnType("integer");
b.Property<int>("AdsPerBreak")
.HasColumnType("integer");
b.Property<double>("BumperEpisodeChangeChance")
.HasColumnType("double precision");
@@ -345,6 +339,9 @@ namespace TeleWave.Infrastructure.Migrations
b.Property<DateTimeOffset>("CreatedAt")
.HasColumnType("timestamp with time zone");
b.Property<TimeOnly>("DayStartTime")
.HasColumnType("time without time zone");
b.Property<DateTimeOffset>("EpochUtc")
.HasColumnType("timestamp with time zone");
@@ -359,10 +356,10 @@ namespace TeleWave.Infrastructure.Migrations
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.Property<int>("NextAdIndex")
b.Property<int>("NextBumperIndex")
.HasColumnType("integer");
b.Property<int>("NextBumperIndex")
b.Property<int?>("Number")
.HasColumnType("integer");
b.Property<string>("Slug")
@@ -370,151 +367,24 @@ namespace TeleWave.Infrastructure.Migrations
.HasMaxLength(128)
.HasColumnType("character varying(128)");
b.Property<Guid?>("TemplateId")
.HasColumnType("uuid");
b.Property<int>("UtcOffsetMinutes")
.HasColumnType("integer");
b.HasKey("Id");
b.HasIndex("Number")
.IsUnique()
.HasFilter("\"Number\" IS NOT NULL");
b.HasIndex("Slug")
.IsUnique();
b.ToTable("Channels");
});
modelBuilder.Entity("TeleWave.Domain.Broadcast.ChannelAd", b =>
{
b.Property<Guid>("Id")
.HasColumnType("uuid");
b.Property<Guid>("ChannelId")
.HasColumnType("uuid");
b.Property<Guid>("MediaAssetId")
.HasColumnType("uuid");
b.Property<int>("Position")
.HasColumnType("integer");
b.HasKey("Id");
b.HasIndex("ChannelId", "Position");
b.ToTable("ChannelAd");
});
modelBuilder.Entity("TeleWave.Domain.Broadcast.ChannelShow", b =>
{
b.Property<Guid>("Id")
.HasColumnType("uuid");
b.Property<int>("BlockMode")
.HasColumnType("integer");
b.Property<int>("BlockValue")
.HasColumnType("integer");
b.Property<Guid>("ChannelId")
.HasColumnType("uuid");
b.Property<bool>("IsEnabled")
.HasColumnType("boolean");
b.Property<int>("NextEpisodeIndex")
.HasColumnType("integer");
b.Property<int>("PreferredWeightMultiplier")
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasDefaultValue(3);
b.Property<Guid>("ShowId")
.HasColumnType("uuid");
b.Property<int>("Weight")
.HasColumnType("integer");
b.HasKey("Id");
b.HasIndex("ChannelId", "ShowId");
b.ToTable("ChannelShow");
});
modelBuilder.Entity("TeleWave.Domain.Broadcast.ChannelShowHour", b =>
{
b.Property<Guid>("Id")
.HasColumnType("uuid");
b.Property<Guid>("ChannelShowId")
.HasColumnType("uuid");
b.Property<int>("EndHour")
.HasColumnType("integer");
b.Property<int>("StartHour")
.HasColumnType("integer");
b.HasKey("Id");
b.HasIndex("ChannelShowId");
b.ToTable("ChannelShowHour");
});
modelBuilder.Entity("TeleWave.Domain.Broadcast.OverrideShow", b =>
{
b.Property<Guid>("Id")
.HasColumnType("uuid");
b.Property<Guid>("ProgrammingOverrideId")
.HasColumnType("uuid");
b.Property<Guid>("ShowId")
.HasColumnType("uuid");
b.Property<int>("Weight")
.HasColumnType("integer");
b.HasKey("Id");
b.HasIndex("ProgrammingOverrideId");
b.ToTable("OverrideShow");
});
modelBuilder.Entity("TeleWave.Domain.Broadcast.ProgrammingOverride", b =>
{
b.Property<Guid>("Id")
.HasColumnType("uuid");
b.Property<Guid>("ChannelId")
.HasColumnType("uuid");
b.Property<int?>("DayOfWeek")
.HasColumnType("integer");
b.Property<int?>("EndMinute")
.HasColumnType("integer");
b.Property<DateTimeOffset?>("EndsAtUtc")
.HasColumnType("timestamp with time zone");
b.Property<int>("Mode")
.HasColumnType("integer");
b.Property<int>("Recurrence")
.HasColumnType("integer");
b.Property<int?>("StartMinute")
.HasColumnType("integer");
b.Property<DateTimeOffset?>("StartsAtUtc")
.HasColumnType("timestamp with time zone");
b.HasKey("Id");
b.HasIndex("ChannelId", "StartsAtUtc", "EndsAtUtc");
b.ToTable("ProgrammingOverride");
});
modelBuilder.Entity("TeleWave.Domain.Broadcast.ScheduleEntry", b =>
{
b.Property<Guid>("Id")
@@ -541,17 +411,23 @@ namespace TeleWave.Infrastructure.Migrations
b.Property<Guid?>("ShowId")
.HasColumnType("uuid");
b.Property<Guid?>("SlotId")
.HasColumnType("uuid");
b.Property<DateTimeOffset>("StartsAtUtc")
.HasColumnType("timestamp with time zone");
b.Property<string>("TraceJson")
.HasColumnType("jsonb");
b.HasKey("Id");
b.HasIndex("ChannelId", "EndsAtUtc");
b.HasIndex("ChannelId", "ShowId");
b.HasIndex("ChannelId", "StartsAtUtc");
b.HasIndex("ChannelId", "ShowId", "StartsAtUtc");
b.ToTable("ScheduleEntries");
});
@@ -582,6 +458,112 @@ namespace TeleWave.Infrastructure.Migrations
b.ToTable("Images");
});
modelBuilder.Entity("TeleWave.Domain.Library.Collection", b =>
{
b.Property<Guid>("Id")
.HasColumnType("uuid");
b.Property<DateTimeOffset>("CreatedAt")
.HasColumnType("timestamp with time zone");
b.Property<string>("Description")
.HasMaxLength(2048)
.HasColumnType("character varying(2048)");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.Property<Guid?>("PosterImageId")
.HasColumnType("uuid");
b.HasKey("Id");
b.ToTable("Collections");
});
modelBuilder.Entity("TeleWave.Domain.Library.CollectionItem", b =>
{
b.Property<Guid>("Id")
.HasColumnType("uuid");
b.Property<Guid>("CollectionId")
.HasColumnType("uuid");
b.Property<int>("Position")
.HasColumnType("integer");
b.Property<Guid>("ShowId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("ShowId");
b.HasIndex("CollectionId", "Position");
b.HasIndex("CollectionId", "ShowId")
.IsUnique();
b.ToTable("CollectionItems");
});
modelBuilder.Entity("TeleWave.Domain.Library.Genre", b =>
{
b.Property<Guid>("Id")
.HasColumnType("uuid");
b.Property<DateTimeOffset>("CreatedAt")
.HasColumnType("timestamp with time zone");
b.Property<bool>("IsSystem")
.HasColumnType("boolean");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("character varying(128)");
b.Property<string>("Slug")
.IsRequired()
.HasMaxLength(64)
.HasColumnType("character varying(64)");
b.Property<int>("SortOrder")
.HasColumnType("integer");
b.HasKey("Id");
b.HasIndex("Slug")
.IsUnique();
b.ToTable("Genres");
});
modelBuilder.Entity("TeleWave.Domain.Library.GenreAlias", b =>
{
b.Property<Guid>("Id")
.HasColumnType("uuid");
b.Property<Guid>("GenreId")
.HasColumnType("uuid");
b.Property<string>("Value")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("character varying(128)");
b.HasKey("Id");
b.HasIndex("GenreId");
b.HasIndex("Value")
.IsUnique();
b.ToTable("GenreAliases");
});
modelBuilder.Entity("TeleWave.Domain.Library.Show", b =>
{
b.Property<Guid>("Id")
@@ -674,6 +656,24 @@ namespace TeleWave.Infrastructure.Migrations
b.ToTable("ShowEpisode");
});
modelBuilder.Entity("TeleWave.Domain.Library.ShowGenre", b =>
{
b.Property<Guid>("ShowId")
.HasColumnType("uuid");
b.Property<Guid>("GenreId")
.HasColumnType("uuid");
b.Property<bool>("IsPrimary")
.HasColumnType("boolean");
b.HasKey("ShowId", "GenreId");
b.HasIndex("GenreId");
b.ToTable("ShowGenres");
});
modelBuilder.Entity("TeleWave.Domain.Media.MediaAsset", b =>
{
b.Property<Guid>("Id")
@@ -747,6 +747,295 @@ namespace TeleWave.Infrastructure.Migrations
b.ToTable("MediaAssets");
});
modelBuilder.Entity("TeleWave.Domain.Programming.GridLayer", b =>
{
b.Property<Guid>("Id")
.HasColumnType("uuid");
b.Property<string>("ApplicabilityJson")
.HasColumnType("jsonb");
b.Property<bool>("IsBackground")
.HasColumnType("boolean");
b.Property<bool>("IsEnabled")
.HasColumnType("boolean");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("character varying(128)");
b.Property<int>("Priority")
.HasColumnType("integer");
b.Property<Guid>("TemplateId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("TemplateId", "Priority");
b.ToTable("GridLayers");
});
modelBuilder.Entity("TeleWave.Domain.Programming.Group", b =>
{
b.Property<Guid>("Id")
.HasColumnType("uuid");
b.Property<DateTimeOffset>("CreatedAt")
.HasColumnType("timestamp with time zone");
b.Property<string>("Description")
.HasMaxLength(2048)
.HasColumnType("character varying(2048)");
b.Property<string>("FilterJson")
.HasColumnType("jsonb");
b.Property<int>("ItemCount")
.HasColumnType("integer");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.Property<DateTimeOffset?>("StatsComputedAt")
.HasColumnType("timestamp with time zone");
b.Property<TimeSpan>("TotalDuration")
.HasColumnType("interval");
b.Property<int>("UnitCount")
.HasColumnType("integer");
b.HasKey("Id");
b.ToTable("Groups");
});
modelBuilder.Entity("TeleWave.Domain.Programming.GroupItem", b =>
{
b.Property<Guid>("Id")
.HasColumnType("uuid");
b.Property<Guid>("ElementId")
.HasColumnType("uuid");
b.Property<int>("ElementKind")
.HasColumnType("integer");
b.Property<Guid>("GroupId")
.HasColumnType("uuid");
b.Property<int>("Position")
.HasColumnType("integer");
b.Property<int>("Weight")
.HasColumnType("integer");
b.HasKey("Id");
b.HasIndex("ElementKind", "ElementId");
b.HasIndex("GroupId", "Position");
b.HasIndex("GroupId", "ElementKind", "ElementId")
.IsUnique();
b.ToTable("GroupItems");
});
modelBuilder.Entity("TeleWave.Domain.Programming.JunctionElement", b =>
{
b.Property<Guid>("Id")
.HasColumnType("uuid");
b.Property<int>("AmountMode")
.HasColumnType("integer");
b.Property<int>("AmountValue")
.HasColumnType("integer");
b.Property<Guid?>("BumperTemplateId")
.HasColumnType("uuid");
b.Property<string>("ConditionsJson")
.HasColumnType("jsonb");
b.Property<Guid?>("GroupId")
.HasColumnType("uuid");
b.Property<bool>("IsRequired")
.HasColumnType("boolean");
b.Property<Guid>("JunctionTemplateId")
.HasColumnType("uuid");
b.Property<int>("Kind")
.HasColumnType("integer");
b.Property<int>("Position")
.HasColumnType("integer");
b.HasKey("Id");
b.HasIndex("GroupId");
b.HasIndex("JunctionTemplateId", "Position");
b.ToTable("JunctionElements");
});
modelBuilder.Entity("TeleWave.Domain.Programming.JunctionTemplate", b =>
{
b.Property<Guid>("Id")
.HasColumnType("uuid");
b.Property<Guid>("ChannelId")
.HasColumnType("uuid");
b.Property<DateTimeOffset>("CreatedAt")
.HasColumnType("timestamp with time zone");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("character varying(128)");
b.HasKey("Id");
b.HasIndex("ChannelId");
b.ToTable("JunctionTemplates");
});
modelBuilder.Entity("TeleWave.Domain.Programming.ScheduleTemplate", b =>
{
b.Property<Guid>("Id")
.HasColumnType("uuid");
b.Property<int>("AppliedRevision")
.HasColumnType("integer");
b.Property<Guid>("ChannelId")
.HasColumnType("uuid");
b.Property<DateTimeOffset>("CreatedAt")
.HasColumnType("timestamp with time zone");
b.Property<Guid?>("DefaultJunctionId")
.HasColumnType("uuid");
b.Property<Guid?>("FallbackGroupId")
.HasColumnType("uuid");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.Property<int>("Revision")
.HasColumnType("integer");
b.HasKey("Id");
b.HasIndex("ChannelId");
b.ToTable("ScheduleTemplates");
});
modelBuilder.Entity("TeleWave.Domain.Programming.Slot", b =>
{
b.Property<Guid>("Id")
.HasColumnType("uuid");
b.Property<int>("BlockMode")
.HasColumnType("integer");
b.Property<int>("BlockValue")
.HasColumnType("integer");
b.Property<int>("Daypart")
.HasColumnType("integer");
b.Property<Guid?>("GroupId")
.HasColumnType("uuid");
b.Property<bool>("IsAnchor")
.HasColumnType("boolean");
b.Property<Guid?>("JunctionAfterId")
.HasColumnType("uuid");
b.Property<Guid?>("JunctionBetweenId")
.HasColumnType("uuid");
b.Property<Guid>("LayerId")
.HasColumnType("uuid");
b.Property<int>("MaxDriftMinutes")
.HasColumnType("integer");
b.Property<int>("OverflowPolicy")
.HasColumnType("integer");
b.Property<string>("RepeatSourceJson")
.HasColumnType("jsonb");
b.Property<int>("SlotKind")
.HasColumnType("integer");
b.Property<int?>("SnapToMinutes")
.HasColumnType("integer");
b.Property<string>("StrategyJson")
.HasColumnType("jsonb");
b.Property<int>("TargetDurationMinutes")
.HasColumnType("integer");
b.Property<TimeOnly>("TargetStart")
.HasColumnType("time without time zone");
b.Property<string>("Title")
.IsRequired()
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.Property<int?>("Weekday")
.HasColumnType("integer");
b.HasKey("Id");
b.HasIndex("GroupId");
b.HasIndex("LayerId", "TargetStart");
b.ToTable("Slots");
});
modelBuilder.Entity("TeleWave.Domain.Programming.SlotState", b =>
{
b.Property<Guid>("SlotId")
.HasColumnType("uuid");
b.Property<Guid?>("CurrentElementId")
.HasColumnType("uuid");
b.Property<int?>("CurrentElementKind")
.HasColumnType("integer");
b.Property<int>("NextUnitIndex")
.HasColumnType("integer");
b.HasKey("SlotId");
b.ToTable("SlotStates");
});
modelBuilder.Entity("TeleWave.Domain.Settings.AppSetting", b =>
{
b.Property<string>("Key")
@@ -933,47 +1222,26 @@ namespace TeleWave.Infrastructure.Migrations
.IsRequired();
});
modelBuilder.Entity("TeleWave.Domain.Broadcast.ChannelAd", b =>
modelBuilder.Entity("TeleWave.Domain.Library.CollectionItem", b =>
{
b.HasOne("TeleWave.Domain.Broadcast.Channel", null)
.WithMany("Ads")
.HasForeignKey("ChannelId")
b.HasOne("TeleWave.Domain.Library.Collection", null)
.WithMany("Items")
.HasForeignKey("CollectionId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("TeleWave.Domain.Library.Show", null)
.WithMany()
.HasForeignKey("ShowId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("TeleWave.Domain.Broadcast.ChannelShow", b =>
modelBuilder.Entity("TeleWave.Domain.Library.GenreAlias", b =>
{
b.HasOne("TeleWave.Domain.Broadcast.Channel", null)
.WithMany("Shows")
.HasForeignKey("ChannelId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("TeleWave.Domain.Broadcast.ChannelShowHour", b =>
{
b.HasOne("TeleWave.Domain.Broadcast.ChannelShow", null)
.WithMany("PreferredHours")
.HasForeignKey("ChannelShowId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("TeleWave.Domain.Broadcast.OverrideShow", b =>
{
b.HasOne("TeleWave.Domain.Broadcast.ProgrammingOverride", null)
.WithMany("Shows")
.HasForeignKey("ProgrammingOverrideId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("TeleWave.Domain.Broadcast.ProgrammingOverride", b =>
{
b.HasOne("TeleWave.Domain.Broadcast.Channel", null)
.WithMany("Overrides")
.HasForeignKey("ChannelId")
b.HasOne("TeleWave.Domain.Library.Genre", null)
.WithMany("Aliases")
.HasForeignKey("GenreId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
@@ -987,6 +1255,76 @@ namespace TeleWave.Infrastructure.Migrations
.IsRequired();
});
modelBuilder.Entity("TeleWave.Domain.Library.ShowGenre", b =>
{
b.HasOne("TeleWave.Domain.Library.Genre", null)
.WithMany()
.HasForeignKey("GenreId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.HasOne("TeleWave.Domain.Library.Show", null)
.WithMany("Genres")
.HasForeignKey("ShowId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("TeleWave.Domain.Programming.GridLayer", b =>
{
b.HasOne("TeleWave.Domain.Programming.ScheduleTemplate", null)
.WithMany("Layers")
.HasForeignKey("TemplateId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("TeleWave.Domain.Programming.GroupItem", b =>
{
b.HasOne("TeleWave.Domain.Programming.Group", null)
.WithMany("Items")
.HasForeignKey("GroupId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("TeleWave.Domain.Programming.JunctionElement", b =>
{
b.HasOne("TeleWave.Domain.Programming.Group", null)
.WithMany()
.HasForeignKey("GroupId")
.OnDelete(DeleteBehavior.Restrict);
b.HasOne("TeleWave.Domain.Programming.JunctionTemplate", null)
.WithMany("Elements")
.HasForeignKey("JunctionTemplateId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("TeleWave.Domain.Programming.Slot", b =>
{
b.HasOne("TeleWave.Domain.Programming.Group", null)
.WithMany()
.HasForeignKey("GroupId")
.OnDelete(DeleteBehavior.Restrict);
b.HasOne("TeleWave.Domain.Programming.GridLayer", null)
.WithMany("Slots")
.HasForeignKey("LayerId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("TeleWave.Domain.Programming.SlotState", b =>
{
b.HasOne("TeleWave.Domain.Programming.Slot", null)
.WithOne()
.HasForeignKey("TeleWave.Domain.Programming.SlotState", "SlotId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("TeleWave.Domain.Broadcast.BumperTemplate", b =>
{
b.Navigation("Variants");
@@ -994,28 +1332,44 @@ namespace TeleWave.Infrastructure.Migrations
modelBuilder.Entity("TeleWave.Domain.Broadcast.Channel", b =>
{
b.Navigation("Ads");
b.Navigation("BumperTemplates");
b.Navigation("Overrides");
b.Navigation("Shows");
});
modelBuilder.Entity("TeleWave.Domain.Broadcast.ChannelShow", b =>
modelBuilder.Entity("TeleWave.Domain.Library.Collection", b =>
{
b.Navigation("PreferredHours");
b.Navigation("Items");
});
modelBuilder.Entity("TeleWave.Domain.Broadcast.ProgrammingOverride", b =>
modelBuilder.Entity("TeleWave.Domain.Library.Genre", b =>
{
b.Navigation("Shows");
b.Navigation("Aliases");
});
modelBuilder.Entity("TeleWave.Domain.Library.Show", b =>
{
b.Navigation("Episodes");
b.Navigation("Genres");
});
modelBuilder.Entity("TeleWave.Domain.Programming.GridLayer", b =>
{
b.Navigation("Slots");
});
modelBuilder.Entity("TeleWave.Domain.Programming.Group", b =>
{
b.Navigation("Items");
});
modelBuilder.Entity("TeleWave.Domain.Programming.JunctionTemplate", b =>
{
b.Navigation("Elements");
});
modelBuilder.Entity("TeleWave.Domain.Programming.ScheduleTemplate", b =>
{
b.Navigation("Layers");
});
#pragma warning restore 612, 618
}