using System.IO.Compression; using System.Text; using System.Text.Encodings.Web; using System.Text.Unicode; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Components.Authorization; using Microsoft.AspNetCore.ResponseCompression; using Microsoft.IdentityModel.Tokens; using MudBlazor.Services; using TaxBaik.Application; using TaxBaik.Infrastructure; using TaxBaik.Web.Services; var builder = WebApplication.CreateBuilder(args); // Controllers (API) builder.Services.AddControllers(); // Razor Pages + Blazor Server 통합 builder.Services.AddRazorPages(); builder.Services.AddRazorComponents().AddInteractiveServerComponents(); // JWT 인증 var jwtKey = builder.Configuration["Jwt:SecretKey"] ?? throw new InvalidOperationException("Missing JWT SecretKey"); var key = Encoding.ASCII.GetBytes(jwtKey); builder.Services.AddAuthentication(opts => { opts.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; opts.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; }) .AddJwtBearer(opts => { opts.TokenValidationParameters = new TokenValidationParameters { ValidateIssuerSigningKey = true, IssuerSigningKey = new SymmetricSecurityKey(key), ValidateIssuer = false, ValidateAudience = false }; }); // Blazor 인증 builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(sp => sp.GetRequiredService()); builder.Services.AddScoped(); builder.Services.AddCascadingAuthenticationState(); builder.Services.AddAuthorizationCore(); // HTTP Client for API builder.Services.AddHttpClient(); // UI & 캐시 builder.Services.AddMudServices(); builder.Services.AddMemoryCache(); builder.Services.AddResponseCompression(opts => { opts.Providers.Add(); }); // 한글 포함 다국어 문자를 유니코드 엔티티로 변환하지 않도록 설정 builder.Services.AddSingleton(HtmlEncoder.Create(UnicodeRanges.All)); builder.Services.AddInfrastructure(); builder.Services.AddApplication(); // Register version info var versionInfo = new VersionInfo(); var versionFilePath = Path.Combine(AppContext.BaseDirectory, "wwwroot", "version.txt"); if (File.Exists(versionFilePath)) { var lines = File.ReadAllLines(versionFilePath); foreach (var line in lines) { if (line.StartsWith("Version:")) versionInfo.Version = line.Substring("Version:".Length).Trim(); else if (line.StartsWith("Built:")) versionInfo.Built = line.Substring("Built:".Length).Trim(); } } builder.Services.AddSingleton(versionInfo); var app = builder.Build(); // Run migrations on startup (non-blocking for development) try { using (var scope = app.Services.CreateScope()) { var connectionFactory = scope.ServiceProvider.GetRequiredService(); var cs = builder.Configuration.GetConnectionString("Default") ?? throw new InvalidOperationException("Missing connection string"); var migrationRunner = new TaxBaik.Infrastructure.Data.MigrationRunner(cs, connectionFactory); await migrationRunner.RunAsync(); } } catch (Exception ex) { Console.WriteLine($"⚠️ Migration warning (non-blocking): {ex.Message}"); } app.UsePathBase("/taxbaik"); app.UseResponseCompression(); app.UseStaticFiles(); app.UseRouting(); app.UseAuthentication(); app.UseAuthorization(); app.UseAntiforgery(); if (!app.Environment.IsDevelopment()) { app.UseExceptionHandler("/Error"); app.UseHsts(); } // API + Razor Pages + Blazor 매핑 app.MapControllers(); app.MapRazorPages(); app.MapRazorComponents().AddInteractiveServerRenderMode(); app.Run();