A vibe coded tangled fork which supports pijul.
at icy/yrzpyt 146 lines 4.2 kB view raw
1package state 2 3import ( 4 "fmt" 5 "io" 6 "net/http" 7 8 "github.com/bluesky-social/indigo/atproto/identity" 9 "github.com/go-chi/chi/v5" 10 "tangled.org/core/appview/models" 11) 12 13// allowedResponseHeaders is the set of headers we will forward from the knot 14// back to the client. everything else is stripped. 15var allowedResponseHeaders = map[string]bool{ 16 "Content-Encoding": true, 17 "Transfer-Encoding": true, 18 "Cache-Control": true, 19 "Expires": true, 20 "Pragma": true, 21} 22 23func copyAllowedHeaders(dst, src http.Header) { 24 for k, vv := range src { 25 if allowedResponseHeaders[http.CanonicalHeaderKey(k)] { 26 for _, v := range vv { 27 dst.Add(k, v) 28 } 29 } 30 } 31} 32 33func setGitHeaders(w http.ResponseWriter, contentType string) { 34 w.Header().Set("Content-Type", contentType) 35 w.Header().Set("Content-Disposition", "attachment") 36 w.Header().Set("X-Content-Type-Options", "nosniff") 37} 38 39func (s *State) InfoRefs(w http.ResponseWriter, r *http.Request) { 40 user := r.Context().Value("resolvedId").(identity.Identity) 41 repo := r.Context().Value("repo").(*models.Repo) 42 43 scheme := "https" 44 if s.config.Core.Dev { 45 scheme = "http" 46 } 47 48 // check for the 'service' url param 49 service := r.URL.Query().Get("service") 50 var contentType string 51 switch service { 52 case "git-receive-pack": 53 contentType = "application/x-git-receive-pack-advertisement" 54 default: 55 // git-upload-pack is the default service for git-clone / git-fetch. 56 contentType = "application/x-git-upload-pack-advertisement" 57 } 58 59 targetURL := fmt.Sprintf("%s://%s/%s/%s/info/refs?%s", scheme, repo.Knot, user.DID, repo.Name, r.URL.RawQuery) 60 s.proxyRequest(w, r, targetURL, contentType) 61} 62 63func (s *State) UploadArchive(w http.ResponseWriter, r *http.Request) { 64 user, ok := r.Context().Value("resolvedId").(identity.Identity) 65 if !ok { 66 http.Error(w, "failed to resolve user", http.StatusInternalServerError) 67 return 68 } 69 repo := r.Context().Value("repo").(*models.Repo) 70 71 scheme := "https" 72 if s.config.Core.Dev { 73 scheme = "http" 74 } 75 76 targetURL := fmt.Sprintf("%s://%s/%s/%s/git-upload-archive?%s", scheme, repo.Knot, user.DID, repo.Name, r.URL.RawQuery) 77 s.proxyRequest(w, r, targetURL, "application/x-git-upload-archive-result") 78} 79 80func (s *State) UploadPack(w http.ResponseWriter, r *http.Request) { 81 user, ok := r.Context().Value("resolvedId").(identity.Identity) 82 if !ok { 83 http.Error(w, "failed to resolve user", http.StatusInternalServerError) 84 return 85 } 86 repo := r.Context().Value("repo").(*models.Repo) 87 88 scheme := "https" 89 if s.config.Core.Dev { 90 scheme = "http" 91 } 92 93 targetURL := fmt.Sprintf("%s://%s/%s/%s/git-upload-pack?%s", scheme, repo.Knot, user.DID, repo.Name, r.URL.RawQuery) 94 s.proxyRequest(w, r, targetURL, "application/x-git-upload-pack-result") 95} 96 97func (s *State) ReceivePack(w http.ResponseWriter, r *http.Request) { 98 user, ok := r.Context().Value("resolvedId").(identity.Identity) 99 if !ok { 100 http.Error(w, "failed to resolve user", http.StatusInternalServerError) 101 return 102 } 103 repo := r.Context().Value("repo").(*models.Repo) 104 105 scheme := "https" 106 if s.config.Core.Dev { 107 scheme = "http" 108 } 109 110 targetURL := fmt.Sprintf("%s://%s/%s/%s/git-receive-pack?%s", scheme, repo.Knot, user.DID, repo.Name, r.URL.RawQuery) 111 s.proxyRequest(w, r, targetURL, "application/x-git-receive-pack-result") 112} 113 114func (s *State) proxyRequest(w http.ResponseWriter, r *http.Request, targetURL string, contentType string) { 115 client := &http.Client{} 116 117 proxyReq, err := http.NewRequest(r.Method, targetURL, r.Body) 118 if err != nil { 119 http.Error(w, err.Error(), http.StatusInternalServerError) 120 return 121 } 122 123 proxyReq.Header = r.Header.Clone() 124 125 repoOwnerHandle := chi.URLParam(r, "user") 126 proxyReq.Header.Set("x-tangled-repo-owner-handle", repoOwnerHandle) 127 128 resp, err := client.Do(proxyReq) 129 if err != nil { 130 http.Error(w, err.Error(), http.StatusInternalServerError) 131 return 132 } 133 defer resp.Body.Close() 134 135 // selectively copy only allowed headers 136 copyAllowedHeaders(w.Header(), resp.Header) 137 138 setGitHeaders(w, contentType) 139 140 w.WriteHeader(resp.StatusCode) 141 142 if _, err := io.Copy(w, resp.Body); err != nil { 143 http.Error(w, err.Error(), http.StatusInternalServerError) 144 return 145 } 146}