Files
pfm/PostFundManagement.Api/Program.cs
T

222 lines
7.8 KiB
C#

using PostFundManagement.Application.Extensions;
using PostFundManagement.Application.Filters;
using PostFundManagement.Domain.Abstractions;
using PostFundManagement.Domain.Health;
using PostFundManagement.Domain.Mediator;
using PostFundManagement.Infrastructure.Database.Services;
using static PostFundManagement.Domain.Extensions.Constants;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddFeatureManagement();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddApiVersioning(options =>
{
options.DefaultApiVersion = new ApiVersion(1, 0);
options.AssumeDefaultVersionWhenUnspecified = true;
options.ApiVersionReader = ApiVersionReader.Combine(
new UrlSegmentApiVersionReader(),
new HeaderApiVersionReader("x-api-version"),
new MediaTypeApiVersionReader("version")
);
})
.AddApiExplorer(options =>
{
options.GroupNameFormat = "'v'VVV";
options.SubstituteApiVersionInUrl = true;
});
builder.Services.AddScoped<ActiveUserFilter>();
builder.Services.AddScoped<UserRoleFilter>();
builder.Services.AddScoped<ITenantContextService, TenantContextService>();
builder.Services.AddEndpoints(Assembly.GetExecutingAssembly());
builder.Services.AddApiServices(builder.Configuration);
builder.Services.AddSharedServices();
builder.Services.AddRegisterServicesWithCaching(builder.Configuration);
builder.Services.AddEndpointAndServiceCaching(builder.Configuration);
builder.Services.AddMediator();
builder.Services.AddSecurityApiSdk(builder.Configuration);
builder.Services.AddApiSecurity(builder.Configuration);
builder.Services.AddSkorubaApiService(builder.Configuration);
builder.Services.AddScoped(typeof(IPipelineBehavior<,>), typeof(TelemetryPipelineBehavior<,>));
builder.Services.AddScoped(typeof(IPipelineBehavior<,>), typeof(LoggingPipelineBehavior<,>));
builder.Services.AddQuartzScheduler(DefaultSchedulerName, builder.Configuration);
builder.Services.AddEmailServices(builder.Configuration);
builder.Services.AddHashServices(builder.Configuration);
builder.Services.AddGarageS3(builder.Configuration);
builder.Services.AddHttpClient();
builder.Services.AddDataProtectionDatabase(builder.Configuration);
builder.Services.AddApplicationDbContext(builder.Configuration);
builder.Services.AddHealthChecks()
.AddCheck<QuartzHealthCheck>("QuartzScheduler")
.AddCheck<PostgresHealthCheck>("PostgresDatabase")
.AddCheck("Self", () => HealthCheckResult.Healthy());
IEnumerable<string> identityScopes = ["openid", "profile", "roles", "email", "address"];
builder.Services.AddValidation()
.AddEndpointValidation();
builder.Services.AddOpenApi(options =>
{
options.AddDocumentTransformer((document, context, cancellationToken) =>
{
document.Components ??= new OpenApiComponents();
document.Components?.SecuritySchemes!["OAuth2"] = new OpenApiSecurityScheme
{
Type = SecuritySchemeType.OAuth2,
Description = "Interactive OAuth2 flow for authentication and authorization",
Flows = new OpenApiOAuthFlows
{
AuthorizationCode = new OpenApiOAuthFlow
{
AuthorizationUrl = new Uri($"{builder.Configuration["SecuritySettings:Authority"]}/connect/authorize"),
TokenUrl = new Uri($"{builder.Configuration["SecuritySettings:Authority"]}/connect/token"),
Scopes = new Dictionary<string, string>
{
{ "openid", "OpenID Connect" },
{ "profile", "User Profile" },
{ "roles", "User Roles" },
{ "email", "User Email" },
{ "address", "User Address" },
{ builder.Configuration["SecuritySettings:Audience"]!, "PFM API Scope" }
}
}
}
};
return Task.CompletedTask;
});
options.AddOperationTransformer((operation, context, cancellationToken) =>
{
operation.Security ??= [];
var schemeReference = new OpenApiSecuritySchemeReference("OAuth2");
operation.Security.Add(new OpenApiSecurityRequirement
{
[schemeReference] = [.. identityScopes, builder.Configuration["SecuritySettings:Audience"]!]
});
return Task.CompletedTask;
});
});
var allowedOrigins = builder.Configuration.GetSection("CorsOrigins").Get<string[]>() ?? [];
builder.Services.AddCors(options =>
{
options.AddPolicy(CorsPolicy, policy =>
{
policy.WithOrigins(allowedOrigins)
.AllowAnyHeader()
.AllowAnyMethod()
.AllowCredentials();
});
});
var app = builder.Build();
var schedulerFactory = app.Services.GetRequiredService<ISchedulerFactory>();
var scheduler = await schedulerFactory.GetScheduler();
if (scheduler!.Status != SchedulerStatus.Running)
await scheduler.Start();
app.UseHsts();
app.UseHttpsRedirection();
app.UseRouting();
app.UseCors(CorsPolicy);
app.UseOutputCache();
app.UseAuthentication();
app.UseAuthorization();
ApiVersionSet versionSet = app.NewApiVersionSet("v1")
.HasApiVersion(new ApiVersion(1))
.ReportApiVersions()
.Build();
var versionGroups = new Dictionary<int, RouteGroupBuilder>
{
{
1, app.MapGroup("v{version:apiVersion}").WithApiVersionSet(versionSet)
.RequireCors(CorsPolicy)
.AddEndpointFilter(async (context, next) =>
{
if (HttpMethods.IsOptions(context.HttpContext.Request.Method))
return await next(context);
var filter = context.HttpContext.RequestServices.GetRequiredService<ActiveUserFilter>();
return await filter.InvokeAsync(context, next);
})
.AddEndpointFilter(async (context, next) =>
{
if (HttpMethods.IsOptions(context.HttpContext.Request.Method))
return await next(context);
var filter = context.HttpContext.RequestServices.GetRequiredService<UserRoleFilter>();
return await filter.InvokeAsync(context, next);
})
}
};
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.AddPreferredSecuritySchemes("OAuth2")
.AddOAuth2Flows("OAuth2", flow =>
{
flow.AuthorizationCode = new AuthorizationCodeFlow
{
ClientId = builder.Configuration["SecuritySettings:ClientId"]!,
Pkce = Pkce.Sha256,
SelectedScopes = [.. identityScopes, builder.Configuration["SecuritySettings:Audience"]!]
};
});
});
}
var latestVersionGroup = apiVersions.FirstOrDefault()?.GroupName ?? "v1";
app.MapGet("/", () => Results.Redirect($"/openapi/{latestVersionGroup}"))
.ExcludeFromDescription();
if (!app.Environment.IsDevelopment())
app.UseExceptionHandler("/Error", createScopeForErrors: true);
app.Run();