using System.Text; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Identity; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.IdentityModel.Tokens; using TeleWave.Application.Broadcast.Bumpers; using TeleWave.Application.Broadcast.Scheduling; using TeleWave.Application.Common.Interfaces; using TeleWave.Application.Streaming; using TeleWave.Domain.Broadcast.Scheduling; using TeleWave.Infrastructure.Broadcast; using TeleWave.Infrastructure.Identity; using TeleWave.Infrastructure.Media; using TeleWave.Infrastructure.Persistence; using TeleWave.Infrastructure.Settings; using TeleWave.Infrastructure.Streaming; namespace TeleWave.Infrastructure; /// /// Регистрация сервисов инфраструктуры: EF Core (PostgreSQL), Identity/JWT-аутентификация. /// public static class DependencyInjection { public static IServiceCollection AddInfrastructure( this IServiceCollection services, IConfiguration configuration ) { services.AddDbContext(options => options.UseNpgsql( configuration["ConnectionStrings:Default"] ?? throw new InvalidOperationException( "Строка подключения 'ConnectionStrings:Default' не сконфигурирована." ) ) ); services.AddScoped(sp => sp.GetRequiredService()); services .AddIdentityCore(options => { options.User.RequireUniqueEmail = false; options.Password.RequiredLength = 8; options.Password.RequireDigit = true; options.Password.RequireUppercase = true; options.Password.RequireNonAlphanumeric = false; options.Lockout.MaxFailedAccessAttempts = 5; options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(5); options.Lockout.AllowedForNewUsers = true; }) .AddRoles() .AddEntityFrameworkStores() .AddSignInManager() .AddDefaultTokenProviders(); services.Configure(configuration.GetSection(JwtOptions.SectionName)); services.Configure( configuration.GetSection(AdminSeedOptions.SectionName) ); var jwtOptions = configuration.GetSection(JwtOptions.SectionName).Get() ?? throw new InvalidOperationException("Секция конфигурации 'Jwt' не задана."); services .AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => { options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidIssuer = jwtOptions.Issuer, ValidateAudience = true, ValidAudience = jwtOptions.Audience, ValidateIssuerSigningKey = true, IssuerSigningKey = new SymmetricSecurityKey( Encoding.UTF8.GetBytes(jwtOptions.SigningKey) ), ValidateLifetime = true, ClockSkew = TimeSpan.FromSeconds(30), }; }); services.AddAuthorization(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); AddMedia(services, configuration); AddBroadcast(services, configuration); return services; } /// Планировщик расписания: генератор, источник случайности и фоновый сервис горизонта. private static void AddBroadcast(IServiceCollection services, IConfiguration configuration) { services.Configure(configuration.GetSection(SchedulerOptions.SectionName)); services.Configure(configuration.GetSection(StreamingOptions.SectionName)); services.Configure(configuration.GetSection(BumperOptions.SectionName)); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddScoped(); services.AddHostedService(); } /// Хранилище медиа, обработка ffmpeg, очередь и фоновые сервисы (очередь + inbox-сканер). private static void AddMedia(IServiceCollection services, IConfiguration configuration) { services.Configure(configuration.GetSection(StorageOptions.SectionName)); services.Configure(configuration.GetSection(MediaOptions.SectionName)); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddHostedService(); services.AddHostedService(); } }