using System.Text; using Hangfire; using Hangfire.MemoryStorage; using HealthChecks.UI.Client; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Diagnostics.HealthChecks; using Microsoft.AspNetCore.Identity; using Microsoft.EntityFrameworkCore; using Microsoft.IdentityModel.Tokens; using Microsoft.OpenApi.Models; using Newsbot.Collector.Api; using Newsbot.Collector.Api.Authentication; using Newsbot.Collector.Api.Domain; using Newsbot.Collector.Api.Services; using Newsbot.Collector.Database; using Newsbot.Collector.Database.Repositories; using Newsbot.Collector.Domain.Consts; using Newsbot.Collector.Domain.Entities; using Newsbot.Collector.Domain.Interfaces; using Newsbot.Collector.Domain.Models; using Newsbot.Collector.Domain.Models.Config; using Newsbot.Collector.Domain.Models.Config.Sources; using Serilog; using Serilog.Events; using ILogger = Serilog.ILogger; var builder = WebApplication.CreateBuilder(args); // Define Logger builder.Host.UseSerilog(); // Build the config var config = GetConfiguration(); builder.Configuration.AddConfiguration(config); Log.Logger = GetLogger(config); Log.Information("Starting up"); // configure Entity Framework var dbconn = config.GetConnectionString("Database"); builder.Services.AddDbContext(o => o.UseNpgsql(dbconn ?? "")); // Configure how Identity will be managed builder.Services.AddIdentity() .AddRoles() .AddEntityFrameworkStores(); // Allow the controllers to access all the table repositories based on the interface builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); // Configure Identity builder.Services.AddScoped(); // Configure Hangfire builder.Services.AddHangfire(f => f.UseMemoryStorage()); builder.Services.AddHangfireServer(); GlobalConfiguration.Configuration.UseSerilogLogProvider(); // Build Health Checks builder.Services.AddHealthChecks() .AddNpgSql(config.GetValue(ConfigConnectionStringConst.Database) ?? ""); builder.Services.AddControllers(); // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); builder.Services.Configure(config.GetSection("ConnectionStrings")); builder.Services.Configure(config.GetSection(ConfigSectionsConst.ConnectionStrings)); builder.Services.Configure(config.GetSection(ConfigSectionsConst.Rss)); builder.Services.Configure(config.GetSection(ConfigSectionsConst.Youtube)); // Configure JWT for auth and load it into DI so we can use it in the controllers var jwtSettings = new JwtSettings(); config.Bind(nameof(jwtSettings), jwtSettings); builder.Services.AddSingleton(jwtSettings); // Configure how the Token Validation will be handled var tokenValidationParameters = new TokenValidationParameters { ValidateIssuerSigningKey = true, IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(jwtSettings.Secret ?? "")), ValidateIssuer = false, ValidateAudience = false, RequireExpirationTime = false, ValidateLifetime = true }; builder.Services.AddSingleton(tokenValidationParameters); // Build the Authentication that will be used builder.Services.AddAuthentication(x => { x.DefaultScheme = JwtBearerDefaults.AuthenticationScheme; x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; }).AddJwtBearer(x => { x.SaveToken = true; x.TokenValidationParameters = tokenValidationParameters; }); // Build the Authorization Policy that the users will conform to. builder.Services.AddAuthorization(options => options.AddPolicy(Authorization.AdministratorPolicy, b => b.RequireClaim( Authorization.AdministratorClaim, "true") )); // Configure swagger authentication builder.Services.AddSwaggerGen(cfg => { cfg.AddSecurityDefinition("ApiKey", new OpenApiSecurityScheme { Description = "The API key to access the API", Type = SecuritySchemeType.ApiKey, Name = "x-api-key", In = ParameterLocation.Header, Scheme = "ApiKeyScheme" }); cfg.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme { Description = "JWT Authorization Header using the bearer scheme", Name = "Authorization", In = ParameterLocation.Header, Type = SecuritySchemeType.ApiKey }); cfg.AddSecurityRequirement(new OpenApiSecurityRequirement { //{ // new OpenApiSecurityScheme // { // Reference = new OpenApiReference // { // Type = ReferenceType.SecurityScheme, // Id = "ApiKey" // }, // In = ParameterLocation.Header // }, // new List() //}, { new OpenApiSecurityScheme { Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "Bearer" }, Scheme = "oauth2", Name = "Bearer", In = ParameterLocation.Header }, new List() } }); }); var app = builder.Build(); // Configure the HTTP request pipeline. // Enable Swagger if requested based on config if (config.GetValue(ConfigConst.EnableSwagger)) { app.UseSwagger(); app.UseSwaggerUI(); } app.UseHttpsRedirection(); // Enable Hangfire background jobs app.UseHangfireDashboard(); BackgroundJobs.SetupRecurringJobs(config); app.UseAuthorization(); app.UseAuthentication(); // Add middleware //app.UseMiddleware(); // Add HealthChecks app.MapHealthChecks("/health", new HealthCheckOptions { Predicate = _ => true, ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse }); app.MapControllers(); // Run Database Migrations if requested using var serviceScope = app.Services.CreateScope(); if (config.GetValue(ConfigConst.RunDatabaseMigrationsOnStartup)) { var dbContext = serviceScope.ServiceProvider.GetRequiredService(); dbContext.Database.Migrate(); } else { Log.Warning("Database Migrations have been skipped. Make sure you run them on your own"); } // Inject the roles var roleManager = serviceScope.ServiceProvider.GetRequiredService>(); if (!await roleManager.RoleExistsAsync("Administrators")) { var adminRole = new IdentityRole("Administrators"); await roleManager.CreateAsync(adminRole); } if (!await roleManager.RoleExistsAsync("Users")) { var userRole = new IdentityRole("Users"); await roleManager.CreateAsync(userRole); } // Start the application app.Run(); static IConfiguration GetConfiguration() { return new ConfigurationBuilder() .AddJsonFile("appsettings.json", true) .AddJsonFile("config/appsettings.json", true) .AddEnvironmentVariables() .Build(); } static ILogger GetLogger(IConfiguration configuration) { var otel = configuration.GetValue(ConfigConnectionStringConst.OpenTelemetry) ?? ""; if (otel == "") return Log.Logger = new LoggerConfiguration() .WriteTo.Console() .MinimumLevel.Information() .CreateLogger(); return Log.Logger = new LoggerConfiguration() .WriteTo.Console() .MinimumLevel.Information() .WriteTo.OpenTelemetry( otel, resourceAttributes: new Dictionary { { "Application", "newsbot-collector-api"}, { "service.name", "newsbot-collector-api" } }) .CreateLogger(); }