Files
lux/Lux.UI/Program.cs
T
2026-03-07 09:49:33 +01:00

273 lines
9.6 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.Services;
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("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.");
}
// 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.");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(connectionString));
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<DataLayerServices>();
builder.Services.AddSingleton<ImageCacheService>();
builder.Services.AddSingleton<CalcRequestService>();
builder.Services.AddSingleton<ConfigDataService>();
builder.Services.AddSingleton<ProdService>();
// 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();