diff --git a/common/time.go b/common/time.go new file mode 100644 index 0000000..3383bc4 --- /dev/null +++ b/common/time.go @@ -0,0 +1,42 @@ +package common + +import ( + "strings" + "time" +) + +type CustomTime struct { + time.Time +} + +var locCST = time.FixedZone("CST", 8*3600) + +func (ct *CustomTime) UnmarshalJSON(data []byte) error { + s := strings.Trim(string(data), `"`) + // Ignore null, like in the main JSON package. + if s == "null" { + return nil + } + // Fractional seconds are handled implicitly by Parse. + var err error + ct.Time, err = time.Parse(time.RFC3339, s) + if err != nil { + ct.Time, err = time.ParseInLocation(`2006-01-02T15:04:05`, s, locCST) + } + return err +} + +func (ct *CustomTime) UnmarshalText(data []byte) error { + s := strings.Trim(string(data), `"`) + // Ignore null, like in the main JSON package. + if s == "" { + return nil + } + // Fractional seconds are handled implicitly by Parse. + var err error + ct.Time, err = time.Parse(time.RFC3339, s) + if err != nil { + ct.Time, err = time.ParseInLocation(`2006-01-02T15:04:05`, s, locCST) + } + return err +} diff --git a/danke/api/review.go b/danke/api/review.go index ecddbcb..f02f3ad 100644 --- a/danke/api/review.go +++ b/danke/api/review.go @@ -8,6 +8,8 @@ import ( . "github.com/opentreehole/backend/danke/model" . "github.com/opentreehole/backend/danke/schema" "gorm.io/gorm" + "gorm.io/gorm/clause" + "gorm.io/plugin/dbresolver" "time" ) @@ -435,3 +437,156 @@ func GetRandomReviewV1(c *fiber.Ctx) (err error) { return c.JSON(new(RandomReviewV1Response).FromModel(&review)) } + +// ListSensitiveReviews +// +// @Summary List sensitive reviews, admin only +// @Tags Review +// @Produce application/json +// @Router /reviews/_sensitive [get] +// @Param object query SensitiveReviewRequest false "query" +// @Success 200 {array} SensitiveReviewResponse +// @Failure 404 {object} MessageModel +func ListSensitiveReviews(c *fiber.Ctx) (err error) { + // validate query + var query SensitiveReviewRequest + err = ValidateQuery(c, &query) + if err != nil { + return err + } + + // set default time + if query.Offset.Time.IsZero() { + query.Offset.Time = time.Now() + } + + // get user + user, err := GetCurrentUser(c) + if err != nil { + return err + } + + // permission, admin only + if !user.IsAdmin { + return Forbidden() + } + + // get reviews + var reviews ReviewList + querySet := DB + if query.All == true { + querySet = querySet.Where("is_sensitive = true") + } else { + if query.Open == true { + querySet = querySet.Where("is_sensitive = true and is_actual_sensitive IS NULL") + } else { + querySet = querySet.Where("is_sensitive = true and is_actual_sensitive IS NOT NULL") + } + } + + result := querySet. + Where("updated_at < ?", query.Offset.Time). + Order("updated_at desc"). + Limit(query.Size). + Find(&reviews) + if result.Error != nil { + return result.Error + } + + var responses = make([]SensitiveReviewResponse, len(reviews)) + for i := range responses { + responses[i].FromModel(reviews[i]) + } + + return c.JSON(responses) +} + +// ModifyReviewSensitive +// +// @Summary Modify A Review's actual_sensitive, admin only +// @Tags Review +// @Produce application/json +// @Router /reviews/{id}/_sensitive [put] +// @Param id path int true "id" +// @Param json body ModifySensitiveReviewRequest true "json" +// @Success 200 {object} Review +// @Failure 404 {object} MessageModel +func ModifyReviewSensitive(c *fiber.Ctx) (err error) { + // validate body + var body ModifySensitiveReviewRequest + err = ValidateBody(c, &body) + if err != nil { + return err + } + + // parse review_id + reviewID, err := c.ParamsInt("id") + if err != nil { + return err + } + + // get user + user, err := GetCurrentUser(c) + if err != nil { + return err + } + + // permission check + if !user.IsAdmin { + return Forbidden() + } + + var review Review + err = DB.Clauses(dbresolver.Write).Transaction(func(tx *gorm.DB) error { + err = tx.Clauses(clause.Locking{Strength: "UPDATE"}).First(&review, reviewID).Error + if err != nil { + return err + } + + // modify actual_sensitive + review.IsActuallySensitive = &body.IsActuallySensitive + //MyLog("Review", "Modify", reviewID, user.ID, RoleAdmin, "actual_sensitive to: ", fmt.Sprintf("%v", body.IsActuallySensitive)) + //CreateAdminLog(tx, AdminLogTypeChangeSensitive, user.ID, body) + + if !body.IsActuallySensitive { + // save actual_sensitive only + return tx.Model(&review).Select("IsActuallySensitive").UpdateColumns(&review).Error + } + + //reason := "违反社区规范" + //err = review.Backup(tx, user.ID, reason) + //if err != nil { + // return err + //} + + return tx.Delete(&review).Error + }) + if err != nil { + return err + } + + //// clear cache + //err = DeleteCache(fmt.Sprintf("hole_%v", review.HoleID)) + //if err != nil { + // return err + //} + + //if review.IsActuallySensitive != nil && *review.IsActuallySensitive == false { + // go ReviewIndex(ReviewModel{ + // ID: review.ID, + // UpdatedAt: review.UpdatedAt, + // Content: review.Content, + // }) + //} else { + // go ReviewDelete(review.ID) + // + // MyLog("Review", "Delete", reviewID, user.ID, RoleAdmin, "reason: ", "sensitive") + // + // err = review.SendModify(DB) + // if err != nil { + // log.Err(err).Str("model", "Notification").Msg("SendModify failed") + // } + //} + + return c.JSON(review) +} diff --git a/danke/api/routes.go b/danke/api/routes.go index cb6a1b9..9e10897 100644 --- a/danke/api/routes.go +++ b/danke/api/routes.go @@ -53,4 +53,7 @@ func registerRoutes(r fiber.Router) { //router.Get("/static/cedict_ts.u8", func(c *fiber.Ctx) error { // return c.Send(data.CreditTs) //}) + + r.Get("/v3/reviews/_sensitive", ListSensitiveReviews) + r.Put("/v3/reviews/:id/_sensitive", ModifyReviewSensitive) } diff --git a/danke/schema/review.go b/danke/schema/review.go index dba0d9f..499e219 100644 --- a/danke/schema/review.go +++ b/danke/schema/review.go @@ -296,3 +296,39 @@ func (r *ReviewV3Response) FromModel( return r } + +type SensitiveReviewRequest struct { + Size int `json:"size" query:"size" default:"10" validate:"max=10"` + Offset common.CustomTime `json:"offset" query:"offset" swaggertype:"string"` + Open bool `json:"open" query:"open"` + All bool `json:"all" query:"all"` +} + +type SensitiveReviewResponse struct { + ID int `json:"id"` + CreatedAt time.Time `json:"time_created"` + UpdatedAt time.Time `json:"time_updated"` + Content string `json:"content"` + IsActuallySensitive *bool `json:"is_actually_sensitive"` + SensitiveDetail string `json:"sensitive_detail,omitempty"` + ModifyCount int `json:"modify_count"` + Title string `json:"title"` + Course *CourseV1Response `json:"course"` +} + +func (s *SensitiveReviewResponse) FromModel(review *model.Review) *SensitiveReviewResponse { + s.ID = review.ID + s.CreatedAt = review.CreatedAt + s.UpdatedAt = review.UpdatedAt + s.Content = review.Content + s.ModifyCount = review.ModifyCount + s.Title = review.Title + s.Course = new(CourseV1Response).FromModel(nil, review.Course) + s.IsActuallySensitive = review.IsActuallySensitive + s.SensitiveDetail = review.SensitiveDetail + return s +} + +type ModifySensitiveReviewRequest struct { + IsActuallySensitive bool `json:"is_actually_sensitive"` +} diff --git a/go.mod b/go.mod index e9e32dd..eccd547 100644 --- a/go.mod +++ b/go.mod @@ -25,6 +25,7 @@ require ( gorm.io/driver/mysql v1.5.6 gorm.io/driver/postgres v1.5.7 gorm.io/gorm v1.25.9 + gorm.io/plugin/dbresolver v1.5.1 mvdan.cc/xurls/v2 v2.5.0 ) diff --git a/go.sum b/go.sum index 0e4d2c7..ac9792b 100644 --- a/go.sum +++ b/go.sum @@ -64,6 +64,7 @@ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJn github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= github.com/go-playground/validator/v10 v10.19.0 h1:ol+5Fu+cSq9JD7SoSqe04GMI92cbn0+wvQ3bZ8b/AU4= github.com/go-playground/validator/v10 v10.19.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= +github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= @@ -110,6 +111,7 @@ github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8= github.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg= github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= @@ -295,13 +297,18 @@ gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gorm.io/driver/mysql v1.4.3/go.mod h1:sSIebwZAVPiT+27jK9HIwvsqOGKx3YMPmrA3mBJR10c= gorm.io/driver/mysql v1.5.6 h1:Ld4mkIickM+EliaQZQx3uOJDJHtrd70MxAUqWqlx3Y8= gorm.io/driver/mysql v1.5.6/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkDM= gorm.io/driver/postgres v1.5.7 h1:8ptbNJTDbEmhdr62uReG5BGkdQyeasu/FZHxI0IMGnM= gorm.io/driver/postgres v1.5.7/go.mod h1:3e019WlBaYI5o5LIdNV+LyxCMNtLOQETBXL2h4chKpA= +gorm.io/gorm v1.23.8/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk= +gorm.io/gorm v1.25.2/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k= gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= gorm.io/gorm v1.25.9 h1:wct0gxZIELDk8+ZqF/MVnHLkA1rvYlBWUMv2EdsK1g8= gorm.io/gorm v1.25.9/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= +gorm.io/plugin/dbresolver v1.5.1 h1:s9Dj9f7r+1rE3nx/Ywzc85nXptUEaeOO0pt27xdopM8= +gorm.io/plugin/dbresolver v1.5.1/go.mod h1:l4Cn87EHLEYuqUncpEeTC2tTJQkjngPSD+lo8hIvcT0= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= modernc.org/cc/v4 v4.20.0 h1:45Or8mQfbUqJOG9WaxvlFYOAQO0lQ5RvqBcFCXngjxk=