test fixes and new product advisories work

This commit is contained in:
master
2026-01-28 02:30:48 +02:00
parent 82caceba56
commit 644887997c
288 changed files with 69101 additions and 375 deletions

View File

@@ -57,24 +57,28 @@ internal static class FederationEndpointExtensions
CompressionLevel = compressLevel
};
// Set response headers for streaming
context.Response.ContentType = "application/zstd";
var exportTimestamp = timeProvider.GetUtcNow().UtcDateTime;
context.Response.Headers.ContentDisposition =
$"attachment; filename=\"feedser-bundle-{exportTimestamp.ToString("yyyyMMdd-HHmmss", CultureInfo.InvariantCulture)}.zst\"";
// Export directly to response stream
// Export to memory first so we can set headers before writing body
// (HTTP headers must be set before any body content is written)
using var bufferStream = new MemoryStream();
var result = await exportService.ExportToStreamAsync(
context.Response.Body,
bufferStream,
sinceCursor,
exportOptions,
cancellationToken);
// Add metadata headers
// Now set all response headers before writing body
context.Response.ContentType = "application/zstd";
var exportTimestamp = timeProvider.GetUtcNow().UtcDateTime;
context.Response.Headers.ContentDisposition =
$"attachment; filename=\"feedser-bundle-{exportTimestamp.ToString("yyyyMMdd-HHmmss", CultureInfo.InvariantCulture)}.zst\"";
context.Response.Headers.Append("X-Bundle-Hash", result.BundleHash);
context.Response.Headers.Append("X-Export-Cursor", result.ExportCursor);
context.Response.Headers.Append("X-Items-Count", result.Counts.Total.ToString());
// Write the buffered content to response
bufferStream.Position = 0;
await bufferStream.CopyToAsync(context.Response.Body, cancellationToken);
return HttpResults.Empty;
})
.WithName("ExportFederationBundle")

View File

@@ -542,6 +542,9 @@ app.MapConcelierMirrorEndpoints(authorityConfigured, enforceAuthority);
app.MapCanonicalAdvisoryEndpoints();
app.MapInterestScoreEndpoints();
// Federation endpoints for site-to-site bundle sync
app.MapConcelierFederationEndpoints();
app.MapGet("/.well-known/openapi", ([FromServices] OpenApiDiscoveryDocumentProvider provider, HttpContext context) =>
{
var (payload, etag) = provider.GetDocument();
@@ -3750,8 +3753,12 @@ var concelierTimelineEndpoint = app.MapGet("/obs/concelier/timeline", async (
}
var logger = loggerFactory.CreateLogger("ConcelierTimeline");
// Compute next cursor BEFORE writing any response content (headers must be set before body)
var nextCursor = startId + take;
context.Response.Headers.CacheControl = "no-store";
context.Response.Headers["X-Accel-Buffering"] = "no";
context.Response.Headers["X-Next-Cursor"] = nextCursor.ToString(CultureInfo.InvariantCulture);
context.Response.ContentType = "text/event-stream";
// SSE retry hint (5s) to encourage clients to reconnect with cursor
@@ -3784,8 +3791,6 @@ var concelierTimelineEndpoint = app.MapGet("/obs/concelier/timeline", async (
await context.Response.Body.FlushAsync(cancellationToken).ConfigureAwait(false);
var nextCursor = startId + events.Count;
context.Response.Headers["X-Next-Cursor"] = nextCursor.ToString(CultureInfo.InvariantCulture);
logger.LogInformation("obs timeline emitted {Count} events for tenant {Tenant} starting at {StartId} next {Next}", events.Count, tenant, startId, nextCursor);
return HttpResults.Empty;