using Microsoft.EntityFrameworkCore; using DramaLing.Api.Data; using DramaLing.Api.Services; using DramaLing.Api.Services.Caching; using DramaLing.Api.Services.Infrastructure.Caching; using DramaLing.Api.Services.AI.Generation; using DramaLing.Api.Services.AI.Gemini; using DramaLing.Api.Services.Storage; using DramaLing.Api.Contracts.Repositories; using DramaLing.Api.Repositories; using DramaLing.Api.Contracts.Services.Core; using DramaLing.Api.Models.Configuration; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.IdentityModel.Tokens; using Microsoft.Extensions.Options; using System.Text; namespace DramaLing.Api.Extensions; /// /// 服務集合擴展方法,用於組織和模組化依賴注入配置 /// public static class ServiceCollectionExtensions { /// /// 配置資料庫服務 /// public static IServiceCollection AddDatabaseServices(this IServiceCollection services, IConfiguration configuration) { var useInMemoryDb = Environment.GetEnvironmentVariable("USE_INMEMORY_DB") == "true"; if (useInMemoryDb) { services.AddDbContext(options => options.UseSqlite("Data Source=:memory:")); } else { var connectionString = Environment.GetEnvironmentVariable("DRAMALING_DB_CONNECTION") ?? configuration.GetConnectionString("DefaultConnection") ?? "Data Source=dramaling_test.db"; services.AddDbContext(options => options.UseSqlite(connectionString)); } return services; } /// /// 配置 Repository 服務 /// public static IServiceCollection AddRepositoryServices(this IServiceCollection services) { services.AddScoped(typeof(IRepository<>), typeof(BaseRepository<>)); services.AddScoped(); services.AddScoped(); services.AddScoped(); return services; } /// /// 配置快取服務 /// public static IServiceCollection AddCachingServices(this IServiceCollection services) { services.AddMemoryCache(); // 快取組件 services.AddSingleton(); services.AddSingleton(); services.AddScoped(); // 快取提供者 - 使用具名註冊 services.AddScoped(provider => { var memoryCache = provider.GetRequiredService(); var logger = provider.GetRequiredService>(); var databaseCacheManager = provider.GetRequiredService(); var strategyManager = provider.GetRequiredService(); var memoryProvider = new MemoryCacheProvider( memoryCache, provider.GetRequiredService>()); ICacheProvider? distributedProvider = null; var distributedCache = provider.GetService(); if (distributedCache != null) { distributedProvider = new DistributedCacheProvider( distributedCache, provider.GetRequiredService(), provider.GetRequiredService>()); } return new HybridCacheService( memoryProvider, distributedProvider, databaseCacheManager, strategyManager, logger); }); return services; } /// /// 配置 AI 服務 /// public static IServiceCollection AddAIServices(this IServiceCollection services, IConfiguration configuration) { // 強型別配置 services.Configure(configuration.GetSection(GeminiOptions.SectionName)); services.AddSingleton, GeminiOptionsValidator>(); // Gemini 服務組件 services.AddHttpClient(); services.AddScoped(); services.AddScoped(); // 主要 Gemini 服務 (Facade) services.AddScoped(); // 圖片生成服務組件 services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); return services; } /// /// 配置業務服務 /// public static IServiceCollection AddBusinessServices(this IServiceCollection services) { services.AddScoped(); // 媒體服務 services.AddScoped(); services.AddScoped(); // Replicate 服務 services.AddHttpClient(); // 詞彙服務 services.AddScoped(); // 分析服務 services.AddScoped(); // 複習服務 services.AddScoped(); return services; } /// /// 配置身份驗證 /// public static IServiceCollection AddAuthenticationServices(this IServiceCollection services, IConfiguration configuration) { var supabaseUrl = Environment.GetEnvironmentVariable("DRAMALING_SUPABASE_URL") ?? configuration["Supabase:Url"] ?? "https://localhost"; var jwtSecret = Environment.GetEnvironmentVariable("DRAMALING_SUPABASE_JWT_SECRET") ?? configuration["Supabase:JwtSecret"] ?? "dev-secret-minimum-32-characters-long-for-jwt-signing-in-development-mode-only"; services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => { options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidateAudience = true, ValidateLifetime = true, ValidateIssuerSigningKey = true, ValidIssuer = supabaseUrl, ValidAudience = "authenticated", IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSecret)) }; }); return services; } /// /// 配置 CORS 政策 /// public static IServiceCollection AddCorsServices(this IServiceCollection services) { services.AddCors(options => { options.AddPolicy("AllowFrontend", policy => { policy.WithOrigins("http://localhost:3000", "http://localhost:3001", "http://localhost:3002") .AllowAnyHeader() .AllowAnyMethod() .AllowCredentials() .SetPreflightMaxAge(TimeSpan.FromMinutes(5)); }); options.AddPolicy("AllowAll", policy => { policy.AllowAnyOrigin() .AllowAnyHeader() .AllowAnyMethod(); }); }); return services; } /// /// 配置 API 文檔服務 /// public static IServiceCollection AddApiDocumentationServices(this IServiceCollection services) { services.AddEndpointsApiExplorer(); services.AddSwaggerGen(c => { c.SwaggerDoc("v1", new() { Title = "DramaLing API", Version = "v1" }); // JWT Authentication for Swagger c.AddSecurityDefinition("Bearer", new Microsoft.OpenApi.Models.OpenApiSecurityScheme { Description = "JWT Authorization header using the Bearer scheme", Name = "Authorization", In = Microsoft.OpenApi.Models.ParameterLocation.Header, Type = Microsoft.OpenApi.Models.SecuritySchemeType.ApiKey, Scheme = "Bearer" }); c.AddSecurityRequirement(new Microsoft.OpenApi.Models.OpenApiSecurityRequirement { { new Microsoft.OpenApi.Models.OpenApiSecurityScheme { Reference = new Microsoft.OpenApi.Models.OpenApiReference { Type = Microsoft.OpenApi.Models.ReferenceType.SecurityScheme, Id = "Bearer" } }, Array.Empty() } }); }); return services; } }