diff --git a/appview/state/git_http.go b/appview/state/git_http.go new file mode 100644 index 00000000..321b1372 --- /dev/null +++ b/appview/state/git_http.go @@ -0,0 +1,85 @@ +package state + +import ( + "fmt" + "io" + "net/http" + + "github.com/bluesky-social/indigo/atproto/identity" + "github.com/go-chi/chi/v5" +) + +func (s *State) InfoRefs(w http.ResponseWriter, r *http.Request) { + user := r.Context().Value("resolvedId").(identity.Identity) + knot := r.Context().Value("knot").(string) + repo := chi.URLParam(r, "repo") + + // TODO: make this https for production! + targetURL := fmt.Sprintf("http://%s/%s/%s/info/refs?%s", knot, user.DID, repo, r.URL.RawQuery) + resp, err := http.Get(targetURL) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + defer resp.Body.Close() + + // Copy response headers + for k, v := range resp.Header { + w.Header()[k] = v + } + + // Set response status code + w.WriteHeader(resp.StatusCode) + + // Copy response body + if _, err := io.Copy(w, resp.Body); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + +} + +func (s *State) UploadPack(w http.ResponseWriter, r *http.Request) { + user, ok := r.Context().Value("resolvedId").(identity.Identity) + if !ok { + http.Error(w, "failed to resolve user", http.StatusInternalServerError) + return + } + knot := r.Context().Value("knot").(string) + repo := chi.URLParam(r, "repo") + // TODO: make this https for production! + targetURL := fmt.Sprintf("http://%s/%s/%s/git-upload-pack?%s", knot, user.DID, repo, r.URL.RawQuery) + client := &http.Client{} + + // Create new request + proxyReq, err := http.NewRequest(r.Method, targetURL, r.Body) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + // Copy original headers + proxyReq.Header = r.Header + + // Execute request + resp, err := client.Do(proxyReq) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + defer resp.Body.Close() + + // Copy response headers + for k, v := range resp.Header { + w.Header()[k] = v + } + + // Set response status code + w.WriteHeader(resp.StatusCode) + + // Copy response body + if _, err := io.Copy(w, resp.Body); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } +} diff --git a/appview/state/middleware.go b/appview/state/middleware.go index ad275a41..a9879d48 100644 --- a/appview/state/middleware.go +++ b/appview/state/middleware.go @@ -131,7 +131,7 @@ func ResolveIdent(next http.Handler) http.Handler { }) } -func ResolveRepoDomain(s *State) Middleware { +func ResolveRepoKnot(s *State) Middleware { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { repoName := chi.URLParam(req, "repo") @@ -150,7 +150,7 @@ func ResolveRepoDomain(s *State) Middleware { return } - ctx := context.WithValue(req.Context(), "domain", repo.Knot) + ctx := context.WithValue(req.Context(), "knot", repo.Knot) next.ServeHTTP(w, req.WithContext(ctx)) }) } diff --git a/appview/state/repo.go b/appview/state/repo.go index 3ad7de6b..95e7f6f1 100644 --- a/appview/state/repo.go +++ b/appview/state/repo.go @@ -14,7 +14,7 @@ func (s *State) RepoIndex(w http.ResponseWriter, r *http.Request) { ctx := r.Context() repoName := chi.URLParam(r, "repo") - domain, ok := ctx.Value("domain").(string) + knot, ok := ctx.Value("knot").(string) if !ok { log.Println("malformed middleware") w.WriteHeader(http.StatusInternalServerError) @@ -28,7 +28,7 @@ func (s *State) RepoIndex(w http.ResponseWriter, r *http.Request) { return } - resp, err := http.Get(fmt.Sprintf("http://%s/%s/%s", domain, id.DID.String(), repoName)) + resp, err := http.Get(fmt.Sprintf("http://%s/%s/%s", knot, id.DID.String(), repoName)) if err != nil { log.Println("failed to reach knotserver", err) return diff --git a/appview/state/state.go b/appview/state/state.go index 21b3f1cf..59f622e7 100644 --- a/appview/state/state.go +++ b/appview/state/state.go @@ -599,9 +599,11 @@ func (s *State) UserRouter() http.Handler { r.With(ResolveIdent).Route("/{user}", func(r chi.Router) { r.Get("/", s.ProfilePage) - r.With(ResolveRepoDomain(s)).Route("/{repo}", func(r chi.Router) { + r.With(ResolveRepoKnot(s)).Route("/{repo}", func(r chi.Router) { r.Get("/", s.RepoIndex) - // r.Get("/info/refs", s.InfoRefs) + // These routes get proxied to the knot + r.Get("/info/refs", s.InfoRefs) + r.Post("/git-upload-pack", s.UploadPack) }) })