| @@ -303,3 +303,13 @@ func (a *Attachment) LinkedDataSet() (*Dataset, error) { | |||||
| } | } | ||||
| return nil, nil | return nil, nil | ||||
| } | } | ||||
| // InsertAttachment insert a record into attachment. | |||||
| func InsertAttachment(attach *Attachment) (_ *Attachment, err error) { | |||||
| if _, err := x.Insert(attach); err != nil { | |||||
| return nil, err | |||||
| } | |||||
| return attach, nil | |||||
| } | |||||
| @@ -72,3 +72,7 @@ func (l *LocalStorage) PresignedGetURL(path string, fileName string) (string,err | |||||
| func (l *LocalStorage) PresignedPutURL(path string) (string,error) { | func (l *LocalStorage) PresignedPutURL(path string) (string,error) { | ||||
| return "",nil | return "",nil | ||||
| } | } | ||||
| func (l *LocalStorage) HasObject(path string) (bool,error) { | |||||
| return false,nil | |||||
| } | |||||
| @@ -101,3 +101,24 @@ func (m *MinioStorage) PresignedPutURL(path string) (string,error) { | |||||
| return preURL.String(),nil | return preURL.String(),nil | ||||
| } | } | ||||
| //check if has the object | |||||
| func (m *MinioStorage) HasObject(path string) (bool,error) { | |||||
| hasObject := false | |||||
| // Create a done channel to control 'ListObjects' go routine. | |||||
| doneCh := make(chan struct{}) | |||||
| // Indicate to our routine to exit cleanly upon return. | |||||
| defer close(doneCh) | |||||
| objectCh := m.client.ListObjects(m.bucket, m.buildMinioPath(path), false, doneCh) | |||||
| for object := range objectCh { | |||||
| if object.Err != nil { | |||||
| return hasObject, object.Err | |||||
| } | |||||
| hasObject = true | |||||
| } | |||||
| return hasObject,nil | |||||
| } | |||||
| @@ -23,6 +23,7 @@ type ObjectStorage interface { | |||||
| Delete(path string) error | Delete(path string) error | ||||
| PresignedGetURL(path string, fileName string) (string, error) | PresignedGetURL(path string, fileName string) (string, error) | ||||
| PresignedPutURL(path string) (string, error) | PresignedPutURL(path string) (string, error) | ||||
| HasObject(path string) (bool, error) | |||||
| } | } | ||||
| // Copy copys a file from source ObjectStorage to dest ObjectStorage | // Copy copys a file from source ObjectStorage to dest ObjectStorage | ||||
| @@ -31,11 +31,15 @@ func (err ErrFileTypeForbidden) Error() string { | |||||
| func VerifyAllowedContentType(buf []byte, allowedTypes []string) error { | func VerifyAllowedContentType(buf []byte, allowedTypes []string) error { | ||||
| fileType := http.DetectContentType(buf) | fileType := http.DetectContentType(buf) | ||||
| return VerifyFileType(fileType, allowedTypes) | |||||
| } | |||||
| func VerifyFileType(fileType string, allowedTypes []string) error { | |||||
| for _, t := range allowedTypes { | for _, t := range allowedTypes { | ||||
| t := strings.Trim(t, " ") | t := strings.Trim(t, " ") | ||||
| if t == "*/*" || t == fileType || | if t == "*/*" || t == fileType || | ||||
| // Allow directives after type, like 'text/plain; charset=utf-8' | |||||
| // Allow directives after type, like 'text/plain; charset=utf-8' | |||||
| strings.HasPrefix(fileType, t+";") { | strings.HasPrefix(fileType, t+";") { | ||||
| return nil | return nil | ||||
| } | } | ||||
| @@ -203,6 +203,12 @@ func GetPresignedPutObjectURL(ctx *context.Context) { | |||||
| return | return | ||||
| } | } | ||||
| err := upload.VerifyFileType(ctx.Params("file_type"), strings.Split(setting.Attachment.AllowedTypes, ",")) | |||||
| if err != nil { | |||||
| ctx.Error(400, err.Error()) | |||||
| return | |||||
| } | |||||
| if setting.Attachment.StoreType == storage.MinioStorageType { | if setting.Attachment.StoreType == storage.MinioStorageType { | ||||
| uuid := gouuid.NewV4().String() | uuid := gouuid.NewV4().String() | ||||
| url, err := storage.Attachments.PresignedPutURL(models.AttachmentRelativePath(uuid)) | url, err := storage.Attachments.PresignedPutURL(models.AttachmentRelativePath(uuid)) | ||||
| @@ -219,6 +225,35 @@ func GetPresignedPutObjectURL(ctx *context.Context) { | |||||
| ctx.Error(404, "storage type is not enabled") | ctx.Error(404, "storage type is not enabled") | ||||
| return | return | ||||
| } | } | ||||
| } | |||||
| // AddAttachment response for add attachment record | |||||
| func AddAttachment(ctx *context.Context) { | |||||
| uuid := ctx.Query("uuid") | |||||
| has,err := storage.Attachments.HasObject(models.AttachmentRelativePath(uuid)) | |||||
| if err != nil { | |||||
| ctx.ServerError("HasObject", err) | |||||
| return | |||||
| } | |||||
| if !has { | |||||
| ctx.Error(404, "attachment has not been uploaded") | |||||
| return | |||||
| } | |||||
| _, err = models.InsertAttachment(&models.Attachment{ | |||||
| UUID: uuid, | |||||
| UploaderID: ctx.User.ID, | |||||
| Name: ctx.Query("file_name"), | |||||
| Size: ctx.QueryInt64("size"), | |||||
| }) | |||||
| if err != nil { | |||||
| ctx.Error(500, fmt.Sprintf("InsertAttachment: %v", err)) | |||||
| return | |||||
| } | |||||
| ctx.JSON(200, map[string]string{ | |||||
| "result_code": "0", | |||||
| }) | |||||
| } | } | ||||
| @@ -520,6 +520,7 @@ func RegisterRoutes(m *macaron.Macaron) { | |||||
| m.Post("", repo.UploadAttachment) | m.Post("", repo.UploadAttachment) | ||||
| m.Post("/delete", repo.DeleteAttachment) | m.Post("/delete", repo.DeleteAttachment) | ||||
| m.Get("/get_pre_url", repo.GetPresignedPutObjectURL) | m.Get("/get_pre_url", repo.GetPresignedPutObjectURL) | ||||
| m.Post("/add", repo.AddAttachment) | |||||
| }, reqSignIn) | }, reqSignIn) | ||||
| m.Group("/:username", func() { | m.Group("/:username", func() { | ||||