git_file_upload_handler.go

  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}