feat(privilege): add privileges API

This commit is contained in:
LinkinStars 2023-04-17 18:04:28 +08:00
parent 910484013c
commit 8800b376d5
22 changed files with 284 additions and 190 deletions

View File

@ -7919,10 +7919,6 @@ const docTemplate = `{
"custom_header": { "custom_header": {
"type": "string", "type": "string",
"maxLength": 65536 "maxLength": 65536
},
"custom_sidebar": {
"type": "string",
"maxLength": 65536
} }
} }
}, },
@ -7944,10 +7940,6 @@ const docTemplate = `{
"custom_header": { "custom_header": {
"type": "string", "type": "string",
"maxLength": 65536 "maxLength": 65536
},
"custom_sidebar": {
"type": "string",
"maxLength": 65536
} }
} }
}, },
@ -8049,18 +8041,10 @@ const docTemplate = `{
"schema.SiteInterfaceReq": { "schema.SiteInterfaceReq": {
"type": "object", "type": "object",
"required": [ "required": [
"default_avatar",
"language", "language",
"time_zone" "time_zone"
], ],
"properties": { "properties": {
"default_avatar": {
"type": "string",
"enum": [
"system",
"gravatar"
]
},
"language": { "language": {
"type": "string", "type": "string",
"maxLength": 128 "maxLength": 128
@ -8074,18 +8058,10 @@ const docTemplate = `{
"schema.SiteInterfaceResp": { "schema.SiteInterfaceResp": {
"type": "object", "type": "object",
"required": [ "required": [
"default_avatar",
"language", "language",
"time_zone" "time_zone"
], ],
"properties": { "properties": {
"default_avatar": {
"type": "string",
"enum": [
"system",
"gravatar"
]
},
"language": { "language": {
"type": "string", "type": "string",
"maxLength": 128 "maxLength": 128

View File

@ -7907,10 +7907,6 @@
"custom_header": { "custom_header": {
"type": "string", "type": "string",
"maxLength": 65536 "maxLength": 65536
},
"custom_sidebar": {
"type": "string",
"maxLength": 65536
} }
} }
}, },
@ -7932,10 +7928,6 @@
"custom_header": { "custom_header": {
"type": "string", "type": "string",
"maxLength": 65536 "maxLength": 65536
},
"custom_sidebar": {
"type": "string",
"maxLength": 65536
} }
} }
}, },
@ -8037,18 +8029,10 @@
"schema.SiteInterfaceReq": { "schema.SiteInterfaceReq": {
"type": "object", "type": "object",
"required": [ "required": [
"default_avatar",
"language", "language",
"time_zone" "time_zone"
], ],
"properties": { "properties": {
"default_avatar": {
"type": "string",
"enum": [
"system",
"gravatar"
]
},
"language": { "language": {
"type": "string", "type": "string",
"maxLength": 128 "maxLength": 128
@ -8062,18 +8046,10 @@
"schema.SiteInterfaceResp": { "schema.SiteInterfaceResp": {
"type": "object", "type": "object",
"required": [ "required": [
"default_avatar",
"language", "language",
"time_zone" "time_zone"
], ],
"properties": { "properties": {
"default_avatar": {
"type": "string",
"enum": [
"system",
"gravatar"
]
},
"language": { "language": {
"type": "string", "type": "string",
"maxLength": 128 "maxLength": 128

View File

@ -1463,9 +1463,6 @@ definitions:
custom_header: custom_header:
maxLength: 65536 maxLength: 65536
type: string type: string
custom_sidebar:
maxLength: 65536
type: string
type: object type: object
schema.SiteCustomCssHTMLResp: schema.SiteCustomCssHTMLResp:
properties: properties:
@ -1481,9 +1478,6 @@ definitions:
custom_header: custom_header:
maxLength: 65536 maxLength: 65536
type: string type: string
custom_sidebar:
maxLength: 65536
type: string
type: object type: object
schema.SiteGeneralReq: schema.SiteGeneralReq:
properties: properties:
@ -1554,11 +1548,6 @@ definitions:
type: object type: object
schema.SiteInterfaceReq: schema.SiteInterfaceReq:
properties: properties:
default_avatar:
enum:
- system
- gravatar
type: string
language: language:
maxLength: 128 maxLength: 128
type: string type: string
@ -1566,17 +1555,11 @@ definitions:
maxLength: 128 maxLength: 128
type: string type: string
required: required:
- default_avatar
- language - language
- time_zone - time_zone
type: object type: object
schema.SiteInterfaceResp: schema.SiteInterfaceResp:
properties: properties:
default_avatar:
enum:
- system
- gravatar
type: string
language: language:
maxLength: 128 maxLength: 128
type: string type: string
@ -1584,7 +1567,6 @@ definitions:
maxLength: 128 maxLength: 128
type: string type: string
required: required:
- default_avatar
- language - language
- time_zone - time_zone
type: object type: object

View File

@ -40,6 +40,60 @@ backend:
other: Have the full power to access the site. other: Have the full power to access the site.
moderator: moderator:
other: Has access to all posts except admin settings. 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: email:
other: Email other: Email
password: password:

View File

@ -37,6 +37,60 @@ backend:
other: 拥有管理网站的全部权限。 other: 拥有管理网站的全部权限。
moderator: moderator:
other: 拥有访问除管理员设置以外的所有权限。 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: email:
other: 邮箱 other: 邮箱
password: password:

View File

@ -1,9 +1,11 @@
package constant package constant
import "github.com/answerdev/answer/internal/base/reason"
type Privilege struct { type Privilege struct {
Key string `json:"key"`
Label string `json:"label"` Label string `json:"label"`
Value int `json:"value"` Value int `json:"value"`
Key string `json:"-"`
} }
const ( const (
@ -41,80 +43,29 @@ const (
RankTagUseReservedTagKey = "rank.tag.use_reserved_tag" 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 ( var (
RankAllPrivileges = []*Privilege{ RankAllPrivileges = []*Privilege{
{Label: RankQuestionAddLabel, Key: RankQuestionAddKey}, {Label: reason.RankQuestionAddLabel, Key: RankQuestionAddKey},
{Label: RankAnswerAddLabel, Key: RankAnswerAddKey}, {Label: reason.RankAnswerAddLabel, Key: RankAnswerAddKey},
{Label: RankCommentAddLabel, Key: RankCommentAddKey}, {Label: reason.RankCommentAddLabel, Key: RankCommentAddKey},
{Label: RankAnswerAcceptLabel, Key: RankAnswerAcceptKey}, {Label: reason.RankAnswerAcceptLabel, Key: RankAnswerAcceptKey},
{Label: RankReportAddLabel, Key: RankReportAddKey}, {Label: reason.RankReportAddLabel, Key: RankReportAddKey},
{Label: RankCommentVoteUpLabel, Key: RankCommentVoteUpKey}, {Label: reason.RankCommentVoteUpLabel, Key: RankCommentVoteUpKey},
{Label: RankLinkUrlLimitLabel, Key: RankLinkUrlLimitKey}, {Label: reason.RankLinkUrlLimitLabel, Key: RankLinkUrlLimitKey},
{Label: RankQuestionVoteUpLabel, Key: RankQuestionVoteUpKey}, {Label: reason.RankQuestionVoteUpLabel, Key: RankQuestionVoteUpKey},
{Label: RankAnswerVoteUpLabel, Key: RankAnswerVoteUpKey}, {Label: reason.RankAnswerVoteUpLabel, Key: RankAnswerVoteUpKey},
{Label: RankQuestionVoteDownLabel, Key: RankQuestionVoteDownKey}, {Label: reason.RankQuestionVoteDownLabel, Key: RankQuestionVoteDownKey},
{Label: RankAnswerVoteDownLabel, Key: RankAnswerVoteDownKey}, {Label: reason.RankAnswerVoteDownLabel, Key: RankAnswerVoteDownKey},
{Label: RankTagAddLabel, Key: RankTagAddKey}, {Label: reason.RankTagAddLabel, Key: RankTagAddKey},
{Label: RankTagEditLabel, Key: RankTagEditKey}, {Label: reason.RankTagEditLabel, Key: RankTagEditKey},
{Label: RankQuestionEditLabel, Key: RankQuestionEditKey}, {Label: reason.RankQuestionEditLabel, Key: RankQuestionEditKey},
{Label: RankAnswerEditLabel, Key: RankAnswerEditKey}, {Label: reason.RankAnswerEditLabel, Key: RankAnswerEditKey},
{Label: RankQuestionEditWithoutReviewLabel, Key: RankQuestionEditWithoutReviewKey}, {Label: reason.RankQuestionEditWithoutReviewLabel, Key: RankQuestionEditWithoutReviewKey},
{Label: RankAnswerEditWithoutReviewLabel, Key: RankAnswerEditWithoutReviewKey}, {Label: reason.RankAnswerEditWithoutReviewLabel, Key: RankAnswerEditWithoutReviewKey},
{Label: RankQuestionAuditLabel, Key: RankQuestionAuditKey}, {Label: reason.RankQuestionAuditLabel, Key: RankQuestionAuditKey},
{Label: RankAnswerAuditLabel, Key: RankAnswerAuditKey}, {Label: reason.RankAnswerAuditLabel, Key: RankAnswerAuditKey},
{Label: RankTagAuditLabel, Key: RankTagAuditKey}, {Label: reason.RankTagAuditLabel, Key: RankTagAuditKey},
{Label: RankTagEditWithoutReviewLabel, Key: RankTagEditWithoutReviewKey}, {Label: reason.RankTagEditWithoutReviewLabel, Key: RankTagEditWithoutReviewKey},
{Label: RankTagSynonymLabel, Key: RankTagSynonymKey}, {Label: reason.RankTagSynonymLabel, Key: RankTagSynonymKey},
} }
) )

View File

@ -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"
)

View File

@ -68,6 +68,7 @@ func NewHTTPServer(debug bool,
// plugin routes // plugin routes
pluginAPIRouter.RegisterUnAuthConnectorRouter(mustUnAuthV1) pluginAPIRouter.RegisterUnAuthConnectorRouter(mustUnAuthV1)
pluginAPIRouter.RegisterAuthUserConnectorRouter(authV1) pluginAPIRouter.RegisterAuthUserConnectorRouter(authV1)
pluginAPIRouter.RegisterAuthAdminConnectorRouter(adminauthV1)
_ = plugin.CallAgent(func(agent plugin.Agent) error { _ = plugin.CallAgent(func(agent plugin.Agent) error {
agent.RegisterUnAuthRouter(unAuthV1) agent.RegisterUnAuthRouter(unAuthV1)

View File

@ -137,7 +137,7 @@ func (uc *UserCenterController) UserCenterLoginCallback(ctx *gin.Context) {
return return
} }
if len(resp.ErrMsg) > 0 { if len(resp.ErrMsg) > 0 {
ctx.String(http.StatusOK, resp.ErrMsg) ctx.Redirect(http.StatusFound, "/50x?msg="+resp.ErrMsg)
return return
} }
userCenter.AfterLogin(userInfo.ExternalID, resp.AccessToken) userCenter.AfterLogin(userInfo.ExternalID, resp.AccessToken)
@ -172,7 +172,7 @@ func (uc *UserCenterController) UserCenterSignUpCallback(ctx *gin.Context) {
return return
} }
if len(resp.ErrMsg) > 0 { if len(resp.ErrMsg) > 0 {
ctx.String(http.StatusOK, resp.ErrMsg) ctx.Redirect(http.StatusFound, "/50x?msg="+resp.ErrMsg)
return return
} }
userCenter.AfterLogin(userInfo.ExternalID, resp.AccessToken) userCenter.AfterLogin(userInfo.ExternalID, resp.AccessToken)

View File

@ -32,7 +32,7 @@ func NewUserAdminController(userService *user_admin.UserAdminService) *UserAdmin
// @Success 200 {object} handler.RespBody // @Success 200 {object} handler.RespBody
// @Router /answer/admin/api/user/status [put] // @Router /answer/admin/api/user/status [put]
func (uc *UserAdminController) UpdateUserStatus(ctx *gin.Context) { 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) handler.HandleResponse(ctx, errors.Forbidden(reason.ForbiddenError), nil)
return return
} }

View File

@ -6,4 +6,5 @@ type UserCacheInfo struct {
UserStatus int `json:"user_status"` UserStatus int `json:"user_status"`
EmailStatus int `json:"email_status"` EmailStatus int `json:"email_status"`
RoleID int `json:"role_id"` RoleID int `json:"role_id"`
ExternalID string `json:"external_id"`
} }

View File

@ -7,6 +7,7 @@ import (
"github.com/answerdev/answer/internal/base/constant" "github.com/answerdev/answer/internal/base/constant"
"github.com/answerdev/answer/internal/entity" "github.com/answerdev/answer/internal/entity"
"github.com/answerdev/answer/internal/schema" "github.com/answerdev/answer/internal/schema"
"github.com/tidwall/gjson"
"xorm.io/xorm" "xorm.io/xorm"
) )
@ -22,10 +23,47 @@ func addLoginLimitations(x *xorm.Engine) error {
content := &schema.SiteLoginReq{} content := &schema.SiteLoginReq{}
_ = json.Unmarshal([]byte(loginSiteInfo.Content), content) _ = json.Unmarshal([]byte(loginSiteInfo.Content), content)
content.AllowEmailRegistrations = true content.AllowEmailRegistrations = true
content.AllowEmailDomains = make([]string, 0)
_, err = x.ID(loginSiteInfo.ID).Cols("content").Update(loginSiteInfo) _, err = x.ID(loginSiteInfo.ID).Cols("content").Update(loginSiteInfo)
if err != nil { if err != nil {
return fmt.Errorf("update site info failed: %w", err) 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 return nil
} }

View File

@ -206,7 +206,7 @@ func (a *AnswerAPIRouter) RegisterAnswerAPIRouter(r *gin.RouterGroup) {
// user // user
r.PUT("/user/password", middleware.BanAPIWhenUserCenterEnabled, a.userController.UserModifyPassWord) 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.PUT("/user/interface", a.userController.UserUpdateInterface)
r.POST("/user/notice/set", a.userController.UserNoticeSet) r.POST("/user/notice/set", a.userController.UserNoticeSet)

View File

@ -43,9 +43,8 @@ func (r *SiteGeneralReq) FormatSiteUrl() {
// SiteInterfaceReq site interface request // SiteInterfaceReq site interface request
type SiteInterfaceReq struct { type SiteInterfaceReq struct {
Language string `validate:"required,gt=1,lte=128" form:"language" json:"language"` 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"` 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"`
} }
// SiteBrandingReq site branding request // SiteBrandingReq site branding request
@ -275,6 +274,7 @@ type GetPrivilegesConfigResp struct {
// PrivilegeOption privilege option // PrivilegeOption privilege option
type PrivilegeOption struct { type PrivilegeOption struct {
Level PrivilegeLevel `json:"level"` Level PrivilegeLevel `json:"level"`
LevelDesc string `json:"level_desc"`
Privileges []*constant.Privilege `json:"privileges"` Privileges []*constant.Privilege `json:"privileges"`
} }
@ -312,21 +312,27 @@ var (
) )
func init() { func init() {
for _, option := range []PrivilegeLevel{PrivilegeLevel1, PrivilegeLevel2, PrivilegeLevel3} { DefaultPrivilegeOptions = append(DefaultPrivilegeOptions, &PrivilegeOption{
op := &PrivilegeOption{ Level: PrivilegeLevel1,
Level: option, LevelDesc: reason.PrivilegeLevel1Desc,
} }, &PrivilegeOption{
Level: PrivilegeLevel2,
LevelDesc: reason.PrivilegeLevel2Desc,
}, &PrivilegeOption{
Level: PrivilegeLevel3,
LevelDesc: reason.PrivilegeLevel3Desc,
})
for _, option := range DefaultPrivilegeOptions {
for _, privilege := range constant.RankAllPrivileges { for _, privilege := range constant.RankAllPrivileges {
if len(privilegeOptionsLevelMapping[privilege.Key]) == 0 { if len(privilegeOptionsLevelMapping[privilege.Key]) == 0 {
fmt.Println("privilege key not found: ", privilege.Key)
continue continue
} }
op.Privileges = append(op.Privileges, &constant.Privilege{ option.Privileges = append(option.Privileges, &constant.Privilege{
Label: privilege.Label, Label: privilege.Label,
Value: privilegeOptionsLevelMapping[privilege.Key][option-1], Value: privilegeOptionsLevelMapping[privilege.Key][option.Level-1],
Key: privilege.Key, Key: privilege.Key,
}) })
} }
DefaultPrivilegeOptions = append(DefaultPrivilegeOptions, op)
} }
} }

View File

@ -68,7 +68,7 @@ type UserCenterUserSettingsResp struct {
} }
type UserCenterAdminFunctionAgentResp struct { type UserCenterAdminFunctionAgentResp struct {
RoleAgentEnabled bool `json:"role_agent_enabled"` UserStatusAgentEnabled bool `json:"user_status_agent_enabled"`
} }
type UserSettingAgent struct { type UserSettingAgent struct {

View File

@ -5,6 +5,7 @@ import (
"github.com/answerdev/answer/internal/entity" "github.com/answerdev/answer/internal/entity"
"github.com/answerdev/answer/pkg/token" "github.com/answerdev/answer/pkg/token"
"github.com/answerdev/answer/plugin"
"github.com/segmentfault/pacman/log" "github.com/segmentfault/pacman/log"
) )
@ -52,6 +53,14 @@ func (as *AuthService) GetUserCacheInfo(ctx context.Context, accessToken string)
return nil, err 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 return userCacheInfo, nil
} }

View File

@ -6,6 +6,7 @@ import (
"fmt" "fmt"
"github.com/answerdev/answer/internal/base/constant" "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/reason"
"github.com/answerdev/answer/internal/base/translator" "github.com/answerdev/answer/internal/base/translator"
"github.com/answerdev/answer/internal/entity" "github.com/answerdev/answer/internal/entity"
@ -35,7 +36,7 @@ func NewSiteInfoService(
tagCommonService *tagcommon.TagCommonService, tagCommonService *tagcommon.TagCommonService,
configRepo config.ConfigRepo, configRepo config.ConfigRepo,
) *SiteInfoService { ) *SiteInfoService {
resp, err := siteInfoCommonService.GetSiteInterface(context.Background()) resp, err := siteInfoCommonService.GetSiteUsers(context.Background())
if err != nil { if err != nil {
log.Error(err) log.Error(err)
} else { } 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) { func (s *SiteInfoService) SaveSiteInterface(ctx context.Context, req schema.SiteInterfaceReq) (err error) {
var (
siteType = "interface"
content []byte
)
// check language // check language
if !translator.CheckLanguageIsValid(req.Language) { if !translator.CheckLanguageIsValid(req.Language) {
err = errors.BadRequest(reason.LangNotFound) err = errors.BadRequest(reason.LangNotFound)
return return
} }
content, _ = json.Marshal(req) content, _ := json.Marshal(req)
data := entity.SiteInfo{ data := entity.SiteInfo{
Type: siteType, Type: constant.SiteTypeInterface,
Content: string(content), Content: string(content),
} }
return s.siteInfoRepo.SaveByType(ctx, constant.SiteTypeInterface, &data)
err = s.siteInfoRepo.SaveByType(ctx, siteType, &data)
if err == nil {
constant.DefaultAvatar = req.DefaultAvatar
}
return
} }
// SaveSiteBranding save site branding information // SaveSiteBranding save site branding information
@ -235,7 +225,11 @@ func (s *SiteInfoService) SaveSiteUsers(ctx context.Context, req *schema.SiteUse
Content: string(content), Content: string(content),
Status: 1, 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 // GetSMTPConfig get smtp config
@ -329,8 +323,8 @@ func (s *SiteInfoService) GetPrivilegesConfig(ctx context.Context) (resp *schema
return nil, err return nil, err
} }
resp = &schema.GetPrivilegesConfigResp{ resp = &schema.GetPrivilegesConfigResp{
Options: schema.DefaultPrivilegeOptions, Options: s.translatePrivilegeOptions(ctx),
SelectedLevel: schema.PrivilegeLevel2, SelectedLevel: schema.PrivilegeLevel3,
} }
if privilege != nil && privilege.Level > 0 { if privilege != nil && privilege.Level > 0 {
resp.SelectedLevel = privilege.Level resp.SelectedLevel = privilege.Level
@ -338,6 +332,25 @@ func (s *SiteInfoService) GetPrivilegesConfig(ctx context.Context) (resp *schema
return resp, nil 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) { func (s *SiteInfoService) UpdatePrivilegesConfig(ctx context.Context, req *schema.UpdatePrivilegesConfigReq) (err error) {
var chooseOption *schema.PrivilegeOption var chooseOption *schema.PrivilegeOption
for _, option := range schema.DefaultPrivilegeOptions { for _, option := range schema.DefaultPrivilegeOptions {

View File

@ -150,7 +150,7 @@ func (us *UserCommon) MakeUsername(ctx context.Context, displayName string) (use
return username + suffix, nil 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) { accessToken string, userCacheInfo *entity.UserCacheInfo, err error) {
roleID, err := us.userRoleService.GetUserRole(ctx, userID) roleID, err := us.userRoleService.GetUserRole(ctx, userID)
if err != nil { if err != nil {
@ -162,6 +162,7 @@ func (us *UserCommon) CacheLoginUserInfo(ctx context.Context, userID string, use
EmailStatus: emailStatus, EmailStatus: emailStatus,
UserStatus: userStatus, UserStatus: userStatus,
RoleID: roleID, RoleID: roleID,
ExternalID: externalID,
} }
accessToken, err = us.authService.SetUserCacheInfo(ctx, userCacheInfo) accessToken, err = us.authService.SetUserCacheInfo(ctx, userCacheInfo)

View File

@ -78,7 +78,7 @@ func (us *UserCenterLoginService) ExternalLogin(
log.Errorf("update user last login date failed: %v", err) log.Errorf("update user last login date failed: %v", err)
} }
accessToken, _, err := us.userCommonService.CacheLoginUserInfo( 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 return &schema.UserExternalLoginResp{AccessToken: accessToken}, err
} }
} }
@ -97,7 +97,7 @@ func (us *UserCenterLoginService) ExternalLogin(
us.activeUser(ctx, oldUserInfo) us.activeUser(ctx, oldUserInfo)
accessToken, _, err := us.userCommonService.CacheLoginUserInfo( 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 return &schema.UserExternalLoginResp{AccessToken: accessToken}, err
} }
@ -203,7 +203,7 @@ func (us *UserCenterLoginService) UserCenterAdminFunctionAgent(ctx context.Conte
return return
} }
desc := userCenter.Description() desc := userCenter.Description()
resp.RoleAgentEnabled = desc.RoleAgentEnabled resp.UserStatusAgentEnabled = desc.UserStatusAgentEnabled
return resp, nil return resp, nil
} }

View File

@ -83,7 +83,7 @@ func (us *UserExternalLoginService) ExternalLogin(
log.Error(err) log.Error(err)
} }
accessToken, _, err := us.userCommonService.CacheLoginUserInfo( 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 return &schema.UserExternalLoginResp{AccessToken: accessToken}, err
} }
} }
@ -122,7 +122,7 @@ func (us *UserExternalLoginService) ExternalLogin(
} }
accessToken, _, err := us.userCommonService.CacheLoginUserInfo( 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 return &schema.UserExternalLoginResp{AccessToken: accessToken}, err
} }
@ -251,7 +251,7 @@ func (us *UserExternalLoginService) ExternalLoginBindingUserSendEmail(
return nil, err return nil, err
} }
resp.AccessToken, _, err = us.userCommonService.CacheLoginUserInfo( 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 { if err != nil {
log.Error(err) log.Error(err)
} }

View File

@ -499,7 +499,7 @@ func (us *UserService) UserVerifyEmail(ctx context.Context, req *schema.UserVeri
} }
accessToken, userCacheInfo, err := us.userCommonService.CacheLoginUserInfo( accessToken, userCacheInfo, err := us.userCommonService.CacheLoginUserInfo(
ctx, userInfo.ID, userInfo.MailStatus, userInfo.Status) ctx, userInfo.ID, userInfo.MailStatus, userInfo.Status, "")
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -12,6 +12,8 @@ type UserCenter interface {
SignUpCallback(ctx *GinContext) (userInfo *UserCenterBasicUserInfo, err error) SignUpCallback(ctx *GinContext) (userInfo *UserCenterBasicUserInfo, err error)
// UserInfo returns the user information // UserInfo returns the user information
UserInfo(externalID string) (userInfo *UserCenterBasicUserInfo, err error) 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 returns the user list information
UserList(externalIDs []string) (userInfo []*UserCenterBasicUserInfo, err error) UserList(externalIDs []string) (userInfo []*UserCenterBasicUserInfo, err error)
// UserSettings returns the user settings // UserSettings returns the user settings
@ -23,14 +25,14 @@ type UserCenter interface {
} }
type UserCenterDesc struct { type UserCenterDesc struct {
Name string `json:"name"` Name string `json:"name"`
Icon string `json:"icon"` Icon string `json:"icon"`
Url string `json:"url"` Url string `json:"url"`
LoginRedirectURL string `json:"login_redirect_url"` LoginRedirectURL string `json:"login_redirect_url"`
SignUpRedirectURL string `json:"sign_up_redirect_url"` SignUpRedirectURL string `json:"sign_up_redirect_url"`
RankAgentEnabled bool `json:"rank_agent_enabled"` RankAgentEnabled bool `json:"rank_agent_enabled"`
RoleAgentEnabled bool `json:"role_agent_enabled"` UserStatusAgentEnabled bool `json:"user_status_agent_enabled"`
MustAuthEmailEnabled bool `json:"must_auth_email_enabled"` MustAuthEmailEnabled bool `json:"must_auth_email_enabled"`
} }
type UserStatus int type UserStatus int