using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Diagnostics; using MP.Core.Conf; using MP.Data; using MP.RIOC.Services; using NLog; using NLog.Web; using StackExchange.Redis; using System.Diagnostics; using System.Net; using System.Reflection; var builder = WebApplication.CreateBuilder(args); // recupero env corrente var env = builder.Environment; var logger = LogManager.Setup() .LoadConfigurationFromAppSettings() .GetCurrentClassLogger(); var assemblyVersion = Assembly.GetExecutingAssembly().GetName().Version?.ToString(); logger.Info($"MP.RIOC | Program.cs: startup | v.{assemblyVersion}"); logger.Info($"Current ASPNETCORE_ENVIRONMENT: {env.EnvironmentName}"); // Config setup ConfigurationManager configuration = builder.Configuration; // REDIS setup logger.Info("Config OK"); string confRedis = configuration.GetConnectionString("Redis") ?? "localhost:6379"; string redisSrvAddr = confRedis.Substring(0, confRedis.IndexOf(":")); logger.Info("Setup REDIS OK"); builder.Services.Configure( builder.Configuration.GetSection("RedisScripts")); logger.Info("RedisScript Provider configured"); // Metodi principali x accesso dati var connStr = builder.Configuration.GetConnectionString("MP.Data") ?? throw new InvalidOperationException("ConnString 'MP.Data' mancante."); //builder.Services.AddMemoryCache(); builder.Services.AddDbContextFactory(options => options.UseSqlServer(connStr) .EnableSensitiveDataLogging(false) // true solo in Sviluppo .ConfigureWarnings(w => w.Ignore(CoreEventId.ManyServiceProvidersCreatedWarning))); // MP.Data DbContext for Stats repositories string utilsConnString = builder.Configuration.GetConnectionString("MP.Utils") ?? "Server=localhost;Database=MoonPro_Utils; integrated security=True; MultipleActiveResultSets=True; App=MP.IOC;"; builder.Services.AddDbContextFactory(options => options.UseSqlServer(utilsConnString)); // MP.Data Services Utils - Statistiche DB builder.Services.AddIocDataLayer(); // 1. Configurazione dell'invoker personalizzato (Risolve i tuoi errori) var httpClientInvoker = new HttpMessageInvoker(new SocketsHttpHandler { UseProxy = false, AllowAutoRedirect = false, AutomaticDecompression = DecompressionMethods.None, UseCookies = false, // Correzione per il tracing: usa il propagatore corrente di sistema ActivityHeadersPropagator = DistributedContextPropagator.Current, ConnectTimeout = TimeSpan.FromSeconds(60), // Gestione certificato (ignora errori per localhost/test) SslOptions = new System.Net.Security.SslClientAuthenticationOptions { RemoteCertificateValidationCallback = (sender, cert, chain, sslPolicyErrors) => true } }); builder.Services.AddSingleton(httpClientInvoker); builder.Services.AddHttpForwarder(); // avvio oggetto shared x redis... var redisMux = ConnectionMultiplexer.Connect(confRedis); builder.Services.AddSingleton(redisMux); // Registrazione dei servizi custom builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddHostedService(); builder.Services.AddHostedService(); builder.Services.AddSingleton(); logger.Info("Standard service configured"); // WeightProvider: Redis/Memory da config var weightOnRedis = builder.Configuration.GetValue("ServerConf:RedisWeight", false); if (weightOnRedis) { builder.Services.AddSingleton(); } else { builder.Services.AddSingleton(); } logger.Info($"Weight service configured | use Redis: {weightOnRedis}"); // RouteManager registration (singleton) builder.Services.AddSingleton(); logger.Info("Singleton Route Manager registered"); // aggiunta pagina razor di stato builder.Services.AddRazorPages(); var app = builder.Build(); // Blocco per la migrazione automatica del DB Utils... using (var scope = app.Services.CreateScope()) { var services = scope.ServiceProvider; try { var context = services.GetRequiredService(); context.Database.Migrate(); } catch (Exception ex) { var migrateLogger = services.GetRequiredService>(); migrateLogger.LogError(ex, "Si � verificato un errore durante l'aggiornamento del database."); } } // 1. Configurazione Base Path string baseUrl = configuration.GetValue("ServerConf:BaseUrlIoc") ?? "/MP/RIOC"; app.UsePathBase(baseUrl); // 2. Middleware statici (essenziali per CSS/JS delle pagine Razor) app.UseStaticFiles(); // 3. Abilita il Routing (necessario per MapGet e MapRazorPages) app.UseRouting(); // 4. Logging middleware app.Use(async (ctx, next) => { logger.Debug($"Incoming request PathBase='{ctx.Request.PathBase}' Path='{ctx.Request.Path}'"); await next(); }); // 5. Il cuore del Proxy (MapWhen è terminale per le richieste che lo soddisfano) string routePath = configuration.GetValue("ServerConf:RoutePath") ?? "/api/IOB"; string fullPath = $"{baseUrl}{routePath}".Replace("//", "/"); logger.Info($"BaseUrl: {baseUrl}"); app.MapWhen(ctx => ctx.Request.Path.StartsWithSegments(routePath, StringComparison.OrdinalIgnoreCase), builder => { builder.Run(async ctx => { var routeManager = ctx.RequestServices.GetRequiredService(); await routeManager.HandleAsync(ctx); }); }); // 6. Definizione degli Endpoints locali app.MapRazorPages(); app.MapGet("/router-status", (RouteStatsManager stats) => Results.Ok(new { Status = "Online", Version = assemblyVersion, Mode = weightOnRedis ? "Redis" : "InMemory", Time = DateTime.Now, Metrics = stats.Snapshot() })); // 7. Fallback "intelligente" // Invece di app.Run, usiamo MapFallback che viene eseguito SOLO se nessun altro endpoint o MapWhen ha risposto app.MapFallback(async context => { context.Response.StatusCode = 404; await context.Response.WriteAsync("Router: Endpoint non trovato o non mappato."); }); app.Run();