first
This commit is contained in:
+106
-12
@@ -1,22 +1,116 @@
|
||||
using System.Text.Json.Serialization;
|
||||
using Microsoft.OpenApi.Models;
|
||||
using Csob.Client;
|
||||
using Csob.Configuration;
|
||||
using Csob.Credentials;
|
||||
using Csob.Infrastructure;
|
||||
using Csob.Services;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
// Configuration resolved from environment variables (non-secret infra config only).
|
||||
var settings = new CsobSettings();
|
||||
builder.Services.AddSingleton(settings);
|
||||
|
||||
// The eIDAS client certificate is supplied per request as a Base64 PFX header, which is large.
|
||||
// Raise Kestrel's header size limit so the certificate header is not rejected.
|
||||
builder.WebHost.ConfigureKestrel(options =>
|
||||
{
|
||||
options.Limits.MaxRequestHeadersTotalSize = 1024 * 1024; // 1 MiB
|
||||
});
|
||||
|
||||
builder.Services.AddHttpContextAccessor();
|
||||
|
||||
// Builds/caches mutual-TLS HttpClients by certificate thumbprint; shared across requests.
|
||||
builder.Services.AddSingleton<CsobHttpClientProvider>();
|
||||
|
||||
// Per-request credential resolution and API access.
|
||||
builder.Services.AddScoped<RequestCredentialsProvider>();
|
||||
builder.Services.AddScoped<CsobApiAccessor>();
|
||||
|
||||
// Agenda services.
|
||||
builder.Services.AddScoped<AccountsService>();
|
||||
builder.Services.AddScoped<PaymentsService>();
|
||||
builder.Services.AddScoped<StandingOrdersService>();
|
||||
builder.Services.AddScoped<DirectDebitsService>();
|
||||
builder.Services.AddScoped<ConsentsService>();
|
||||
builder.Services.AddScoped<OAuthService>();
|
||||
|
||||
builder.Services
|
||||
.AddControllers()
|
||||
.AddJsonOptions(options =>
|
||||
{
|
||||
// camelCase (web defaults) matches the COBS JSON contract; omit null properties on write.
|
||||
options.JsonSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull;
|
||||
});
|
||||
|
||||
builder.Services.AddEndpointsApiExplorer();
|
||||
builder.Services.AddSwaggerGen(options =>
|
||||
{
|
||||
options.SwaggerDoc("v1", new OpenApiInfo
|
||||
{
|
||||
Title = settings.AppName,
|
||||
Version = settings.AppVersion,
|
||||
Description =
|
||||
"Multi-tenant REST integration with the ČSOB PSD2 (Open Banking) API, following the " +
|
||||
"Czech Open Banking Standard (COBS). Covers AISP (account information), PISP (payment, " +
|
||||
"standing-order and direct-debit initiation with the sign/SCA flow), consents and the " +
|
||||
"OAuth2 Authorization Code helper.\n\n" +
|
||||
"**Credentials.** This service stores no secrets — every sensitive value is supplied per " +
|
||||
"request in a header and forwarded to ČSOB over TLS (never the query string or body):\n\n" +
|
||||
"- `X-CSOB-Certificate` — eIDAS client certificate (QWAC) as a Base64 PFX (mutual TLS). Required.\n" +
|
||||
"- `X-CSOB-Certificate-Password` — PFX passphrase (optional).\n" +
|
||||
"- `X-Access-Token` — OAuth2 Bearer access token for the PSU (AISP/PISP/consents). Required.\n" +
|
||||
"- `X-API-Key` — ČSOB application API key (sent upstream as `APIKEY`). Required.\n" +
|
||||
"- `X-TPP-Name` — TPP organisation name (sent upstream as `TPP-Name`). Required.\n" +
|
||||
"- `X-CSOB-Client-Id` / `X-CSOB-Client-Secret` — OAuth2 app credentials (OAuth endpoints only).\n\n" +
|
||||
"Obtain the access token via the `/oauth/*` endpoints (Authorization Code flow with PSU redirect).",
|
||||
});
|
||||
options.OperationFilter<CredentialHeadersOperationFilter>();
|
||||
|
||||
var xmlPath = Path.Combine(AppContext.BaseDirectory, $"{System.Reflection.Assembly.GetExecutingAssembly().GetName().Name}.xml");
|
||||
if (File.Exists(xmlPath))
|
||||
{
|
||||
options.IncludeXmlComments(xmlPath, includeControllerXmlComments: true);
|
||||
}
|
||||
});
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
var rootPath = Environment.GetEnvironmentVariable("ROOT_PATH");
|
||||
if (!string.IsNullOrWhiteSpace(rootPath))
|
||||
// ROOT_PATH carries the public reverse-proxy prefix (e.g. /apps/csob). AppFactory's Caddy uses
|
||||
// handle_path, which strips that prefix before the request reaches the container, so PathBase ends
|
||||
// up empty and the OpenAPI server URL must come from ROOT_PATH directly (see the filter below).
|
||||
var publicPrefix = string.IsNullOrWhiteSpace(settings.RootPath) ? null : "/" + settings.RootPath.Trim('/');
|
||||
|
||||
if (publicPrefix is not null)
|
||||
{
|
||||
app.UsePathBase(rootPath);
|
||||
app.UsePathBase(publicPrefix);
|
||||
}
|
||||
|
||||
app.MapGet("/", () => Results.Json(new
|
||||
{
|
||||
name = "csob",
|
||||
service = "csob",
|
||||
status = "ok"
|
||||
}));
|
||||
app.UseMiddleware<ExceptionHandlingMiddleware>();
|
||||
|
||||
app.MapGet("/health", () => Results.Json(new
|
||||
// Serve the OpenAPI document under the same /docs prefix as the UI so a relative endpoint resolves
|
||||
// correctly both locally and behind a reverse-proxy prefix (ROOT_PATH).
|
||||
app.UseSwagger(options =>
|
||||
{
|
||||
status = "ok"
|
||||
}));
|
||||
options.RouteTemplate = "docs/{documentName}/swagger.json";
|
||||
// Advertise the public prefix (e.g. /apps/csob) as the OpenAPI server so Swagger UI "Try it out"
|
||||
// targets {prefix}/accounts, not the host root. The prefix comes from ROOT_PATH because
|
||||
// handle_path has already stripped it from the request, leaving PathBase empty. Fall back to
|
||||
// PathBase (a proxy that keeps the prefix) and finally "/".
|
||||
options.PreSerializeFilters.Add((swaggerDoc, httpReq) =>
|
||||
{
|
||||
var serverUrl = publicPrefix ?? (httpReq.PathBase.HasValue ? httpReq.PathBase.Value : "/");
|
||||
swaggerDoc.Servers = new List<OpenApiServer> { new() { Url = serverUrl } };
|
||||
});
|
||||
});
|
||||
app.UseSwaggerUI(options =>
|
||||
{
|
||||
options.RoutePrefix = "docs";
|
||||
options.SwaggerEndpoint("v1/swagger.json", $"{settings.AppName} v1");
|
||||
options.DocumentTitle = $"{settings.AppName} – API docs";
|
||||
});
|
||||
|
||||
app.MapControllers();
|
||||
|
||||
app.Run();
|
||||
|
||||
Reference in New Issue
Block a user