Added api boostrap logic

This commit is contained in:
2026-08-20 17:45:27 +02:00
parent 8f7af90344
commit 77517579e3
6 changed files with 213 additions and 25 deletions
@@ -8,18 +8,96 @@
<AssemblyOriginatorKeyFile>..\PostFundManagement.snk</AssemblyOriginatorKeyFile>
</PropertyGroup>
<!-- Security (IODC)-->
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="10.0.11" />
<PackageReference Include="Microsoft.OpenApi" Version="2.12.0" />
<PackageReference Include="Scalar.AspNetCore" Version="2.16.20" />
</ItemGroup>
<!-- Global Usings -->
<ItemGroup>
<Using Include="System.Security.Cryptography.X509Certificates" />
<PackageReference Include="IdentityModel.AspNetCore" Version="4.3.0" />
<PackageReference Include="IdentityModel.AspNetCore.OAuth2introspection" Version="6.2.0" />
<PackageReference Include="IdentityServer4.AccessTokenValidation" Version="3.0.1" />
<PackageReference Include="IdentityModel" Version="6.2.0" />
<PackageReference Include="KubernetesClient" Version="19.0.2" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.Certificate" Version="10.0.11" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="10.0.11" />
<Using Include="Microsoft.AspNetCore.Authentication" />
<Using Include="Microsoft.AspNetCore.Authentication.Cookies" />
<Using Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" />
</ItemGroup>
<!-- Health Checks -->
<ItemGroup>
<PackageReference Include="AspNetCore.HealthChecks.UI" Version="9.0.0" />
<PackageReference Include="AspNetCore.HealthChecks.UI.Client" Version="9.0.0" />
<PackageReference Include="AspNetCore.HealthChecks.UI.InMemory.Storage" Version="9.0.0" />
<PackageReference Include="AspNetCore.HealthChecks.NpgSql" Version="9.0.0" />
</ItemGroup>
<!-- API Versioning -->
<ItemGroup>
<PackageReference Include="AccessTokenClient.Extensions" Version="5.1.0" />
<PackageReference Include="Asp.Versioning.Abstractions" Version="10.2.1" />
<PackageReference Include="Asp.Versioning.Http" Version="10.2.2" />
<PackageReference Include="Asp.Versioning.Mvc.ApiExplorer" Version="10.2.1" />
<Using Include="Asp.Versioning" />
<Using Include="Asp.Versioning.Builder" />
</ItemGroup>
<!-- API Documentation -->
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="10.0.11" />
<PackageReference Include="Scalar.AspNetCore" Version="2.16.20" />
<Using Include="Scalar.AspNetCore" />
<Using Include="Microsoft.OpenApi" />
<Using Include="Microsoft.AspNetCore.OpenApi" />
</ItemGroup>
<!-- file nesting -->
<ItemGroup>
<ProjectCapability Include="ConfigurableFileNesting" />
<ProjectCapability Include="ConfigurableFileNestingFeatureEnabled" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="LiteCharms.Features" Version="1.137.0" />
</ItemGroup>
<!-- UI -->
<ItemGroup>
<PackageReference Include="ANM.Blazored.Toast" Version="0.1.1" />
<PackageReference Include="LiteCharms.Features.MidrandBooks" Version="1.137.0" />
<!-- Global Usings -->
<Using Include="Blazored.Toast.Services" />
</ItemGroup>
<!-- CQRS -->
<ItemGroup>
<PackageReference Include="Mediator.SourceGenerator" Version="3.0.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<!-- Global Usings -->
<Using Include="FluentResults" />
<Using Include="Mediator" />
<Using Include="Quartz" />
</ItemGroup>
<!-- Health Checks -->
<ItemGroup>
<Using Include="Microsoft.AspNetCore.Diagnostics.HealthChecks" />
</ItemGroup>
<!-- Shared Global Usings -->
<ItemGroup>
<Using Include="System.Web" />
<Using Include="System.Diagnostics" />
<Using Include="System.Reflection" />
<Using Include="Microsoft.AspNetCore.Mvc" />
<Using Include="System.ComponentModel.DataAnnotations" />
<Using Include="Microsoft.Extensions.DependencyInjection.Extensions" />
<Using Include="System.Security.Cryptography.X509Certificates" />
<Using Include="Microsoft.AspNetCore.DataProtection" />
<Using Include="Refit" />
</ItemGroup>
+90 -13
View File
@@ -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<ISchedulerFactory>();
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<int, RouteGroupBuilder>
{
{ 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();
+12 -2
View File
@@ -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": "*"
}
}
@@ -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";
@@ -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<SmtpSettings>(configuration.GetSection("Email"));
services.AddSingleton<EmailService>();
services.AddOpenTelemetry()
.WithTracing(tracing => tracing.AddSource("Pfm.EmailService"))
.WithMetrics(metrics => metrics.AddMeter("Pfm.EmailService"));
return services;
}
}
@@ -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);