using Serilog; using Serilog.Ui.Core.Extensions; using Serilog.Ui.SqliteDataProvider.Extensions; using Serilog.Ui.Web.Extensions; using Scalar.AspNetCore; using DocumentService.Infrastructure.Configuration; using DocumentService.Application; using DocumentService.Application.Common.Configuration; using DocumentService.Infrastructure; using DocumentService.API.Middleware; using DocumentService.API.Configuration; var builder = WebApplication.CreateBuilder(args); // ======================================== // 1. Serilog Configuration // ======================================== Log.Logger = new LoggerConfiguration() .ReadFrom.Configuration(builder.Configuration) .Enrich.FromLogContext() .Enrich.WithProperty("Application", "DocumentService") .CreateLogger(); builder.Host.UseSerilog(); Log.Information("Starting DocumentService API..."); try { // ======================================== // 2. Options Pattern Configuration // ======================================== builder.Services.Configure( builder.Configuration.GetSection(DocumentServiceSettings.SectionName)); builder.Services.Configure( builder.Configuration.GetSection("ZugferdSettings")); builder.Services.Configure( builder.Configuration.GetSection(RedisSettings.SectionName)); builder.Services.Configure( builder.Configuration.GetSection(ApiKeySettings.SectionName)); builder.Services.Configure( builder.Configuration.GetSection(SwaggerSettings.SectionName)); // ======================================== // 3. Services (Clean Architecture Layers) // ======================================== builder.Services.AddApplication(builder.Configuration); // Application Layer (MediatR, FluentValidation, Behaviors) builder.Services.AddInfrastructure(); // Infrastructure Layer (DevExpress, Services) builder.Services.AddControllers(); // Controllers (Controller-based API) builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerDocumentation(builder.Configuration); // ======================================== // 4. Serilog.UI Configuration // ======================================== var logDirectory = builder.Configuration.GetValue("Application:LogDirectory") ?? throw new InvalidOperationException("Application:LogDirectory not found in configuration."); var sqliteDbPath = Path.Combine(logDirectory, "logs.db"); builder.Services.AddSerilogUi(logUIOpt => { logUIOpt.UseSqliteServer(dbOpt => { dbOpt.WithConnectionString($"Data Source={sqliteDbPath}"); dbOpt.WithTable("Logs"); }); }); // ======================================== // 5. Build App // ======================================== var app = builder.Build(); // ======================================== // 5. Middleware Pipeline (Order matters!) // ======================================== // Exception Handling FIRST (catches all exceptions from subsequent middleware) app.UseMiddleware(); // ======================================== // Swagger/OpenAPI (Conditional based on settings) // ======================================== var swaggerSettings = builder.Configuration.GetSection(SwaggerSettings.SectionName).Get() ?? new SwaggerSettings(); if (app.Environment.IsDevelopment() || swaggerSettings.EnableInProduction) { app.UseSwagger(); // Swagger UI (classic) app.UseSwaggerUI(options => { options.SwaggerEndpoint($"/swagger/{swaggerSettings.Version}/swagger.json", $"{swaggerSettings.Title} {swaggerSettings.Version}"); options.RoutePrefix = "swagger"; // /swagger }); // Scalar UI (modern alternative) app.MapScalarApiReference(options => { options .WithTitle(swaggerSettings.Title) .WithTheme(ScalarTheme.DeepSpace) .WithDefaultHttpClient(ScalarTarget.CSharp, ScalarClient.HttpClient); }); } app.UseSerilogRequestLogging(); // Log HTTP Requests app.UseHttpsRedirection(); // ======================================== // 6. Serilog.UI Dashboard // ======================================== app.UseSerilogUi(); // Accessible at /serilog-ui // ======================================== // 7. Endpoints (Controller-based API) // ======================================== app.MapControllers(); // Maps all [ApiController] controllers Log.Information("DocumentService API started successfully"); app.Run(); } catch (Exception ex) { Log.Fatal(ex, "Application startup failed"); throw; } finally { Log.CloseAndFlush(); } // Make Program class accessible for Integration Tests /// /// Entry point class for the DocumentService API. /// Made partial and public for integration test access. /// public partial class Program { }