// imageUploader is a uploader that uploading files. func imageUploader() concurrency.ConcurrencyManager { return func(reader *multipart.Reader) concurrency.Result { atomic.AddInt32(concurrency.BusyWorker, 1) var result concurrency.Result result.Code = http.StatusOK for { part, err := reader.NextPart() uploadedNow := atomic.AddUint32(concurrency.Done, 1) log.Debugf("count %d", uploadedNow) if err == io.EOF { log.Debug("End of file.") break } if part.FileName() == "" { log.Debug("File name is empty.") continue } err = upload.UploadImageFile(s3UploadPath, part) if err != nil { log.Error("Image uploading failed. : " + err.Error()) result.Code = http.StatusBadRequest result.Error = err return result } log.Debug("File uploaded.") } log.Debug("Iteration concurrency.Done.") return result } }
// SetCookieHandler sets a cookie with email and password. func SetCookieHandler(c *gin.Context, email string, pass string) (int, error) { if email != "" && pass != "" { log.Debugf("User email : %s , password : %s", email, pass) var user model.User isValidEmail := validation.EmailValidation(email) if isValidEmail { log.Debug("User entered valid email.") if db.ORM.Where("email = ?", email).First(&user).RecordNotFound() { return http.StatusNotFound, errors.New("User is not found.") } } else { log.Debug("User entered username.") if db.ORM.Where("username = ?", email).First(&user).RecordNotFound() { return http.StatusNotFound, errors.New("User is not found.") } } err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(pass)) if err != nil { return http.StatusUnauthorized, errors.New("Password incorrect.") } status, err := SetCookie(c, user.Token) if err != nil { return status, err } c.Writer.Header().Set("X-Auth-Token", user.Token) return http.StatusOK, nil } else { return http.StatusNotFound, errors.New("User is not found.") } }
// DeleteFile deletes a file. func DeleteFile(c *gin.Context) (int, error) { log.Debug("deleteFile performed") var targetFile model.File id := c.Params.ByName("id") if db.ORM.First(&targetFile, id).RecordNotFound() { return http.StatusNotFound, errors.New("File is not found.") } status, err := userPermission.CurrentUserIdentical(c, targetFile.UserId) if err != nil { return status, err } switch config.UploadTarget { case "S3": s3UploadPath := config.UploadS3Path + strconv.FormatInt(targetFile.UserId, 10) + "/" log.Debugf("s3UploadPath %s", s3UploadPath) err = aws.DelFromMyBucket(s3UploadPath, targetFile.Name) if err != nil { return http.StatusInternalServerError, err } case "LOCAL": err = file.DeleteLocal(targetFile.Name) if err != nil { return http.StatusInternalServerError, err } } if db.ORM.Delete(&targetFile).Delete(targetFile).Error != nil { return http.StatusInternalServerError, errors.New("File is not deleted.") } return http.StatusOK, nil }
// RetrieveLocations retrieves locations. func RetrieveLocations(c *gin.Context) ([]model.Location, bool, int, bool, bool, int, error) { var locations []model.Location var locationCount, locationPerPage int filterQuery := c.Request.URL.Query().Get("filter") locationPerPage = config.LocationPerPage filter := &LocationFilter{} whereBuffer := new(bytes.Buffer) whereValues := []interface{}{} if len(filterQuery) > 0 { log.Debugf("retrieve Locations filter : %s\n", filterQuery) json.Unmarshal([]byte(filterQuery), &filter) if filter.UserId > 0 { stringHelper.Concat(whereBuffer, "user_id = ?") whereValues = append(whereValues, filter.UserId) log.Debugf("userId : %d\n", filter.UserId) } if filter.LocationPerPage > 0 { locationPerPage = filter.LocationPerPage log.Debugf("locationPerPage : %d\n", filter.LocationPerPage) } } else { log.Debug("no filters found.\n") } log.Debugf("filterQuery %v.\n", filterQuery) log.Debugf("filter %v.\n", filter) whereStr := whereBuffer.String() db.ORM.Model(model.Location{}).Where(whereStr, whereValues...).Count(&locationCount) offset, currentPage, hasPrev, hasNext := pagination.Paginate(filter.CurrentPage, locationPerPage, locationCount) db.ORM.Limit(locationPerPage).Offset(offset).Order(config.LocationOrder).Where(whereStr, whereValues...).Find(&locations) return locations, canUserWrite(c), currentPage, hasPrev, hasNext, http.StatusOK, nil }
// UpdateUserLikingCount updates user liking count. func UpdateUserLikingCount(user *model.User) (int, error) { log.Debug("UpdateUserLikingCount performed") user.LikingCount = db.ORM.Model(user).Association("Likings").Count() if db.ORM.Save(user).Error != nil { return http.StatusInternalServerError, errors.New("User liking count is not updated.") } return http.StatusOK, nil }
// articleUploader is a uploader that uploading files and sync articles. func articleUploader() Uploader { return func(reader *multipart.Reader) UploadStatus { atomic.AddInt32(workingUploader, 1) var articles []model.Article formDataBuffer := make([]byte, 100000) fileCount := 0 sqlStrBuffer := new(bytes.Buffer) stringHelper.Concat(sqlStrBuffer, "INSERT INTO article(user_id, title, url, content, image_name, created_at) VALUES ") values := []interface{}{} for { part, err := reader.NextPart() if err == io.EOF { break } if part.FileName() == "" { if part.FormName() != "" { log.Debug("formName : " + part.FormName()) n, err := part.Read(formDataBuffer) log.Debugf("n, err %d %s", n, err) log.Debugf("data : %s ", formDataBuffer) err = json.Unmarshal(formDataBuffer[:n], &articles) log.Debugf("err %s", err) log.Debugf("article : %v\n", articles) log.Debugf("article len : %d\n", len(articles)) } continue } UploadImageFile(part) if fileCount < len(articles) { stringHelper.Concat(sqlStrBuffer, "(?, ?, ?, ?, ?, ?),") values = append(values, user.Id, articles[fileCount].Title, articles[fileCount].Url, articles[fileCount].Content, articles[fileCount].ImageName, time.Now()) // db.ORM.Create(&articles[fileCount]) fileCount += 1 } log.Debug("File uploaded.") log.Infof("File Count : %d\n", fileCount) } sqlStr := sqlStrBuffer.String() sqlStr = sqlStr[0 : len(sqlStr)-1] log.Debugf("sqlStr for Article : %s", sqlStr) db.ORM.Exec(sqlStr, values...) return UploadStatus(true) } }
func LoadPage(parentRoute *gin.Engine) { //type Page struct { // Title string //} parentRoute.SetHTMLTemplate(template.Must(template.ParseFiles("frontend/canjs/templates/message.html", "frontend/canjs/templates/app.html", "frontend/canjs/templates/base.html", "frontend/canjs/templates/404.html"))) log.Debug("url : " + config.StaticUrl) log.Debug("guid : " + config.Guid) log.Debug("path : " + staticPath) parentRoute.Static(config.StaticUrl+"/"+config.Guid, staticPath) // route.ServeFiles doesn't exist in the current version of gin. If you want to use this, use the 59d949d35080b83864dbeafadecef112d46aaeee. //parentRoute.ServeFiles(config.StaticUrl+"/"+config.Guid+"/*filepath", http.Dir(staticPath)) parentRoute.NoRoute(func(c *gin.Context) { c.HTML(404, "404.html", map[string]string{"language": config.DefaultLanguage, "title": config.Title}) }) route.Route(parentRoute.Group("")) }
// @Title deleteFile // @Description Delete a file. // @Accept json // @Param id path int true "File ID" // @Success 200 {object} response.BasicResponse // @Failure 401 {object} response.BasicResponse "Authentication required" // @Failure 404 {object} response.BasicResponse "Not found" // @Resource /upload/files // @Router /upload/{id} [delete] func deleteFile(c *gin.Context) { log.Debug("deleteFile performed") status, err := uploadService.DeleteFile(c) messageTypes := &response.MessageTypes{ OK: "destroy.done", BadRequest: "destroy.fail", Unauthorized: "upload.file.error.unauthorized", NotFound: "upload.file.error.notFound"} messages := &response.Messages{OK: "File is deleted successfully."} response.JSON(c, status, messageTypes, messages, err) }
// CurrentUser get a current user. func CurrentUser(c *gin.Context) (model.User, error) { var user model.User var token string var err error token = c.Request.Header.Get("X-Auth-Token") if len(token) > 0 { log.Debug("header token exist.") } else { token, err = Token(c) log.Debug("header token not exist.") if err != nil { return user, err } } if db.ORM.Select(config.UserPublicFields+", email").Where("token = ?", token).First(&user).RecordNotFound() { return user, errors.New("User is not found.") } db.ORM.Model(&user).Association("Languages").Find(&user.Languages) db.ORM.Model(&user).Association("Roles").Find(&user.Roles) return user, nil }
// DeleteRole deletes a role. func DeleteRole(c *gin.Context) (int, error) { log.Debug("deleteRole performed") var role model.Role id := c.Params.ByName("id") if db.ORM.First(&role, id).RecordNotFound() { return http.StatusNotFound, errors.New("Role is not found.") } if db.ORM.Delete(&role).Delete(role).Error != nil { return http.StatusInternalServerError, errors.New("Role is not deleted.") } return http.StatusOK, nil }
// basicUploader is a uploader that uploading files. func basicUploader() Uploader { return func(reader *multipart.Reader) UploadStatus { atomic.AddInt32(workingUploader, 1) for { part, err := reader.NextPart() uploadedNow := atomic.AddUint32(uploaded, 1) log.Debugf("count %d", uploadedNow) if err == io.EOF { log.Warn("End of file.") break } if part.FileName() == "" { log.Warn("File name is empty.") continue } UploadImageFile(part) log.Debug("File uploaded.") } log.Debug("Iteration done.") return UploadStatus(true) } }
// UpdateUserLikedCount updates user liked count. func UpdateUserLikedCount(c *gin.Context) (int, error) { log.Debug("UpdateUserLikedCount performed") currentUserSrc, err := userService.CurrentUser(c) var currentUser model.User if err != nil { return http.StatusUnauthorized, err } db.ORM.First(¤tUser, currentUserSrc.Id) currentUser.LikedCount = db.ORM.Model(currentUser).Association("Liked").Count() log.Debugf("LikedCount : %d", currentUser.LikedCount) if db.ORM.Save(currentUser).Error != nil { return http.StatusInternalServerError, errors.New("User liked count is not updated.") } return http.StatusOK, nil }
// AdminRequired run function when user logged in and user has an admin role. func AdminRequired(f func(c *gin.Context)) gin.HandlerFunc { return func(c *gin.Context) { user, err := userService.CurrentUser(c) if err == nil { if HasAdmin(&user) { f(c) log.Debug("User has admin role.") return } } log.Error("Admin role required.") response.KnownErrorJSON(c, http.StatusUnauthorized, "error.adminRequired", errors.New("Admin role required.")) return } }
// @Title deleteRole // @Description Delete a role. // @Accept json // @Param id path int true "Role ID" // @Success 200 {object} response.BasicResponse // @Failure 401 {object} response.BasicResponse "Authentication required" // @Failure 404 {object} response.BasicResponse "Not found" // @Resource /roles // @Router /roles/{id} [delete] func deleteRole(c *gin.Context) { log.Debug("deleteRole performed") status, err := roleService.DeleteRole(c) messageTypes := &response.MessageTypes{ OK: "destroy.done", BadRequest: "destroy.fail", Unauthorized: "role.error.unauthorized", NotFound: "role.error.notFound"} messages := &response.Messages{OK: "Role is deleted successfully."} response.JSON(c, status, messageTypes, messages, err) // // if err == nil { // c.JSON(status, response.BasicResponse{}) // } else { // c.JSON(400, response.BasicResponse{}) // } }
// RetrieveArticles retrieves articles. func RetrieveArticles(c *gin.Context) ([]model.Article, bool, int, int, bool, bool, int, error) { var articles []model.Article var category int var articleCount, articlePerPage int filterQuery := c.Request.URL.Query().Get("filter") articlePerPage = config.ArticlePerPage filter := &ArticleFilter{} whereBuffer := new(bytes.Buffer) whereValues := []interface{}{} if len(filterQuery) > 0 { log.Debugf("retrieve Articles filter : %s\n", filterQuery) json.Unmarshal([]byte(filterQuery), &filter) if filter.UserId > 0 { stringHelper.Concat(whereBuffer, "user_id = ?") whereValues = append(whereValues, filter.UserId) log.Debugf("userId : %d\n", filter.UserId) } if len(filter.Categories) > 0 { if len(whereValues) == 1 { stringHelper.Concat(whereBuffer, " and ") } stringHelper.Concat(whereBuffer, "category_id = ?") whereValues = append(whereValues, filter.Categories[0]) log.Debugf("categories : %d\n", filter.Categories[0]) category = filter.Categories[0] } if filter.ArticlePerPage > 0 { articlePerPage = filter.ArticlePerPage log.Debugf("articlePerPage : %d\n", filter.ArticlePerPage) } } else { log.Debug("no filters found.\n") } log.Debugf("filterQuery %v.\n", filterQuery) log.Debugf("filter %v.\n", filter) whereStr := whereBuffer.String() log.Debugf("whereStr %s.\n", whereStr) log.Debugf("whereValues %v.\n", whereValues) db.ORM.Model(model.Article{}).Where(whereStr, whereValues...).Count(&articleCount) offset, currentPage, hasPrev, hasNext := pagination.Paginate(filter.CurrentPage, articlePerPage, articleCount) log.Debugf("currentPage, perPage, total : %d, %d, %d", filter.CurrentPage, articlePerPage, articleCount) log.Debugf("offset, currentPage, hasPrev, hasNext : %d, %d, %t, %t", offset, currentPage, hasPrev, hasNext) db.ORM.Limit(articlePerPage).Offset(offset).Order(config.ArticleOrder).Where(whereStr, whereValues...).Find(&articles) return articles, canUserWrite(c, category), category, currentPage, hasPrev, hasNext, http.StatusOK, nil }
func SendEmailFromAdmin(to string, subject string, body string, bodyHTML string) error { msg := gomail.NewMessage() msg.SetHeader("From", config.EmailFrom) msg.SetHeader("To", to, config.EmailTestTo) msg.SetHeader("Subject", subject) msg.SetBody("text/plain", body) msg.AddAlternative("text/html", bodyHTML) log.Debugf("to : %s", to) log.Debugf("subject : %s", subject) log.Debugf("body : %s", body) log.Debugf("bodyHTML : %s", bodyHTML) if config.SendEmail { log.Debug("SendEmail performed.") err := SendEmail(msg) return err } return nil }
// UploadImageFile uploads an image file to a storage. func UploadImageFile(part *multipart.Part) { mediatype, _, _ := mime.ParseMediaType(part.Header.Get("Content-Type")) log.Debugf("params %s", mediatype) log.Debug("fileName : " + part.FileName()) inbuf, err := ioutil.ReadAll(part) if err != nil { log.CheckErrorWithMessage(err, "Image read failed.") } // Image resize is a bottleneck. How can we improve this? // https://github.com/fawick/speedtest-resize said vipsthumbnail is fastest one. // Currenctly goyangi uses vips(https://github.com/DAddYE/vips). // dst, _ := image.ResizeMedium(mediatype, bytes.NewReader(inBuf)) var dst *bytes.Buffer buf, err := image.ResizeMediumVips(inbuf) if err != nil { log.CheckErrorWithMessage(err, "Image resizing failed.") dst = bytes.NewBuffer(inbuf) } else { dst = bytes.NewBuffer(buf) } // var thumbDst *bytes.Buffer // thumbBuf, err := image.ResizeThumbnailVips(buf) // if err != nil { // log.CheckErrorWithMessage(err, "Image thumbnailing failed.") // thumbDst = bytes.NewBuffer(buf) // } else { // thumbDst = bytes.NewBuffer(thumbBuf) // } switch config.UploadTarget { case "LOCAL": err = file.SaveLocal(part.FileName(), dst) // err = file.SaveLocal(part.FileName()+"Thumbnail", thumbDst) case "S3": err = aws.PutToMyPublicBucket(s3UploadPath, part.FileName(), dst, mediatype) // err = aws.PutToMyPublicBucket("images/", part.FileName()+"Thumbnail", thumbDst, mediatype) } if err != nil { log.CheckErrorWithMessage(err, "Uploading failed.") } }
// DeleteLikingOnUser deletes liking on a user. func DeleteLikingOnUser(c *gin.Context) (int, error) { user := &model.User{} status, err := likingService.DeleteLiking(c, user) if err != nil { return status, err } // if err == nil { log.Debug("DeleteLikingOnUser likingDeleted") status, err = UpdateUserLikingCount(user) if err != nil { return status, err } status, err = UpdateUserLikedCount(c) // if err != nil { return status, err // } // log.Debugf("DeleteLikingOnUser error %v", err) // if err == nil { // err = UpdateUserLikedCount(c) // } // // } // return err }
// articleUploader is a uploader that uploading files and sync articles. func articleUploader() concurrency.ConcurrencyManager { return func(reader *multipart.Reader) concurrency.Result { atomic.AddInt32(concurrency.BusyWorker, 1) var articles []model.Article formDataBuffer := make([]byte, 100000) fileCount := 0 sqlStrBuffer := new(bytes.Buffer) stringHelper.Concat(sqlStrBuffer, "INSERT INTO article(user_id, title, url, content, image_name, created_at) VALUES ") values := []interface{}{} var result concurrency.Result result.Code = http.StatusOK for { part, err := reader.NextPart() if err == io.EOF { break } if part.FileName() == "" { if part.FormName() != "" { log.Debug("formName : " + part.FormName()) n, err := part.Read(formDataBuffer) if err != nil { log.Warnf("Multipart read failed. , (Error Detail : %s)", err.Error()) result.Code = http.StatusBadRequest result.Error = err return result } log.Debugf("data : %s ", formDataBuffer) err = json.Unmarshal(formDataBuffer[:n], &articles) if err != nil { log.Warnf("Json unmarshal failed. , (Error Detail : %s)", err.Error()) result.Code = http.StatusBadRequest result.Error = err return result } log.Debugf("err %s", err) log.Debugf("article : %v\n", articles) log.Debugf("article len : %d\n", len(articles)) } continue } err = upload.UploadImageFile("", part) if err != nil { log.Error("Image uploading failed. : " + err.Error()) result.Code = http.StatusBadRequest result.Error = err return result } if fileCount < len(articles) { stringHelper.Concat(sqlStrBuffer, "(?, ?, ?, ?, ?, ?),") values = append(values, user.Id, articles[fileCount].Title, articles[fileCount].Url, articles[fileCount].Content, articles[fileCount].ImageName, time.Now()) // db.ORM.Create(&articles[fileCount]) fileCount += 1 } log.Debug("File uploaded.") log.Infof("File Count : %d\n", fileCount) } sqlStr := sqlStrBuffer.String() sqlStr = sqlStr[0 : len(sqlStr)-1] log.Debugf("sqlStr for Article : %s", sqlStr) db.ORM.Exec(sqlStr, values...) return result } }
// RevokeGithub revokes github oauth connection. func RevokeGithub(c *gin.Context) (map[string]bool, int, error) { log.Debug("RevokeGithub performed") return RevokeOauth(c, github.ProviderId) }
// UploadImageFile uploads an image file to a storage. func UploadImageFile(s3UploadPath string, part *multipart.Part) error { mediatype, _, _ := mime.ParseMediaType(part.Header.Get("Content-Type")) log.Debugf("params %s", mediatype) log.Debug("fileName : " + part.FileName()) inbuf, err := ioutil.ReadAll(part) if err != nil { // log.CheckErrorWithMessage(err, "Image read failed.") return err } // Image resize is a bottleneck. How can we improve this? // https://github.com/fawick/speedtest-resize said vipsthumbnail is fastest one. // Currenctly goyangi uses vips(https://github.com/DAddYE/vips). // dst, _ := image.ResizeMedium(mediatype, bytes.NewReader(inBuf)) var dst, dstLarge, dstMedium, dstThumbnail *bytes.Buffer dst = bytes.NewBuffer(inbuf) buf, err := image.ResizeLargeVips(inbuf) if err != nil { // log.CheckErrorWithMessage(err, "Image resizing failed.") log.Errorf("Image large resizing failed. %s", err.Error()) dstLarge = nil } else { dstLarge = bytes.NewBuffer(buf) mbuf, err := image.ResizeMediumVips(buf) if err != nil { dstMedium = nil log.Errorf("Image medium resizing failed. %s", err.Error()) } else { dstMedium = bytes.NewBuffer(mbuf) tbuf, err := image.ResizeThumbnailVips(mbuf) if err != nil { dstThumbnail = nil log.Errorf("Image small resizing failed. %s", err.Error()) } else { dstThumbnail = bytes.NewBuffer(tbuf) } } } // var thumbDst *bytes.Buffer // thumbBuf, err := image.ResizeThumbnailVips(buf) // if err != nil { // log.CheckErrorWithMessage(err, "Image thumbnailing failed.") // thumbDst = bytes.NewBuffer(buf) // } else { // thumbDst = bytes.NewBuffer(thumbBuf) // } basename := part.FileName() ext := filepath.Ext(basename) name := strings.TrimSuffix(basename, ext) originName := basename largeName := name + "_large" + ext mediumName := name + "_medium" + ext thumbnailName := name + "_thumbnail" + ext switch config.UploadTarget { case "LOCAL": err = file.SaveLocal(originName, dst) if dstLarge != nil { err = file.SaveLocal(largeName, dstLarge) } if dstMedium != nil { err = file.SaveLocal(mediumName, dstMedium) } if dstThumbnail != nil { err = file.SaveLocal(thumbnailName, dstThumbnail) } case "S3": switch config.Environment { case "DEVELOPMENT": fallthrough case "TEST": err = aws.PutToMyPublicTestBucket(s3UploadPath, originName, dst, mediatype) if dstLarge != nil { err = aws.PutToMyPublicTestBucket(s3UploadPath, largeName, dstLarge, mediatype) } if dstMedium != nil { err = aws.PutToMyPublicTestBucket(s3UploadPath, mediumName, dstMedium, mediatype) } if dstThumbnail != nil { err = aws.PutToMyPublicTestBucket(s3UploadPath, thumbnailName, dstThumbnail, mediatype) } case "PRODUCTION": err = aws.PutToMyPublicBucket(s3UploadPath, originName, dst, mediatype) if dstLarge != nil { err = aws.PutToMyPublicBucket(s3UploadPath, largeName, dstLarge, mediatype) } if dstMedium != nil { err = aws.PutToMyPublicBucket(s3UploadPath, mediumName, dstMedium, mediatype) } if dstThumbnail != nil { err = aws.PutToMyPublicBucket(s3UploadPath, thumbnailName, dstThumbnail, mediatype) } } // err = aws.PutToMyPublicBucket("images/", part.FileName()+"Thumbnail", thumbDst, mediatype) } if err != nil { // log.CheckErrorWithMessage(err, "Uploading failed.") return err } return nil }