using MeterVault.App.Components; using MeterVault.Infrastructure; using MeterVault.Infrastructure.Options; using MeterVault.Infrastructure.Persistence; using Microsoft.EntityFrameworkCore; using Serilog; Log.Logger = new LoggerConfiguration() .WriteTo.Console() .CreateBootstrapLogger(); try { var builder = WebApplication.CreateBuilder(args); builder.Host.UseSerilog((context, services, configuration) => configuration .ReadFrom.Configuration(context.Configuration) .ReadFrom.Services(services) .Enrich.FromLogContext() .WriteTo.Console()); builder.Services.Configure( builder.Configuration.GetSection(MeterVaultOptions.SectionName)); var connectionString = builder.Configuration.GetConnectionString("Default") ?? "Host=localhost;Port=5432;Database=metervault;Username=metervault;Password=metervault"; builder.Services.AddMeterVaultInfrastructure(connectionString); builder.Services.AddRazorComponents() .AddInteractiveServerComponents(); var app = builder.Build(); await MigrateDatabaseAsync(app).ConfigureAwait(false); if (!app.Environment.IsDevelopment()) { app.UseExceptionHandler("/Error", createScopeForErrors: true); app.UseHsts(); } app.UseStatusCodePagesWithReExecute("/not-found", createScopeForStatusCodePages: true); app.UseSerilogRequestLogging(); // No HTTPS redirection: the app serves plain HTTP (port 8080) behind a reverse proxy // that terminates TLS (SDD §10). HTTPS redirection here would break the container and proxy. app.UseAntiforgery(); app.MapStaticAssets(); app.MapRazorComponents() .AddInteractiveServerRenderMode(); // Liveness/readiness probe for Gatus/Compose healthchecks (SDD §9). app.MapGet("/healthz", () => Results.Ok(new { status = "ok" })); await app.RunAsync().ConfigureAwait(false); } catch (Exception ex) { Log.Fatal(ex, "MeterVault terminated unexpectedly"); throw; } finally { await Log.CloseAndFlushAsync().ConfigureAwait(false); } static async Task MigrateDatabaseAsync(WebApplication app) { var options = app.Configuration .GetSection(MeterVaultOptions.SectionName) .Get() ?? new MeterVaultOptions(); if (!options.RunMigrationsAtStartup) { return; } await using var scope = app.Services.CreateAsyncScope(); var db = scope.ServiceProvider.GetRequiredService(); await db.Database.MigrateAsync().ConfigureAwait(false); await DatabaseSeeder.SeedAsync(db).ConfigureAwait(false); Log.Information("Database migrations applied and defaults seeded"); } /// Exposed for WebApplicationFactory-based integration tests. public partial class Program;