Files
lux/Lux.API/Program.cs
T
2026-03-18 16:14:47 +01:00

304 lines
12 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using EgwCoreLib.Lux.Data;
using EgwCoreLib.Lux.Data.Repository.Config;
using EgwCoreLib.Lux.Data.Repository.Cost;
using EgwCoreLib.Lux.Data.Repository.Items;
using EgwCoreLib.Lux.Data.Repository.Job;
using EgwCoreLib.Lux.Data.Repository.Sales;
using EgwCoreLib.Lux.Data.Repository.Utils;
using EgwCoreLib.Lux.Data.Services;
using EgwCoreLib.Lux.Data.Services.Config;
using EgwCoreLib.Lux.Data.Services.Cost;
using EgwCoreLib.Lux.Data.Services.Items;
using EgwCoreLib.Lux.Data.Services.Job;
using EgwCoreLib.Lux.Data.Services.Sales;
using EgwCoreLib.Lux.Data.Services.Utils;
using Lux.API.Services;
using Microsoft.EntityFrameworkCore;
using Microsoft.OpenApi.Models;
using NLog;
using NLog.Targets;
using NLog.Web;
using OpenTelemetry.Resources;
using OpenTelemetry.Trace;
using StackExchange.Redis;
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();
ConfigurationManager configuration = builder.Configuration;
logger.Info($"Lux.API | Program.cs: startup | v.{assemblyVersion}");
logger.Info($"Current ASPNETCORE_ENVIRONMENT: {env.EnvironmentName}");
// costruzione connectionMultiplexer redis...
string connStr = configuration.GetConnectionString("Redis") ?? "localhost";
ConnectionMultiplexer redisConn = ConnectionMultiplexer.Connect(connStr);
// ====================================================================
// Setup Tracing e Telemetria...
// ====================================================================
// 1. Leggiamo la configurazione
var otelEnabled = builder.Configuration.GetValue<bool>("Otel:EnableTracing", false);
var otelEndpoint = builder.Configuration["Otel:Endpoint"];
var otelDsn = builder.Configuration["Otel:Dsn"];
if (otelEnabled)
{
// ====================================================================
// SETUP OPENTELEMETRY BASE (Genera gli oggetti Activity)
// Questo gira per i Livelli 1, 2 e 3.
// ====================================================================
var appVersion = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version?.ToString() ?? "1.0.0";
builder.Services.AddOpenTelemetry()
.WithTracing(tracerProviderBuilder =>
{
tracerProviderBuilder
.SetResourceBuilder(OpenTelemetry.Resources.ResourceBuilder.CreateDefault()
.AddService(serviceName: "LUX", serviceVersion: appVersion))
.AddSource("Lux.API")
.AddSource("Lux.DATA")
.AddSource("Lux.UI")
.AddAspNetCoreInstrumentation(options => { options.Filter = ctx => !ctx.Request.Path.StartsWithSegments("/health"); })
.AddEntityFrameworkCoreInstrumentation()
.AddRedisInstrumentation(redisConn);
// ====================================================================
// ESPORTAZIONE DI RETE (Solo Livelli 1 e 2)
// ====================================================================
if (!string.IsNullOrWhiteSpace(otelEndpoint))
{
tracerProviderBuilder.AddOtlpExporter(options =>
{
options.Endpoint = new Uri(otelEndpoint);
if (!string.IsNullOrWhiteSpace(otelDsn))
{
options.Headers = $"uptrace-dsn={otelDsn}";
}
options.Protocol = OpenTelemetry.Exporter.OtlpExportProtocol.Grpc;
});
}
// Se otelEndpoint è vuoto (Livello 3), le tracce nascono e muoiono in RAM.
});
// ====================================================================
// ESPORTAZIONE NLOG REALTIME (Solo Livelli 1 e 2)
// ====================================================================
if (!string.IsNullOrWhiteSpace(otelEndpoint))
{
var otlpTarget = new OtlpTarget
{
Name = "UptraceRealtime",
Endpoint = otelEndpoint,
ServiceName = "LUX.Data.Tracer"
};
if (!string.IsNullOrWhiteSpace(otelDsn))
{
otlpTarget.Headers = $"uptrace-dsn={otelDsn}";
}
var config = LogManager.Configuration ?? new NLog.Config.LoggingConfiguration();
config.AddTarget(otlpTarget);
config.AddRule(NLog.LogLevel.Info, NLog.LogLevel.Fatal, otlpTarget);
LogManager.Configuration = config;
LogManager.ReconfigExistingLoggers();
logger.Info($"🚀 NLog & OTel attivi e in invio verso: {otelEndpoint}");
}
else
{
logger.Info("️ OTel attivo (Local mode). Esportazione di rete disabilitata.");
}
}
else
{
// ====================================================================
// LIVELLO 4: TUTTO SPENTO
// ====================================================================
logger.Info("⏸️ Telemetria e Tracing completamente disabilitati.");
}
// fix logging
builder.Logging.AddFilter("Microsoft.EntityFrameworkCore", Microsoft.Extensions.Logging.LogLevel.None);
// Add services to the container.
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
//builder.Services.AddSwaggerGen();
builder.Services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo
{
Title = "EgaWare's Lux API",
Version = $"v1 | {assemblyVersion}",
Description = $"API documentation for v1 | AssemblyVersion {assemblyVersion}"
});
});
// registro connMultiplexer REDIS
builder.Services.AddSingleton<IConnectionMultiplexer>(redisConn);
// registro wrapper servizi REDIS
builder.Services.AddSingleton<IRedisService, RedisService>();
builder.Services.AddSingleton<RedisSubscriptionManager>();
var connectionString = builder.Configuration.GetConnectionString("Lux.All") ?? throw new InvalidOperationException("Connection string 'DefaultConnection' not found.");
// DataLayerContext (manca!)
builder.Services.AddDbContextFactory<DataLayerContext>(options =>
{
var conn = builder.Configuration.GetConnectionString("Lux.All");
options.UseMySql(conn, ServerVersion.AutoDetect(conn), mySqlOptions =>
{
mySqlOptions.EnableStringComparisonTranslations();
})
.EnableSensitiveDataLogging(false)
.EnableDetailedErrors(false)
.LogTo(_ => { }); // disabilita EF logging;
});
// servizi Scoped (usano DB)
//builder.Services.AddSingleton<DataLayerServices>();
builder.Services.AddScoped<DataLayerServices>();
// Repository con interfaccia
builder.Services.AddScoped<IConfGlassRepository, ConfGlassRepository>();
builder.Services.AddScoped<IConfProfileRepository, ConfProfileRepository>();
builder.Services.AddScoped<IConfWoodRepository, ConfWoodRepository>();
builder.Services.AddScoped<IEnvirParamRepository, EnvirParamRepository>();
builder.Services.AddScoped<IGenClassRepository, GenClassRepository>();
builder.Services.AddScoped<IGenValRepository, GenValRepository>();
builder.Services.AddScoped<IItemRepository, ItemRepository>();
builder.Services.AddScoped<IJobStepRepository, JobStepRepository>();
builder.Services.AddScoped<IJobTaskRepository, JobTaskRepository>();
builder.Services.AddScoped<IOfferRepository, OfferRepository>();
builder.Services.AddScoped<IOfferRowRepository, OfferRowRepository>();
builder.Services.AddScoped<IOrderRepository, OrderRepository>();
builder.Services.AddScoped<IOrderRowRepository, OrderRowRepository>();
builder.Services.AddScoped<IResourceRepository, ResourceRepository>();
builder.Services.AddScoped<ISellingItemRepository, SellingItemRepository>();
builder.Services.AddScoped<ITemplateRepository, TemplateRepository>();
builder.Services.AddScoped<ITemplateRowRepository, TemplateRowRepository>();
// Servizi dati con interfaccia
builder.Services.AddScoped<IConfGlassService, ConfGlassService>();
builder.Services.AddScoped<IConfProfileService, ConfProfileService>();
builder.Services.AddScoped<IConfWoodService, ConfWoodService>();
builder.Services.AddScoped<IEnvirParamService, EnvirParamService>();
builder.Services.AddScoped<IGenClassService, GenClassService>();
builder.Services.AddScoped<IGenValService, GenValService>();
builder.Services.AddScoped<IItemService, ItemService>();
builder.Services.AddScoped<IJobStepService, JobStepService>();
builder.Services.AddScoped<IJobTaskService, JobTaskService>();
builder.Services.AddScoped<IOfferService, OfferService>();
builder.Services.AddScoped<IOfferRowService, OfferRowService>();
builder.Services.AddScoped<IOrderService, OrderService>();
builder.Services.AddScoped<IOrderRowService, OrderRowService>();
builder.Services.AddScoped<IResourceService, ResourceService>();
builder.Services.AddScoped<ISellingItemService, SellingItemService>();
builder.Services.AddScoped<ITemplateService, TemplateService>();
builder.Services.AddScoped<ITemplateRowService, TemplateRowService>();
// Da rivedere!!!
#if false
// registrazione automatica Repository e Servizi con Scrutor
builder.Services.Scan(scan => scan
.FromAssemblyOf<BaseServ>() // Cerca nell'assembly dove si trova BaseServ
// Registra tutti i Repository
.AddClasses(classes => classes.Where(c => c.Name.EndsWith("Repository")))
.AsImplementedInterfaces() // Es: associa GenClassRepository a IGenClassRepository
.WithScopedLifetime()
// Registra tutti i Servizi
.AddClasses(classes => classes.Where(c => c.Name.EndsWith("Service")))
.AsImplementedInterfaces()
.WithScopedLifetime()
);
#endif
// servizi Singleton (no DB)
builder.Services.AddSingleton<ImageCacheService>();
builder.Services.AddSingleton<ConfigDataService>();
builder.Services.AddSingleton<ProdService>();
// servizi con dipendenze" ma Scoped
builder.Services.AddScoped<ExternalMessageProcessor>();
// servizi hosted usano scope factory
builder.Services.AddHostedService<RedisSubscriberService>();
// init servizio gestone ReqIndex
string cleanupDayTTL = configuration.GetValue<string>("ServerConf:CleanupDayTTL") ?? "360";
string rBaseKey = configuration.GetValue<string>("ServerConf:RedisBaseKey") ?? "Lux";
int dayTTL = 360;
int archTTL = 2;
int.TryParse(cleanupDayTTL, out dayTTL);
builder.Services.AddSingleton(new CalcRuidService(
configuration,
redisConn,
redisBaseKey: rBaseKey,
retention: TimeSpan.FromDays(dayTTL),
archivePeriod: TimeSpan.FromDays(archTTL)
));
builder.Services.AddHostedService<StatsCollectService>();
var app = builder.Build();
// aggiunt base URL x routing corretto
string baseUrl = configuration.GetValue<string>("ServerConf:BaseUrl") ?? "";
app.UsePathBase(baseUrl);
logger.Info($"BaseUrl: {baseUrl}");
// log channels di ritorno UI
List<string> listParams = new List<string>() { "ChannelPng", "ChannelSub", "ChannelPub", "ChannelSvg" };
foreach (var param in listParams)
{
logger.Info($"{param}: {configuration.GetValue<string>($"ServerConf:{param}") ?? ""}");
}
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment() || app.Environment.IsStaging())
{
app.UseSwagger();
//app.UseSwaggerUI();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint($"{baseUrl}swagger/v1/swagger.json", "EgalWare's Lux API v1");
});
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
// inserisco evento in chiusura x fermare i thread di calcolo sottostanti:
// https://shazwazza.com/post/aspnet-core-application-shutdown-events/
// https://dotnetblog.asphostportal.com/how-to-handle-application-shutdown-in-asp-net-core/
app.Lifetime.ApplicationStopping.Register(() =>
{
#if false
if (myExecProcessManager != null)
{
myExecProcessManager.StopExecutionThread();
}
#endif
});
// avvio app
app.Run();