using System.Text.Json; using System.Text.Json.Serialization; using System.Threading.Channels; using Femto.Api; using Femto.Api.Auth; using Femto.Api.Infrastructure; using Femto.Common; using Femto.Common.Domain; using Femto.Common.Integration; using Femto.Modules.Auth.Application; using Femto.Modules.Blog.Application; using Femto.Modules.Media.Application; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Diagnostics; using Microsoft.AspNetCore.Mvc.Infrastructure; using Microsoft.AspNetCore.WebUtilities; const string CorsPolicyName = "DefaultCorsPolicy"; var builder = WebApplication.CreateBuilder(args); builder.Services.AddOpenApi(); var loggerFactory = LoggerFactory.Create(b => { b.SetMinimumLevel(LogLevel.Information) .AddConfiguration(builder.Configuration.GetSection("Logging")) .AddConsole() .AddDebug(); }); builder.Services.AddSingleton(loggerFactory); builder.Services.AddSingleton(typeof(ILogger<>), typeof(Logger<>)); var connectionString = builder.Configuration.GetConnectionString("Database"); if (connectionString is null) throw new Exception("no database connection string found"); var blobStorageRoot = builder.Configuration.GetValue("BlobStorageRoot"); if (blobStorageRoot is null) throw new Exception("no blob storage root found"); var eventBus = new EventBus(Channel.CreateUnbounded()); builder.Services.AddHostedService(_ => eventBus); builder.Services.InitializeBlogModule(connectionString, eventBus, loggerFactory); builder.Services.InitializeMediaModule(connectionString, blobStorageRoot); builder.Services.InitializeAuthenticationModule(connectionString, eventBus, loggerFactory); builder.Services.AddScoped(); builder.Services.AddScoped(s => s.GetRequiredService()); builder.Services.AddCors(options => { options.AddPolicy( CorsPolicyName, b => { b.AllowAnyHeader() .AllowAnyMethod() .WithOrigins(builder.Configuration.GetValue("CorsOrigins")?.Split(';') ?? []) .AllowCredentials(); } ); }); builder .Services.AddControllers() .AddJsonOptions(options => { options.JsonSerializerOptions.PropertyNameCaseInsensitive = true; options.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase; options.JsonSerializerOptions.DictionaryKeyPolicy = JsonNamingPolicy.CamelCase; options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter()); options.JsonSerializerOptions.NumberHandling = JsonNumberHandling.AllowReadingFromString; }); builder.Services.Configure( builder.Configuration.GetSection("Cookies")); builder .Services.AddAuthentication("SessionAuth") .AddScheme( "SessionAuth", options => { } ); builder.Services.AddAuthorization(); var app = builder.Build(); app.UseCors(CorsPolicyName); app.UseAuthentication(); app.UseAuthorization(); app.UseExceptionHandler(errorApp => { errorApp.Run(async context => { var exceptionHandlerFeature = context.Features.Get(); var exception = exceptionHandlerFeature?.Error; var problemDetailsFactory = errorApp.ApplicationServices.GetRequiredService(); var statusCode = exception switch { DomainError => 400, _ => 500, }; var message = exception switch { DomainError domainError => domainError.Message, { } e => e.Message, _ => ReasonPhrases.GetReasonPhrase(statusCode), }; var problemDetails = problemDetailsFactory.CreateProblemDetails( httpContext: context, title: "An error occurred", detail: message, statusCode: statusCode ); // problemDetails.Extensions["traceId"] = context.TraceIdentifier; context.Response.StatusCode = statusCode; context.Response.ContentType = "application/problem+json"; await context.Response.WriteAsJsonAsync(problemDetails); }); }); // app.UseMiddleware(); if (app.Environment.IsDevelopment()) { app.MapOpenApi(); } app.MapControllers(); app.UseHttpsRedirection(); app.Run();