using Microsoft.EntityFrameworkCore; using Microsoft.AspNetCore.Identity; using Microsoft.Extensions.DependencyInjection; using Npgsql; using Tiku.Application.Assets; using Tiku.Application.Auth; using Tiku.Application.Backoffice; using Tiku.Application.Catalog; using Tiku.Application.Commerce; using Tiku.Application.Content; using Tiku.Application.Growth; using Tiku.Application.Jobs; using Tiku.Application.Learning; using Tiku.Application.Notifications; using Tiku.Application.QuestionBanks; using Tiku.Application.Profile; using Tiku.Application.Points; using Tiku.Application.PlatformAdmin; using Tiku.Application.PlatformBilling; using Tiku.Application.Scoreline; using Tiku.Application.Storage; using Tiku.Application.StudyContent; using Tiku.Application.TenantAdmin; using Tiku.Application.Security; using Tiku.Application.Tenancy; using Tiku.Infrastructure.Assets; using Tiku.Infrastructure.Auth; using Tiku.Infrastructure.Backoffice; using Tiku.Infrastructure.Catalog; using Tiku.Infrastructure.Commerce; using Tiku.Infrastructure.Content; using Tiku.Infrastructure.Growth; using Tiku.Infrastructure.Jobs; using Tiku.Infrastructure.Learning; using Tiku.Infrastructure.Notifications; using Tiku.Infrastructure.Persistence; using Tiku.Infrastructure.Profile; using Tiku.Infrastructure.Points; using Tiku.Infrastructure.PlatformAdmin; using Tiku.Infrastructure.PlatformBilling; using Tiku.Infrastructure.QuestionBanks; using Tiku.Infrastructure.Scoreline; using Tiku.Infrastructure.Security; using Tiku.Infrastructure.Storage; using Tiku.Infrastructure.StudyContent; using Tiku.Infrastructure.TenantAdmin; using Tiku.Infrastructure.Tenancy; using Tiku.Domain.Identity; using StackExchange.Redis; using Tiku.Infrastructure.Observability; namespace Tiku.Infrastructure; public static class DependencyInjection { public static IServiceCollection AddInfrastructure( this IServiceCollection services, string connectionString, Action? configureDatabase = null) { ArgumentException.ThrowIfNullOrWhiteSpace(connectionString); services.AddSingleton(_ => NpgsqlDataSource.Create(connectionString)); services.AddScoped(); services.AddSingleton(); services.AddDbContext((serviceProvider, options) => { var dataSource = serviceProvider.GetRequiredService(); options.UseNpgsql(dataSource, npgsql => npgsql.MigrationsAssembly(typeof(TikuDbContext).Assembly.FullName)); configureDatabase?.Invoke(options); options.AddInterceptors( serviceProvider.GetRequiredService(), serviceProvider.GetRequiredService()); }); services.AddIdentityCore(options => { options.Password.RequiredLength = 8; options.Password.RequireDigit = false; options.Password.RequireLowercase = false; options.Password.RequireUppercase = false; options.Password.RequireNonAlphanumeric = false; options.Lockout.MaxFailedAccessAttempts = 5; options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(15); options.User.RequireUniqueEmail = false; }) .AddEntityFrameworkStores() .AddSignInManager() .AddDefaultTokenProviders() .AddPasswordValidator>(); services.Configure(options => options.IterationCount = 210_000); services.AddScoped(); services.AddSingleton(); services.AddMemoryCache(); services.AddScoped(); services.AddScoped(); services.AddSingleton(); services.AddSingleton(); services.AddHttpClient(); services.AddHttpClient(); services.AddScoped(); services.AddOptions(); services.AddSingleton(); services.AddSingleton(); services.AddScoped(); services.AddScoped(); services.AddScoped(provider => provider.GetRequiredService()); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddOptions(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddOptions(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddSingleton(); services.AddSingleton(provider => provider.GetRequiredService()); services.AddScoped(); services.AddScoped(); services.AddOptions(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddSingleton(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddOptions() .Validate( AliyunOssOptions.BeValid, "Aliyun OSS presign default expiration must be positive."); services.AddOptions() .Validate( ObjectStorageOptions.BeValid, "Storage options must include a default provider, default bucket and positive max upload bytes."); services.AddSingleton(); return services; } public static IServiceCollection AddRedisSecurity( this IServiceCollection services, string connectionString, string environmentName) { ArgumentException.ThrowIfNullOrWhiteSpace(connectionString); var options = ConfigurationOptions.Parse(connectionString); options.AbortOnConnectFail = false; options.ClientName = $"tiku-{environmentName.ToLowerInvariant()}"; services.AddSingleton(_ => ConnectionMultiplexer.Connect(options)); services.AddSingleton(provider => new RedisSecurityStore( provider.GetRequiredService(), environmentName, provider.GetRequiredService>())); services.AddSingleton(provider => provider.GetRequiredService()); services.AddStackExchangeRedisCache(cache => cache.ConfigurationOptions = options); return services; } }