···
10
+
"github.com/bluesky-social/indigo/atproto/identity"
11
+
indigoxrpc "github.com/bluesky-social/indigo/xrpc"
12
+
"github.com/go-chi/chi/v5"
13
+
"github.com/go-git/go-git/v5/plumbing"
14
+
"github.com/hashicorp/go-version"
15
+
"tangled.org/core/api/tangled"
16
+
"tangled.org/core/appview/models"
17
+
xrpcclient "tangled.org/core/appview/xrpcclient"
20
+
func (s *State) InfoRefs(w http.ResponseWriter, r *http.Request) {
21
+
user := r.Context().Value("resolvedId").(identity.Identity)
22
+
repo := r.Context().Value("repo").(*models.Repo)
25
+
if s.config.Core.Dev {
29
+
targetURL := fmt.Sprintf("%s://%s/%s/%s/info/refs?%s", scheme, repo.Knot, user.DID, repo.Name, r.URL.RawQuery)
30
+
s.proxyRequest(w, r, targetURL)
34
+
func (s *State) UploadPack(w http.ResponseWriter, r *http.Request) {
35
+
user, ok := r.Context().Value("resolvedId").(identity.Identity)
37
+
http.Error(w, "failed to resolve user", http.StatusInternalServerError)
40
+
repo := r.Context().Value("repo").(*models.Repo)
43
+
if s.config.Core.Dev {
47
+
targetURL := fmt.Sprintf("%s://%s/%s/%s/git-upload-pack?%s", scheme, repo.Knot, user.DID, repo.Name, r.URL.RawQuery)
48
+
s.proxyRequest(w, r, targetURL)
51
+
func (s *State) ReceivePack(w http.ResponseWriter, r *http.Request) {
52
+
user, ok := r.Context().Value("resolvedId").(identity.Identity)
54
+
http.Error(w, "failed to resolve user", http.StatusInternalServerError)
57
+
repo := r.Context().Value("repo").(*models.Repo)
60
+
if s.config.Core.Dev {
64
+
targetURL := fmt.Sprintf("%s://%s/%s/%s/git-receive-pack?%s", scheme, repo.Knot, user.DID, repo.Name, r.URL.RawQuery)
65
+
s.proxyRequest(w, r, targetURL)
68
+
var knotVersionDownloadArchiveConstraint = version.MustConstraints(version.NewConstraint(">= 1.12.0-alpha"))
70
+
func (s *State) DownloadArchive(w http.ResponseWriter, r *http.Request) {
71
+
l := s.logger.With("handler", "DownloadArchive")
72
+
ref := chi.URLParam(r, "ref")
74
+
user, ok := r.Context().Value("resolvedId").(identity.Identity)
76
+
l.Error("failed to resolve user")
77
+
http.Error(w, "failed to resolve user", http.StatusInternalServerError)
80
+
repo := r.Context().Value("repo").(*models.Repo)
83
+
if s.config.Core.Dev {
87
+
host := fmt.Sprintf("%s://%s", scheme, repo.Knot)
88
+
xrpcc := &indigoxrpc.Client{
91
+
l = l.With("knot", repo.Knot)
93
+
isCompatible := func() bool {
94
+
out, err := tangled.KnotVersion(r.Context(), xrpcc)
96
+
l.Warn("failed to get knot version", "err", err)
100
+
v, err := version.NewVersion(out.Version)
102
+
l.Warn("failed to parse knot version", "version", out.Version, "err", err)
106
+
if !knotVersionDownloadArchiveConstraint.Check(v) {
107
+
l.Warn("knot version incompatible.", "version", v)
112
+
l.Debug("knot compatibility check", "isCompatible", isCompatible)
114
+
targetURL := fmt.Sprintf("%s://%s/%s/%s/archive/%s", scheme, repo.Knot, user.DID, repo.Name, ref)
115
+
s.proxyRequest(w, r, targetURL)
117
+
l.Debug("requesting xrpc/sh.tangled.repo.archive")
118
+
archiveBytes, err := tangled.RepoArchive(r.Context(), xrpcc, "tar.gz", "", ref, repo.DidSlashRepo())
119
+
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
120
+
l.Error("failed to call XRPC repo.archive", "err", xrpcerr)
121
+
s.pages.Error503(w)
124
+
safeRefFilename := strings.ReplaceAll(plumbing.ReferenceName(ref).Short(), "/", "-")
125
+
filename := fmt.Sprintf("%s-%s.tar.gz", repo.Name, safeRefFilename)
126
+
w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", filename))
127
+
w.Header().Set("Content-Type", "application/gzip")
128
+
w.Header().Set("Content-Length", fmt.Sprintf("%d", len(archiveBytes)))
129
+
w.Write(archiveBytes)
133
+
func (s *State) proxyRequest(w http.ResponseWriter, r *http.Request, targetURL string) {
134
+
client := &http.Client{}
136
+
// Create new request
137
+
proxyReq, err := http.NewRequest(r.Method, targetURL, r.Body)
139
+
http.Error(w, err.Error(), http.StatusInternalServerError)
143
+
// Copy original headers
144
+
proxyReq.Header = r.Header
146
+
repoOwnerHandle := chi.URLParam(r, "user")
147
+
proxyReq.Header.Add("x-tangled-repo-owner-handle", repoOwnerHandle)
150
+
resp, err := client.Do(proxyReq)
152
+
http.Error(w, err.Error(), http.StatusInternalServerError)
155
+
defer resp.Body.Close()
157
+
// Copy response headers
158
+
maps.Copy(w.Header(), resp.Header)
160
+
// Set response status code
161
+
w.WriteHeader(resp.StatusCode)
163
+
// Copy response body
164
+
if _, err := io.Copy(w, resp.Body); err != nil {
165
+
http.Error(w, err.Error(), http.StatusInternalServerError)