FEAT-12a: POST /gerbils/sale-ad endpoint (503 AiKeyMissing when unconfigured, 502 AiUpstreamError) + DI wiring

This commit is contained in:
2026-06-06 07:37:42 +02:00
parent f6bb13ca2f
commit d9e5a757e9
2 changed files with 42 additions and 0 deletions

View File

@@ -0,0 +1,34 @@
using GerbilManagerWebAPI.SaleAd;
namespace GerbilManagerWebAPI.Endpoints
{
/// <summary>
/// FEAT-12a: POST /gerbils/sale-ad — AI-gestütztes Abgabe-Inserat (frozen
/// contract, see gerbil-manager-web/src/api/saleAd.ts).
///
/// 503 {code:"AiKeyMissing"} while the AI section is unconfigured — the
/// frontend maps exactly this code to its German disabled-state hint.
/// </summary>
public static class SaleAdEndpoints
{
public static IEndpointRouteBuilder MapSaleAdEndpoints(this IEndpointRouteBuilder app)
{
app.MapPost("/gerbils/sale-ad",
async (SaleAdRequest request, SaleAdService service, CancellationToken ct) =>
{
var result = await service.GenerateAsync(request, ct);
return result.Status switch
{
SaleAdStatus.Ok => Results.Ok(new SaleAdResponse(result.Text!)),
SaleAdStatus.NotConfigured => Results.Json(
new SaleAdError("AiKeyMissing", result.Error ?? ""), statusCode: 503),
_ => Results.Json(
new SaleAdError("AiUpstreamError", result.Error ?? ""), statusCode: 502),
};
})
.WithTags("SaleAd");
return app;
}
}
}

View File

@@ -35,6 +35,13 @@ builder.Services.AddCors(options => options.AddPolicy(LanCorsPolicy, policy =>
// OpenAPI document for the Scalar API reference (Swashbuckle removed). // OpenAPI document for the Scalar API reference (Swashbuckle removed).
builder.Services.AddOpenApi(); builder.Services.AddOpenApi();
// FEAT-12a: provider-agnostic AI config (env vars AI__BaseUrl/AI__ApiKey/AI__Model
// or user-secrets — never committed) + typed client for sale-ad generation.
builder.Services.AddOptions<GerbilManagerWebAPI.SaleAd.AiOptions>()
.BindConfiguration(GerbilManagerWebAPI.SaleAd.AiOptions.SectionName);
builder.Services.AddHttpClient<GerbilManagerWebAPI.SaleAd.SaleAdService>(
http => http.Timeout = TimeSpan.FromSeconds(60));
var app = builder.Build(); var app = builder.Build();
app.MapDefaultEndpoints(); app.MapDefaultEndpoints();
@@ -62,5 +69,6 @@ app.MapHealthRecordEndpoints();
app.MapWeightRecordEndpoints(); app.MapWeightRecordEndpoints();
app.MapInbreedingEndpoints(); app.MapInbreedingEndpoints();
app.MapPhotoEndpoints(); app.MapPhotoEndpoints();
app.MapSaleAdEndpoints();
app.Run(); app.Run();