package handlers import ( "net/http" "time" "sneak.berlin/go/webhooker/internal/database" "sneak.berlin/go/webhooker/internal/delivery" ) // HandleTargetDownload serves a database target's archive as one // gzipped JSON file, named for the webhook, the target and the time; // see delivery.ArchiveExport.WriteGzipJSON for what it holds. Other // target types have no archive and are a 404. func (h *Handlers) HandleTargetDownload() http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { webhook, target, export, ok := h.openTargetArchive(w, r) if !ok { return } defer func() { _ = export.Close() }() now := time.Now() w.Header().Set("Content-Type", "application/gzip") w.Header().Set( "Content-Disposition", `attachment; filename="`+delivery.ArchiveExportFileName( webhook.Name, target.Name, now, )+`"`, ) err := export.WriteGzipJSON(r.Context(), w, &webhook, target, now) if err != nil { // The 200 has gone out. The client is left with a file // that does not decompress; the log is the record. h.log.Error( "failed to export archive", "target_id", target.ID, "error", err, ) } } } // openTargetArchive opens the archive of the request's database target // for export. It reports false once it has written the response. // // It holds renameMu, which every archive rename runs under, while it // reads the stored names and opens the file, so the file it opens is // the one those names give. It lets go before the export is streamed: // once the file is open, a rename does not affect the export. func (h *Handlers) openTargetArchive( w http.ResponseWriter, r *http.Request, ) (database.Webhook, *database.Target, *delivery.ArchiveExport, bool) { h.renameMu.Lock() defer h.renameMu.Unlock() webhook, target, ok := h.ownedTarget(w, r) if !ok { return database.Webhook{}, nil, nil, false } if target.Type != database.TargetTypeDatabase { h.renderError(w, r, http.StatusNotFound) return database.Webhook{}, nil, nil, false } export, err := delivery.OpenArchiveExport( r.Context(), delivery.ArchivePath(h.dbMgr, &webhook, target), ) if err != nil { h.serverError(w, r, "failed to open archive for export", err) return database.Webhook{}, nil, nil, false } return webhook, target, export, true }