using Asp.Versioning; using FluentValidation; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.Extensions.Options; using System.Diagnostics; using System.Reflection; using Vegasco.Server.Api.Authentication; using Vegasco.Server.Api.Common; using Vegasco.Server.Api.Persistence; namespace Vegasco.Server.Api.Common; public static class DependencyInjectionExtensions { /// /// Adds all the Api related services to the Dependency Injection container. /// /// public static void AddApiServices(this IHostApplicationBuilder builder) { builder.AddBuilderServices(); builder.Services .AddMiscellaneousServices() .AddCustomOpenApi() .AddApiVersioning() .AddAuthenticationAndAuthorization(builder.Environment); builder.AddDbContext(); } private static IHostApplicationBuilder AddBuilderServices(this IHostApplicationBuilder builder) { string? seqHost = builder.Configuration.GetConnectionString("seq"); if (!string.IsNullOrEmpty(seqHost)) { builder.AddSeqEndpoint("seq", o => { var apiKey = builder.Configuration.GetValue("seq-api-key"); if (!string.IsNullOrEmpty(apiKey)) { o.ApiKey = apiKey; } }); } return builder; } private static IServiceCollection AddMiscellaneousServices(this IServiceCollection services) { services.AddSingleton(() => { string assemblyName = Assembly.GetExecutingAssembly() .GetName() .Name ?? "Vegasco.Server.Api"; return new ActivitySource(assemblyName); }); services.AddResponseCompression(); services.AddValidatorsFromAssemblies( [ typeof(IApiMarker).Assembly ], ServiceLifetime.Singleton); services.AddHealthChecks(); services.AddHttpContextAccessor(); services.AddHostedService(); services.AddRequestLocalization(o => { string[] cultures = [ "en-US", "en", "de-DE", "de" ]; o.SetDefaultCulture(cultures[0]) .AddSupportedCultures(cultures) .AddSupportedUICultures(cultures); }); return services; } private static IServiceCollection AddCustomOpenApi(this IServiceCollection services) { services.AddEndpointsApiExplorer(); services.AddOpenApi(o => { o.CreateSchemaReferenceId = jsonTypeInfo => { if (string.IsNullOrEmpty(jsonTypeInfo.Type.FullName)) { return jsonTypeInfo.Type.Name; } string? fullClassName = jsonTypeInfo.Type.FullName; if (!string.IsNullOrEmpty(jsonTypeInfo.Type.Namespace)) { fullClassName = fullClassName .Replace(jsonTypeInfo.Type.Namespace, "") .TrimStart('.'); } fullClassName = fullClassName.Replace('+', '_'); return fullClassName; }; }); return services; } private static IServiceCollection AddApiVersioning(this IServiceCollection services) { services.AddApiVersioning(o => { o.DefaultApiVersion = new ApiVersion(1); o.ApiVersionReader = new UrlSegmentApiVersionReader(); o.ReportApiVersions = true; }) .AddApiExplorer(o => { o.GroupNameFormat = "'v'V"; o.SubstituteApiVersionInUrl = true; }); return services; } private static IServiceCollection AddAuthenticationAndAuthorization(this IServiceCollection services, IHostEnvironment environment) { services.AddOptions() .BindConfiguration(JwtOptions.SectionName) .ValidateFluently() .ValidateOnStart(); IOptions jwtOptions = services.BuildServiceProvider().GetRequiredService>(); services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, o => { o.MetadataAddress = jwtOptions.Value.MetadataUrl; o.TokenValidationParameters.ValidAudience = jwtOptions.Value.ValidAudience; o.TokenValidationParameters.ValidateAudience = true; if (!string.IsNullOrWhiteSpace(jwtOptions.Value.NameClaimType)) { o.TokenValidationParameters.NameClaimType = jwtOptions.Value.NameClaimType; } o.RequireHttpsMetadata = !jwtOptions.Value.AllowHttpMetadataUrl && !environment.IsDevelopment(); }); services.AddAuthorizationBuilder() .AddPolicy(Constants.Authorization.RequireAuthenticatedUserPolicy, p => p .RequireAuthenticatedUser() .AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme)); services.AddScoped(); return services; } private static IHostApplicationBuilder AddDbContext(this IHostApplicationBuilder builder) { builder.AddNpgsqlDbContext(AppHost.Shared.Constants.Database.Name); return builder; } }