using System.Text.Json.Serialization; using HealthChecks.UI.Client; using Managing.Api.Workers.Filters; using Managing.Application.Hubs; using Managing.Bootstrap; using Managing.Common; using Managing.Core.Middleawares; using Managing.Infrastructure.Databases.InfluxDb.Models; using Managing.Infrastructure.Databases.PostgreSql; using Managing.Infrastructure.Evm.Models.Privy; using Microsoft.AspNetCore.Diagnostics.HealthChecks; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Diagnostics.HealthChecks; using Microsoft.OpenApi.Models; using NSwag; using NSwag.Generation.Processors.Security; using Serilog; using Serilog.Events; using Serilog.Sinks.Elasticsearch; using OpenApiSecurityRequirement = Microsoft.OpenApi.Models.OpenApiSecurityRequirement; using OpenApiSecurityScheme = NSwag.OpenApiSecurityScheme; // Builder var builder = WebApplication.CreateBuilder(args); builder.Configuration.SetBasePath(AppContext.BaseDirectory); builder.Configuration.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) .AddJsonFile($"appsettings.{builder.Environment.EnvironmentName}.json"); var influxUrl = builder.Configuration.GetSection(Constants.Databases.InfluxDb)["Url"]; var web3ProxyUrl = builder.Configuration.GetSection("Web3Proxy")["BaseUrl"]; var postgreSqlConnectionString = builder.Configuration.GetSection("PostgreSql")["ConnectionString"]; // Initialize Sentry SentrySdk.Init(options => { // A Sentry Data Source Name (DSN) is required. options.Dsn = builder.Configuration["Sentry:Dsn"]; // When debug is enabled, the Sentry client will emit detailed debugging information to the console. options.Debug = false; // Adds request URL and headers, IP and name for users, etc. options.SendDefaultPii = true; // This option is recommended. It enables Sentry's "Release Health" feature. options.AutoSessionTracking = true; // Enabling this option is recommended for client applications only. It ensures all threads use the same global scope. options.IsGlobalModeEnabled = false; // Example sample rate for your transactions: captures 10% of transactions options.TracesSampleRate = 0.1; options.Environment = builder.Environment.EnvironmentName; }); // Add service discovery for Aspire builder.Services.AddServiceDiscovery(); // Configure health checks builder.Services.AddHealthChecks() .AddCheck("self", () => HealthCheckResult.Healthy(), ["live"]) .AddUrlGroup(new Uri($"{influxUrl}/health"), name: "influxdb", tags: ["database"]) .AddUrlGroup(new Uri($"{web3ProxyUrl}/health"), name: "web3proxy", tags: ["api"]); builder.WebHost.UseUrls("http://localhost:5001"); builder.Host.UseSerilog((hostBuilder, loggerConfiguration) => { var envName = builder.Environment.EnvironmentName.ToLower().Replace(".", "-"); var indexFormat = $"managing-worker-{envName}-" + "{0:yyyy.MM.dd}"; var yourTemplateName = "dotnetlogs"; var es = new ElasticsearchSinkOptions(new Uri(hostBuilder.Configuration["ElasticConfiguration:Uri"])) { IndexFormat = indexFormat.ToLower(), AutoRegisterTemplate = true, OverwriteTemplate = true, TemplateName = yourTemplateName, AutoRegisterTemplateVersion = AutoRegisterTemplateVersion.ESv7, TypeName = null, BatchAction = ElasticOpType.Create, MinimumLogEventLevel = LogEventLevel.Information, DetectElasticsearchVersion = true, RegisterTemplateFailure = RegisterTemplateRecovery.IndexAnyway, }; loggerConfiguration .WriteTo.Console() .WriteTo.Elasticsearch(es); }); builder.Services.AddOptions(); builder.Services.Configure(builder.Configuration.GetSection(Constants.Databases.InfluxDb)); builder.Services.Configure(builder.Configuration.GetSection(Constants.ThirdParty.Privy)); builder.Services.AddControllers().AddJsonOptions(options => options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter())); builder.Services.AddCors(o => o.AddPolicy("CorsPolicy", builder => { builder .SetIsOriginAllowed((host) => true) .AllowAnyOrigin() .WithOrigins("http://localhost:3000/") .AllowAnyMethod() .AllowAnyHeader() .AllowCredentials(); })); builder.Services.AddSignalR().AddJsonProtocol(); // Add PostgreSQL DbContext for worker services builder.Services.AddDbContext(options => { options.UseNpgsql(postgreSqlConnectionString, npgsqlOptions => { npgsqlOptions.CommandTimeout(60); npgsqlOptions.EnableRetryOnFailure(maxRetryCount: 5, maxRetryDelay: TimeSpan.FromSeconds(10), errorCodesToAdd: null); }); if (builder.Environment.IsDevelopment()) { options.EnableDetailedErrors(); options.EnableSensitiveDataLogging(); options.EnableThreadSafetyChecks(); } options.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking); options.EnableServiceProviderCaching(); options.LogTo(msg => Console.WriteLine(msg), LogLevel.Warning); }, ServiceLifetime.Scoped); builder.Services.RegisterWorkersDependencies(builder.Configuration); builder.Services.AddEndpointsApiExplorer(); builder.Services.AddOpenApiDocument(document => { document.AddSecurity("JWT", Enumerable.Empty(), new OpenApiSecurityScheme { Type = OpenApiSecuritySchemeType.ApiKey, Name = "Authorization", In = OpenApiSecurityApiKeyLocation.Header, Description = "Type into the textbox: Bearer {your JWT token}." }); document.OperationProcessors.Add( new AspNetCoreOperationSecurityScopeProcessor("JWT")); }); builder.Services.AddSwaggerGen(options => { options.SchemaFilter(); options.AddSecurityDefinition("Bearer,", new Microsoft.OpenApi.Models.OpenApiSecurityScheme { Description = "Please insert your JWT Token into field : Bearer {your_token}", Name = "Authorization", Type = SecuritySchemeType.Http, In = ParameterLocation.Header, Scheme = "Bearer", BearerFormat = "JWT" }); options.AddSecurityRequirement(new OpenApiSecurityRequirement { { new Microsoft.OpenApi.Models.OpenApiSecurityScheme { Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "Bearer" } }, new string[] { } } }); }); builder.WebHost.SetupDiscordBot(); // App var app = builder.Build(); app.UseSerilogRequestLogging(); app.UseOpenApi(); app.UseSwaggerUI(c => { c.SwaggerEndpoint("/swagger/v1/swagger.json", "Managing Workers v1"); c.RoutePrefix = string.Empty; }); app.UseCors("CorsPolicy"); // Add Sentry diagnostics middleware (now using shared version from Core) app.UseSentryDiagnostics(); // Using shared GlobalErrorHandlingMiddleware from Core project app.UseMiddleware(); app.UseHttpsRedirection(); app.UseRouting(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); endpoints.MapHub("/positionhub"); endpoints.MapHealthChecks("/health", new HealthCheckOptions { ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse }); endpoints.MapHealthChecks("/alive", new HealthCheckOptions { Predicate = r => r.Tags.Contains("live"), ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse }); }); app.Run();