using Microsoft.EntityFrameworkCore; using DramaLing.Api.Data; using DramaLing.Api.Services; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.IdentityModel.Tokens; using System.Text; var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.AddControllers(); // Entity Framework - 使用 SQLite 進行測試 var useInMemoryDb = Environment.GetEnvironmentVariable("USE_INMEMORY_DB") == "true"; if (useInMemoryDb) { builder.Services.AddDbContext(options => options.UseSqlite("Data Source=:memory:")); } else { var connectionString = Environment.GetEnvironmentVariable("DRAMALING_DB_CONNECTION") ?? builder.Configuration.GetConnectionString("DefaultConnection") ?? "Data Source=dramaling_test.db"; // SQLite 檔案 builder.Services.AddDbContext(options => options.UseSqlite(connectionString)); } // Custom Services builder.Services.AddScoped(); builder.Services.AddHttpClient(); // Authentication - 從環境變數讀取 JWT 配置 var supabaseUrl = Environment.GetEnvironmentVariable("DRAMALING_SUPABASE_URL") ?? builder.Configuration["Supabase:Url"] ?? "https://localhost"; var jwtSecret = Environment.GetEnvironmentVariable("DRAMALING_SUPABASE_JWT_SECRET") ?? builder.Configuration["Supabase:JwtSecret"] ?? "dev-secret-minimum-32-characters-long-for-jwt-signing-in-development-mode-only"; builder.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)) }; }); // CORS for frontend builder.Services.AddCors(options => { options.AddPolicy("AllowFrontend", policy => { policy.WithOrigins("http://localhost:3000", "http://localhost:3001") .AllowAnyHeader() .AllowAnyMethod() .AllowCredentials() .SetPreflightMaxAge(TimeSpan.FromMinutes(5)); }); // 開發環境允許所有來源 options.AddPolicy("AllowAll", policy => { policy.AllowAnyOrigin() .AllowAnyHeader() .AllowAnyMethod(); }); }); // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle builder.Services.AddEndpointsApiExplorer(); builder.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() } }); }); var app = builder.Build(); // Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(c => { c.SwaggerEndpoint("/swagger/v1/swagger.json", "DramaLing API v1"); c.RoutePrefix = "swagger"; }); } // 開發環境使用寬鬆的 CORS 政策 if (app.Environment.IsDevelopment()) { app.UseCors("AllowAll"); } else { app.UseCors("AllowFrontend"); } app.UseHttpsRedirection(); app.UseAuthentication(); app.UseAuthorization(); app.MapControllers(); // Health check endpoint app.MapGet("/health", () => new { Status = "Healthy", Timestamp = DateTime.UtcNow }); // 確保資料庫已創建 using (var scope = app.Services.CreateScope()) { var context = scope.ServiceProvider.GetRequiredService(); try { context.Database.EnsureCreated(); app.Logger.LogInformation("Database ensured created"); } catch (Exception ex) { app.Logger.LogError(ex, "Error creating database"); } } app.Run();