git_file_upload_handler.go

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