using System.Text; using System.Text.Json.Serialization; using Managing.Api.Authorization; using Managing.Api.Filters; using Managing.Api.Workers; using Managing.Application.Hubs; using Managing.Bootstrap; using Managing.Common; using Managing.Core.Middleawares; using Managing.Infrastructure.Databases.InfluxDb.Models; using Managing.Infrastructure.Databases.MongoDb.Configurations; using Managing.Infrastructure.Evm.Models.Privy; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.IdentityModel.Tokens; using Microsoft.OpenApi.Models; using NSwag; using NSwag.Generation.Processors.Security; using Serilog; using Serilog.Events; using Serilog.Sinks.Elasticsearch; using Microsoft.Extensions.ServiceDiscovery; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Diagnostics.HealthChecks; using Microsoft.Extensions.Diagnostics.HealthChecks; using Microsoft.Extensions.Hosting; using HealthChecks.UI.Client; using OpenApiSecurityRequirement = Microsoft.OpenApi.Models.OpenApiSecurityRequirement; using OpenApiSecurityScheme = NSwag.OpenApiSecurityScheme; using Sentry; // 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") .AddJsonFile($"config.{builder.Environment.EnvironmentName}.json", optional: true, reloadOnChange: true); builder.Configuration.AddEnvironmentVariables(); builder.Configuration.AddUserSecrets(); SentrySdk.Init(options => { // A Sentry Data Source Name (DSN) is required. // See https://docs.sentry.io/concepts/key-terms/dsn-explainer/ // You can set it in the SENTRY_DSN environment variable, or you can set it in code here. options.Dsn = builder.Configuration["Sentry:Dsn"]; // When debug is enabled, the Sentry client will emit detailed debugging information to the console. // This might be helpful, or might interfere with the normal operation of your application. // We enable it here for demonstration purposes when first trying Sentry. // You shouldn't do this in your applications unless you're troubleshooting issues with Sentry. 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; }); // Add Service Defaults - using extension methods directly builder.Services.AddServiceDiscovery(); builder.Services.AddHealthChecks() .AddCheck("self", () => HealthCheckResult.Healthy(), ["live"]); var mongoConnectionString = builder.Configuration.GetSection(Constants.Databases.MongoDb)["ConnectionString"]; var influxUrl = builder.Configuration.GetSection(Constants.Databases.InfluxDb)["Url"]; var web3ProxyUrl = builder.Configuration.GetSection("Web3Proxy")["BaseUrl"]; // Add specific health checks for databases and other services builder.Services.AddHealthChecks() .AddMongoDb(mongoConnectionString, name: "mongodb", tags: ["database"]) .AddUrlGroup(new Uri($"{influxUrl}/health"), name: "influxdb", tags: ["database"]) .AddUrlGroup(new Uri($"{web3ProxyUrl}/health"), name: "web3proxy", tags: ["api"]); builder.Host.UseSerilog((hostBuilder, loggerConfiguration) => { var envName = builder.Environment.EnvironmentName.ToLower().Replace(".", "-"); var indexFormat = $"managing-{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.MongoDb)); 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.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(o => { o.SaveToken = true; o.TokenValidationParameters = new TokenValidationParameters { ValidIssuer = builder.Configuration["Authentication:Schemes:Bearer:ValidIssuer"], ValidAudience = builder.Configuration["Authentication:Schemes:Bearer:ValidAudiences"], IssuerSigningKey = new SymmetricSecurityKey (Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Secret"])), ValidateIssuer = false, ValidateAudience = false, ValidateIssuerSigningKey = true }; }); builder.Services.AddAuthorization(); builder.Services.AddCors(o => o.AddPolicy("CorsPolicy", builder => { builder .SetIsOriginAllowed((host) => true) .AllowAnyOrigin() .WithOrigins("http://localhost:3000/") .AllowAnyMethod() .AllowAnyHeader() .AllowCredentials(); })); builder.Services.AddSignalR().AddJsonProtocol(); builder.Services.AddScoped(); builder.Services.RegisterApiDependencies(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(); builder.Services.AddHostedService(); // App var app = builder.Build(); app.UseSerilogRequestLogging(); if (app.Environment.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseOpenApi(); app.UseSwaggerUI(c => { c.SwaggerEndpoint("/swagger/v1/swagger.json", "Managing API v1"); c.RoutePrefix = string.Empty; }); app.UseCors("CorsPolicy"); // Add Sentry diagnostics middleware (now using shared version) app.UseSentryDiagnostics(); // Using shared GlobalErrorHandlingMiddleware from core project app.UseMiddleware(); app.UseMiddleware(); app.UseHttpsRedirection(); app.UseRouting(); app.UseAuthentication(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); endpoints.MapHub("/bothub"); endpoints.MapHub("/backtesthub"); endpoints.MapHub("/candlehub"); endpoints.MapHealthChecks("/health", new HealthCheckOptions { ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse }); endpoints.MapHealthChecks("/alive", new HealthCheckOptions { Predicate = r => r.Tags.Contains("live"), ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse }); }); app.Run();