1package http
2
3import (
4 "encoding/json"
5 "fmt"
6 "io/ioutil"
7 "net/http"
8
9 "github.com/gorilla/mux"
10
11 "github.com/MichaelMure/git-bug/api/auth"
12 "github.com/MichaelMure/git-bug/cache"
13)
14
15// implement a http.Handler that will accept and store content into git blob.
16//
17// Expected gorilla/mux parameters:
18// - "repo" : the ref of the repo or "" for the default one
19type gitUploadFileHandler struct {
20 mrc *cache.MultiRepoCache
21}
22
23func NewGitUploadFileHandler(mrc *cache.MultiRepoCache) http.Handler {
24 return &gitUploadFileHandler{mrc: mrc}
25}
26
27func (gufh *gitUploadFileHandler) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
28 var repo *cache.RepoCache
29 var err error
30
31 repoVar := mux.Vars(r)["repo"]
32 switch repoVar {
33 case "":
34 repo, err = gufh.mrc.DefaultRepo()
35 default:
36 repo, err = gufh.mrc.ResolveRepo(repoVar)
37 }
38
39 if err != nil {
40 http.Error(rw, "invalid repo reference", http.StatusBadRequest)
41 return
42 }
43
44 _, err = auth.UserFromCtx(r.Context(), repo)
45 if err == auth.ErrNotAuthenticated {
46 http.Error(rw, "read-only mode or not logged in", http.StatusForbidden)
47 return
48 } else if err != nil {
49 http.Error(rw, fmt.Sprintf("loading identity: %v", err), http.StatusInternalServerError)
50 return
51 }
52
53 // 100MB (github limit)
54 var maxUploadSize int64 = 100 * 1000 * 1000
55 r.Body = http.MaxBytesReader(rw, r.Body, maxUploadSize)
56 if err := r.ParseMultipartForm(maxUploadSize); err != nil {
57 http.Error(rw, "file too big (100MB max)", http.StatusBadRequest)
58 return
59 }
60
61 file, _, err := r.FormFile("uploadfile")
62 if err != nil {
63 http.Error(rw, "invalid file", http.StatusBadRequest)
64 return
65 }
66 defer file.Close()
67 fileBytes, err := ioutil.ReadAll(file)
68 if err != nil {
69 http.Error(rw, "invalid file", http.StatusBadRequest)
70 return
71 }
72
73 filetype := http.DetectContentType(fileBytes)
74 if filetype != "image/jpeg" && filetype != "image/jpg" &&
75 filetype != "image/gif" && filetype != "image/png" {
76 http.Error(rw, "invalid file type", http.StatusBadRequest)
77 return
78 }
79
80 hash, err := repo.StoreData(fileBytes)
81 if err != nil {
82 http.Error(rw, err.Error(), http.StatusInternalServerError)
83 return
84 }
85
86 type response struct {
87 Hash string `json:"hash"`
88 }
89
90 resp := response{Hash: string(hash)}
91
92 js, err := json.Marshal(resp)
93 if err != nil {
94 http.Error(rw, err.Error(), http.StatusInternalServerError)
95 return
96 }
97
98 rw.Header().Set("Content-Type", "application/json")
99 _, err = rw.Write(js)
100 if err != nil {
101 http.Error(rw, err.Error(), http.StatusInternalServerError)
102 return
103 }
104}