Files
MeterVault/src/App/Program.cs
T
schmidt.florian fe3d81b192 Deploy: change default HTTP port 8080 -> 8760
8080 is a very common, collision-prone port. 8760 (hours in a year) is an uncommon default that also avoids Home Assistant's 8123. Applied across Dockerfile (ASPNETCORE_URLS/EXPOSE), compose (mapping + healthcheck), the Unraid template, README and a code comment. The METERVAULT_PORT host override still works.

Claude-Session: https://claude.ai/code/session_01Kib2MniVFbD95fkgLgBBnB
2026-07-17 11:05:01 +02:00

119 lines
4.0 KiB
C#

using MeterVault.App.Api;
using MeterVault.App.Components;
using MeterVault.Infrastructure;
using MeterVault.Infrastructure.Options;
using MeterVault.Infrastructure.Persistence;
using Microsoft.EntityFrameworkCore;
using MudBlazor.Services;
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<MeterVaultOptions>(
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);
var options = builder.Configuration.GetSection(MeterVaultOptions.SectionName).Get<MeterVaultOptions>()
?? new MeterVaultOptions();
if (options.EnableLiveIngestion)
{
builder.Services.AddMeterVaultIngestion();
}
builder.Services.AddMudServices();
builder.Services.AddRazorComponents()
.AddInteractiveServerComponents();
// Serialize/accept enums as strings on the REST API (e.g. event Type "Delivery").
builder.Services.ConfigureHttpJsonOptions(o =>
o.SerializerOptions.Converters.Add(new System.Text.Json.Serialization.JsonStringEnumConverter()));
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(c =>
c.SwaggerDoc("v1", new() { Title = "MeterVault API", Version = "v1" }));
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();
app.UseReverseProxyTrust();
// No HTTPS redirection: the app serves plain HTTP (port 8760) behind a reverse proxy
// that terminates TLS (SDD §10). HTTPS redirection here would break the container and proxy.
app.UseAntiforgery();
app.UseSwagger();
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "MeterVault API v1"));
app.MapStaticAssets();
app.MapRazorComponents<App>()
.AddInteractiveServerRenderMode();
app.MapMeterVaultApi();
// 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<MeterVaultOptions>() ?? new MeterVaultOptions();
if (!options.RunMigrationsAtStartup)
{
return;
}
await using var scope = app.Services.CreateAsyncScope();
var db = scope.ServiceProvider.GetRequiredService<MeterVaultDbContext>();
await db.Database.MigrateAsync().ConfigureAwait(false);
await DatabaseSeeder.SeedAsync(db).ConfigureAwait(false);
Log.Information("Database migrations applied and defaults seeded");
if (options.SeedReferenceData)
{
var importer = scope.ServiceProvider.GetRequiredService<MeterVault.Infrastructure.Import.ReferenceDataImporter>();
var dir = Path.Combine(AppContext.BaseDirectory, "sampledata");
await importer.LoadAsync(dir).ConfigureAwait(false);
Log.Information("Reference dataset ensured (SeedReferenceData=true)");
}
}
/// <summary>Exposed for WebApplicationFactory-based integration tests.</summary>
public partial class Program;