257 lines
9.7 KiB
C#
257 lines
9.7 KiB
C#
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;
|
|
|
|
/// <summary>
|
|
/// 服務集合擴展方法,用於組織和模組化依賴注入配置
|
|
/// </summary>
|
|
public static class ServiceCollectionExtensions
|
|
{
|
|
/// <summary>
|
|
/// 配置資料庫服務
|
|
/// </summary>
|
|
public static IServiceCollection AddDatabaseServices(this IServiceCollection services, IConfiguration configuration)
|
|
{
|
|
var useInMemoryDb = Environment.GetEnvironmentVariable("USE_INMEMORY_DB") == "true";
|
|
|
|
if (useInMemoryDb)
|
|
{
|
|
services.AddDbContext<DramaLingDbContext>(options =>
|
|
options.UseSqlite("Data Source=:memory:"));
|
|
}
|
|
else
|
|
{
|
|
var connectionString = Environment.GetEnvironmentVariable("DRAMALING_DB_CONNECTION")
|
|
?? configuration.GetConnectionString("DefaultConnection")
|
|
?? "Data Source=dramaling_test.db";
|
|
|
|
services.AddDbContext<DramaLingDbContext>(options =>
|
|
options.UseSqlite(connectionString));
|
|
}
|
|
|
|
return services;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 配置 Repository 服務
|
|
/// </summary>
|
|
public static IServiceCollection AddRepositoryServices(this IServiceCollection services)
|
|
{
|
|
services.AddScoped(typeof(IRepository<>), typeof(BaseRepository<>));
|
|
services.AddScoped<IUserRepository, UserRepository>();
|
|
services.AddScoped<IFlashcardRepository, FlashcardRepository>();
|
|
services.AddScoped<IFlashcardReviewRepository, FlashcardReviewRepository>();
|
|
|
|
return services;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 配置快取服務
|
|
/// </summary>
|
|
public static IServiceCollection AddCachingServices(this IServiceCollection services)
|
|
{
|
|
services.AddMemoryCache();
|
|
|
|
// 快取組件
|
|
services.AddSingleton<ICacheSerializer, JsonCacheSerializer>();
|
|
services.AddSingleton<ICacheStrategyManager, CacheStrategyManager>();
|
|
services.AddScoped<IDatabaseCacheManager, DatabaseCacheManager>();
|
|
|
|
// 快取提供者 - 使用具名註冊
|
|
services.AddScoped<ICacheService>(provider =>
|
|
{
|
|
var memoryCache = provider.GetRequiredService<Microsoft.Extensions.Caching.Memory.IMemoryCache>();
|
|
var logger = provider.GetRequiredService<ILogger<HybridCacheService>>();
|
|
var databaseCacheManager = provider.GetRequiredService<IDatabaseCacheManager>();
|
|
var strategyManager = provider.GetRequiredService<ICacheStrategyManager>();
|
|
|
|
var memoryProvider = new MemoryCacheProvider(
|
|
memoryCache,
|
|
provider.GetRequiredService<ILogger<MemoryCacheProvider>>());
|
|
|
|
ICacheProvider? distributedProvider = null;
|
|
var distributedCache = provider.GetService<Microsoft.Extensions.Caching.Distributed.IDistributedCache>();
|
|
if (distributedCache != null)
|
|
{
|
|
distributedProvider = new DistributedCacheProvider(
|
|
distributedCache,
|
|
provider.GetRequiredService<ICacheSerializer>(),
|
|
provider.GetRequiredService<ILogger<DistributedCacheProvider>>());
|
|
}
|
|
|
|
return new HybridCacheService(
|
|
memoryProvider,
|
|
distributedProvider,
|
|
databaseCacheManager,
|
|
strategyManager,
|
|
logger);
|
|
});
|
|
|
|
return services;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 配置 AI 服務
|
|
/// </summary>
|
|
public static IServiceCollection AddAIServices(this IServiceCollection services, IConfiguration configuration)
|
|
{
|
|
// 強型別配置
|
|
services.Configure<GeminiOptions>(configuration.GetSection(GeminiOptions.SectionName));
|
|
services.AddSingleton<IValidateOptions<GeminiOptions>, GeminiOptionsValidator>();
|
|
|
|
// Gemini 服務組件
|
|
services.AddHttpClient<IGeminiClient, GeminiClient>();
|
|
services.AddScoped<ISentenceAnalyzer, SentenceAnalyzer>();
|
|
services.AddScoped<IImageDescriptionGenerator, ImageDescriptionGenerator>();
|
|
|
|
// 主要 Gemini 服務 (Facade)
|
|
services.AddScoped<IGeminiService, GeminiService>();
|
|
|
|
// 圖片生成服務組件
|
|
services.AddScoped<IGenerationStateManager, GenerationStateManager>();
|
|
services.AddScoped<IImageSaveManager, ImageSaveManager>();
|
|
services.AddScoped<IGenerationPipelineService, GenerationPipelineService>();
|
|
services.AddScoped<IImageGenerationWorkflow, ImageGenerationWorkflow>();
|
|
services.AddScoped<IImageGenerationOrchestrator, ImageGenerationOrchestrator>();
|
|
|
|
return services;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 配置業務服務
|
|
/// </summary>
|
|
public static IServiceCollection AddBusinessServices(this IServiceCollection services)
|
|
{
|
|
services.AddScoped<IAuthService, AuthService>();
|
|
|
|
// 媒體服務
|
|
services.AddScoped<IImageProcessingService, ImageProcessingService>();
|
|
services.AddScoped<IImageStorageService, LocalImageStorageService>();
|
|
|
|
// Replicate 服務
|
|
services.AddHttpClient<IReplicateService, ReplicateService>();
|
|
|
|
// 詞彙服務
|
|
services.AddScoped<IOptionsVocabularyService, OptionsVocabularyService>();
|
|
|
|
// 分析服務
|
|
services.AddScoped<IAnalysisService, AnalysisService>();
|
|
|
|
// 複習服務
|
|
services.AddScoped<DramaLing.Api.Contracts.Services.Review.IReviewService, DramaLing.Api.Services.Review.ReviewService>();
|
|
|
|
return services;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 配置身份驗證
|
|
/// </summary>
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 配置 CORS 政策
|
|
/// </summary>
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 配置 API 文檔服務
|
|
/// </summary>
|
|
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<string>()
|
|
}
|
|
});
|
|
});
|
|
|
|
return services;
|
|
}
|
|
} |