+56
-14
internal/api/handlers.go
+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) {