From 77517579e3c1d990aa16ce0a6597a4525c06d8c2 Mon Sep 17 00:00:00 2001 From: Hermes Agent Date: Thu, 20 Aug 2026 17:45:27 +0200 Subject: [PATCH] Added api boostrap logic --- .../PostFundManagement.Api.csproj | 94 ++++++++++++++-- PostFundManagement.Api/Program.cs | 103 +++++++++++++++--- PostFundManagement.Api/appsettings.json | 14 ++- .../Extensions/Constants.cs | 2 + PostFundManagement.Domain/Extensions/Email.cs | 23 ++++ .../Extensions/Quartz.cs | 2 - 6 files changed, 213 insertions(+), 25 deletions(-) create mode 100644 PostFundManagement.Domain/Extensions/Email.cs diff --git a/PostFundManagement.Api/PostFundManagement.Api.csproj b/PostFundManagement.Api/PostFundManagement.Api.csproj index 891a5fe..746d861 100644 --- a/PostFundManagement.Api/PostFundManagement.Api.csproj +++ b/PostFundManagement.Api/PostFundManagement.Api.csproj @@ -8,18 +8,96 @@ ..\PostFundManagement.snk + - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + + + + + + + + + + + diff --git a/PostFundManagement.Api/Program.cs b/PostFundManagement.Api/Program.cs index aaf8987..4f84139 100644 --- a/PostFundManagement.Api/Program.cs +++ b/PostFundManagement.Api/Program.cs @@ -1,23 +1,100 @@ -using Scalar.AspNetCore; +using PostFundManagement.Api.Extensions; +using PostFundManagement.Domain.Extensions; +using PostFundManagement.Domain.Mediator; +using PostFundManagement.Infrastructure.Extensions; +using static PostFundManagement.Domain.Extensions.Constants; var builder = WebApplication.CreateBuilder(args); -builder.Services.AddApiVersioning(); -builder.Services.AddOpenApi(); +builder.Services.AddEndpointsApiExplorer() + .AddApiVersioning(options => options.ApiVersionReader = new HeaderApiVersionReader()); + +builder.Services.AddApiVersioning().AddApiExplorer(); + +builder.Services.AddEndpoints(Assembly.GetExecutingAssembly()); +builder.Services.AddApiServices(builder.Configuration); + +builder.Services.AddMediator(); +builder.Services.AddSecurityApiSdk(builder.Configuration); +builder.Services.AddWebSecurity(builder.Configuration); + +builder.Services.AddScoped(typeof(IPipelineBehavior<,>), typeof(TelemetryPipelineBehavior<,>)); +builder.Services.AddScoped(typeof(IPipelineBehavior<,>), typeof(LoggingPipelineBehavior<,>)); + +builder.Services.AddQuartzSchedulerClient(DefaultSchedulerName, builder.Configuration); + +builder.Services.AddEmailServices(builder.Configuration); + +builder.Services.AddHttpClient(); +builder.Services.AddHashServices(builder.Configuration); +builder.Services.AddDataProtectionDatabase(builder.Configuration); +builder.Services.AddApplicationDbContext(builder.Configuration); + +// TODO: add healthcheck services +// builder.Services.AddMidrandShopPostgresHealthCheck(); +// builder.Services.AddMidrandShopQuartzHealthCheck(); +// builder.Services.AddHealthChecksSupport(builder.Configuration); var app = builder.Build(); -if (app.Environment.IsDevelopment()) -{ - app.MapScalarApiReference(options => - { - options.WithTitle("Post-fund Management API") - .WithTheme(ScalarTheme.BluePlanet); - }); +var schedulerFactory = app.Services.GetRequiredService(); +var scheduler = await schedulerFactory.GetScheduler(DefaultSchedulerName); - app.MapOpenApi(); -} +if (!scheduler!.IsStarted) + await scheduler.Start(); +app.UseHsts(); app.UseHttpsRedirection(); -app.Run(); +app.UseRouting(); +app.UseAuthentication(); +app.UseAuthorization(); + +ApiVersionSet versionSet = app.NewApiVersionSet("v1") + .HasApiVersion(new ApiVersion(1)) + .HasApiVersion(new ApiVersion(2)) + .ReportApiVersions() + .Build(); + +var versionGroups = new Dictionary +{ + { 1, app.MapGroup("v{version:apiVersion}").WithApiVersionSet(versionSet) } +}; + +app.MapEndpoints(versionGroups); + +app.UseHealthChecks("/health", new HealthCheckOptions +{ + Predicate = _ => true, + AllowCachingResponses = true, + ResponseWriter = HealthChecks.UI.Client.UIResponseWriter.WriteHealthCheckUIResponse +}); + +app.MapHealthChecksUI(options => { options.UIPath = "/healthui"; }); +app.UseHealthChecks("/ready"); + +app.MapOpenApi(); + +var apiVersions = app.DescribeApiVersions() + .OrderByDescending(o => o.ApiVersion.MajorVersion) + .ToList(); + +foreach (var description in app.DescribeApiVersions().OrderByDescending(o => o.ApiVersion.MajorVersion)) + app.MapScalarApiReference($"/openapi/{description.GroupName}", (options, context) => + { + options.AddServer(new ScalarServer($"https://{context.Request.Host}")); + options.WithOpenApiRoutePattern($"/openapi/{description.GroupName}.json"); + options.WithTheme(ScalarTheme.DeepSpace); + options.Agent = new ScalarAgentOptions { Disabled = true }; + options.Authentication = new ScalarAuthenticationOptions { PreferredSecuritySchemes = ["Bearer"] }; + }); + +var latestVersionGroup = apiVersions.FirstOrDefault()?.GroupName ?? "v1"; + +app.MapGet("/", () => Results.Redirect($"/openapi/{latestVersionGroup}")) + .ExcludeFromDescription(); + +if (!app.Environment.IsDevelopment()) + app.UseExceptionHandler("/Error", createScopeForErrors: true); + +app.Run(); \ No newline at end of file diff --git a/PostFundManagement.Api/appsettings.json b/PostFundManagement.Api/appsettings.json index a2002e3..9cefeec 100644 --- a/PostFundManagement.Api/appsettings.json +++ b/PostFundManagement.Api/appsettings.json @@ -1,5 +1,15 @@ { - "MachineIdentity": { + "GeneralS3Settings": { + "ServiceUrl": "http://192.168.1.177:30900", + "Region": "garage", + "BucketName": "pfm.general", + "CdnBaseUrl": "https://pfm.general.cdn.khongisa.co.za" + }, + "Monitoring": { + "Address": "http://aspire-dashboard-service.aspire.svc.cluster.local:18889", + "ServiceName": "MidrandBooks.DEV" + }, + "SecuritySettings": { "Authority": "https://sts.security.khongisa.co.za", "Audience": "pfm-api-dev" }, @@ -10,4 +20,4 @@ } }, "AllowedHosts": "*" -} +} \ No newline at end of file diff --git a/PostFundManagement.Domain/Extensions/Constants.cs b/PostFundManagement.Domain/Extensions/Constants.cs index c16be94..1d7d474 100644 --- a/PostFundManagement.Domain/Extensions/Constants.cs +++ b/PostFundManagement.Domain/Extensions/Constants.cs @@ -2,6 +2,8 @@ namespace PostFundManagement.Domain.Extensions; public static class Constants { + public const string DefaultSchedulerName = "pfm-scheduler"; + public const string GeneralS3SettingsSection = "PfmS3Settings"; public const string EvidenceS3SettingsSection = "EvidenceS3Settings"; diff --git a/PostFundManagement.Domain/Extensions/Email.cs b/PostFundManagement.Domain/Extensions/Email.cs new file mode 100644 index 0000000..3e2da51 --- /dev/null +++ b/PostFundManagement.Domain/Extensions/Email.cs @@ -0,0 +1,23 @@ +using PostFundManagement.Domain.Configuration.Email; +using PostFundManagement.Domain.Services; + +namespace PostFundManagement.Domain.Extensions; + +public static class Email +{ + public const string EmailFromName = "PFM Team"; + public const string EmailFromAddress = "info@pfm.co.za"; // TODO: replace with real address + + public static IServiceCollection AddEmailServices(this IServiceCollection services, IConfiguration configuration) + { + services.Configure(configuration.GetSection("Email")); + + services.AddSingleton(); + + services.AddOpenTelemetry() + .WithTracing(tracing => tracing.AddSource("Pfm.EmailService")) + .WithMetrics(metrics => metrics.AddMeter("Pfm.EmailService")); + + return services; + } +} \ No newline at end of file diff --git a/PostFundManagement.Domain/Extensions/Quartz.cs b/PostFundManagement.Domain/Extensions/Quartz.cs index f8aeaec..e52d909 100644 --- a/PostFundManagement.Domain/Extensions/Quartz.cs +++ b/PostFundManagement.Domain/Extensions/Quartz.cs @@ -6,8 +6,6 @@ namespace PostFundManagement.Domain.Extensions; public static class Quartz { - public const string DefaultSchedulerName = "tech-shop"; - public static IServiceCollection AddQuartzSchedulerClient(this IServiceCollection services, string schedulerName, IConfiguration configuration) { var connectionString = configuration.GetConnectionString(DatabaseConfigName);