using LiteCqrs; using TeleWave.Api.Common; using TeleWave.Application.Broadcast; using TeleWave.Application.Broadcast.CreateChannel; using TeleWave.Application.Broadcast.GetChannel; using TeleWave.Application.Broadcast.GetSchedule; using TeleWave.Application.Broadcast.ListChannels; using TeleWave.Application.Broadcast.RegenerateSchedule; using TeleWave.Application.Broadcast.UpdateChannelSettings; using TeleWave.Domain.Broadcast; using TeleWave.Infrastructure.Identity; namespace TeleWave.Api.Endpoints; /// /// Админ-эндпоинты канала. Реализация разнесена по partial-файлам под-ресурсов: /// ChannelEndpoints.Shows.cs (шоу+реклама), ChannelEndpoints.Bumpers.cs /// (блоки/подблоки/файлы/preview), ChannelEndpoints.Overrides.cs (override'ы). Здесь — /// регистрация всех маршрутов и хендлеры уровня канала (создание/список/настройки/расписание). /// public static partial class ChannelEndpoints { public static IEndpointRouteBuilder MapChannelEndpoints(this IEndpointRouteBuilder app) { var admin = app.MapGroup("/api/admin/channels") .WithTags("Admin.Channels") .RequireAuthorization(policy => policy.RequireRole(RoleNames.Admin)); admin.MapPost("", CreateChannel).Produces(StatusCodes.Status201Created); admin.MapGet("", ListChannels).Produces>(); admin.MapGet("/{id:guid}", GetChannel).Produces(); admin .MapPut("/{id:guid}/settings", UpdateSettings) .Produces(StatusCodes.Status204NoContent); admin .MapPost("/{id:guid}/shows", AddShow) .Produces(StatusCodes.Status201Created); admin .MapPut("/{id:guid}/shows/{channelShowId:guid}", UpdateShow) .Produces(StatusCodes.Status204NoContent); admin .MapDelete("/{id:guid}/shows/{channelShowId:guid}", RemoveShow) .Produces(StatusCodes.Status204NoContent); admin .MapPost("/{id:guid}/ads", AddAd) .Produces(StatusCodes.Status201Created); admin .MapDelete("/{id:guid}/ads/{channelAdId:guid}", RemoveAd) .Produces(StatusCodes.Status204NoContent); admin .MapPost("/{id:guid}/bumper/templates", AddBumperTemplate) .Produces(StatusCodes.Status201Created); admin .MapPut("/{id:guid}/bumper/templates/{templateId:guid}", UpdateBumperTemplate) .Produces(StatusCodes.Status204NoContent); admin .MapDelete("/{id:guid}/bumper/templates/{templateId:guid}", RemoveBumperTemplate) .Produces(StatusCodes.Status204NoContent); admin .MapPut("/{id:guid}/bumper/templates/{templateId:guid}/audio", UploadTemplateAudio) .Produces(StatusCodes.Status204NoContent); admin .MapDelete("/{id:guid}/bumper/templates/{templateId:guid}/audio", ClearTemplateAudio) .Produces(StatusCodes.Status204NoContent); admin .MapPut( "/{id:guid}/bumper/templates/{templateId:guid}/background", SetTemplateBackground ) .Produces(StatusCodes.Status204NoContent); admin .MapDelete( "/{id:guid}/bumper/templates/{templateId:guid}/background", ClearTemplateBackground ) .Produces(StatusCodes.Status204NoContent); admin .MapPost("/{id:guid}/bumper/templates/{templateId:guid}/preview", RenderPreview) .Produces(StatusCodes.Status204NoContent); admin.MapGet( "/{id:guid}/bumper/templates/{templateId:guid}/preview/{variantId:guid}/index.m3u8", PreviewPlaylist ); admin.MapGet( "/{id:guid}/bumper/templates/{templateId:guid}/preview/{variantId:guid}/{file}", PreviewSegment ); admin .MapPost("/{id:guid}/bumper/templates/{templateId:guid}/variants", AddBumperVariant) .Produces(StatusCodes.Status201Created); admin .MapPut( "/{id:guid}/bumper/templates/{templateId:guid}/variants/{variantId:guid}", UpdateBumperVariant ) .Produces(StatusCodes.Status204NoContent); admin .MapDelete( "/{id:guid}/bumper/templates/{templateId:guid}/variants/{variantId:guid}", RemoveBumperVariant ) .Produces(StatusCodes.Status204NoContent); admin .MapPost("/{id:guid}/overrides", CreateOverride) .Produces(StatusCodes.Status201Created); admin .MapDelete("/{id:guid}/overrides/{overrideId:guid}", DeleteOverride) .Produces(StatusCodes.Status204NoContent); admin.MapPost("/{id:guid}/regenerate", Regenerate).Produces(StatusCodes.Status204NoContent); admin .MapGet("/{id:guid}/schedule", GetSchedule) .Produces>(); return app; } private static async Task CreateChannel( CreateChannelCommand command, ISender sender, CancellationToken cancellationToken ) { var result = await sender.Send(command, cancellationToken); return result.IsSuccess ? Results.Created( $"/api/admin/channels/{result.Value}", new CreatedIdResponse(result.Value) ) : result.ToHttpResult(); } private static async Task ListChannels( ISender sender, CancellationToken cancellationToken ) { var result = await sender.Send(new ListChannelsQuery(), cancellationToken); return Results.Ok(result); } private static async Task GetChannel( Guid id, ISender sender, CancellationToken cancellationToken ) { var result = await sender.Send(new GetChannelQuery(id), cancellationToken); return result.ToHttpResult(); } private static async Task UpdateSettings( Guid id, UpdateChannelSettingsBody body, ISender sender, CancellationToken cancellationToken ) { var result = await sender.Send( new UpdateChannelSettingsCommand( id, body.Name, body.IsEnabled, body.AdInsertion, body.AdsPerBreak, body.BumpersEnabled, body.Bumper, body.FillerAssetId ), cancellationToken ); return result.ToHttpResult(); } private static async Task Regenerate( Guid id, ISender sender, CancellationToken cancellationToken ) { var result = await sender.Send(new RegenerateChannelScheduleCommand(id), cancellationToken); return result.ToHttpResult(); } private static async Task GetSchedule( Guid id, DateTimeOffset? from, DateTimeOffset? to, ISender sender, CancellationToken cancellationToken ) { var fromUtc = from ?? DateTimeOffset.UtcNow; var toUtc = to ?? fromUtc.AddDays(1); var result = await sender.Send( new GetChannelScheduleQuery(id, fromUtc, toUtc), cancellationToken ); return result.ToHttpResult(); } } public sealed record UpdateChannelSettingsBody( string Name, bool IsEnabled, AdInsertion AdInsertion, int AdsPerBreak, bool BumpersEnabled, BumperSettingsInput Bumper, Guid? FillerAssetId );