using System.Net; using System.Text; using FozFc.Api.Application.Auth; using FozFc.Api.Application.Progress; using FozFc.Api.Application.Seeding; using FozFc.Api.Application.Zerozero; using FozFc.Api.Configuration; using FozFc.Api.Infrastructure.Mongo; using FozFc.Api.Infrastructure.Security; using FozFc.Api.Infrastructure.Storage; using FozFc.Api.Infrastructure.Zerozero; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.IdentityModel.Tokens; namespace FozFc.Api.Api; /// Composition root: registers all services, authentication and CORS. public static class DependencyInjection { public static IServiceCollection AddFozFcServices(this IServiceCollection services, AppOptions options) { services.AddSingleton(options); // Infrastructure services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddHttpClient(); services.AddSingleton(); services.AddSingleton(); // Application services.AddSingleton(); services.AddSingleton(); services.AddTransient(); // ZeroZero football feeds (scrapes zerozero.pt; replaces the Cloudflare worker) services.AddMemoryCache(); services.AddHttpClient("zerozero", client => client.Timeout = TimeSpan.FromSeconds(20)) .ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler { AutomaticDecompression = DecompressionMethods.All, AllowAutoRedirect = true, }); services.AddSingleton(); services.AddSingleton(); return services; } public static IServiceCollection AddFozFcAuth(this IServiceCollection services, AppOptions options) { var signingKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(options.JwtSecret)); services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(jwt => { jwt.RequireHttpsMetadata = false; jwt.MapInboundClaims = false; // keep raw "uid"/"role" claim names jwt.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidIssuer = AppOptions.JwtIssuer, ValidateAudience = false, ValidateLifetime = true, ValidateIssuerSigningKey = true, IssuerSigningKey = signingKey, ClockSkew = TimeSpan.FromMinutes(1), NameClaimType = JwtClaims.Uid, RoleClaimType = JwtClaims.Role, }; }); services.AddAuthorization(); return services; } public static IServiceCollection AddFozFcCors(this IServiceCollection services, AppOptions options) { services.AddCors(cors => cors.AddDefaultPolicy(policy => policy.WithOrigins(options.CorsOrigins) .AllowAnyHeader() .AllowAnyMethod())); return services; } }