From 8800b376d5fc1e762e08f8d7d9be592d81f3506a Mon Sep 17 00:00:00 2001 From: LinkinStars Date: Mon, 17 Apr 2023 18:04:28 +0800 Subject: [PATCH] feat(privilege): add privileges API --- docs/docs.go | 24 ----- docs/swagger.json | 24 ----- docs/swagger.yaml | 18 ---- i18n/en_US.yaml | 54 ++++++++++ i18n/zh_CN.yaml | 54 ++++++++++ internal/base/constant/rank.go | 99 +++++-------------- internal/base/reason/privilege.go | 30 ++++++ internal/base/server/http.go | 1 + .../plugin_user_center_controller.go | 4 +- .../user_backyard_controller.go | 2 +- internal/entity/auth_user_entity.go | 1 + internal/migrations/v9.go | 38 +++++++ internal/router/answer_api_router.go | 2 +- internal/schema/siteinfo_schema.go | 28 +++--- internal/schema/user_external_login_schema.go | 2 +- internal/service/auth/auth.go | 9 ++ internal/service/siteinfo/siteinfo_service.go | 49 +++++---- internal/service/user_common/user.go | 3 +- .../user_center_login_service.go | 6 +- .../user_external_login_service.go | 6 +- internal/service/user_service.go | 2 +- plugin/user_center.go | 18 ++-- 22 files changed, 284 insertions(+), 190 deletions(-) create mode 100644 internal/base/reason/privilege.go diff --git a/docs/docs.go b/docs/docs.go index dc3e84eb..d394921a 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -7919,10 +7919,6 @@ const docTemplate = `{ "custom_header": { "type": "string", "maxLength": 65536 - }, - "custom_sidebar": { - "type": "string", - "maxLength": 65536 } } }, @@ -7944,10 +7940,6 @@ const docTemplate = `{ "custom_header": { "type": "string", "maxLength": 65536 - }, - "custom_sidebar": { - "type": "string", - "maxLength": 65536 } } }, @@ -8049,18 +8041,10 @@ const docTemplate = `{ "schema.SiteInterfaceReq": { "type": "object", "required": [ - "default_avatar", "language", "time_zone" ], "properties": { - "default_avatar": { - "type": "string", - "enum": [ - "system", - "gravatar" - ] - }, "language": { "type": "string", "maxLength": 128 @@ -8074,18 +8058,10 @@ const docTemplate = `{ "schema.SiteInterfaceResp": { "type": "object", "required": [ - "default_avatar", "language", "time_zone" ], "properties": { - "default_avatar": { - "type": "string", - "enum": [ - "system", - "gravatar" - ] - }, "language": { "type": "string", "maxLength": 128 diff --git a/docs/swagger.json b/docs/swagger.json index e5bb8b3c..8a98a852 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -7907,10 +7907,6 @@ "custom_header": { "type": "string", "maxLength": 65536 - }, - "custom_sidebar": { - "type": "string", - "maxLength": 65536 } } }, @@ -7932,10 +7928,6 @@ "custom_header": { "type": "string", "maxLength": 65536 - }, - "custom_sidebar": { - "type": "string", - "maxLength": 65536 } } }, @@ -8037,18 +8029,10 @@ "schema.SiteInterfaceReq": { "type": "object", "required": [ - "default_avatar", "language", "time_zone" ], "properties": { - "default_avatar": { - "type": "string", - "enum": [ - "system", - "gravatar" - ] - }, "language": { "type": "string", "maxLength": 128 @@ -8062,18 +8046,10 @@ "schema.SiteInterfaceResp": { "type": "object", "required": [ - "default_avatar", "language", "time_zone" ], "properties": { - "default_avatar": { - "type": "string", - "enum": [ - "system", - "gravatar" - ] - }, "language": { "type": "string", "maxLength": 128 diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 2f0c90e3..b25306f1 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -1463,9 +1463,6 @@ definitions: custom_header: maxLength: 65536 type: string - custom_sidebar: - maxLength: 65536 - type: string type: object schema.SiteCustomCssHTMLResp: properties: @@ -1481,9 +1478,6 @@ definitions: custom_header: maxLength: 65536 type: string - custom_sidebar: - maxLength: 65536 - type: string type: object schema.SiteGeneralReq: properties: @@ -1554,11 +1548,6 @@ definitions: type: object schema.SiteInterfaceReq: properties: - default_avatar: - enum: - - system - - gravatar - type: string language: maxLength: 128 type: string @@ -1566,17 +1555,11 @@ definitions: maxLength: 128 type: string required: - - default_avatar - language - time_zone type: object schema.SiteInterfaceResp: properties: - default_avatar: - enum: - - system - - gravatar - type: string language: maxLength: 128 type: string @@ -1584,7 +1567,6 @@ definitions: maxLength: 128 type: string required: - - default_avatar - language - time_zone type: object diff --git a/i18n/en_US.yaml b/i18n/en_US.yaml index bfe7da55..c7ba2b28 100644 --- a/i18n/en_US.yaml +++ b/i18n/en_US.yaml @@ -40,6 +40,60 @@ backend: other: Have the full power to access the site. moderator: other: Has access to all posts except admin settings. + privilege: + level_1: + description: + other: Level 1 (less reputation required for startup community) + level_2: + description: + other: Level 2 (low reputation required for startup community) + level_3: + description: + other: Level 3 (high reputation required for mature community) + rank_question_add_label: + other: Ask question + rank_answer_add_label: + other: Write answer + rank_comment_add_label: + other: Write comment + rank_answer_accept_label: + other: Accept answer + rank_report_add_label: + other: Flag + rank_comment_vote_up_label: + other: Upvote comment + rank_link_url_limit_label: + other: Post more than 2 links at a time + rank_question_vote_up_label: + other: Upvote question + rank_answer_vote_up_label: + other: Upvote answer + rank_question_vote_down_label: + other: Downvote question + rank_answer_vote_down_label: + other: Downvote answer + rank_tag_add_label: + other: Create new tag + rank_tag_edit_label: + other: Edit tag description (need to review) + rank_question_edit_label: + other: Edit other's question (need to review) + rank_answer_edit_label: + other: Edit other's answer (need to review) + rank_question_edit_without_review_label: + other: Edit other's question without review + rank_answer_edit_without_review_label: + other: Edit other's answer without review + rank_question_audit_label: + other: Review question edits + rank_answer_audit_label: + other: Review answer edits + rank_tag_audit_label: + other: Review tag edits + rank_tag_edit_without_review_label: + other: Edit tag description without review + rank_tag_synonym_label: + other: Manage tag synonyms email: other: Email password: diff --git a/i18n/zh_CN.yaml b/i18n/zh_CN.yaml index 7e174512..b468fd38 100644 --- a/i18n/zh_CN.yaml +++ b/i18n/zh_CN.yaml @@ -37,6 +37,60 @@ backend: other: 拥有管理网站的全部权限。 moderator: other: 拥有访问除管理员设置以外的所有权限。 + privilege: + level_1: + description: + other: 等级1(创业社区所需的声望最低) + level_2: + description: + other: 等级2(创业社区所需的声望较低) + level_3: + description: + other: 等级3(成熟社区所需的声望较高) + rank_question_add_label: + other: 提问 + rank_answer_add_label: + other: 回答问题 + rank_comment_add_label: + other: 发表评论 + rank_answer_accept_label: + other: 接受答案 + rank_report_add_label: + other: 举报 + rank_comment_vote_up_label: + other: 评论点赞 + rank_link_url_limit_label: + other: 一次发布超过两个链接 + rank_question_vote_up_label: + other: 问题点赞 + rank_answer_vote_up_label: + other: 答案点赞 + rank_question_vote_down_label: + other: 问题点踩 + rank_answer_vote_down_label: + other: 答案点踩 + rank_tag_add_label: + other: 创建新标签 + rank_tag_edit_label: + other: 编辑标签描述(需要审核) + rank_question_edit_label: + other: 编辑他人提问(需要审核) + rank_answer_edit_label: + other: 编辑他人回答(需要审核) + rank_question_edit_without_review_label: + other: 编辑他人提问(无需审核) + rank_answer_edit_without_review_label: + other: 编辑他人回答(无需审核) + rank_question_audit_label: + other: 审核问题编辑 + rank_answer_audit_label: + other: 审核答案编辑 + rank_tag_audit_label: + other: 审核标签编辑 + rank_tag_edit_without_review_label: + other: 编辑标签描述(无需审核) + rank_tag_synonym_label: + other: 管理标签同义词 email: other: 邮箱 password: diff --git a/internal/base/constant/rank.go b/internal/base/constant/rank.go index c7c86a48..8644c743 100644 --- a/internal/base/constant/rank.go +++ b/internal/base/constant/rank.go @@ -1,9 +1,11 @@ package constant +import "github.com/answerdev/answer/internal/base/reason" + type Privilege struct { + Key string `json:"key"` Label string `json:"label"` Value int `json:"value"` - Key string `json:"-"` } const ( @@ -41,80 +43,29 @@ 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 ( 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}, + {Label: reason.RankQuestionAddLabel, Key: RankQuestionAddKey}, + {Label: reason.RankAnswerAddLabel, Key: RankAnswerAddKey}, + {Label: reason.RankCommentAddLabel, Key: RankCommentAddKey}, + {Label: reason.RankAnswerAcceptLabel, Key: RankAnswerAcceptKey}, + {Label: reason.RankReportAddLabel, Key: RankReportAddKey}, + {Label: reason.RankCommentVoteUpLabel, Key: RankCommentVoteUpKey}, + {Label: reason.RankLinkUrlLimitLabel, Key: RankLinkUrlLimitKey}, + {Label: reason.RankQuestionVoteUpLabel, Key: RankQuestionVoteUpKey}, + {Label: reason.RankAnswerVoteUpLabel, Key: RankAnswerVoteUpKey}, + {Label: reason.RankQuestionVoteDownLabel, Key: RankQuestionVoteDownKey}, + {Label: reason.RankAnswerVoteDownLabel, Key: RankAnswerVoteDownKey}, + {Label: reason.RankTagAddLabel, Key: RankTagAddKey}, + {Label: reason.RankTagEditLabel, Key: RankTagEditKey}, + {Label: reason.RankQuestionEditLabel, Key: RankQuestionEditKey}, + {Label: reason.RankAnswerEditLabel, Key: RankAnswerEditKey}, + {Label: reason.RankQuestionEditWithoutReviewLabel, Key: RankQuestionEditWithoutReviewKey}, + {Label: reason.RankAnswerEditWithoutReviewLabel, Key: RankAnswerEditWithoutReviewKey}, + {Label: reason.RankQuestionAuditLabel, Key: RankQuestionAuditKey}, + {Label: reason.RankAnswerAuditLabel, Key: RankAnswerAuditKey}, + {Label: reason.RankTagAuditLabel, Key: RankTagAuditKey}, + {Label: reason.RankTagEditWithoutReviewLabel, Key: RankTagEditWithoutReviewKey}, + {Label: reason.RankTagSynonymLabel, Key: RankTagSynonymKey}, } ) diff --git a/internal/base/reason/privilege.go b/internal/base/reason/privilege.go new file mode 100644 index 00000000..9f4b5dff --- /dev/null +++ b/internal/base/reason/privilege.go @@ -0,0 +1,30 @@ +package reason + +const ( + PrivilegeLevel1Desc = "privilege.level_1.description" + PrivilegeLevel2Desc = "privilege.level_2.description" + PrivilegeLevel3Desc = "privilege.level_3.description" + + RankQuestionAddLabel = "privilege.rank_question_add_label" + RankAnswerAddLabel = "privilege.rank_answer_add_label" + RankCommentAddLabel = "privilege.rank_comment_add_label" + RankAnswerAcceptLabel = "privilege.rank_answer_accept_label" + RankReportAddLabel = "privilege.rank_report_add_label" + RankCommentVoteUpLabel = "privilege.rank_comment_vote_up_label" + RankLinkUrlLimitLabel = "privilege.rank_link_url_limit_label" + RankQuestionVoteUpLabel = "privilege.rank_question_vote_up_label" + RankAnswerVoteUpLabel = "privilege.rank_answer_vote_up_label" + RankQuestionVoteDownLabel = "privilege.rank_question_vote_down_label" + RankAnswerVoteDownLabel = "privilege.rank_answer_vote_down_label" + RankTagAddLabel = "privilege.rank_tag_add_label" + RankTagEditLabel = "privilege.rank_tag_edit_label" + RankQuestionEditLabel = "privilege.rank_question_edit_label" + RankAnswerEditLabel = "privilege.rank_answer_edit_label" + RankQuestionEditWithoutReviewLabel = "privilege.rank_question_edit_without_review_label" + RankAnswerEditWithoutReviewLabel = "privilege.rank_answer_edit_without_review_label" + RankQuestionAuditLabel = "privilege.rank_question_audit_label" + RankAnswerAuditLabel = "privilege.rank_answer_audit_label" + RankTagAuditLabel = "privilege.rank_tag_audit_label" + RankTagEditWithoutReviewLabel = "privilege.rank_tag_edit_without_review_label" + RankTagSynonymLabel = "privilege.rank_tag_synonym_label" +) diff --git a/internal/base/server/http.go b/internal/base/server/http.go index 55ff1a31..2845e786 100644 --- a/internal/base/server/http.go +++ b/internal/base/server/http.go @@ -68,6 +68,7 @@ func NewHTTPServer(debug bool, // plugin routes pluginAPIRouter.RegisterUnAuthConnectorRouter(mustUnAuthV1) pluginAPIRouter.RegisterAuthUserConnectorRouter(authV1) + pluginAPIRouter.RegisterAuthAdminConnectorRouter(adminauthV1) _ = plugin.CallAgent(func(agent plugin.Agent) error { agent.RegisterUnAuthRouter(unAuthV1) diff --git a/internal/controller/plugin_user_center_controller.go b/internal/controller/plugin_user_center_controller.go index 8c624a2d..b8160f72 100644 --- a/internal/controller/plugin_user_center_controller.go +++ b/internal/controller/plugin_user_center_controller.go @@ -137,7 +137,7 @@ func (uc *UserCenterController) UserCenterLoginCallback(ctx *gin.Context) { return } if len(resp.ErrMsg) > 0 { - ctx.String(http.StatusOK, resp.ErrMsg) + ctx.Redirect(http.StatusFound, "/50x?msg="+resp.ErrMsg) return } userCenter.AfterLogin(userInfo.ExternalID, resp.AccessToken) @@ -172,7 +172,7 @@ func (uc *UserCenterController) UserCenterSignUpCallback(ctx *gin.Context) { return } if len(resp.ErrMsg) > 0 { - ctx.String(http.StatusOK, resp.ErrMsg) + ctx.Redirect(http.StatusFound, "/50x?msg="+resp.ErrMsg) return } userCenter.AfterLogin(userInfo.ExternalID, resp.AccessToken) diff --git a/internal/controller_admin/user_backyard_controller.go b/internal/controller_admin/user_backyard_controller.go index 76fb8378..f1a93ca3 100644 --- a/internal/controller_admin/user_backyard_controller.go +++ b/internal/controller_admin/user_backyard_controller.go @@ -32,7 +32,7 @@ func NewUserAdminController(userService *user_admin.UserAdminService) *UserAdmin // @Success 200 {object} handler.RespBody // @Router /answer/admin/api/user/status [put] func (uc *UserAdminController) UpdateUserStatus(ctx *gin.Context) { - if plugin.UserCenterEnabled() { + if u, ok := plugin.GetUserCenter(); ok && u.Description().UserStatusAgentEnabled { handler.HandleResponse(ctx, errors.Forbidden(reason.ForbiddenError), nil) return } diff --git a/internal/entity/auth_user_entity.go b/internal/entity/auth_user_entity.go index a417cd2a..79d24fb1 100644 --- a/internal/entity/auth_user_entity.go +++ b/internal/entity/auth_user_entity.go @@ -6,4 +6,5 @@ type UserCacheInfo struct { UserStatus int `json:"user_status"` EmailStatus int `json:"email_status"` RoleID int `json:"role_id"` + ExternalID string `json:"external_id"` } diff --git a/internal/migrations/v9.go b/internal/migrations/v9.go index 90331820..7ad0c990 100644 --- a/internal/migrations/v9.go +++ b/internal/migrations/v9.go @@ -7,6 +7,7 @@ import ( "github.com/answerdev/answer/internal/base/constant" "github.com/answerdev/answer/internal/entity" "github.com/answerdev/answer/internal/schema" + "github.com/tidwall/gjson" "xorm.io/xorm" ) @@ -22,10 +23,47 @@ func addLoginLimitations(x *xorm.Engine) error { content := &schema.SiteLoginReq{} _ = json.Unmarshal([]byte(loginSiteInfo.Content), content) content.AllowEmailRegistrations = true + content.AllowEmailDomains = make([]string, 0) _, err = x.ID(loginSiteInfo.ID).Cols("content").Update(loginSiteInfo) if err != nil { return fmt.Errorf("update site info failed: %w", err) } } + + interfaceSiteInfo := &entity.SiteInfo{ + Type: constant.SiteTypeInterface, + } + exist, err = x.Get(interfaceSiteInfo) + if err != nil { + return fmt.Errorf("get config failed: %w", err) + } + siteUsers := &schema.SiteUsersReq{ + AllowUpdateDisplayName: true, + AllowUpdateUsername: true, + AllowUpdateAvatar: true, + AllowUpdateBio: true, + AllowUpdateWebsite: true, + AllowUpdateLocation: true, + } + if exist { + siteUsers.DefaultAvatar = gjson.Get(interfaceSiteInfo.Content, "default_avatar").String() + } + data, _ := json.Marshal(siteUsers) + + exist, err = x.Get(&entity.SiteInfo{Type: constant.SiteTypeUsers}) + if err != nil { + return fmt.Errorf("get config failed: %w", err) + } + if !exist { + usersSiteInfo := &entity.SiteInfo{ + Type: constant.SiteTypeUsers, + Content: string(data), + Status: 1, + } + _, err = x.InsertOne(usersSiteInfo) + if err != nil { + return fmt.Errorf("insert site info failed: %w", err) + } + } return nil } diff --git a/internal/router/answer_api_router.go b/internal/router/answer_api_router.go index ece32cad..f15a6897 100644 --- a/internal/router/answer_api_router.go +++ b/internal/router/answer_api_router.go @@ -206,7 +206,7 @@ func (a *AnswerAPIRouter) RegisterAnswerAPIRouter(r *gin.RouterGroup) { // user r.PUT("/user/password", middleware.BanAPIWhenUserCenterEnabled, a.userController.UserModifyPassWord) - r.PUT("/user/info", middleware.BanAPIWhenUserCenterEnabled, a.userController.UserUpdateInfo) + r.PUT("/user/info", a.userController.UserUpdateInfo) r.PUT("/user/interface", a.userController.UserUpdateInterface) r.POST("/user/notice/set", a.userController.UserNoticeSet) diff --git a/internal/schema/siteinfo_schema.go b/internal/schema/siteinfo_schema.go index a0167de4..21eab8fa 100644 --- a/internal/schema/siteinfo_schema.go +++ b/internal/schema/siteinfo_schema.go @@ -43,9 +43,8 @@ func (r *SiteGeneralReq) FormatSiteUrl() { // SiteInterfaceReq site interface request type SiteInterfaceReq struct { - Language string `validate:"required,gt=1,lte=128" form:"language" json:"language"` - TimeZone string `validate:"required,gt=1,lte=128" form:"time_zone" json:"time_zone"` - DefaultAvatar string `validate:"required,oneof=system gravatar" form:"default_avatar" json:"default_avatar"` + Language string `validate:"required,gt=1,lte=128" form:"language" json:"language"` + TimeZone string `validate:"required,gt=1,lte=128" form:"time_zone" json:"time_zone"` } // SiteBrandingReq site branding request @@ -275,6 +274,7 @@ type GetPrivilegesConfigResp struct { // PrivilegeOption privilege option type PrivilegeOption struct { Level PrivilegeLevel `json:"level"` + LevelDesc string `json:"level_desc"` Privileges []*constant.Privilege `json:"privileges"` } @@ -312,21 +312,27 @@ var ( ) func init() { - for _, option := range []PrivilegeLevel{PrivilegeLevel1, PrivilegeLevel2, PrivilegeLevel3} { - op := &PrivilegeOption{ - Level: option, - } + DefaultPrivilegeOptions = append(DefaultPrivilegeOptions, &PrivilegeOption{ + Level: PrivilegeLevel1, + LevelDesc: reason.PrivilegeLevel1Desc, + }, &PrivilegeOption{ + Level: PrivilegeLevel2, + LevelDesc: reason.PrivilegeLevel2Desc, + }, &PrivilegeOption{ + Level: PrivilegeLevel3, + LevelDesc: reason.PrivilegeLevel3Desc, + }) + + for _, option := range DefaultPrivilegeOptions { 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{ + option.Privileges = append(option.Privileges, &constant.Privilege{ Label: privilege.Label, - Value: privilegeOptionsLevelMapping[privilege.Key][option-1], + Value: privilegeOptionsLevelMapping[privilege.Key][option.Level-1], Key: privilege.Key, }) } - DefaultPrivilegeOptions = append(DefaultPrivilegeOptions, op) } } diff --git a/internal/schema/user_external_login_schema.go b/internal/schema/user_external_login_schema.go index 54735c3d..0474f58a 100644 --- a/internal/schema/user_external_login_schema.go +++ b/internal/schema/user_external_login_schema.go @@ -68,7 +68,7 @@ type UserCenterUserSettingsResp struct { } type UserCenterAdminFunctionAgentResp struct { - RoleAgentEnabled bool `json:"role_agent_enabled"` + UserStatusAgentEnabled bool `json:"user_status_agent_enabled"` } type UserSettingAgent struct { diff --git a/internal/service/auth/auth.go b/internal/service/auth/auth.go index 618ef5bd..22eba69a 100644 --- a/internal/service/auth/auth.go +++ b/internal/service/auth/auth.go @@ -5,6 +5,7 @@ import ( "github.com/answerdev/answer/internal/entity" "github.com/answerdev/answer/pkg/token" + "github.com/answerdev/answer/plugin" "github.com/segmentfault/pacman/log" ) @@ -52,6 +53,14 @@ func (as *AuthService) GetUserCacheInfo(ctx context.Context, accessToken string) return nil, err } } + + // try to get user status from user center + uc, ok := plugin.GetUserCenter() + if ok && len(userCacheInfo.ExternalID) > 0 { + if userStatus := uc.UserStatus(userCacheInfo.ExternalID); userStatus != plugin.UserStatusAvailable { + userCacheInfo.UserStatus = int(userStatus) + } + } return userCacheInfo, nil } diff --git a/internal/service/siteinfo/siteinfo_service.go b/internal/service/siteinfo/siteinfo_service.go index e0d09fe4..b3bb6c80 100644 --- a/internal/service/siteinfo/siteinfo_service.go +++ b/internal/service/siteinfo/siteinfo_service.go @@ -6,6 +6,7 @@ import ( "fmt" "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" "github.com/answerdev/answer/internal/entity" @@ -35,7 +36,7 @@ func NewSiteInfoService( tagCommonService *tagcommon.TagCommonService, configRepo config.ConfigRepo, ) *SiteInfoService { - resp, err := siteInfoCommonService.GetSiteInterface(context.Background()) + resp, err := siteInfoCommonService.GetSiteUsers(context.Background()) if err != nil { log.Error(err) } else { @@ -131,29 +132,18 @@ func (s *SiteInfoService) SaveSiteGeneral(ctx context.Context, req schema.SiteGe } func (s *SiteInfoService) SaveSiteInterface(ctx context.Context, req schema.SiteInterfaceReq) (err error) { - var ( - siteType = "interface" - content []byte - ) - // check language if !translator.CheckLanguageIsValid(req.Language) { err = errors.BadRequest(reason.LangNotFound) return } - content, _ = json.Marshal(req) - + content, _ := json.Marshal(req) data := entity.SiteInfo{ - Type: siteType, + Type: constant.SiteTypeInterface, Content: string(content), } - - err = s.siteInfoRepo.SaveByType(ctx, siteType, &data) - if err == nil { - constant.DefaultAvatar = req.DefaultAvatar - } - return + return s.siteInfoRepo.SaveByType(ctx, constant.SiteTypeInterface, &data) } // SaveSiteBranding save site branding information @@ -235,7 +225,11 @@ func (s *SiteInfoService) SaveSiteUsers(ctx context.Context, req *schema.SiteUse Content: string(content), Status: 1, } - return s.siteInfoRepo.SaveByType(ctx, constant.SiteTypeUsers, data) + err = s.siteInfoRepo.SaveByType(ctx, constant.SiteTypeUsers, data) + if err == nil { + constant.DefaultAvatar = req.DefaultAvatar + } + return err } // GetSMTPConfig get smtp config @@ -329,8 +323,8 @@ func (s *SiteInfoService) GetPrivilegesConfig(ctx context.Context) (resp *schema return nil, err } resp = &schema.GetPrivilegesConfigResp{ - Options: schema.DefaultPrivilegeOptions, - SelectedLevel: schema.PrivilegeLevel2, + Options: s.translatePrivilegeOptions(ctx), + SelectedLevel: schema.PrivilegeLevel3, } if privilege != nil && privilege.Level > 0 { resp.SelectedLevel = privilege.Level @@ -338,6 +332,25 @@ func (s *SiteInfoService) GetPrivilegesConfig(ctx context.Context) (resp *schema return resp, nil } +func (s *SiteInfoService) translatePrivilegeOptions(ctx context.Context) (options []*schema.PrivilegeOption) { + la := handler.GetLangByCtx(ctx) + for _, option := range schema.DefaultPrivilegeOptions { + op := &schema.PrivilegeOption{ + Level: option.Level, + LevelDesc: translator.Tr(la, option.LevelDesc), + } + for _, privilege := range option.Privileges { + op.Privileges = append(op.Privileges, &constant.Privilege{ + Key: privilege.Key, + Label: translator.Tr(la, privilege.Label), + Value: privilege.Value, + }) + } + options = append(options, op) + } + return +} + func (s *SiteInfoService) UpdatePrivilegesConfig(ctx context.Context, req *schema.UpdatePrivilegesConfigReq) (err error) { var chooseOption *schema.PrivilegeOption for _, option := range schema.DefaultPrivilegeOptions { diff --git a/internal/service/user_common/user.go b/internal/service/user_common/user.go index e863c8c9..5432acf8 100644 --- a/internal/service/user_common/user.go +++ b/internal/service/user_common/user.go @@ -150,7 +150,7 @@ func (us *UserCommon) MakeUsername(ctx context.Context, displayName string) (use return username + suffix, nil } -func (us *UserCommon) CacheLoginUserInfo(ctx context.Context, userID string, userStatus, emailStatus int) ( +func (us *UserCommon) CacheLoginUserInfo(ctx context.Context, userID string, userStatus, emailStatus int, externalID string) ( accessToken string, userCacheInfo *entity.UserCacheInfo, err error) { roleID, err := us.userRoleService.GetUserRole(ctx, userID) if err != nil { @@ -162,6 +162,7 @@ func (us *UserCommon) CacheLoginUserInfo(ctx context.Context, userID string, use EmailStatus: emailStatus, UserStatus: userStatus, RoleID: roleID, + ExternalID: externalID, } accessToken, err = us.authService.SetUserCacheInfo(ctx, userCacheInfo) diff --git a/internal/service/user_external_login/user_center_login_service.go b/internal/service/user_external_login/user_center_login_service.go index e7f75664..307590c8 100644 --- a/internal/service/user_external_login/user_center_login_service.go +++ b/internal/service/user_external_login/user_center_login_service.go @@ -78,7 +78,7 @@ func (us *UserCenterLoginService) ExternalLogin( log.Errorf("update user last login date failed: %v", err) } accessToken, _, err := us.userCommonService.CacheLoginUserInfo( - ctx, oldUserInfo.ID, oldUserInfo.MailStatus, oldUserInfo.Status) + ctx, oldUserInfo.ID, oldUserInfo.MailStatus, oldUserInfo.Status, oldExternalLoginUserInfo.ExternalID) return &schema.UserExternalLoginResp{AccessToken: accessToken}, err } } @@ -97,7 +97,7 @@ func (us *UserCenterLoginService) ExternalLogin( us.activeUser(ctx, oldUserInfo) accessToken, _, err := us.userCommonService.CacheLoginUserInfo( - ctx, oldUserInfo.ID, oldUserInfo.MailStatus, oldUserInfo.Status) + ctx, oldUserInfo.ID, oldUserInfo.MailStatus, oldUserInfo.Status, oldExternalLoginUserInfo.ExternalID) return &schema.UserExternalLoginResp{AccessToken: accessToken}, err } @@ -203,7 +203,7 @@ func (us *UserCenterLoginService) UserCenterAdminFunctionAgent(ctx context.Conte return } desc := userCenter.Description() - resp.RoleAgentEnabled = desc.RoleAgentEnabled + resp.UserStatusAgentEnabled = desc.UserStatusAgentEnabled return resp, nil } diff --git a/internal/service/user_external_login/user_external_login_service.go b/internal/service/user_external_login/user_external_login_service.go index fefecf12..24b9037d 100644 --- a/internal/service/user_external_login/user_external_login_service.go +++ b/internal/service/user_external_login/user_external_login_service.go @@ -83,7 +83,7 @@ func (us *UserExternalLoginService) ExternalLogin( log.Error(err) } accessToken, _, err := us.userCommonService.CacheLoginUserInfo( - ctx, oldUserInfo.ID, newMailStatus, oldUserInfo.Status) + ctx, oldUserInfo.ID, newMailStatus, oldUserInfo.Status, oldExternalLoginUserInfo.ExternalID) return &schema.UserExternalLoginResp{AccessToken: accessToken}, err } } @@ -122,7 +122,7 @@ func (us *UserExternalLoginService) ExternalLogin( } accessToken, _, err := us.userCommonService.CacheLoginUserInfo( - ctx, oldUserInfo.ID, newMailStatus, oldUserInfo.Status) + ctx, oldUserInfo.ID, newMailStatus, oldUserInfo.Status, oldExternalLoginUserInfo.ExternalID) return &schema.UserExternalLoginResp{AccessToken: accessToken}, err } @@ -251,7 +251,7 @@ func (us *UserExternalLoginService) ExternalLoginBindingUserSendEmail( return nil, err } resp.AccessToken, _, err = us.userCommonService.CacheLoginUserInfo( - ctx, userInfo.ID, userInfo.MailStatus, userInfo.Status) + ctx, userInfo.ID, userInfo.MailStatus, userInfo.Status, externalLoginInfo.ExternalID) if err != nil { log.Error(err) } diff --git a/internal/service/user_service.go b/internal/service/user_service.go index 47256b06..f9a300c2 100644 --- a/internal/service/user_service.go +++ b/internal/service/user_service.go @@ -499,7 +499,7 @@ func (us *UserService) UserVerifyEmail(ctx context.Context, req *schema.UserVeri } accessToken, userCacheInfo, err := us.userCommonService.CacheLoginUserInfo( - ctx, userInfo.ID, userInfo.MailStatus, userInfo.Status) + ctx, userInfo.ID, userInfo.MailStatus, userInfo.Status, "") if err != nil { return nil, err } diff --git a/plugin/user_center.go b/plugin/user_center.go index 66687dc1..45501b24 100644 --- a/plugin/user_center.go +++ b/plugin/user_center.go @@ -12,6 +12,8 @@ type UserCenter interface { SignUpCallback(ctx *GinContext) (userInfo *UserCenterBasicUserInfo, err error) // UserInfo returns the user information UserInfo(externalID string) (userInfo *UserCenterBasicUserInfo, err error) + // UserStatus returns the latest user status + UserStatus(externalID string) (userStatus UserStatus) // UserList returns the user list information UserList(externalIDs []string) (userInfo []*UserCenterBasicUserInfo, err error) // UserSettings returns the user settings @@ -23,14 +25,14 @@ type UserCenter interface { } type UserCenterDesc struct { - Name string `json:"name"` - Icon string `json:"icon"` - Url string `json:"url"` - LoginRedirectURL string `json:"login_redirect_url"` - SignUpRedirectURL string `json:"sign_up_redirect_url"` - RankAgentEnabled bool `json:"rank_agent_enabled"` - RoleAgentEnabled bool `json:"role_agent_enabled"` - MustAuthEmailEnabled bool `json:"must_auth_email_enabled"` + Name string `json:"name"` + Icon string `json:"icon"` + Url string `json:"url"` + LoginRedirectURL string `json:"login_redirect_url"` + SignUpRedirectURL string `json:"sign_up_redirect_url"` + RankAgentEnabled bool `json:"rank_agent_enabled"` + UserStatusAgentEnabled bool `json:"user_status_agent_enabled"` + MustAuthEmailEnabled bool `json:"must_auth_email_enabled"` } type UserStatus int