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}