Compare commits

..

1 Commits

Author SHA1 Message Date
woikos
ea7bc75fac Fix NIP-11 caching and export streaming issues (v0.46.2)
Some checks failed
Go / build-and-release (push) Has been cancelled
- Fix Content-Type header being set on request instead of response
- Add Vary: Accept header to prevent browser/CDN caching NIP-11 for HTML
- Add periodic flushing during export for HTTP streaming (every 100 events)
- Add initial flush after headers to start streaming immediately
- Add X-Content-Type-Options: nosniff to prevent browser buffering

Files modified:
- app/handle-relayinfo.go: Fix header and add Vary: Accept
- app/server.go: Add initial flush and nosniff header for export
- pkg/database/export.go: Add periodic and final flushing for streaming

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-03 07:17:48 +01:00
4 changed files with 46 additions and 2 deletions

View File

@@ -37,7 +37,8 @@ type ExtendedRelayInfo struct {
// Informer interface implementation or predefined server configuration. It
// returns this document as a JSON response to the client.
func (s *Server) HandleRelayInfo(w http.ResponseWriter, r *http.Request) {
r.Header.Set("Content-Type", "application/json")
w.Header().Set("Content-Type", "application/json")
w.Header().Set("Vary", "Accept")
log.D.Ln("handling relay information document")
var info *relayinfo.T
nips := []relayinfo.NIP{

View File

@@ -738,6 +738,12 @@ func (s *Server) handleExport(w http.ResponseWriter, r *http.Request) {
w.Header().Set(
"Content-Disposition", "attachment; filename=\""+filename+"\"",
)
w.Header().Set("X-Content-Type-Options", "nosniff")
// Flush headers to start streaming immediately
if flusher, ok := w.(http.Flusher); ok {
flusher.Flush()
}
// Stream export
s.DB.Export(s.Ctx, w, pks...)

View File

@@ -17,6 +17,11 @@ import (
"git.mleku.dev/mleku/nostr/utils/units"
)
// Flusher interface for HTTP streaming
type flusher interface {
Flush()
}
// Export the complete database of stored events to an io.Writer in line structured minified
// JSON. Supports both legacy and compact event formats.
func (d *D) Export(c context.Context, w io.Writer, pubkeys ...[]byte) {
@@ -24,11 +29,18 @@ func (d *D) Export(c context.Context, w io.Writer, pubkeys ...[]byte) {
evB := make([]byte, 0, units.Mb)
evBuf := bytes.NewBuffer(evB)
// Get flusher for HTTP streaming if available
var f flusher
if fl, ok := w.(flusher); ok {
f = fl
}
// Performance tracking
startTime := time.Now()
var eventCount, bytesWritten int64
lastLogTime := startTime
const logInterval = 5 * time.Second
const flushInterval = 100 // Flush every N events
log.I.F("export: starting export operation")
@@ -109,6 +121,11 @@ func (d *D) Export(c context.Context, w io.Writer, pubkeys ...[]byte) {
eventCount++
ev.Free()
// Flush periodically for HTTP streaming
if f != nil && eventCount%flushInterval == 0 {
f.Flush()
}
// Progress logging every logInterval
if time.Since(lastLogTime) >= logInterval {
elapsed := time.Since(startTime)
@@ -169,6 +186,11 @@ func (d *D) Export(c context.Context, w io.Writer, pubkeys ...[]byte) {
eventCount++
ev.Free()
// Flush periodically for HTTP streaming
if f != nil && eventCount%flushInterval == 0 {
f.Flush()
}
// Progress logging every logInterval
if time.Since(lastLogTime) >= logInterval {
elapsed := time.Since(startTime)
@@ -186,6 +208,11 @@ func (d *D) Export(c context.Context, w io.Writer, pubkeys ...[]byte) {
return
}
// Final flush
if f != nil {
f.Flush()
}
// Final export summary
elapsed := time.Since(startTime)
eventsPerSec := float64(eventCount) / elapsed.Seconds()
@@ -244,6 +271,11 @@ func (d *D) Export(c context.Context, w io.Writer, pubkeys ...[]byte) {
eventCount++
ev.Free()
// Flush periodically for HTTP streaming
if f != nil && eventCount%flushInterval == 0 {
f.Flush()
}
// Progress logging every logInterval
if time.Since(lastLogTime) >= logInterval {
elapsed := time.Since(startTime)
@@ -261,6 +293,11 @@ func (d *D) Export(c context.Context, w io.Writer, pubkeys ...[]byte) {
}
}
// Final flush
if f != nil {
f.Flush()
}
// Final export summary for pubkey export
elapsed := time.Since(startTime)
eventsPerSec := float64(eventCount) / elapsed.Seconds()

View File

@@ -1 +1 @@
v0.46.1
v0.46.2