using System.Net; using Eis.Infrastructure.Authentication; using Eis.Infrastructure.Candidate; using Eis.Application.Public; using Eis.Infrastructure; using Eis.Infrastructure.Data; using Eis.Infrastructure.Migration; using Eis.Infrastructure.Security; using Eis.Web.Configuration; using Eis.Web.Authentication; using Eis.Web.Candidate; using Eis.Web.Frontend; using Eis.Web.Legacy; using Eis.Web.Public; var applicationRoot = ApplicationPaths.FindApplicationRoot(); EnvironmentFile.Load(applicationRoot); var builder = WebApplication.CreateBuilder(args); builder.WebHost.ConfigureKestrel(options => options.AddServerHeader = false); builder.Services.Configure(builder.Configuration.GetSection(LegacyNodeOptions.SectionName)); builder.Services.AddHttpClient((services, client) => { var options = services.GetRequiredService>().Value; client.BaseAddress = options.BaseUrl; client.Timeout = TimeSpan.FromSeconds(30); client.DefaultRequestHeaders.UserAgent.ParseAdd("Eis.AspNetCore.Migration/1.0"); }).ConfigurePrimaryHttpMessageHandler(() => new SocketsHttpHandler { AllowAutoRedirect = false, AutomaticDecompression = DecompressionMethods.None, UseCookies = false }); builder.Services.AddProblemDetails(); builder.Services.AddSingleton(); var authenticationOptions = AuthenticationOptions.FromEnvironment( builder.Environment.IsProduction(), builder.Configuration.GetValue("AuthenticationMigration:NativeEnabled")); var candidateMigrationOptions = CandidateMigrationOptions.FromEnvironment( builder.Configuration.GetValue("CandidateMigration:NativeReadEnabled"), authenticationOptions.NativeEnabled, authenticationOptions.SharesLegacySessions); builder.Services.AddEisInfrastructure( DatabaseOptions.FromEnvironment(applicationRoot, builder.Environment.IsProduction()), DocumentVerificationOptions.FromEnvironment(builder.Environment.IsProduction()), authenticationOptions, candidateMigrationOptions); var app = builder.Build(); app.Services.EnsureNativeAuthenticationReady(authenticationOptions); app.UseExceptionHandler(); app.Use(async (context, next) => { context.Response.Headers.XContentTypeOptions = "nosniff"; context.Response.Headers.XFrameOptions = "DENY"; context.Response.Headers["Referrer-Policy"] = "same-origin"; await next(); }); app.MapGet("/health/live", () => Results.Json(new { status = "healthy", service = "Eis.Web", framework = ".NET 10" })); app.MapGet("/health/migration", async (LegacyApiProxy proxy, CancellationToken cancellationToken) => { var legacyAvailable = await proxy.IsAvailableAsync(cancellationToken); var statusCode = legacyAvailable ? StatusCodes.Status200OK : StatusCodes.Status503ServiceUnavailable; return Results.Json(new { status = legacyAvailable ? "healthy" : "degraded", legacyApiAvailable = legacyAvailable, authentication = new { nativeEnabled = authenticationOptions.NativeEnabled, stateBackend = authenticationOptions.UsesRedis ? "redis" : "memory", sharesLegacySessions = authenticationOptions.SharesLegacySessions }, candidate = new { nativeReadEnabled = candidateMigrationOptions.NativeReadEnabled, nativeRoutes = candidateMigrationOptions.NativeReadEnabled ? new[] { "dashboard", "notices", "profile", "exams", "registrations" } : [] }, features = MigrationFeatureCatalog.Current(authenticationOptions.NativeEnabled) }, statusCode: statusCode); }); app.MapNativePublicEndpoints(); app.MapNativeAuthenticationEndpoints(authenticationOptions); app.MapNativeCandidateReadEndpoints(candidateMigrationOptions); string[] methods = [ HttpMethods.Get, HttpMethods.Head, HttpMethods.Post, HttpMethods.Put, HttpMethods.Patch, HttpMethods.Delete, HttpMethods.Options ]; app.MapMethods("/api/{**path}", methods, (HttpContext context, LegacyApiProxy proxy) => proxy.ForwardAsync(context)); app.MapFrontendAssets(); app.Run(); public partial class Program;