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

378 lines
14 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.UI.Components;
using Lux.UI.Components.Account;
using Lux.UI.Data;
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Localization;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.FileProviders;
using NLog;
using NLog.Targets;
using NLog.Web;
using OpenTelemetry.Resources;
using OpenTelemetry.Trace;
using Radzen;
using StackExchange.Redis;
using System.Globalization;
var builder = WebApplication.CreateBuilder(args);
// recupero env corrente
var env = builder.Environment;
var logger = LogManager.Setup()
.LoadConfigurationFromAppSettings()
.GetCurrentClassLogger();
ConfigurationManager configuration = builder.Configuration;
logger.Info("Lux.UI | Program.cs: startup");
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.AddRazorComponents()
.AddInteractiveServerComponents()
.AddInteractiveWebAssemblyComponents();
builder.Services.AddCascadingAuthenticationState();
builder.Services.AddScoped<IdentityUserAccessor>();
builder.Services.AddScoped<IdentityRedirectManager>();
builder.Services.AddScoped<AuthenticationStateProvider, PersistingRevalidatingAuthenticationStateProvider>();
builder.Services.AddAuthentication(options =>
{
options.DefaultScheme = IdentityConstants.ApplicationScheme;
options.DefaultSignInScheme = IdentityConstants.ExternalScheme;
})
.AddIdentityCookies();
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection") ?? throw new InvalidOperationException("Connection string 'DefaultConnection' not found.");
#if false
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(connectionString));
#endif
// ApplicationDbContext (già presente)
builder.Services.AddDbContext<ApplicationDbContext>(options =>
{
options.UseMySql(
builder.Configuration.GetConnectionString("DefaultConnection"),
ServerVersion.AutoDetect(builder.Configuration.GetConnectionString("DefaultConnection")))
.EnableSensitiveDataLogging(false)
.EnableDetailedErrors(true);
//.LogTo(_ => { }); // disabilita EF logging
});
// 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(true);
//.LogTo(_ => { }, Microsoft.Extensions.Logging.LogLevel.None); // disabilita EF logging
});
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddIdentityCore<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddSignInManager()
.AddDefaultTokenProviders();
builder.Services.AddSingleton<IEmailSender<ApplicationUser>, IdentityNoOpEmailSender>();
// registro connMultiplexer REDIS
builder.Services.AddSingleton<IConnectionMultiplexer>(redisConn);
// registro wrapper servizi REDIS
builder.Services.AddSingleton<IRedisService, RedisService>();
builder.Services.AddSingleton<RedisSubscriptionManager>();
// Aggiunta servizi specifici
builder.Services.AddSingleton<ImageCacheService>();
builder.Services.AddSingleton<FileService>();
builder.Services.AddSingleton<CalcRequestService>();
builder.Services.AddSingleton<ConfigDataService>();
builder.Services.AddSingleton<ProdService>();
//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
// 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)
));
// lo gestisco solo via API...
#if false
builder.Services.AddHostedService<StatsCollectService>();
#endif
// aggiunta componenti Radzen
builder.Services.AddRadzenComponents();
var app = builder.Build();
// aggiunt base URL x routing corretto
string baseUrl = configuration.GetValue<string>("ServerConf:BaseUrl") ?? "";
app.UsePathBase(baseUrl);
logger.Info($"BaseUrl: {baseUrl}");
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseWebAssemblyDebugging();
app.UseMigrationsEndPoint();
}
else
{
app.UseExceptionHandler("/Error", createScopeForErrors: true);
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
// cultura IT...
var supportedCultures = new[]{
new CultureInfo("it-IT")
};
app.UseRequestLocalization(new RequestLocalizationOptions
{
DefaultRequestCulture = new RequestCulture("it-IT"),
SupportedCultures = supportedCultures,
FallBackToParentCultures = false
});
CultureInfo.DefaultThreadCurrentCulture = CultureInfo.CreateSpecificCulture("it-IT");
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseAntiforgery();
// gestione fileshare x uploads: verifico da conf se sia linux o windows x file da accedere...
if (configuration["ServerConf:HostOs"] == "Win")
{
app.UseFileServer(new FileServerOptions
{
FileProvider = new PhysicalFileProvider(@"\\stor01\TEAM DRIVES\40_FileUpload\LuxUploads"),
RequestPath = new PathString("/unsafe_uploads"),
EnableDirectoryBrowsing = true
//EnableDirectoryBrowsing = false
});
}
else
{
app.UseFileServer(new FileServerOptions
{
FileProvider = new PhysicalFileProvider(Path.Combine(env.ContentRootPath, "unsafe_uploads")),
RequestPath = new PathString("/unsafe_uploads"),
EnableDirectoryBrowsing = true
//EnableDirectoryBrowsing = false
});
}
app.MapRazorComponents<App>()
.AddInteractiveServerRenderMode()
.AddInteractiveWebAssemblyRenderMode()
.AddAdditionalAssemblies(typeof(Lux.UI.Client._Imports).Assembly);
app.MapGet("/download", (HttpContext ctx, IWebHostEnvironment env) =>
{
var fileName = ctx.Request.Query["fileName"].ToString() ?? "file.txt";
var filePath = Path.Combine(env.ContentRootPath, "temp", fileName);
if (!File.Exists(filePath))
return Results.NotFound();
return Results.File(
File.ReadAllBytes(filePath),
"application/octet-stream",
fileName
);
});
// Add additional endpoints required by the Identity /Account Razor components.
app.MapAdditionalIdentityEndpoints();
app.Run();