Monorepo for Tangled
0

Configure Feed

Select the types of activity you want to include in your feed.

at master 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 // force application/gzip here 48 w.Header().Set("Content-Type", "application/gzip") 49 50 filename := "" 51 if cd := resp.Header.Get("Content-Disposition"); strings.HasPrefix(cd, "attachment;") { 52 filename = cd // knot has already set the attachment CD 53 } 54 if filename == "" { 55 filename = fmt.Sprintf("attachment; filename=\"%s-%s.tar.gz\"", f.Name, ref) 56 } 57 w.Header().Set("Content-Disposition", filename) 58 w.Header().Set("X-Content-Type-Options", "nosniff") 59 60 if link := resp.Header.Get("Link"); link != "" { 61 if resolvedRef, err := extractImmutableLink(link); err == nil { 62 newLink := fmt.Sprintf("<%s/%s/archive/%s.tar.gz>; rel=\"immutable\"", 63 rp.config.Core.BaseUrl(), f.RepoIdentifier(), resolvedRef) 64 w.Header().Set("Link", newLink) 65 } 66 } 67 68 // stream the archive data directly 69 if _, err := io.Copy(w, resp.Body); err != nil { 70 l.Error("failed to write response", "err", err) 71 } 72} 73 74func extractImmutableLink(linkHeader string) (string, error) { 75 trimmed := strings.TrimPrefix(linkHeader, "<") 76 trimmed = strings.TrimSuffix(trimmed, ">; rel=\"immutable\"") 77 78 parsedLink, err := url.Parse(trimmed) 79 if err != nil { 80 return "", err 81 } 82 83 resolvedRef := parsedLink.Query().Get("ref") 84 if resolvedRef == "" { 85 return "", fmt.Errorf("no ref found in link header") 86 } 87 88 return resolvedRef, nil 89}