A vibe coded tangled fork which supports pijul.
at master 115 lines 3.5 kB view raw
1package xrpc 2 3import ( 4 "compress/gzip" 5 "fmt" 6 "net/http" 7 "net/url" 8 "strings" 9 10 "github.com/bluesky-social/indigo/atproto/atclient" 11 "github.com/bluesky-social/indigo/atproto/syntax" 12 "github.com/go-git/go-git/v5/plumbing" 13 "tangled.org/core/api/tangled" 14 "tangled.org/core/knotmirror/db" 15 "tangled.org/core/knotserver/git" 16) 17 18func (x *Xrpc) GetArchive(w http.ResponseWriter, r *http.Request) { 19 var ( 20 repoQuery = r.URL.Query().Get("repo") 21 ref = r.URL.Query().Get("ref") 22 format = r.URL.Query().Get("format") 23 prefix = r.URL.Query().Get("prefix") 24 ) 25 26 repo, err := syntax.ParseATURI(repoQuery) 27 if err != nil || repo.RecordKey() == "" { 28 writeJson(w, http.StatusBadRequest, atclient.ErrorBody{Name: "BadRequest", Message: fmt.Sprintf("repo parameter invalid: %s", repoQuery)}) 29 return 30 } 31 32 if format != "tar.gz" { 33 writeJson(w, http.StatusBadRequest, atclient.ErrorBody{Name: "BadRequest", Message: "only tar.gz format is supported"}) 34 return 35 } 36 if format == "" { 37 format = "tar.gz" 38 } 39 40 l := x.logger.With("repo", repo, "ref", ref, "format", format, "prefix", prefix) 41 ctx := r.Context() 42 43 repoPath, err := x.makeRepoPath(ctx, repo) 44 if err != nil { 45 l.Warn("local mirror failed, trying proxy", "err", err) 46 if x.proxyToKnot(w, r, repo) { 47 return 48 } 49 writeJson(w, http.StatusInternalServerError, atclient.ErrorBody{Name: "InternalServerError", Message: "failed to resolve repo"}) 50 return 51 } 52 53 gr, err := git.Open(repoPath, ref) 54 if err != nil { 55 l.Warn("local mirror failed, trying proxy", "err", err) 56 if x.proxyToKnot(w, r, repo) { 57 return 58 } 59 writeJson(w, http.StatusInternalServerError, atclient.ErrorBody{Name: "InternalServerError", Message: "failed to open git repo"}) 60 return 61 } 62 63 repoName, err := func() (string, error) { 64 r, err := db.GetRepoByAtUri(ctx, x.db, repo) 65 if err != nil { 66 return "", err 67 } 68 if r == nil { 69 return "", fmt.Errorf("repo not found: %s", repo) 70 } 71 return r.Name, nil 72 }() 73 if err != nil { 74 l.Warn("local mirror failed, trying proxy", "err", err) 75 if x.proxyToKnot(w, r, repo) { 76 return 77 } 78 writeJson(w, http.StatusInternalServerError, atclient.ErrorBody{Name: "InternalServerError", Message: "failed to retrieve repo name"}) 79 return 80 } 81 82 safeRefFilename := strings.ReplaceAll(plumbing.ReferenceName(ref).Short(), "/", "-") 83 immutableLink := func() string { 84 params := url.Values{} 85 params.Set("repo", repo.String()) 86 params.Set("ref", gr.Hash().String()) 87 params.Set("format", format) 88 params.Set("prefix", prefix) 89 return fmt.Sprintf("%s/xrpc/%s?%s", x.cfg.BaseUrl(), tangled.GitTempGetArchiveNSID, params.Encode()) 90 }() 91 92 filename := fmt.Sprintf("%s-%s.tar.gz", repoName, safeRefFilename) 93 w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", filename)) 94 w.Header().Set("Content-Type", "application/gzip") 95 w.Header().Set("Link", fmt.Sprintf("<%s>; rel=\"immutable\"", immutableLink)) 96 97 gw := gzip.NewWriter(w) 98 defer gw.Close() 99 100 if err := gr.WriteTar(gw, prefix); err != nil { 101 // once we start writing to the body we can't report error anymore 102 // so we are only left with logging the error 103 l.Error("writing tar file", "err", err.Error()) 104 w.WriteHeader(http.StatusInternalServerError) 105 return 106 } 107 108 if err := gw.Flush(); err != nil { 109 // once we start writing to the body we can't report error anymore 110 // so we are only left with logging the error 111 l.Error("flushing", "err", err.Error()) 112 w.WriteHeader(http.StatusInternalServerError) 113 return 114 } 115}