A vibe coded tangled fork which supports pijul.
at sl/knotmirror 86 lines 2.3 kB view raw
1package repo 2 3import ( 4 "fmt" 5 "io" 6 "net/http" 7 "net/url" 8 "strings" 9 10 "github.com/go-chi/chi/v5" 11 "tangled.org/core/api/tangled" 12) 13 14func (rp *Repo) DownloadArchive(w http.ResponseWriter, r *http.Request) { 15 l := rp.logger.With("handler", "DownloadArchive") 16 ref := chi.URLParam(r, "ref") 17 ref, _ = url.PathUnescape(ref) 18 ref = strings.TrimSuffix(ref, ".tar.gz") 19 f, err := rp.repoResolver.Resolve(r) 20 if err != nil { 21 l.Error("failed to get repo and knot", "err", err) 22 return 23 } 24 25 // build the xrpc url 26 query := url.Values{} 27 query.Set("repo", f.RepoAt().String()) 28 query.Set("ref", ref) 29 query.Set("format", "tar.gz") 30 query.Set("prefix", r.URL.Query().Get("prefix")) 31 xrpcURL := fmt.Sprintf( 32 "%s/xrpc/%s?%s", 33 rp.config.KnotMirror.Url, 34 tangled.GitTempGetArchiveNSID, 35 query.Encode(), 36 ) 37 38 // make the get request 39 resp, err := http.Get(xrpcURL) 40 if err != nil { 41 l.Error("failed to call XRPC repo.archive", "err", err) 42 rp.pages.Error503(w) 43 return 44 } 45 defer resp.Body.Close() 46 47 // pass through headers from upstream response 48 if contentDisposition := resp.Header.Get("Content-Disposition"); contentDisposition != "" { 49 w.Header().Set("Content-Disposition", contentDisposition) 50 } 51 if contentType := resp.Header.Get("Content-Type"); contentType != "" { 52 w.Header().Set("Content-Type", contentType) 53 } 54 if contentLength := resp.Header.Get("Content-Length"); contentLength != "" { 55 w.Header().Set("Content-Length", contentLength) 56 } 57 if link := resp.Header.Get("Link"); link != "" { 58 if resolvedRef, err := extractImmutableLink(link); err == nil { 59 newLink := fmt.Sprintf("<%s/%s/archive/%s.tar.gz>; rel=\"immutable\"", 60 rp.config.Core.BaseUrl(), f.DidSlashRepo(), resolvedRef) 61 w.Header().Set("Link", newLink) 62 } 63 } 64 65 // stream the archive data directly 66 if _, err := io.Copy(w, resp.Body); err != nil { 67 l.Error("failed to write response", "err", err) 68 } 69} 70 71func extractImmutableLink(linkHeader string) (string, error) { 72 trimmed := strings.TrimPrefix(linkHeader, "<") 73 trimmed = strings.TrimSuffix(trimmed, ">; rel=\"immutable\"") 74 75 parsedLink, err := url.Parse(trimmed) 76 if err != nil { 77 return "", err 78 } 79 80 resolvedRef := parsedLink.Query().Get("ref") 81 if resolvedRef == "" { 82 return "", fmt.Errorf("no ref found in link header") 83 } 84 85 return resolvedRef, nil 86}