Files
tiku-backend.net/Tiku.Infrastructure/DependencyInjection.cs

223 lines
13 KiB
C#

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<DbContextOptionsBuilder>? configureDatabase = null)
{
ArgumentException.ThrowIfNullOrWhiteSpace(connectionString);
services.AddSingleton(_ => NpgsqlDataSource.Create(connectionString));
services.AddScoped<TenantIsolationSaveChangesInterceptor>();
services.AddSingleton<DatabasePerformanceInterceptor>();
services.AddDbContext<TikuDbContext>((serviceProvider, options) =>
{
var dataSource = serviceProvider.GetRequiredService<NpgsqlDataSource>();
options.UseNpgsql(dataSource, npgsql =>
npgsql.MigrationsAssembly(typeof(TikuDbContext).Assembly.FullName));
configureDatabase?.Invoke(options);
options.AddInterceptors(
serviceProvider.GetRequiredService<TenantIsolationSaveChangesInterceptor>(),
serviceProvider.GetRequiredService<DatabasePerformanceInterceptor>());
});
services.AddIdentityCore<User>(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<TikuDbContext>()
.AddSignInManager()
.AddDefaultTokenProviders()
.AddPasswordValidator<LetterAndDigitPasswordValidator<User>>();
services.Configure<PasswordHasherOptions>(options => options.IterationCount = 210_000);
services.AddScoped<ITenantDirectory, TenantDirectory>();
services.AddSingleton<IRedisSecurityStore, NullRedisSecurityStore>();
services.AddSingleton<NullAuthorizationCache>();
services.AddSingleton<IAccessSecurityCache>(provider => provider.GetRequiredService<NullAuthorizationCache>());
services.AddSingleton<IAuthorizationSnapshotCache>(provider => provider.GetRequiredService<NullAuthorizationCache>());
services.AddMemoryCache();
services.AddScoped<ITenantFrontendConfigService, TenantFrontendConfigService>();
services.AddScoped<ITenantExternalProviderConfigService, TenantExternalProviderConfigService>();
services.AddSingleton<ITenantRuntimeCacheInvalidator, TenantRuntimeCacheInvalidator>();
services.AddSingleton<ITenantPublicCacheInvalidator, NullTenantPublicCacheInvalidator>();
services.AddHttpClient<IDomainOwnershipVerifier, DnsDomainOwnershipVerifier>();
services.AddHttpClient<IDomainGatewayProvisioner, HttpDomainGatewayProvisioner>();
services.AddScoped<ITenantDomainLifecycleService, TenantDomainLifecycleService>();
services.AddOptions<DomainLifecycleOptions>();
services.AddSingleton<ITenantExecutionScope, TenantExecutionScope>();
services.AddSingleton<IJwtKeyRing, JwtKeyRing>();
services.AddScoped<ITokenService, TokenService>();
services.AddScoped<AuthSessionStore>();
services.AddScoped<IAuthSessionStore>(provider => provider.GetRequiredService<AuthSessionStore>());
services.AddScoped<IRequestAccessValidator, RequestAccessValidator>();
services.AddScoped<ISmsProvider, AliyunSmsProvider>();
services.AddScoped<ISmsVerificationService, SmsVerificationService>();
services.AddScoped<IWechatOAuthClient, WechatOAuthClient>();
services.AddScoped<IAuthService, AuthService>();
services.AddScoped<IAuthAdministrationService, AuthAdministrationService>();
services.AddScoped<IOwnerActivationService, OwnerActivationService>();
services.AddScoped<IIdentityProvider, SelfHostedIdentityProvider>();
services.AddScoped<ICatalogQueryService, CatalogQueryService>();
services.AddScoped<ITaxonomyService, TaxonomyService>();
services.AddScoped<IContentNavigationQueryService, ContentNavigationQueryService>();
services.AddScoped<IContentManagementService, ContentManagementService>();
services.AddScoped<IDirectContentService, DirectContentService>();
services.AddScoped<IQuestionBankQueryService, QuestionBankQueryService>();
services.AddScoped<IPublicQuestionAccessPolicy, PublicQuestionAccessPolicy>();
services.AddScoped<IQuestionReferenceService, QuestionReferenceService>();
services.AddScoped<IProfileService, ProfileService>();
services.AddScoped<ScorelineRecordQuery>();
services.AddScoped<IScorelineQueryService, ScorelineQueryService>();
services.AddScoped<IStudyContentQueryService, StudyContentQueryService>();
services.AddScoped<IAssetQueryService, AssetQueryService>();
services.AddScoped<IAssetAccessService, AssetAccessService>();
services.AddScoped<IAssetManagementService, AssetManagementService>();
services.AddScoped<IAssetSecurityScanner, ClamAvAssetSecurityScanner>();
services.AddOptions<ClamAvOptions>();
services.AddScoped<IVideoPlaybackService, VideoPlaybackService>();
services.AddScoped<ILearningActivityService, LearningActivityService>();
services.AddScoped<ITenantAdminDirectService, TenantAdminDirectService>();
services.AddScoped<IBackofficeService, BackofficeService>();
services.AddScoped<IPlatformAdminService, PlatformAdminService>();
services.AddOptions<TenantProvisioningOptions>();
services.AddScoped<IPlatformQuestionBankService, PlatformQuestionBankService>();
services.AddScoped<IPlatformCrmAdminService, PlatformCrmAdminService>();
services.AddScoped<IPlatformSmsAdminService, PlatformSmsAdminService>();
services.AddScoped<IPlatformPaymentSettingsService, PlatformPaymentSettingsService>();
services.AddScoped<IPlatformTenantPaymentAdminService, PlatformTenantPaymentAdminService>();
services.AddScoped<ISaasCatalogAdminService, SaasCatalogAdminService>();
services.AddScoped<ITenantBillingService, TenantBillingService>();
services.AddScoped<IPlatformBillingAdminService, PlatformBillingAdminService>();
services.AddHttpClient<ICommercialBillingProcessor, CommercialBillingProcessor>();
services.AddScoped<IPlatformBillingPaymentGateway, PlatformBillingPaymentGateway>();
services.AddScoped<IPlatformBillingNotificationService, PlatformBillingNotificationService>();
services.AddScoped<IPlatformBillingSettlementService, PlatformBillingSettlementService>();
services.AddScoped<ISaasSubscriptionLifecycleService, SaasSubscriptionLifecycleService>();
services.AddOptions<SaasSubscriptionLifecycleOptions>();
services.AddScoped<ITenantOnboardingService, TenantOnboardingService>();
services.AddScoped<ITenantLifecycleService, TenantLifecycleService>();
services.AddScoped<ICurrentAccessContext, CurrentAccessContext>();
services.AddScoped<IAuthorizationStateInvalidator, AuthorizationStateInvalidator>();
services.AddScoped<IAuthorizationCacheInvalidationProcessor, AuthorizationCacheInvalidationProcessor>();
services.AddScoped<ITenantFeatureSnapshotProvider, TenantFeatureSnapshotProvider>();
services.AddSingleton<TenantFeatureCacheInvalidator>();
services.AddSingleton<ITenantFeatureCacheInvalidator>(provider => provider.GetRequiredService<TenantFeatureCacheInvalidator>());
services.AddScoped<IFeatureAccessService, FeatureAccessService>();
services.AddScoped<IFeatureUsageReconciliationService, FeatureUsageReconciliationService>();
services.AddOptions<FeatureUsageReconciliationOptions>();
services.AddScoped<IOperationAuditService, OperationAuditService>();
services.AddScoped<IBackgroundJobService, BackgroundJobService>();
services.AddScoped<ICommerceService, CommerceService>();
services.AddScoped<ICommerceAdminService, CommerceAdminService>();
services.AddScoped<IPointService, PointService>();
services.AddScoped<IReferralQrcodeGenerator, ReferralQrcodeGenerator>();
services.AddScoped<IReferralService, ReferralService>();
services.AddScoped<ICrmService, CrmService>();
services.AddScoped<ICommissionService, CommissionService>();
services.AddScoped<INotificationProvider, InAppNotificationProvider>();
services.AddSingleton<ITenantSecretProtector, TenantSecretProtector>();
services.AddScoped<ITenantSecretService, TenantSecretService>();
services.AddScoped<IPaymentProviderConfigService, PaymentProviderConfigService>();
services.AddScoped<IPaymentProviderGateway, PaymentProviderGateway>();
services.AddScoped<IPaymentProvider, ManualPaymentProvider>();
services.AddScoped<IPaymentProvider, WechatPayProvider>();
services.AddScoped<IPaymentProvider, AlipayProvider>();
services.AddOptions<AliyunOssOptions>()
.Validate(
AliyunOssOptions.BeValid,
"Aliyun OSS presign default expiration must be positive.");
services.AddOptions<ObjectStorageOptions>()
.Validate(
ObjectStorageOptions.BeValid,
"Storage options must include a default provider, default bucket and positive max upload bytes.");
services.AddSingleton<IObjectStorageService, AliyunOssObjectStorageService>();
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<IConnectionMultiplexer>(_ => ConnectionMultiplexer.Connect(options));
services.AddSingleton(provider => new RedisSecurityStore(
provider.GetRequiredService<IConnectionMultiplexer>(),
environmentName,
provider.GetRequiredService<Microsoft.Extensions.Logging.ILogger<RedisSecurityStore>>()));
services.AddSingleton<IRedisSecurityStore>(provider => provider.GetRequiredService<RedisSecurityStore>());
services.AddSingleton(provider => new RedisAuthorizationCache(
provider.GetRequiredService<IConnectionMultiplexer>(),
provider.GetRequiredService<Microsoft.Extensions.Options.IOptions<AuthorizationCacheOptions>>(),
environmentName));
services.AddSingleton<IAccessSecurityCache>(provider => provider.GetRequiredService<RedisAuthorizationCache>());
services.AddSingleton<IAuthorizationSnapshotCache>(provider => provider.GetRequiredService<RedisAuthorizationCache>());
services.AddStackExchangeRedisCache(cache => cache.ConfigurationOptions = options);
return services;
}
}