1package web23import (4 "net/http"5 "net/url"6 "path"7 "text/template"89 "github.com/charmbracelet/log/v2"10 "github.com/charmbracelet/soft-serve/pkg/backend"11 "github.com/charmbracelet/soft-serve/pkg/config"12 "github.com/charmbracelet/soft-serve/pkg/utils"13 "github.com/gorilla/mux"14 "github.com/prometheus/client_golang/prometheus"15 "github.com/prometheus/client_golang/prometheus/promauto"16)1718var goGetCounter = promauto.NewCounterVec(prometheus.CounterOpts{19 Namespace: "soft_serve",20 Subsystem: "http",21 Name: "go_get_total",22 Help: "The total number of go get requests",23}, []string{"repo"})2425var repoIndexHTMLTpl = template.Must(template.New("index").Parse(`<!DOCTYPE html>26<html lang="en">27<head>28 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>29 <meta http-equiv="refresh" content="0; url=https://godoc.org/{{ .ImportRoot }}/{{.Repo}}">30 <meta name="go-import" content="{{ .ImportRoot }}/{{ .Repo }} git {{ .Config.HTTP.PublicURL }}/{{ .Repo }}.git">31</head>32<body>33Redirecting to docs at <a href="https://godoc.org/{{ .ImportRoot }}/{{ .Repo }}">godoc.org/{{ .ImportRoot }}/{{ .Repo }}</a>...34</body>35</html>36`))3738// GoGetHandler handles go get requests.39func GoGetHandler(w http.ResponseWriter, r *http.Request) {40 ctx := r.Context()41 cfg := config.FromContext(ctx)42 be := backend.FromContext(ctx)43 logger := log.FromContext(ctx)44 repo := mux.Vars(r)["repo"]4546 // Handle go get requests.47 //48 // Always return a 200 status code, even if the repo path doesn't exist.49 // It will try to find the repo by walking up the path until it finds one.50 // If it can't find one, it will return a 404.51 //52 // https://golang.org/cmd/go/#hdr-Remote_import_paths53 // https://go.dev/ref/mod#vcs-branch54 if r.URL.Query().Get("go-get") == "1" {55 repo := repo56 importRoot, err := url.Parse(cfg.HTTP.PublicURL)57 if err != nil {58 http.Error(w, err.Error(), http.StatusInternalServerError)59 return60 }6162 // find the repo63 for {64 if _, err := be.Repository(ctx, repo); err == nil {65 break66 }6768 if repo == "" || repo == "." || repo == "/" {69 renderNotFound(w, r)70 return71 }7273 repo = path.Dir(repo)74 }7576 if err := repoIndexHTMLTpl.Execute(w, struct {77 Repo string78 Config *config.Config79 ImportRoot string80 }{81 Repo: utils.SanitizeRepo(repo),82 Config: cfg,83 ImportRoot: importRoot.Host,84 }); err != nil {85 logger.Error("failed to render go get template", "err", err)86 renderInternalServerError(w, r)87 return88 }8990 goGetCounter.WithLabelValues(repo).Inc()91 return92 }9394 renderNotFound(w, r)95}