using Htmx.ApiDemo; using Htmx.ApiDemo.Data; using Htmx.ApiDemo.Templates; using Microsoft.AspNetCore.Antiforgery; using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; using MongoDB.Bson; using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Serializers; using MongoDB.Driver; // ── Explicit serializer registrations — force AOT to preserve constructors ─ BsonSerializer.RegisterSerializer(new ObjectIdSerializer()); BsonSerializer.RegisterSerializer(new StringSerializer()); BsonSerializer.RegisterSerializer(new DateTimeSerializer()); BsonSerializer.RegisterSerializer(new BooleanSerializer()); BsonSerializer.RegisterSerializer(new NullableSerializer(new DateTimeSerializer())); // ── Explicit BsonClassMap — no AutoMap() reflection, fully AOT-safe ─────── BsonClassMap.RegisterClassMap(cm => { cm.AutoMap(); cm.MapIdProperty(u => u.Id).SetSerializer(new ObjectIdSerializer()); cm.MapProperty(u => u.Email).SetElementName("email"); cm.MapProperty(u => u.NormalizedEmail).SetElementName("normalizedEmail"); cm.MapProperty(u => u.PasswordHash).SetElementName("passwordHash"); cm.MapProperty(u => u.DisplayName).SetElementName("displayName"); cm.MapProperty(u => u.CreatedAtUtc).SetElementName("createdAtUtc"); cm.SetIgnoreExtraElements(true); }); var builder = WebApplication.CreateSlimBuilder(args); // ── Antiforgery ─────────────────────────────────────────────────────────── builder.Services.AddAntiforgery(); // ── JSON ────────────────────────────────────────────────────────────────── builder.Services.ConfigureHttpJsonOptions(options => { options.SerializerOptions.TypeInfoResolverChain.Insert(0, AppJsonSerializerContext.Default); }); // ── MongoDB ─────────────────────────────────────────────────────────────── builder.Services.AddSingleton( new MongoClient(builder.Configuration.GetConnectionString("DefaultConnection"))); builder.Services.AddScoped(); // ── Cookie Authentication ───────────────────────────────────────────────── builder.Services .AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme) .AddCookie(options => { options.LoginPath = "/login"; options.LogoutPath = "/logout"; options.AccessDeniedPath = "/login"; options.SlidingExpiration = true; options.ExpireTimeSpan = TimeSpan.FromHours(8); }); builder.Services.AddScoped, PasswordHasher>(); builder.Services.AddScoped(); // ── App services ────────────────────────────────────────────────────────── builder.Services.AddHttpContextAccessor(); builder.Services.AddOpenApi(); builder.Services.AddAuthorization(); var app = builder.Build(); // Ensure the unique index on NormalizedEmail exists (runs once on startup, idempotent). using (var scope = app.Services.CreateScope()) await scope.ServiceProvider.GetRequiredService().EnsureIndexesAsync(); if (app.Environment.IsDevelopment()) app.MapOpenApi(); app.UseStaticFiles(); app.UseAuthentication(); app.UseAuthorization(); app.UseAntiforgery(); app.MapHtmxRoutes(); app.Run();