using Microsoft.EntityFrameworkCore; using DramaLing.Api.Data; using DramaLing.Api.Services; // Services.AI namespace removed using DramaLing.Api.Services.Caching; using DramaLing.Api.Repositories; 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(); return services; } /// /// 配置快取服務 /// public static IServiceCollection AddCachingServices(this IServiceCollection services) { services.AddMemoryCache(); services.AddScoped(); return services; } /// /// 配置 AI 服務 /// public static IServiceCollection AddAIServices(this IServiceCollection services, IConfiguration configuration) { // 強型別配置 services.Configure(configuration.GetSection(GeminiOptions.SectionName)); services.AddSingleton, GeminiOptionsValidator>(); // AI 提供商服務已移除 (使用 GeminiService 替代) // 舊的 Gemini 服務 (向後相容) services.AddHttpClient(); return services; } /// /// 配置業務服務 /// public static IServiceCollection AddBusinessServices(this IServiceCollection services) { services.AddScoped(); 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; } }