Storage: refactor readonly support (#52127)
This commit is contained in:
+75
-73
@@ -15,8 +15,6 @@ import (
|
||||
"github.com/grafana/grafana/pkg/web"
|
||||
)
|
||||
|
||||
var errFileTooBig = response.Error(400, "Please limit file uploaded under 1MB", errors.New("file is too big"))
|
||||
|
||||
// HTTPStorageService passes raw HTTP requests to a well typed storage service
|
||||
type HTTPStorageService interface {
|
||||
List(c *models.ReqContext) response.Response
|
||||
@@ -57,87 +55,91 @@ func UploadErrorToStatusCode(err error) int {
|
||||
}
|
||||
|
||||
func (s *httpStorage) Upload(c *models.ReqContext) response.Response {
|
||||
// 32 MB is the default used by FormFile()
|
||||
if err := c.Req.ParseMultipartForm(32 << 20); err != nil {
|
||||
return response.Error(400, "error in parsing form", err)
|
||||
type rspInfo struct {
|
||||
Message string `json:"message,omitempty"`
|
||||
Path string `json:"path,omitempty"`
|
||||
Count int `json:"count,omitempty"`
|
||||
Bytes int `json:"bytes,omitempty"`
|
||||
Error bool `json:"err,omitempty"`
|
||||
}
|
||||
rsp := &rspInfo{Message: "uploaded"}
|
||||
|
||||
c.Req.Body = http.MaxBytesReader(c.Resp, c.Req.Body, MAX_UPLOAD_SIZE)
|
||||
if err := c.Req.ParseMultipartForm(MAX_UPLOAD_SIZE); err != nil {
|
||||
msg := fmt.Sprintf("Please limit file uploaded under %s", util.ByteCountSI(MAX_UPLOAD_SIZE))
|
||||
return response.Error(400, msg, err)
|
||||
rsp.Message = fmt.Sprintf("Please limit file uploaded under %s", util.ByteCountSI(MAX_UPLOAD_SIZE))
|
||||
rsp.Error = true
|
||||
return response.JSON(400, rsp)
|
||||
}
|
||||
message := getMultipartFormValue(c.Req, "message")
|
||||
overwriteExistingFile := getMultipartFormValue(c.Req, "overwriteExistingFile") != "false" // must explicitly overwrite
|
||||
folder := getMultipartFormValue(c.Req, "folder")
|
||||
|
||||
for k, fileHeaders := range c.Req.MultipartForm.File {
|
||||
path := getMultipartFormValue(c.Req, k+".path") // match the path with a file
|
||||
if len(fileHeaders) > 1 {
|
||||
path = ""
|
||||
}
|
||||
if path == "" && folder == "" {
|
||||
rsp.Message = "please specify the upload folder or full path"
|
||||
rsp.Error = true
|
||||
return response.JSON(400, rsp)
|
||||
}
|
||||
|
||||
for _, fileHeader := range fileHeaders {
|
||||
// restrict file size based on file size
|
||||
// open each file to copy contents
|
||||
file, err := fileHeader.Open()
|
||||
if err != nil {
|
||||
return response.Error(500, "Internal Server Error", err)
|
||||
}
|
||||
err = file.Close()
|
||||
if err != nil {
|
||||
return response.Error(500, "Internal Server Error", err)
|
||||
}
|
||||
data, err := ioutil.ReadAll(file)
|
||||
if err != nil {
|
||||
return response.Error(500, "Internal Server Error", err)
|
||||
}
|
||||
|
||||
if path == "" {
|
||||
path = folder + "/" + fileHeader.Filename
|
||||
}
|
||||
|
||||
entityType := EntityTypeJSON
|
||||
mimeType := http.DetectContentType(data)
|
||||
if strings.HasPrefix(mimeType, "image") {
|
||||
entityType = EntityTypeImage
|
||||
}
|
||||
|
||||
err = s.store.Upload(c.Req.Context(), c.SignedInUser, &UploadRequest{
|
||||
Contents: data,
|
||||
MimeType: mimeType,
|
||||
EntityType: entityType,
|
||||
Path: path,
|
||||
OverwriteExistingFile: overwriteExistingFile,
|
||||
Properties: map[string]string{
|
||||
"message": message, // the commit/changelog entry
|
||||
},
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return response.Error(UploadErrorToStatusCode(err), err.Error(), err)
|
||||
}
|
||||
rsp.Count++
|
||||
rsp.Bytes += len(data)
|
||||
rsp.Path = path
|
||||
}
|
||||
}
|
||||
|
||||
files := c.Req.MultipartForm.File["file"]
|
||||
if len(files) != 1 {
|
||||
return response.JSON(400, map[string]interface{}{
|
||||
"message": "please upload files one at a time",
|
||||
"err": true,
|
||||
})
|
||||
}
|
||||
|
||||
folder, ok := getMultipartFormValue(c.Req, "folder")
|
||||
if !ok || folder == "" {
|
||||
return response.JSON(400, map[string]interface{}{
|
||||
"message": "please specify the upload folder",
|
||||
"err": true,
|
||||
})
|
||||
}
|
||||
overwriteExistingFile, _ := getMultipartFormValue(c.Req, "overwriteExistingFile")
|
||||
|
||||
fileHeader := files[0]
|
||||
if fileHeader.Size > MAX_UPLOAD_SIZE {
|
||||
return errFileTooBig
|
||||
}
|
||||
|
||||
// restrict file size based on file size
|
||||
// open each file to copy contents
|
||||
file, err := fileHeader.Open()
|
||||
if err != nil {
|
||||
return response.Error(500, "Internal Server Error", err)
|
||||
}
|
||||
err = file.Close()
|
||||
if err != nil {
|
||||
return response.Error(500, "Internal Server Error", err)
|
||||
}
|
||||
data, err := ioutil.ReadAll(file)
|
||||
if err != nil {
|
||||
return response.Error(500, "Internal Server Error", err)
|
||||
}
|
||||
|
||||
if (len(data)) > MAX_UPLOAD_SIZE {
|
||||
return errFileTooBig
|
||||
}
|
||||
|
||||
path := folder + "/" + fileHeader.Filename
|
||||
|
||||
mimeType := http.DetectContentType(data)
|
||||
|
||||
err = s.store.Upload(c.Req.Context(), c.SignedInUser, &UploadRequest{
|
||||
Contents: data,
|
||||
MimeType: mimeType,
|
||||
EntityType: EntityTypeImage,
|
||||
Path: path,
|
||||
OverwriteExistingFile: overwriteExistingFile == "true",
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return response.Error(UploadErrorToStatusCode(err), err.Error(), err)
|
||||
}
|
||||
|
||||
return response.JSON(200, map[string]interface{}{
|
||||
"message": "Uploaded successfully",
|
||||
"path": path,
|
||||
"file": fileHeader.Filename,
|
||||
"err": true,
|
||||
})
|
||||
return response.JSON(200, rsp)
|
||||
}
|
||||
|
||||
func getMultipartFormValue(req *http.Request, key string) (string, bool) {
|
||||
func getMultipartFormValue(req *http.Request, key string) string {
|
||||
v, ok := req.MultipartForm.Value[key]
|
||||
if !ok || len(v) != 1 {
|
||||
return "", false
|
||||
return ""
|
||||
}
|
||||
return v[0], ok
|
||||
return v[0]
|
||||
}
|
||||
|
||||
func (s *httpStorage) Read(c *models.ReqContext) response.Response {
|
||||
|
||||
Reference in New Issue
Block a user