using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.ApiExplorer; using Microsoft.AspNetCore.Mvc.Versioning; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Options; using Microsoft.IdentityModel.Tokens; using Mirea.Api.DataAccess.Application; using Mirea.Api.DataAccess.Persistence; using Mirea.Api.DataAccess.Persistence.Common; using Mirea.Api.Endpoint.Common.Interfaces; using Mirea.Api.Endpoint.Common.Services; using Mirea.Api.Endpoint.Common.Services.Security; using Mirea.Api.Endpoint.Configuration; using Mirea.Api.Endpoint.Configuration.General; using Mirea.Api.Endpoint.Configuration.General.Settings; using Mirea.Api.Endpoint.Configuration.General.Validators; using Mirea.Api.Endpoint.Configuration.Swagger; using Mirea.Api.Endpoint.Middleware; using Mirea.Api.Security.Common.Interfaces; using Swashbuckle.AspNetCore.SwaggerGen; using System; using System.Collections; using System.IO; using System.Linq; using System.Text; namespace Mirea.Api.Endpoint; public class Program { private static IConfigurationRoot ConfigureEnvironment() { EnvironmentManager.LoadEnvironment(".env"); var environmentVariables = Environment.GetEnvironmentVariables() .OfType() .ToDictionary( entry => entry.Key.ToString() ?? string.Empty, entry => entry.Value?.ToString() ?? string.Empty ); var result = new ConfigurationBuilder().AddInMemoryCollection(environmentVariables!); return result.Build(); } private static IServiceCollection ConfigureJwtToken(IServiceCollection services, IConfiguration configuration) { var lifeTimeJwt = TimeSpan.FromMinutes(int.Parse(configuration["SECURITY_LIFE_TIME_JWT"]!)); var jwtDecrypt = Encoding.UTF8.GetBytes(configuration["SECURITY_ENCRYPTION_TOKEN"] ?? string.Empty); if (jwtDecrypt.Length != 32) throw new InvalidOperationException("The secret token \"SECURITY_ENCRYPTION_TOKEN\" cannot be less than 32 characters long. Now the size is equal is " + jwtDecrypt.Length); var jwtKey = Encoding.UTF8.GetBytes(configuration["SECURITY_SIGNING_TOKEN"] ?? string.Empty); if (jwtKey.Length != 64) throw new InvalidOperationException("The signature token \"SECURITY_SIGNING_TOKEN\" cannot be less than 64 characters. Now the size is " + jwtKey.Length); var jwtIssuer = configuration["SECURITY_JWT_ISSUER"]; var jwtAudience = configuration["SECURITY_JWT_AUDIENCE"]; if (string.IsNullOrEmpty(jwtAudience) || string.IsNullOrEmpty(jwtIssuer)) throw new InvalidOperationException("The \"SECURITY_JWT_ISSUER\" and \"SECURITY_JWT_AUDIENCE\" are not specified"); services.AddSingleton(_ => new JwtTokenService { Audience = jwtAudience, Issuer = jwtIssuer, Lifetime = lifeTimeJwt, EncryptionKey = jwtDecrypt, SigningKey = jwtKey }); services.AddAuthentication(options => { options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; }).AddJwtBearer(options => { options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidIssuer = jwtIssuer, ValidateAudience = true, ValidAudience = jwtAudience, ValidateLifetime = true, ValidateIssuerSigningKey = true, IssuerSigningKey = new SymmetricSecurityKey(jwtKey), TokenDecryptionKey = new SymmetricSecurityKey(jwtDecrypt) }; }); return services; } private static IServiceCollection ConfigureSecurity(IServiceCollection services) { services.AddSingleton(); services.AddSingleton(); return services; } public static void Main(string[] args) { Directory.SetCurrentDirectory(AppDomain.CurrentDomain.BaseDirectory); var builder = WebApplication.CreateBuilder(args); builder.Configuration.AddConfiguration(ConfigureEnvironment()); builder.Configuration.AddJsonFile(PathBuilder.Combine(GeneralConfig.FilePath), optional: true, reloadOnChange: true); builder.Services.Configure(builder.Configuration); var generalConfig = builder.Configuration.Get(); builder.Services.AddApplication(); builder.Services.AddPersistence(generalConfig?.DbSettings?.DatabaseProvider ?? DatabaseProvider.Sqlite, generalConfig?.DbSettings?.ConnectionStringSql ?? string.Empty); builder.Services.AddControllers(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddCors(options => { options.AddPolicy("AllowAll", policy => { policy.AllowAnyHeader(); policy.AllowAnyMethod(); policy.AllowAnyOrigin(); }); }); builder.Services.AddApiVersioning(options => { options.DefaultApiVersion = new ApiVersion(1, 0); options.AssumeDefaultVersionWhenUnspecified = true; options.ReportApiVersions = true; options.ApiVersionReader = new UrlSegmentApiVersionReader(); }); builder.Services.AddVersionedApiExplorer(options => { options.GroupNameFormat = "'v'VVV"; options.SubstituteApiVersionInUrl = true; }); builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(options => { options.OperationFilter(); var basePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory); var xmlPath = Path.Combine(basePath, "docs.xml"); options.IncludeXmlComments(xmlPath); }); builder.Services.AddTransient, ConfigureSwaggerOptions>(); var app = builder.Build(); #if DEBUG // Write configurations foreach (var item in app.Configuration.AsEnumerable()) Console.WriteLine($"{item.Key}:{item.Value}"); #endif using (var scope = app.Services.CreateScope()) { var serviceProvider = scope.ServiceProvider; var optionsSnapshot = serviceProvider.GetRequiredService>(); var settingsValidator = new SettingsRequiredValidator(optionsSnapshot); var isDoneConfig = settingsValidator.AreSettingsValid(); if (isDoneConfig) { var uberDbContext = serviceProvider.GetRequiredService(); var maintenanceModeService = serviceProvider.GetRequiredService(); maintenanceModeService.DisableMaintenanceMode(); DbInitializer.Initialize(uberDbContext); // todo: if admin not found } } // Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(options => { var provider = app.Services.GetService(); foreach (var description in provider!.ApiVersionDescriptions) { var url = $"/swagger/{description.GroupName}/swagger.json"; var name = description.GroupName.ToUpperInvariant(); options.SwaggerEndpoint(url, name); } }); } app.UseMiddleware(); app.UseMiddleware(); app.UseHttpsRedirection(); app.UseAuthorization(); app.MapControllers(); app.Run(); } }