git_raw_handler.go

 1package http
 2
 3import (
 4	"bytes"
 5	"io"
 6	"net/http"
 7	"time"
 8
 9	"github.com/gorilla/mux"
10
11	"github.com/git-bug/git-bug/cache"
12)
13
14// Serves raw blob content resolved by ref and path, e.g.
15// /gitraw/{repo}/{ref}/{path:.*}
16//
17// This is used by the web UI to render images referenced in markdown
18// files (READMEs etc.) without needing to know the blob hash upfront.
19type gitRawHandler struct {
20	mrc *cache.MultiRepoCache
21}
22
23func NewGitRawHandler(mrc *cache.MultiRepoCache) http.Handler {
24	return &gitRawHandler{mrc: mrc}
25}
26
27func (h *gitRawHandler) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
28	var repo *cache.RepoCache
29	var err error
30
31	repoVar := mux.Vars(r)["repo"]
32	if repoVar == "_" {
33		repo, err = h.mrc.DefaultRepo()
34	} else {
35		repo, err = h.mrc.ResolveRepo(repoVar)
36	}
37	if err != nil {
38		http.Error(rw, "invalid repo reference", http.StatusBadRequest)
39		return
40	}
41
42	ref := mux.Vars(r)["ref"]
43	path := mux.Vars(r)["path"]
44
45	if ref == "" || path == "" {
46		http.Error(rw, "ref and path are required", http.StatusBadRequest)
47		return
48	}
49
50	rc, _, _, err := repo.BlobAtPath(ref, path)
51	if err != nil {
52		http.Error(rw, "file not found", http.StatusNotFound)
53		return
54	}
55	defer rc.Close()
56
57	data, err := io.ReadAll(rc)
58	if err != nil {
59		http.Error(rw, err.Error(), http.StatusInternalServerError)
60		return
61	}
62
63	// ServeContent handles Content-Type detection from the file extension,
64	// Range requests, and caching headers.
65	http.ServeContent(rw, r, path, time.Now(), bytes.NewReader(data))
66}