using System; using System.Reflection; using System.Text; using FastEndpoints; using FastEndpoints.Swagger; using GB5Shared.Connection; using GB5Shared.Telemetry; using GB5Shared.DateConverter; using static GB5Shared.DateConverter.GB5JsonOptions; using GB5Shared.DTO.Framework.CommonConfig; using GB5Shared.GB5CommonFunction; using GB5Shared.GenerateAutoNumber; using GB5Shared.QueryExecutor; using GB5Shared.Storage; using GB5Shared.Validation; using GB5Shared.Vault; using Microsoft.AspNetCore.Server.Kestrel.Core; using Microsoft.Extensions.Caching.Hybrid; using Microsoft.Extensions.Hosting; using OpenTelemetry; using OpenTelemetry.Metrics; using OpenTelemetry.Resources; using OpenTelemetry.Trace; // ------------------------- // 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 Setup // ------------------------- builder.Services.AddDaprClient(); builder.Services.AddControllers() .AddDapr() .AddJsonOptions(options => { options.JsonSerializerOptions.PropertyNamingPolicy = null; options.JsonSerializerOptions.DictionaryKeyPolicy = null; options.JsonSerializerOptions.AddGB5Converters(); }); // ------------------------- // JSON Global (Minimal API / HttpContext) // ------------------------- builder.Services.ConfigureHttpJsonOptions(options => { options.SerializerOptions.PropertyNamingPolicy = null; options.SerializerOptions.DictionaryKeyPolicy = null; options.SerializerOptions.AddGB5Converters(); }); // ------------------------- // Caching // ------------------------- #pragma warning disable EXTEXP0018 builder.Services.AddHybridCache(options => { options.DefaultEntryOptions = new HybridCacheEntryOptions(); options.DisableCompression = false; }); #pragma warning restore EXTEXP0018 builder.Services.AddMemoryCache(); builder.Services.AddDistributedMemoryCache(); // ------------------------- // CORS Policy // ------------------------- // ------------------------- // Application Services // ------------------------- builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.Configure( builder.Configuration.GetSection("Gb5SystemDTO")); // ------------------------- // Storage Provider (DMS file operations) // StorageConfiguration values come from HashiCorp Vault, not appsettings.json // ------------------------- builder.Services.AddGB5Storage(builder.Configuration.GetSection("StorageConfiguration")); builder.Services.AddGB5Vault(builder.Configuration); builder.Services.AddScoped(); // ------------------------- // Kestrel // ------------------------- builder.Services.Configure(options => { options.AllowSynchronousIO = true; }); // -- OpenTelemetry (Tracing + Metrics) ---------------------------------------- builder.Services.AddGB5Telemetry(builder.Configuration, "GB5-DMS"); // ── Attachment path settings (used by BulkIngestionBLL → IAttachmentUploadService) ── // IAttachmentUploadService/ITemplateResolutionEngine/IAttachmentPathResolutionService now live // in GB5Shared.Attachment — not covered by the FrameworkBLL/FrameworkDAL scan below. builder.Services.Configure( builder.Configuration.GetSection(GB5Shared.Attachment.AttachmentPathSettings.Section)); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); // ------------------------- // Assembly Scan for DI // ------------------------- var dmsBLLAssembly = Assembly.Load("DMSBLL"); var dmsDALAssembly = Assembly.Load("DMSDAL"); builder.Services.Scan(scan => scan .FromAssemblies(dmsBLLAssembly, dmsDALAssembly) // Canonical filter (mirrors GB5Framework/FrameworkSL/Program.cs's scan): // - IHostedService: hosted services are registered separately with their own lifetime. // - Exception: Exception implements ISerializable/_Exception, so AsImplementedInterfaces() // would otherwise register exception types (e.g. QualifierActivationException) as services, // and DI then fails trying to activate them via their (string) constructor. // - records (detected via the compiler-generated $ method): a record implicitly // implements IEquatable, so without this, DTOs/records get registered as services and // the container fails trying to activate them via their data constructor. .AddClasses(c => c.Where(t => !t.IsAbstract && !t.IsInterface && !typeof(IHostedService).IsAssignableFrom(t) && !typeof(Exception).IsAssignableFrom(t) && t.GetMethod("$") == null)) .AsImplementedInterfaces() .WithScopedLifetime()); // ── HttpClient for Alfresco migration ──────────────────────────── // Named client used by AlfrescoMigrationDAL; timeout suited for large file downloads. builder.Services.AddHttpClient("alfresco", client => { client.Timeout = TimeSpan.FromMinutes(10); }); // ------------------------- // FastEndpoints // ------------------------- builder.Services.AddFastEndpoints(); // ------------------------- // Swagger // ------------------------- builder.Services.SwaggerDocument(o => { o.DocumentSettings = s => { s.Title = "GB5 DMS API"; s.Version = "v1"; s.Description = "Document Management Service"; }; }); // ------------------------- // Build App // ------------------------- var app = builder.Build(); // ------------------------- // Middleware // ------------------------- app.UseCloudEvents(); // FIX: Was app.MapFastEndpoints() — wrong method, no serializer config app.UseMiddleware(); app.UseMiddleware(); app.UseFastEndpoints(c => { c.Serializer.Options.PropertyNamingPolicy = null; c.Serializer.Options.DictionaryKeyPolicy = null; c.Serializer.Options.AddGB5Converters(); }); app.MapControllers(); app.MapSubscribeHandler(); // ------------------------- // Swagger UI (All Environments) // ------------------------- app.UseOpenApi(); app.UseSwaggerUi(o => { o.Path = "/GB5Documentation"; o.DocumentPath = "/swagger/{documentName}/swagger.json"; o.TransformToExternalPath = (internalUiRoute, _) => "/dms" + internalUiRoute; }); // ------------------------- // Test Endpoint // ------------------------- app.MapGet("/", () => "Hello from GB5 DMS Module (.NET 9)"); // ------------------------- // HTTPS & Authorization // ------------------------- app.UseAuthorization(); // ------------------------- // Run the Application // ------------------------- app.Run();