diff --git a/internal/base/constant/constant.go b/internal/base/constant/constant.go index 40990b93..5705bb72 100644 --- a/internal/base/constant/constant.go +++ b/internal/base/constant/constant.go @@ -65,6 +65,7 @@ const ( SiteTypeLogin = "login" SiteTypeCustomCssHTML = "css-html" SiteTypeTheme = "theme" + SiteTypePrivileges = "privileges" ) func ExistInPathIgnore(name string) bool { diff --git a/internal/base/constant/rank.go b/internal/base/constant/rank.go index 44f7fba7..c7c86a48 100644 --- a/internal/base/constant/rank.go +++ b/internal/base/constant/rank.go @@ -1,5 +1,11 @@ package constant +type Privilege struct { + Label string `json:"label"` + Value int `json:"value"` + Key string `json:"-"` +} + const ( RankQuestionAddKey = "rank.question.add" RankQuestionEditKey = "rank.question.edit" @@ -35,39 +41,80 @@ const ( RankTagUseReservedTagKey = "rank.tag.use_reserved_tag" ) +//| Permission | Level 1 | Level 2 | Level 3 | Custom Level | +//| -------------------------------------- | ------------------------------------------------ | --------------------------------------------- | --------------------------------------------- | ------------ | +//| Description | less reputation required for private team, group | low reputation required for startup community | high reputation required for mature community | | +//| Ask question | 1 | 1 | 1 | | +//| Write answer | 1 | 1 | 1 | | +//| Write comment | 1 | 1 | 1 | | +//| Accept answer | 1 | 1 | 1 | | +//| Flag | 1 | 1 | 1 | | +//| Upvote comment | 1 | 1 | 1 | | +//| Post more than 2 links at a time | 1 | 10 | 10 | | +//| Upvote question | 1 | 1 | 15 | | +//| Upvote answer | 1 | 1 | 15 | | +//| Downvote question | 125 | 125 | 125 | | +//| Downvote answer | 125 | 125 | 125 | | +//| Create new tag | 1 | 750 | 1500 | | +//| Edit tag description (need to review) | 1 | 50 | 100 | | +//| Edit other's question (need to review) | 1 | 100 | 200 | | +//| Edit other's answer (need to review) | 1 | 100 | 200 | | +//| Edit other's question without review | 1 | 1000 | 2000 | | +//| Edit other's answer without review | 1 | 1000 | 2000 | | +//| Revew question edits | 1 | 1000 | 2000 | | +//| Review answer edits | 1 | 1000 | 2000 | | +//| Review tag edits | 1 | 2500 | 5000 | | +//| Edit tag description without review | 1 | 10000 | 20000 | | +//| Manage tag synonyms | 1 | 10000 | 20000 | | + +const ( + RankQuestionAddLabel = "Ask question" + RankAnswerAddLabel = "Write answer" + RankCommentAddLabel = "Write comment" + RankAnswerAcceptLabel = "Accept answer" + RankReportAddLabel = "Flag" + RankCommentVoteUpLabel = "Upvote comment" + RankLinkUrlLimitLabel = "Post more than 2 links at a time" + RankQuestionVoteUpLabel = "Upvote question" + RankAnswerVoteUpLabel = "Upvote answer" + RankQuestionVoteDownLabel = "Downvote question" + RankAnswerVoteDownLabel = "Downvote answer" + RankTagAddLabel = "Create new tag" + RankTagEditLabel = "Edit tag description (need to review)" + RankQuestionEditLabel = "Edit other's question (need to review)" + RankAnswerEditLabel = "Edit other's answer (need to review)" + RankQuestionEditWithoutReviewLabel = "Edit other's question without review" + RankAnswerEditWithoutReviewLabel = "Edit other's answer without review" + RankQuestionAuditLabel = "Review question edits" + RankAnswerAuditLabel = "Review answer edits" + RankTagAuditLabel = "Review tag edits" + RankTagEditWithoutReviewLabel = "Edit tag description without review" + RankTagSynonymLabel = "Manage tag synonyms" +) + var ( - RankAllKeys = []string{ - RankQuestionAddKey, - RankQuestionEditKey, - RankQuestionDeleteKey, - RankQuestionVoteUpKey, - RankQuestionVoteDownKey, - RankAnswerAddKey, - RankAnswerEditKey, - RankAnswerDeleteKey, - RankAnswerAcceptKey, - RankAnswerVoteUpKey, - RankAnswerVoteDownKey, - RankCommentAddKey, - RankCommentEditKey, - RankCommentDeleteKey, - RankReportAddKey, - RankTagAddKey, - RankTagEditKey, - RankTagDeleteKey, - RankTagSynonymKey, - RankLinkUrlLimitKey, - RankVoteDetailKey, - RankCommentVoteUpKey, - RankCommentVoteDownKey, - RankQuestionEditWithoutReviewKey, - RankAnswerEditWithoutReviewKey, - RankTagEditWithoutReviewKey, - RankAnswerAuditKey, - RankQuestionAuditKey, - RankTagAuditKey, - RankQuestionCloseKey, - RankQuestionReopenKey, - RankTagUseReservedTagKey, + RankAllPrivileges = []*Privilege{ + {Label: RankQuestionAddLabel, Key: RankQuestionAddKey}, + {Label: RankAnswerAddLabel, Key: RankAnswerAddKey}, + {Label: RankCommentAddLabel, Key: RankCommentAddKey}, + {Label: RankAnswerAcceptLabel, Key: RankAnswerAcceptKey}, + {Label: RankReportAddLabel, Key: RankReportAddKey}, + {Label: RankCommentVoteUpLabel, Key: RankCommentVoteUpKey}, + {Label: RankLinkUrlLimitLabel, Key: RankLinkUrlLimitKey}, + {Label: RankQuestionVoteUpLabel, Key: RankQuestionVoteUpKey}, + {Label: RankAnswerVoteUpLabel, Key: RankAnswerVoteUpKey}, + {Label: RankQuestionVoteDownLabel, Key: RankQuestionVoteDownKey}, + {Label: RankAnswerVoteDownLabel, Key: RankAnswerVoteDownKey}, + {Label: RankTagAddLabel, Key: RankTagAddKey}, + {Label: RankTagEditLabel, Key: RankTagEditKey}, + {Label: RankQuestionEditLabel, Key: RankQuestionEditKey}, + {Label: RankAnswerEditLabel, Key: RankAnswerEditKey}, + {Label: RankQuestionEditWithoutReviewLabel, Key: RankQuestionEditWithoutReviewKey}, + {Label: RankAnswerEditWithoutReviewLabel, Key: RankAnswerEditWithoutReviewKey}, + {Label: RankQuestionAuditLabel, Key: RankQuestionAuditKey}, + {Label: RankAnswerAuditLabel, Key: RankAnswerAuditKey}, + {Label: RankTagAuditLabel, Key: RankTagAuditKey}, + {Label: RankTagEditWithoutReviewLabel, Key: RankTagEditWithoutReviewKey}, + {Label: RankTagSynonymLabel, Key: RankTagSynonymKey}, } ) diff --git a/internal/controller_admin/siteinfo_controller.go b/internal/controller_admin/siteinfo_controller.go index 25bd9a0b..25a9c979 100644 --- a/internal/controller_admin/siteinfo_controller.go +++ b/internal/controller_admin/siteinfo_controller.go @@ -386,9 +386,9 @@ func (sc *SiteInfoController) GetPrivilegesConfig(ctx *gin.Context) { // @Security ApiKeyAuth // @Tags admin // @Produce json -// @Param data body schema.UpdatePrivilegesConfigReq true "smtp config" +// @Param data body schema.UpdatePrivilegesConfigReq true "config" // @Success 200 {object} handler.RespBody{} -// @Router /answer/admin/api/setting/smtp [put] +// @Router /answer/admin/api/setting/privileges [put] func (sc *SiteInfoController) UpdatePrivilegesConfig(ctx *gin.Context) { req := &schema.UpdatePrivilegesConfigReq{} if handler.BindAndCheck(ctx, req) { diff --git a/internal/schema/siteinfo_schema.go b/internal/schema/siteinfo_schema.go index d1a6f784..84076296 100644 --- a/internal/schema/siteinfo_schema.go +++ b/internal/schema/siteinfo_schema.go @@ -6,6 +6,7 @@ import ( "net/mail" "net/url" + "github.com/answerdev/answer/internal/base/constant" "github.com/answerdev/answer/internal/base/handler" "github.com/answerdev/answer/internal/base/reason" "github.com/answerdev/answer/internal/base/translator" @@ -234,12 +235,78 @@ type GetManifestJsonResp struct { BackgroundColor string `json:"background_color"` } -// GetPrivilegesConfigResp +const ( + // PrivilegeLevel1 low + PrivilegeLevel1 PrivilegeLevel = 1 + // PrivilegeLevel2 medium + PrivilegeLevel2 PrivilegeLevel = 2 + // PrivilegeLevel3 high + PrivilegeLevel3 PrivilegeLevel = 3 +) + +type PrivilegeLevel int + +// GetPrivilegesConfigResp get privileges config response type GetPrivilegesConfigResp struct { - Privileges map[string]int `json:"privileges"` + Options []*PrivilegeOption `json:"options"` + SelectedLevel PrivilegeLevel `json:"selected_level"` } -// UpdatePrivilegesConfigReq -type UpdatePrivilegesConfigReq struct { - Privileges map[string]int `json:"privileges"` +// PrivilegeOption privilege option +type PrivilegeOption struct { + Level PrivilegeLevel `json:"level"` + Privileges []*constant.Privilege `json:"privileges"` +} + +// UpdatePrivilegesConfigReq update privileges config request +type UpdatePrivilegesConfigReq struct { + Level PrivilegeLevel `validate:"required,min=1,max=3" json:"level"` +} + +var ( + DefaultPrivilegeOptions []*PrivilegeOption + privilegeOptionsLevelMapping = map[string][]int{ + constant.RankQuestionAddKey: {1, 1, 1}, + constant.RankAnswerAddKey: {1, 1, 1}, + constant.RankCommentAddKey: {1, 1, 1}, + constant.RankAnswerAcceptKey: {1, 1, 1}, + constant.RankReportAddKey: {1, 1, 1}, + constant.RankCommentVoteUpKey: {1, 1, 1}, + constant.RankLinkUrlLimitKey: {1, 10, 10}, + constant.RankQuestionVoteUpKey: {1, 1, 15}, + constant.RankAnswerVoteUpKey: {1, 1, 15}, + constant.RankQuestionVoteDownKey: {125, 125, 125}, + constant.RankAnswerVoteDownKey: {125, 125, 125}, + constant.RankTagAddKey: {1, 750, 1500}, + constant.RankTagEditKey: {1, 50, 100}, + constant.RankQuestionEditKey: {1, 100, 200}, + constant.RankAnswerEditKey: {1, 100, 200}, + constant.RankQuestionEditWithoutReviewKey: {1, 1000, 2000}, + constant.RankAnswerEditWithoutReviewKey: {1, 1000, 2000}, + constant.RankQuestionAuditKey: {1, 1000, 2000}, + constant.RankAnswerAuditKey: {1, 1000, 2000}, + constant.RankTagAuditKey: {1, 2500, 5000}, + constant.RankTagEditWithoutReviewKey: {1, 10000, 20000}, + constant.RankTagSynonymKey: {1, 10000, 20000}, + } +) + +func init() { + for _, option := range []PrivilegeLevel{PrivilegeLevel1, PrivilegeLevel2, PrivilegeLevel3} { + op := &PrivilegeOption{ + Level: option, + } + for _, privilege := range constant.RankAllPrivileges { + if len(privilegeOptionsLevelMapping[privilege.Key]) == 0 { + fmt.Println("privilege key not found: ", privilege.Key) + continue + } + op.Privileges = append(op.Privileges, &constant.Privilege{ + Label: privilege.Label, + Value: privilegeOptionsLevelMapping[privilege.Key][option-1], + Key: privilege.Key, + }) + } + DefaultPrivilegeOptions = append(DefaultPrivilegeOptions, op) + } } diff --git a/internal/service/siteinfo/siteinfo_service.go b/internal/service/siteinfo/siteinfo_service.go index 06f6205f..bbd7ea29 100644 --- a/internal/service/siteinfo/siteinfo_service.go +++ b/internal/service/siteinfo/siteinfo_service.go @@ -308,25 +308,49 @@ func (s *SiteInfoService) SaveSeo(ctx context.Context, req schema.SiteSeoReq) (e } func (s *SiteInfoService) GetPrivilegesConfig(ctx context.Context) (resp *schema.GetPrivilegesConfigResp, err error) { - resp = &schema.GetPrivilegesConfigResp{ - Privileges: make(map[string]int, 0), + privilege := &schema.UpdatePrivilegesConfigReq{} + if err = s.siteInfoCommonService.GetSiteInfoByType(ctx, constant.SiteTypePrivileges, privilege); err != nil { + return nil, err } - for _, key := range constant.RankAllKeys { - v, err := s.configRepo.GetInt(key) - if err != nil { - log.Error(err) - continue - } - resp.Privileges[key] = v + resp = &schema.GetPrivilegesConfigResp{ + Options: schema.DefaultPrivilegeOptions, + SelectedLevel: schema.PrivilegeLevel2, + } + if privilege != nil && privilege.Level > 0 { + resp.SelectedLevel = privilege.Level } return resp, nil } func (s *SiteInfoService) UpdatePrivilegesConfig(ctx context.Context, req *schema.UpdatePrivilegesConfigReq) (err error) { - for key, value := range req.Privileges { - err = s.configRepo.SetConfig(key, fmt.Sprintf("%d", value)) + var chooseOption *schema.PrivilegeOption + for _, option := range schema.DefaultPrivilegeOptions { + if option.Level == req.Level { + chooseOption = option + break + } + } + if chooseOption == nil { + return nil + } + + // update site info that user choose which privilege level + content, _ := json.Marshal(req) + data := &entity.SiteInfo{ + Type: constant.SiteTypePrivileges, + Content: string(content), + Status: 1, + } + err = s.siteInfoRepo.SaveByType(ctx, constant.SiteTypePrivileges, data) + if err != nil { + return err + } + + // update privilege in config + for _, privilege := range chooseOption.Privileges { + err = s.configRepo.SetConfig(privilege.Key, fmt.Sprintf("%d", privilege.Value)) if err != nil { - return + return err } } return diff --git a/internal/service/siteinfo_common/siteinfo_service.go b/internal/service/siteinfo_common/siteinfo_service.go index b2a89643..81019e4d 100644 --- a/internal/service/siteinfo_common/siteinfo_service.go +++ b/internal/service/siteinfo_common/siteinfo_service.go @@ -43,7 +43,7 @@ func NewSiteInfoCommonService(siteInfoRepo SiteInfoRepo) *SiteInfoCommonService // GetSiteGeneral get site info general func (s *SiteInfoCommonService) GetSiteGeneral(ctx context.Context) (resp *schema.SiteGeneralResp, err error) { resp = &schema.SiteGeneralResp{} - if err = s.getSiteInfoByType(ctx, constant.SiteTypeGeneral, resp); err != nil { + if err = s.GetSiteInfoByType(ctx, constant.SiteTypeGeneral, resp); err != nil { return nil, err } return resp, nil @@ -52,7 +52,7 @@ func (s *SiteInfoCommonService) GetSiteGeneral(ctx context.Context) (resp *schem // GetSiteInterface get site info interface func (s *SiteInfoCommonService) GetSiteInterface(ctx context.Context) (resp *schema.SiteInterfaceResp, err error) { resp = &schema.SiteInterfaceResp{} - if err = s.getSiteInfoByType(ctx, constant.SiteTypeInterface, resp); err != nil { + if err = s.GetSiteInfoByType(ctx, constant.SiteTypeInterface, resp); err != nil { return nil, err } return resp, nil @@ -61,7 +61,7 @@ func (s *SiteInfoCommonService) GetSiteInterface(ctx context.Context) (resp *sch // GetSiteBranding get site info branding func (s *SiteInfoCommonService) GetSiteBranding(ctx context.Context) (resp *schema.SiteBrandingResp, err error) { resp = &schema.SiteBrandingResp{} - if err = s.getSiteInfoByType(ctx, constant.SiteTypeBranding, resp); err != nil { + if err = s.GetSiteInfoByType(ctx, constant.SiteTypeBranding, resp); err != nil { return nil, err } return resp, nil @@ -70,7 +70,7 @@ func (s *SiteInfoCommonService) GetSiteBranding(ctx context.Context) (resp *sche // GetSiteWrite get site info write func (s *SiteInfoCommonService) GetSiteWrite(ctx context.Context) (resp *schema.SiteWriteResp, err error) { resp = &schema.SiteWriteResp{} - if err = s.getSiteInfoByType(ctx, constant.SiteTypeWrite, resp); err != nil { + if err = s.GetSiteInfoByType(ctx, constant.SiteTypeWrite, resp); err != nil { return nil, err } return resp, nil @@ -79,7 +79,7 @@ func (s *SiteInfoCommonService) GetSiteWrite(ctx context.Context) (resp *schema. // GetSiteLegal get site info write func (s *SiteInfoCommonService) GetSiteLegal(ctx context.Context) (resp *schema.SiteLegalResp, err error) { resp = &schema.SiteLegalResp{} - if err = s.getSiteInfoByType(ctx, constant.SiteTypeLegal, resp); err != nil { + if err = s.GetSiteInfoByType(ctx, constant.SiteTypeLegal, resp); err != nil { return nil, err } return resp, nil @@ -88,7 +88,7 @@ func (s *SiteInfoCommonService) GetSiteLegal(ctx context.Context) (resp *schema. // GetSiteLogin get site login config func (s *SiteInfoCommonService) GetSiteLogin(ctx context.Context) (resp *schema.SiteLoginResp, err error) { resp = &schema.SiteLoginResp{} - if err = s.getSiteInfoByType(ctx, constant.SiteTypeLogin, resp); err != nil { + if err = s.GetSiteInfoByType(ctx, constant.SiteTypeLogin, resp); err != nil { return nil, err } return resp, nil @@ -97,7 +97,7 @@ func (s *SiteInfoCommonService) GetSiteLogin(ctx context.Context) (resp *schema. // GetSiteCustomCssHTML get site custom css html config func (s *SiteInfoCommonService) GetSiteCustomCssHTML(ctx context.Context) (resp *schema.SiteCustomCssHTMLResp, err error) { resp = &schema.SiteCustomCssHTMLResp{} - if err = s.getSiteInfoByType(ctx, constant.SiteTypeCustomCssHTML, resp); err != nil { + if err = s.GetSiteInfoByType(ctx, constant.SiteTypeCustomCssHTML, resp); err != nil { return nil, err } return resp, nil @@ -108,7 +108,7 @@ func (s *SiteInfoCommonService) GetSiteTheme(ctx context.Context) (resp *schema. resp = &schema.SiteThemeResp{ ThemeOptions: schema.GetThemeOptions, } - if err = s.getSiteInfoByType(ctx, constant.SiteTypeTheme, resp); err != nil { + if err = s.GetSiteInfoByType(ctx, constant.SiteTypeTheme, resp); err != nil { return nil, err } resp.TrTheme(ctx) @@ -118,13 +118,13 @@ func (s *SiteInfoCommonService) GetSiteTheme(ctx context.Context) (resp *schema. // GetSiteSeo get site seo func (s *SiteInfoCommonService) GetSiteSeo(ctx context.Context) (resp *schema.SiteSeoReq, err error) { resp = &schema.SiteSeoReq{} - if err = s.getSiteInfoByType(ctx, constant.SiteTypeSeo, resp); err != nil { + if err = s.GetSiteInfoByType(ctx, constant.SiteTypeSeo, resp); err != nil { return nil, err } return resp, nil } -func (s *SiteInfoCommonService) getSiteInfoByType(ctx context.Context, siteType string, resp interface{}) (err error) { +func (s *SiteInfoCommonService) GetSiteInfoByType(ctx context.Context, siteType string, resp interface{}) (err error) { siteInfo, exist, err := s.siteInfoRepo.GetByType(ctx, siteType) if err != nil { return err