using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; using System.Globalization; namespace StellaOps.Provcache.Api; public static partial class ProvcacheEndpointExtensions { /// /// GET /v1/provcache/proofs/{proofRoot} /// private static async Task GetEvidenceChunksAsync( string proofRoot, int? offset, int? limit, bool? includeData, [FromServices] IEvidenceChunkRepository chunkRepository, ILogger logger, CancellationToken cancellationToken) { logger.LogDebug("GET /v1/provcache/proofs/{ProofRoot} offset={Offset} limit={Limit}", proofRoot, offset, limit); try { if (offset is < 0) { return BadRequest("Offset must be zero or greater.", "Invalid pagination"); } if (limit is <= 0) { return BadRequest("Limit must be greater than zero.", "Invalid pagination"); } var startIndex = offset ?? 0; var pageSize = Math.Min(limit ?? DefaultPageSize, MaxPageSize); // Get manifest for total count var manifest = await chunkRepository.GetManifestAsync(proofRoot, cancellationToken); if (manifest is null) { return Results.NotFound(); } if (startIndex >= manifest.TotalChunks) { return Results.Ok(new ProofEvidenceResponse { ProofRoot = proofRoot, TotalChunks = manifest.TotalChunks, TotalSize = manifest.TotalSize, Chunks = [], NextCursor = null, HasMore = false }); } // Get chunk range var chunks = await chunkRepository.GetChunkRangeAsync(proofRoot, startIndex, pageSize, cancellationToken); var chunkResponses = chunks .OrderBy(c => c.ChunkIndex) .Select(c => new ProofChunkResponse { ChunkId = c.ChunkId, Index = c.ChunkIndex, Hash = c.ChunkHash, Size = c.BlobSize, ContentType = c.ContentType, Data = includeData == true ? Convert.ToBase64String(c.Blob) : null }).ToList(); var hasMore = startIndex + chunks.Count < manifest.TotalChunks; var nextCursor = hasMore ? (startIndex + pageSize).ToString(CultureInfo.InvariantCulture) : null; return Results.Ok(new ProofEvidenceResponse { ProofRoot = proofRoot, TotalChunks = manifest.TotalChunks, TotalSize = manifest.TotalSize, Chunks = chunkResponses, NextCursor = nextCursor, HasMore = hasMore }); } catch (Exception ex) { logger.LogError(ex, "Error getting evidence chunks for proof root {ProofRoot}", proofRoot); return InternalError("Evidence retrieval failed"); } } }