using FastEndpoints; using FastEndpoints.Swagger; using GB5Shared.ValidationCriteria; using GB5Shared.Connection; using GB5Shared.DaprCache; using GB5Shared.DateConverter; using GB5Shared.DTO.Framework.CommonConfig; using GB5Shared.DTO.Framework.Login; using GB5Shared.GB5CommonFunction; using GB5Shared.GenerateAutoNumber; using GB5Shared.QueryExecutor; using GB5Shared.ScanIdentifier; using GB5Shared.Telemetry; using GB5Shared.Validation; using Microsoft.AspNetCore.HttpOverrides; using Microsoft.AspNetCore.ResponseCompression; using Microsoft.AspNetCore.Server.Kestrel.Core; using Microsoft.Extensions.Caching.Hybrid; using Newtonsoft.Json; using OpenTelemetry; using OpenTelemetry.Metrics; using OpenTelemetry.Resources; using OpenTelemetry.Trace; using System.Reflection; using System.Text; using WiBLL.Common; using WiSL.EndPoints.Hubs; using static GB5Shared.DateConverter.GB5JsonOptions; // ------------------------- // Bootstrap // ------------------------- var builder = WebApplication.CreateBuilder(args); // ── Port from config — change "AppPort" in appsettings.json to use any port ── var appPort = builder.Configuration.GetValue("AppPort"); builder.WebHost.UseUrls($"http://0.0.0.0:{appPort}"); // ------------------------- // Console Encoding // ------------------------- Console.OutputEncoding = Encoding.UTF8; // ------------------------- // Dapr + Controllers // ------------------------- builder.Services.AddDaprClient(); builder.Services.AddControllers() .AddDapr() .AddJsonOptions(options => { options.JsonSerializerOptions.PropertyNamingPolicy = null; options.JsonSerializerOptions.DictionaryKeyPolicy = null; options.JsonSerializerOptions.AddGB5Converters(); }); // ------------------------- // Global JSON // ------------------------- builder.Services.ConfigureHttpJsonOptions(options => { options.SerializerOptions.PropertyNamingPolicy = null; options.SerializerOptions.DictionaryKeyPolicy = null; options.SerializerOptions.AddGB5Converters(); }); // ------------------------- // Cache // ------------------------- #pragma warning disable EXTEXP0018 builder.Services.AddHybridCache(o => { o.DefaultEntryOptions = new HybridCacheEntryOptions(); o.DisableCompression = false; }); #pragma warning restore EXTEXP0018 builder.Services.AddMemoryCache(); builder.Services.AddDistributedMemoryCache(); // ------------------------- // HTTP Context // ------------------------- builder.Services.AddHttpContextAccessor(); // Optional Login Header Mapping builder.Services.AddScoped(sp => { var ctx = sp.GetRequiredService().HttpContext; if (ctx != null && ctx.Request.Headers.TryGetValue("Login", out var header)) { return JsonConvert.DeserializeObject(header!)!; } return new LoginDTO(); }); // ------------------------- // Core Services // ------------------------- builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); // Generic Scan-Identifier & Label Platform (GB5Shared/ScanIdentifier) — the dispatcher only; // WI's own IScanTargetResolver implementations (WiEntityResolver/WorkstationResolver/ // EmployeeResolver) are picked up by the broader WIBLL/WIDAL assembly Scan() below already, // so passing an assembly here would double-register them. builder.Services.AddScanTargetResolvers(); builder.Services.AddScoped(); // ------------------------- // SignalR // ------------------------- builder.Services.AddSignalR(); // WiHub notification service builder.Services.AddScoped(); builder.Services.Configure( builder.Configuration.GetSection("Gb5SystemDTO")); // ------------------------- // Kestrel // ------------------------- builder.Services.Configure(o => { o.AllowSynchronousIO = true; }); // ------------------------- // Compression (IMPORTANT) // ------------------------- builder.Services.AddResponseCompression(o => { o.EnableForHttps = true; o.Providers.Add(); }); // ── OpenTelemetry (Tracing + Metrics) ──────────────────────────────── builder.Services.AddGB5Telemetry(builder.Configuration, "GB5-WI"); // ------------------------- // Assembly Scan // ------------------------- var wiBLL = Assembly.Load("WIBLL"); var wiDAL = Assembly.Load("WIDAL"); builder.Services.Scan(scan => scan .FromAssemblies(wiBLL, wiDAL) .AddClasses(c => c.Where(t => !t.IsAbstract && !t.IsInterface && (t.Namespace == null || !t.Namespace.Contains(".DTO")))) .AsImplementedInterfaces() .WithScopedLifetime()); // ------------------------- // FastEndpoints // ------------------------- var endpointAssemblies = new[] { Assembly.Load("WISL"), Assembly.Load("WIBLL"), Assembly.Load("WIDAL"), Assembly.Load("GB5Shared") }; builder.Services.AddFastEndpoints(o => { o.Assemblies = endpointAssemblies; }); // ------------------------- // Swagger // ------------------------- builder.Services.SwaggerDocument(o => { o.DocumentSettings = s => { s.Title = "GB5 WorkInstruction API"; s.Version = "v1"; s.Description = "GB5 WorkInstruction Microservice"; }; o.EnableJWTBearerAuth = false; o.ShortSchemaNames = true; }); // ------------------------- // Build // ------------------------- var app = builder.Build(); // ------------------------- // Middleware Order // ------------------------- // Reverse proxy (IMPORTANT) app.UseForwardedHeaders(); // CORS // Compression app.UseResponseCompression(); // Dapr app.UseCloudEvents(); // Session Middleware app.UseMiddleware(); app.UseMiddleware(); // FastEndpoints app.UseFastEndpoints(c => { c.Serializer.Options.PropertyNamingPolicy = null; c.Serializer.Options.DictionaryKeyPolicy = null; c.Serializer.Options.AddGB5Converters(); c.Serializer.Options.AddGB5Converters(); }); // Controllers app.MapControllers(); // Dapr subscribe app.MapSubscribeHandler(); // ------------------------- // Swagger UI (LIVE FIX) // ------------------------- app.UseOpenApi(); app.UseSwaggerUi(o => { o.Path = "/GB5Documentation"; o.DocumentPath = "/swagger/{documentName}/swagger.json"; // 🔥 THIS IS CRITICAL FOR LIVE o.TransformToExternalPath = (internalUiRoute, _) => "/wis" + internalUiRoute; }); // ------------------------- // Test // ------------------------- app.MapGet("/", () => "Hello from GB5 WorkInstruction API (.NET 9)"); // ------------------------- app.UseAuthorization(); // ------------------------- app.Run();