update

Changed files
+56 -14
internal
+56 -14
internal/api/handlers.go
··· 261 261 return 262 262 } 263 263 264 + // Check if client wants uncompressed data 265 + compressed := true 266 + if r.URL.Query().Get("compressed") == "false" { 267 + compressed = false 268 + } 269 + 264 270 // Verify bundle exists in database 265 271 bundle, err := s.db.GetBundleByNumber(ctx, bundleNumber) 266 272 if err != nil { ··· 282 288 return 283 289 } 284 290 285 - // Open file 286 - file, err := os.Open(filePath) 287 - if err != nil { 288 - http.Error(w, fmt.Sprintf("error opening bundle file: %v", err), http.StatusInternalServerError) 289 - return 290 - } 291 - defer file.Close() 292 - 293 - // Set headers 294 - w.Header().Set("Content-Type", "application/zstd") 295 - w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=%06d.jsonl.zst", bundleNumber)) 296 - w.Header().Set("Content-Length", fmt.Sprintf("%d", fileInfo.Size())) 291 + // Set common headers 297 292 w.Header().Set("X-Bundle-Number", fmt.Sprintf("%d", bundleNumber)) 298 293 w.Header().Set("X-Bundle-Hash", bundle.Hash) 299 294 w.Header().Set("X-Bundle-Compressed-Hash", bundle.CompressedHash) ··· 302 297 w.Header().Set("X-Bundle-Operation-Count", fmt.Sprintf("%d", plc.BUNDLE_SIZE)) 303 298 w.Header().Set("X-Bundle-DID-Count", fmt.Sprintf("%d", len(bundle.DIDs))) 304 299 305 - // Stream the file 306 - http.ServeContent(w, r, filepath.Base(filePath), bundle.CreatedAt, file) 300 + if compressed { 301 + // Serve compressed file 302 + file, err := os.Open(filePath) 303 + if err != nil { 304 + http.Error(w, fmt.Sprintf("error opening bundle file: %v", err), http.StatusInternalServerError) 305 + return 306 + } 307 + defer file.Close() 308 + 309 + w.Header().Set("Content-Type", "application/zstd") 310 + w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=%06d.jsonl.zst", bundleNumber)) 311 + w.Header().Set("Content-Length", fmt.Sprintf("%d", fileInfo.Size())) 312 + w.Header().Set("X-Compressed-Size", fmt.Sprintf("%d", fileInfo.Size())) 313 + 314 + http.ServeContent(w, r, filepath.Base(filePath), bundle.CreatedAt, file) 315 + } else { 316 + // Serve uncompressed data 317 + compressedData, err := os.ReadFile(filePath) 318 + if err != nil { 319 + http.Error(w, fmt.Sprintf("error reading bundle file: %v", err), http.StatusInternalServerError) 320 + return 321 + } 322 + 323 + // Decompress 324 + decoder, err := zstd.NewReader(nil) 325 + if err != nil { 326 + http.Error(w, fmt.Sprintf("error creating decompressor: %v", err), http.StatusInternalServerError) 327 + return 328 + } 329 + defer decoder.Close() 330 + 331 + decompressed, err := decoder.DecodeAll(compressedData, nil) 332 + if err != nil { 333 + http.Error(w, fmt.Sprintf("error decompressing bundle: %v", err), http.StatusInternalServerError) 334 + return 335 + } 336 + 337 + // Set headers for uncompressed data 338 + w.Header().Set("Content-Type", "application/jsonl") 339 + w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=%06d.jsonl", bundleNumber)) 340 + w.Header().Set("Content-Length", fmt.Sprintf("%d", len(decompressed))) 341 + w.Header().Set("X-Compressed-Size", fmt.Sprintf("%d", fileInfo.Size())) 342 + w.Header().Set("X-Uncompressed-Size", fmt.Sprintf("%d", len(decompressed))) 343 + w.Header().Set("X-Compression-Ratio", fmt.Sprintf("%.2f", float64(len(decompressed))/float64(fileInfo.Size()))) 344 + 345 + // Write decompressed data 346 + w.WriteHeader(http.StatusOK) 347 + w.Write(decompressed) 348 + } 307 349 } 308 350 309 351 func (s *Server) handleGetMempoolStats(w http.ResponseWriter, r *http.Request) {