using System.Net; using Eis.Infrastructure.Authentication; using Eis.Infrastructure.Administration; 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.Administration; 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:NativeEnabled"), authenticationOptions.NativeEnabled, authenticationOptions.SharesLegacySessions); var adminMigrationOptions = AdminMigrationOptions.FromEnvironment( builder.Configuration.GetValue("AdminMigration:NativeReadsEnabled"), authenticationOptions.NativeEnabled, authenticationOptions.SharesLegacySessions, builder.Configuration.GetValue("AdminMigration:NativeOrganizationWritesEnabled")); builder.Services.AddEisInfrastructure( DatabaseOptions.FromEnvironment(applicationRoot, builder.Environment.IsProduction()), DocumentVerificationOptions.FromEnvironment(builder.Environment.IsProduction()), authenticationOptions, candidateMigrationOptions, adminMigrationOptions); 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 { nativeEnabled = candidateMigrationOptions.NativeEnabled, nativeRoutes = candidateMigrationOptions.NativeEnabled ? new[] { "GET dashboard", "GET notices", "GET/PUT profile", "GET exams", "GET/POST registrations", "GET results", "POST result appeals", "GET admit cards", "GET admissions", "PUT admission preferences" } : [] }, administration = new { nativeReadsEnabled = adminMigrationOptions.NativeReadsEnabled, nativeOrganizationWritesEnabled = adminMigrationOptions.NativeOrganizationWritesEnabled, nativeRoutes = (adminMigrationOptions.NativeReadsEnabled ? new[] { "GET context", "GET dashboard", "GET schools", "GET school-organization", "GET admins", "GET exams" } : []) .Concat(adminMigrationOptions.NativeOrganizationWritesEnabled ? new[] { "POST/PATCH schools", "POST/PATCH classes", "POST/PATCH admins", "POST admin password reset", "PUT self-registration setting" } : []) .ToArray() }, features = MigrationFeatureCatalog.Current(authenticationOptions.NativeEnabled, candidateMigrationOptions.NativeEnabled) }, statusCode: statusCode); }); app.MapNativePublicEndpoints(); app.MapNativeAuthenticationEndpoints(authenticationOptions); app.MapNativeCandidateEndpoints(candidateMigrationOptions); app.MapNativeAdminReadEndpoints(adminMigrationOptions); 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;