From e5df07ad7cb0352e7dd98c61106419605b4cbe0c Mon Sep 17 00:00:00 2001 From: LinkinStars Date: Wed, 29 Mar 2023 17:12:45 +0800 Subject: [PATCH 1/5] feat(plugin): add user center plugin --- cmd/wire_gen.go | 4 +- internal/controller/controller.go | 1 + .../plugin_user_center_controller.go | 176 +++++++++++++++ internal/repo/user/user_repo.go | 103 ++++++++- .../user_external_login_repo.go | 4 +- internal/router/plugin_api_router.go | 18 +- internal/schema/plugin_user_center.go | 33 +++ internal/schema/user_external_login_schema.go | 11 + internal/schema/user_schema.go | 12 + internal/service/provider.go | 1 + .../user_center_login_service.go | 211 ++++++++++++++++++ .../user_external_login_service.go | 9 +- plugin/plugin.go | 4 + plugin/user_center.go | 81 +++++++ 14 files changed, 658 insertions(+), 10 deletions(-) create mode 100644 internal/controller/plugin_user_center_controller.go create mode 100644 internal/schema/plugin_user_center.go create mode 100644 internal/service/user_external_login/user_center_login_service.go create mode 100644 plugin/user_center.go diff --git a/cmd/wire_gen.go b/cmd/wire_gen.go index 8df1b9e8..db4eaa89 100644 --- a/cmd/wire_gen.go +++ b/cmd/wire_gen.go @@ -220,7 +220,9 @@ func initApplication(debug bool, serverConf *conf.Server, dbConf *data.Database, templateController := controller.NewTemplateController(templateRenderController, siteInfoCommonService) templateRouter := router.NewTemplateRouter(templateController, templateRenderController, siteInfoController) connectorController := controller.NewConnectorController(siteInfoCommonService, emailService, userExternalLoginService) - pluginAPIRouter := router.NewPluginAPIRouter(connectorController) + userCenterLoginService := user_external_login2.NewUserCenterLoginService(userRepo, userCommon, userExternalLoginRepo, userActiveActivityRepo) + userCenterController := controller.NewUserCenterController(userCenterLoginService, siteInfoCommonService) + pluginAPIRouter := router.NewPluginAPIRouter(connectorController, userCenterController) ginEngine := server.NewHTTPServer(debug, staticRouter, answerAPIRouter, swaggerRouter, uiRouter, authUserMiddleware, avatarMiddleware, templateRouter, pluginAPIRouter) scheduledTaskManager := cron.NewScheduledTaskManager(siteInfoCommonService, questionService) application := newApplication(serverConf, ginEngine, scheduledTaskManager) diff --git a/internal/controller/controller.go b/internal/controller/controller.go index 6a7a62b4..a5daabba 100644 --- a/internal/controller/controller.go +++ b/internal/controller/controller.go @@ -25,4 +25,5 @@ var ProviderSetController = wire.NewSet( NewActivityController, NewTemplateController, NewConnectorController, + NewUserCenterController, ) diff --git a/internal/controller/plugin_user_center_controller.go b/internal/controller/plugin_user_center_controller.go new file mode 100644 index 00000000..8a5dbd91 --- /dev/null +++ b/internal/controller/plugin_user_center_controller.go @@ -0,0 +1,176 @@ +package controller + +import ( + "fmt" + "net/http" + + "github.com/answerdev/answer/internal/base/handler" + "github.com/answerdev/answer/internal/base/middleware" + "github.com/answerdev/answer/internal/schema" + "github.com/answerdev/answer/internal/service/siteinfo_common" + "github.com/answerdev/answer/internal/service/user_external_login" + "github.com/answerdev/answer/plugin" + "github.com/gin-gonic/gin" + "github.com/segmentfault/pacman/log" +) + +const ( + UserCenterLoginRouter = "/user-center/login/redirect" + UserCenterSignUpRedirectRouter = "/user-center/sign-up/redirect" +) + +// UserCenterController comment controller +type UserCenterController struct { + userCenterLoginService *user_external_login.UserCenterLoginService + siteInfoService *siteinfo_common.SiteInfoCommonService +} + +// NewUserCenterController new controller +func NewUserCenterController( + userCenterLoginService *user_external_login.UserCenterLoginService, + siteInfoService *siteinfo_common.SiteInfoCommonService, +) *UserCenterController { + return &UserCenterController{ + userCenterLoginService: userCenterLoginService, + siteInfoService: siteInfoService, + } +} + +// UserCenterAgent get user center agent info +func (uc *UserCenterController) UserCenterAgent(ctx *gin.Context) { + resp := &schema.UserCenterAgentResp{} + resp.Enabled = plugin.UserCenterEnabled() + if !resp.Enabled { + handler.HandleResponse(ctx, nil, resp) + return + } + siteGeneral, err := uc.siteInfoService.GetSiteGeneral(ctx) + if err != nil { + log.Errorf("get site info failed: %v", err) + ctx.Redirect(http.StatusFound, "/50x") + return + } + + resp.AgentInfo = &schema.AgentInfo{} + resp.AgentInfo.LoginRedirectURL = fmt.Sprintf("%s%s%s", siteGeneral.SiteUrl, + commonRouterPrefix, UserCenterLoginRouter) + resp.AgentInfo.SignUpRedirectURL = fmt.Sprintf("%s%s%s", siteGeneral.SiteUrl, + commonRouterPrefix, UserCenterSignUpRedirectRouter) + + _ = plugin.CallUserCenter(func(uc plugin.UserCenter) error { + info := uc.Description() + resp.AgentInfo.Name = info.Name + resp.AgentInfo.Icon = info.Icon + resp.AgentInfo.Url = info.Url + resp.AgentInfo.ControlCenterItems = make([]*schema.ControlCenter, 0) + items := uc.ControlCenterItems() + for _, item := range items { + resp.AgentInfo.ControlCenterItems = append(resp.AgentInfo.ControlCenterItems, &schema.ControlCenter{ + Name: item.Name, + Label: item.Label, + Url: item.Url, + }) + } + return nil + }) + + handler.HandleResponse(ctx, nil, resp) +} + +// UserCenterPersonalBranding get user center personal user info +func (uc *UserCenterController) UserCenterPersonalBranding(ctx *gin.Context) { + req := &schema.GetOtherUserInfoByUsernameReq{} + if handler.BindAndCheck(ctx, req) { + return + } + + resp, err := uc.userCenterLoginService.UserCenterPersonalBranding(ctx, req.Username) + handler.HandleResponse(ctx, err, resp) +} + +func (uc *UserCenterController) UserCenterLoginRedirect(ctx *gin.Context) { + var redirectURL string + _ = plugin.CallUserCenter(func(uc plugin.UserCenter) error { + info := uc.Description() + redirectURL = info.LoginRedirectURL + return nil + }) + ctx.Redirect(http.StatusFound, redirectURL) +} + +func (uc *UserCenterController) UserCenterSignUpRedirect(ctx *gin.Context) { + var redirectURL string + _ = plugin.CallUserCenter(func(uc plugin.UserCenter) error { + info := uc.Description() + redirectURL = info.SignUpRedirectURL + return nil + }) + ctx.Redirect(http.StatusFound, redirectURL) +} + +func (uc *UserCenterController) UserCenterLoginCallback(ctx *gin.Context) { + siteGeneral, err := uc.siteInfoService.GetSiteGeneral(ctx) + if err != nil { + log.Errorf("get site info failed: %v", err) + ctx.Redirect(http.StatusFound, "/50x") + return + } + + userCenter, ok := plugin.GetUserCenter() + if !ok { + ctx.Redirect(http.StatusFound, "/404") + return + } + userInfo, err := userCenter.LoginCallback(ctx) + if err != nil { + log.Error(err) + ctx.Redirect(http.StatusFound, "/50x") + return + } + + resp, err := uc.userCenterLoginService.ExternalLogin(ctx, userCenter.Info().SlugName, userInfo) + if err != nil { + log.Errorf("external login failed: %v", err) + ctx.Redirect(http.StatusFound, "/50x") + return + } + ctx.Redirect(http.StatusFound, fmt.Sprintf("%s/users/oauth?access_token=%s", + siteGeneral.SiteUrl, resp.AccessToken)) +} + +func (uc *UserCenterController) UserCenterSignUpCallback(ctx *gin.Context) { + siteGeneral, err := uc.siteInfoService.GetSiteGeneral(ctx) + if err != nil { + log.Errorf("get site info failed: %v", err) + ctx.Redirect(http.StatusFound, "/50x") + return + } + + userCenter, ok := plugin.GetUserCenter() + if !ok { + ctx.Redirect(http.StatusFound, "/404") + return + } + userInfo, err := userCenter.SignUpCallback(ctx) + if err != nil { + log.Error(err) + ctx.Redirect(http.StatusFound, "/50x") + return + } + + resp, err := uc.userCenterLoginService.ExternalLogin(ctx, userCenter.Info().SlugName, userInfo) + if err != nil { + log.Errorf("external login failed: %v", err) + ctx.Redirect(http.StatusFound, "/50x") + return + } + ctx.Redirect(http.StatusFound, fmt.Sprintf("%s/users/oauth?access_token=%s", + siteGeneral.SiteUrl, resp.AccessToken)) +} + +// UserCenterUserSettings user center user settings +func (uc *UserCenterController) UserCenterUserSettings(ctx *gin.Context) { + userID := middleware.GetLoginUserIDFromContext(ctx) + resp, err := uc.userCenterLoginService.UserCenterUserSettings(ctx, userID) + handler.HandleResponse(ctx, err, resp) +} diff --git a/internal/repo/user/user_repo.go b/internal/repo/user/user_repo.go index 7c47ae91..0a553521 100644 --- a/internal/repo/user/user_repo.go +++ b/internal/repo/user/user_repo.go @@ -7,9 +7,12 @@ import ( "github.com/answerdev/answer/internal/base/data" "github.com/answerdev/answer/internal/base/reason" "github.com/answerdev/answer/internal/entity" + "github.com/answerdev/answer/internal/schema" "github.com/answerdev/answer/internal/service/config" usercommon "github.com/answerdev/answer/internal/service/user_common" + "github.com/answerdev/answer/plugin" "github.com/segmentfault/pacman/errors" + "github.com/segmentfault/pacman/log" "xorm.io/xorm" ) @@ -137,7 +140,9 @@ func (ur *userRepo) GetByUserID(ctx context.Context, userID string) (userInfo *e exist, err = ur.data.DB.Where("id = ?", userID).Get(userInfo) if err != nil { err = errors.InternalServer(reason.DatabaseError).WithError(err).WithStack() + return } + ur.tryToDecorateUserInfoFromUserCenter(ctx, userInfo) return } @@ -147,6 +152,7 @@ func (ur *userRepo) BatchGetByID(ctx context.Context, ids []string) ([]*entity.U if err != nil { return nil, errors.InternalServer(reason.DatabaseError).WithError(err).WithStack() } + ur.tryToDecorateUserListFromUserCenter(ctx, list) return list, nil } @@ -156,7 +162,9 @@ func (ur *userRepo) GetByUsername(ctx context.Context, username string) (userInf exist, err = ur.data.DB.Where("username = ?", username).Get(userInfo) if err != nil { err = errors.InternalServer(reason.DatabaseError).WithError(err).WithStack() + return } + ur.tryToDecorateUserInfoFromUserCenter(ctx, userInfo) return } @@ -171,11 +179,102 @@ func (ur *userRepo) GetByEmail(ctx context.Context, email string) (userInfo *ent return } -func (vr *userRepo) GetUserCount(ctx context.Context) (count int64, err error) { +func (ur *userRepo) GetUserCount(ctx context.Context) (count int64, err error) { list := make([]*entity.User, 0) - count, err = vr.data.DB.Where("mail_status =?", entity.EmailStatusAvailable).And("status =?", entity.UserStatusAvailable).FindAndCount(&list) + count, err = ur.data.DB.Where("mail_status =?", entity.EmailStatusAvailable).And("status =?", entity.UserStatusAvailable).FindAndCount(&list) if err != nil { return count, errors.InternalServer(reason.DatabaseError).WithError(err).WithStack() } return } + +func (ur *userRepo) tryToDecorateUserInfoFromUserCenter(ctx context.Context, original *entity.User) { + uc, ok := plugin.GetUserCenter() + if !ok { + return + } + + userInfo := &entity.UserExternalLogin{} + session := ur.data.DB.Where("user_id = ?", original.ID) + session.Where("provider = ?", uc.Info().SlugName) + exist, err := session.Get(userInfo) + if err != nil { + log.Error(err) + return + } + if !exist { + return + } + + userCenterBasicUserInfo, err := uc.UserInfo(userInfo.ExternalID) + if err != nil { + log.Error(err) + return + } + + // In general, usernames should be guaranteed unique by the User Center plugin, so there are no inconsistencies. + if original.Username != userCenterBasicUserInfo.Username { + log.Warnf("user %s username is inconsistent with user center", original.ID) + } + decorateByUserCenterUser(original, userCenterBasicUserInfo) +} + +func (ur *userRepo) tryToDecorateUserListFromUserCenter(ctx context.Context, original []*entity.User) { + log.Debugf("try to decorate user list from user center, original: %+v", original) + uc, ok := plugin.GetUserCenter() + if !ok { + return + } + + ids := make([]string, 0) + originalUserIDMapping := make(map[string]*entity.User, 0) + for _, user := range original { + originalUserIDMapping[user.ID] = user + ids = append(ids, user.ID) + } + + userExternalLoginList := make([]*entity.UserExternalLogin, 0) + session := ur.data.DB.Where("provider = ?", uc.Info().SlugName) + session.In("user_id", ids) + err := session.Find(&userExternalLoginList) + if err != nil { + log.Error(err) + return + } + + userExternalIDs := make([]string, 0) + originalExternalIDMapping := make(map[string]*entity.User, 0) + for _, u := range userExternalLoginList { + originalExternalIDMapping[u.ExternalID] = originalUserIDMapping[u.UserID] + userExternalIDs = append(userExternalIDs, u.ExternalID) + } + + ucUsers, err := uc.UserList(userExternalIDs) + if err != nil { + log.Error(err) + return + } + + for _, ucUser := range ucUsers { + decorateByUserCenterUser(originalExternalIDMapping[ucUser.ExternalID], ucUser) + } +} + +func decorateByUserCenterUser(original *entity.User, ucUser *plugin.UserCenterBasicUserInfo) { + if original == nil || ucUser == nil { + return + } + // In general, usernames should be guaranteed unique by the User Center plugin, so there are no inconsistencies. + if original.Username != ucUser.Username { + log.Warnf("user %s username is inconsistent with user center", original.ID) + } + original.DisplayName = ucUser.DisplayName + original.EMail = ucUser.Email + original.Avatar = schema.CustomAvatar(ucUser.Avatar).ToJsonString() + original.Mobile = ucUser.Mobile + + // If plugin enable rank agent, use rank from user center. + if plugin.RankAgentEnabled() { + original.Rank = ucUser.Rank + } +} diff --git a/internal/repo/user_external_login/user_external_login_repo.go b/internal/repo/user_external_login/user_external_login_repo.go index aa2a52cb..f7be790e 100644 --- a/internal/repo/user_external_login/user_external_login_repo.go +++ b/internal/repo/user_external_login/user_external_login_repo.go @@ -43,10 +43,10 @@ func (ur *userExternalLoginRepo) UpdateInfo(ctx context.Context, userInfo *entit } // GetByExternalID get by external ID -func (ur *userExternalLoginRepo) GetByExternalID(ctx context.Context, externalID string) ( +func (ur *userExternalLoginRepo) GetByExternalID(ctx context.Context, provider, externalID string) ( userInfo *entity.UserExternalLogin, exist bool, err error) { userInfo = &entity.UserExternalLogin{} - exist, err = ur.data.DB.Where("external_id = ?", externalID).Get(userInfo) + exist, err = ur.data.DB.Where("external_id = ?", externalID).Where("provider = ?", provider).Get(userInfo) if err != nil { err = errors.InternalServer(reason.DatabaseError).WithError(err).WithStack() } diff --git a/internal/router/plugin_api_router.go b/internal/router/plugin_api_router.go index 31a6ad74..46a86528 100644 --- a/internal/router/plugin_api_router.go +++ b/internal/router/plugin_api_router.go @@ -6,27 +6,41 @@ import ( ) type PluginAPIRouter struct { - connectorController *controller.ConnectorController + connectorController *controller.ConnectorController + userCenterController *controller.UserCenterController } func NewPluginAPIRouter( connectorController *controller.ConnectorController, + userCenterController *controller.UserCenterController, ) *PluginAPIRouter { return &PluginAPIRouter{ - connectorController: connectorController, + connectorController: connectorController, + userCenterController: userCenterController, } } func (pr *PluginAPIRouter) RegisterUnAuthConnectorRouter(r *gin.RouterGroup) { + // connector plugin connectorController := pr.connectorController r.GET(controller.ConnectorLoginRouterPrefix+":name", connectorController.ConnectorLoginDispatcher) r.GET(controller.ConnectorRedirectRouterPrefix+":name", connectorController.ConnectorRedirectDispatcher) r.GET("/connector/info", connectorController.ConnectorsInfo) r.POST("/connector/binding/email", connectorController.ExternalLoginBindingUserSendEmail) + + // user center plugin + r.GET("/user-center/agent", pr.userCenterController.UserCenterAgent) + r.GET("/user-center/personal/branding", pr.userCenterController.UserCenterPersonalBranding) + r.GET(controller.UserCenterLoginRouter, pr.userCenterController.UserCenterLoginRedirect) + r.GET(controller.UserCenterSignUpRedirectRouter, pr.userCenterController.UserCenterSignUpRedirect) + r.GET("/user-center/login/callback", pr.userCenterController.UserCenterLoginCallback) + r.GET("/user-center/sign-up/callback", pr.userCenterController.UserCenterSignUpCallback) } func (pr *PluginAPIRouter) RegisterAuthConnectorRouter(r *gin.RouterGroup) { connectorController := pr.connectorController r.GET("/connector/user/info", connectorController.ConnectorsUserInfo) r.DELETE("/connector/user/unbinding", connectorController.ExternalLoginUnbinding) + + r.GET("/user-center/user/settings", pr.userCenterController.UserCenterUserSettings) } diff --git a/internal/schema/plugin_user_center.go b/internal/schema/plugin_user_center.go new file mode 100644 index 00000000..afa4727c --- /dev/null +++ b/internal/schema/plugin_user_center.go @@ -0,0 +1,33 @@ +package schema + +type UserCenterAgentResp struct { + Enabled bool `json:"enabled"` + AgentInfo *AgentInfo `json:"agent_info"` +} + +type AgentInfo 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"` + ControlCenterItems []*ControlCenter `json:"control_center"` +} + +type ControlCenter struct { + Name string `json:"name"` + Label string `json:"label"` + Url string `json:"url"` +} + +type UserCenterPersonalBranding struct { + Enabled bool `json:"enabled"` + PersonalBranding []*PersonalBranding `json:"personal_branding"` +} + +type PersonalBranding struct { + Icon string `json:"icon"` + Name string `json:"name"` + Label string `json:"label"` + Url string `json:"url"` +} diff --git a/internal/schema/user_external_login_schema.go b/internal/schema/user_external_login_schema.go index f0351c18..79cdad86 100644 --- a/internal/schema/user_external_login_schema.go +++ b/internal/schema/user_external_login_schema.go @@ -56,3 +56,14 @@ type ExternalLoginUnbindingReq struct { ExternalID string `validate:"required,gt=0,lte=128" json:"external_id"` UserID string `json:"-"` } + +// UserCenterUserSettingsResp user center user info response +type UserCenterUserSettingsResp struct { + ProfileSettingAgent UserSettingAgent `json:"profile_setting_agent"` + AccountSettingAgent UserSettingAgent `json:"account_setting_agent"` +} + +type UserSettingAgent struct { + Enabled bool `json:"enabled"` + RedirectURL string `json:"redirect_url"` +} diff --git a/internal/schema/user_schema.go b/internal/schema/user_schema.go index c761e6cb..9693c0a4 100644 --- a/internal/schema/user_schema.go +++ b/internal/schema/user_schema.go @@ -143,6 +143,13 @@ func FormatAvatarInfo(avatarJson, email string) (res string) { } } +func CustomAvatar(url string) *AvatarInfo { + return &AvatarInfo{ + Type: AvatarTypeCustom, + Custom: url, + } +} + // GetUserStatusResp get user status info type GetUserStatusResp struct { // user status @@ -316,6 +323,11 @@ type AvatarInfo struct { Custom string `validate:"omitempty,gt=0,lte=200" json:"custom"` } +func (a *AvatarInfo) ToJsonString() string { + data, _ := json.Marshal(a) + return string(data) +} + func (req *UpdateInfoRequest) Check() (errFields []*validator.FormErrorField, err error) { if len(req.Username) > 0 { if checker.IsInvalidUsername(req.Username) { diff --git a/internal/service/provider.go b/internal/service/provider.go index bf7541af..8273866c 100644 --- a/internal/service/provider.go +++ b/internal/service/provider.go @@ -82,5 +82,6 @@ var ProviderSetService = wire.NewSet( role.NewUserRoleRelService, role.NewRolePowerRelService, user_external_login.NewUserExternalLoginService, + user_external_login.NewUserCenterLoginService, plugin_common.NewPluginCommonService, ) diff --git a/internal/service/user_external_login/user_center_login_service.go b/internal/service/user_external_login/user_center_login_service.go new file mode 100644 index 00000000..df48aa63 --- /dev/null +++ b/internal/service/user_external_login/user_center_login_service.go @@ -0,0 +1,211 @@ +package user_external_login + +import ( + "context" + "encoding/json" + "time" + + "github.com/answerdev/answer/internal/entity" + "github.com/answerdev/answer/internal/schema" + "github.com/answerdev/answer/internal/service/activity" + usercommon "github.com/answerdev/answer/internal/service/user_common" + "github.com/answerdev/answer/pkg/random" + "github.com/answerdev/answer/plugin" + "github.com/segmentfault/pacman/log" +) + +// UserCenterLoginService user external login service +type UserCenterLoginService struct { + userRepo usercommon.UserRepo + userExternalLoginRepo UserExternalLoginRepo + userCommonService *usercommon.UserCommon + userActivity activity.UserActiveActivityRepo +} + +// NewUserCenterLoginService new user external login service +func NewUserCenterLoginService( + userRepo usercommon.UserRepo, + userCommonService *usercommon.UserCommon, + userExternalLoginRepo UserExternalLoginRepo, + userActivity activity.UserActiveActivityRepo, +) *UserCenterLoginService { + return &UserCenterLoginService{ + userRepo: userRepo, + userCommonService: userCommonService, + userExternalLoginRepo: userExternalLoginRepo, + userActivity: userActivity, + } +} + +func (us *UserCenterLoginService) ExternalLogin( + ctx context.Context, provider string, basicUserInfo *plugin.UserCenterBasicUserInfo) ( + resp *schema.UserExternalLoginResp, err error) { + + oldExternalLoginUserInfo, exist, err := us.userExternalLoginRepo.GetByExternalID(ctx, + provider, basicUserInfo.ExternalID) + if err != nil { + return nil, err + } + if exist { + // if user is already a member, login directly + oldUserInfo, exist, err := us.userRepo.GetByUserID(ctx, oldExternalLoginUserInfo.UserID) + if err != nil { + return nil, err + } + if exist { + accessToken, _, err := us.userCommonService.CacheLoginUserInfo( + ctx, oldUserInfo.ID, oldUserInfo.MailStatus, oldUserInfo.Status) + return &schema.UserExternalLoginResp{AccessToken: accessToken}, err + } + } + + oldUserInfo, err := us.registerNewUser(ctx, provider, basicUserInfo) + if err != nil { + return nil, err + } + + us.activeUser(ctx, oldUserInfo) + + accessToken, _, err := us.userCommonService.CacheLoginUserInfo( + ctx, oldUserInfo.ID, oldUserInfo.MailStatus, oldUserInfo.Status) + return &schema.UserExternalLoginResp{AccessToken: accessToken}, err +} + +func (us *UserCenterLoginService) registerNewUser(ctx context.Context, provider string, + basicUserInfo *plugin.UserCenterBasicUserInfo) (userInfo *entity.User, err error) { + userInfo = &entity.User{} + userInfo.EMail = basicUserInfo.Email + userInfo.DisplayName = basicUserInfo.DisplayName + + userInfo.Username, err = us.userCommonService.MakeUsername(ctx, basicUserInfo.Username) + if err != nil { + log.Error(err) + userInfo.Username = random.Username() + } + + if len(basicUserInfo.Avatar) > 0 { + avatarInfo := &schema.AvatarInfo{ + Type: schema.AvatarTypeCustom, + Custom: basicUserInfo.Avatar, + } + avatar, _ := json.Marshal(avatarInfo) + userInfo.Avatar = string(avatar) + } + + userInfo.MailStatus = entity.EmailStatusAvailable + userInfo.Status = entity.UserStatusAvailable + userInfo.LastLoginDate = time.Now() + err = us.userRepo.AddUser(ctx, userInfo) + if err != nil { + return nil, err + } + + metaInfo, _ := json.Marshal(basicUserInfo) + newExternalUserInfo := &entity.UserExternalLogin{ + UserID: userInfo.ID, + Provider: provider, + ExternalID: basicUserInfo.ExternalID, + MetaInfo: string(metaInfo), + } + err = us.userExternalLoginRepo.AddUserExternalLogin(ctx, newExternalUserInfo) + + return userInfo, nil +} + +func (us *UserCenterLoginService) activeUser(ctx context.Context, oldUserInfo *entity.User) { + if err := us.userActivity.UserActive(ctx, oldUserInfo.ID); err != nil { + log.Error(err) + } +} + +func (us *UserCenterLoginService) UserCenterUserSettings(ctx context.Context, userID string) ( + resp *schema.UserCenterUserSettingsResp, err error) { + resp = &schema.UserCenterUserSettingsResp{} + + userCenter, ok := plugin.GetUserCenter() + if !ok { + return resp, nil + } + + // get external login info + externalLoginList, err := us.userExternalLoginRepo.GetUserExternalLoginList(ctx, userID) + if err != nil { + return nil, err + } + var externalInfo *entity.UserExternalLogin + for _, t := range externalLoginList { + if t.Provider == userCenter.Info().SlugName { + externalInfo = t + } + } + if externalInfo == nil { + return resp, nil + } + + settings, err := userCenter.UserSettings(externalInfo.ExternalID) + if err != nil { + log.Error(err) + return resp, nil + } + + if len(settings.AccountSettingRedirectURL) > 0 { + resp.AccountSettingAgent = schema.UserSettingAgent{ + Enabled: true, + RedirectURL: settings.AccountSettingRedirectURL, + } + } + if len(settings.ProfileSettingRedirectURL) > 0 { + resp.ProfileSettingAgent = schema.UserSettingAgent{ + Enabled: true, + RedirectURL: settings.ProfileSettingRedirectURL, + } + } + return resp, nil +} + +func (us *UserCenterLoginService) UserCenterPersonalBranding(ctx context.Context, username string) ( + resp *schema.UserCenterPersonalBranding, err error) { + resp = &schema.UserCenterPersonalBranding{ + PersonalBranding: make([]*schema.PersonalBranding, 0), + } + userCenter, ok := plugin.GetUserCenter() + if !ok { + return + } + + userInfo, exist, err := us.userRepo.GetByUsername(ctx, username) + if err != nil { + return nil, err + } + if !exist { + return resp, nil + } + + // get external login info + externalLoginList, err := us.userExternalLoginRepo.GetUserExternalLoginList(ctx, userInfo.ID) + if err != nil { + return nil, err + } + var externalInfo *entity.UserExternalLogin + for _, t := range externalLoginList { + if t.Provider == userCenter.Info().SlugName { + externalInfo = t + } + } + if externalInfo == nil { + return resp, nil + } + + resp.Enabled = true + branding := userCenter.PersonalBranding(externalInfo.ExternalID) + + for _, t := range branding { + resp.PersonalBranding = append(resp.PersonalBranding, &schema.PersonalBranding{ + Icon: t.Icon, + Name: t.Name, + Label: t.Label, + Url: t.Url, + }) + } + 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 e741c0f7..383491a1 100644 --- a/internal/service/user_external_login/user_external_login_service.go +++ b/internal/service/user_external_login/user_external_login_service.go @@ -23,7 +23,7 @@ import ( type UserExternalLoginRepo interface { AddUserExternalLogin(ctx context.Context, user *entity.UserExternalLogin) (err error) UpdateInfo(ctx context.Context, userInfo *entity.UserExternalLogin) (err error) - GetByExternalID(ctx context.Context, externalID string) (userInfo *entity.UserExternalLogin, exist bool, err error) + GetByExternalID(ctx context.Context, provider, externalID string) (userInfo *entity.UserExternalLogin, exist bool, err error) GetUserExternalLoginList(ctx context.Context, userID string) ( resp []*entity.UserExternalLogin, err error) DeleteUserExternalLogin(ctx context.Context, userID, externalID string) (err error) @@ -64,7 +64,8 @@ func NewUserExternalLoginService( func (us *UserExternalLoginService) ExternalLogin( ctx context.Context, externalUserInfo *schema.ExternalLoginUserInfoCache) ( resp *schema.UserExternalLoginResp, err error) { - oldExternalLoginUserInfo, exist, err := us.userExternalLoginRepo.GetByExternalID(ctx, externalUserInfo.ExternalID) + oldExternalLoginUserInfo, exist, err := us.userExternalLoginRepo.GetByExternalID(ctx, + externalUserInfo.Provider, externalUserInfo.ExternalID) if err != nil { return nil, err } @@ -156,7 +157,9 @@ func (us *UserExternalLoginService) registerNewUser(ctx context.Context, func (us *UserExternalLoginService) bindOldUser(ctx context.Context, externalUserInfo *schema.ExternalLoginUserInfoCache, oldUserInfo *entity.User) (err error) { - oldExternalUserInfo, exist, err := us.userExternalLoginRepo.GetByExternalID(ctx, externalUserInfo.ExternalID) + oldExternalUserInfo, exist, err := us.userExternalLoginRepo.GetByExternalID(ctx, + externalUserInfo.Provider, + externalUserInfo.ExternalID) if err != nil { return err } diff --git a/plugin/plugin.go b/plugin/plugin.go index d837f85e..833de372 100644 --- a/plugin/plugin.go +++ b/plugin/plugin.go @@ -48,6 +48,10 @@ func Register(p Base) { if _, ok := p.(Cache); ok { registerCache(p.(Cache)) } + + if _, ok := p.(UserCenter); ok { + registerUserCenter(p.(UserCenter)) + } } type Stack[T Base] struct { diff --git a/plugin/user_center.go b/plugin/user_center.go new file mode 100644 index 00000000..a32e6276 --- /dev/null +++ b/plugin/user_center.go @@ -0,0 +1,81 @@ +package plugin + +type UserCenter interface { + Base + Description() UserCenterDesc + ControlCenterItems() []ControlCenter + LoginCallback(ctx *GinContext) (userInfo *UserCenterBasicUserInfo, err error) + SignUpCallback(ctx *GinContext) (userInfo *UserCenterBasicUserInfo, err error) + UserInfo(externalID string) (userInfo *UserCenterBasicUserInfo, err error) + UserList(externalIDs []string) (userInfo []*UserCenterBasicUserInfo, err error) + UserSettings(externalID string) (userSettings *SettingInfo, err error) + PersonalBranding(externalID string) (branding []*PersonalBranding) +} + +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"` +} + +type UserCenterBasicUserInfo struct { + ExternalID string `json:"external_id"` + Username string `json:"username"` + DisplayName string `json:"display_name"` + Email string `json:"email"` + Rank int `json:"rank"` + Avatar string `json:"avatar"` + Mobile string `json:"mobile"` +} + +type ControlCenter struct { + Name string `json:"name"` + Label string `json:"label"` + Url string `json:"url"` +} + +type SettingInfo struct { + ProfileSettingRedirectURL string `json:"profile_setting_redirect_url"` + AccountSettingRedirectURL string `json:"account_setting_redirect_url"` +} + +type PersonalBranding struct { + Icon string `json:"icon"` + Name string `json:"name"` + Label string `json:"label"` + Url string `json:"url"` +} + +var ( + // CallUserCenter is a function that calls all registered parsers + CallUserCenter, + registerUserCenter = MakePlugin[UserCenter](false) +) + +func UserCenterEnabled() (enabled bool) { + _ = CallUserCenter(func(fn UserCenter) error { + enabled = true + return nil + }) + return +} + +func RankAgentEnabled() (enabled bool) { + _ = CallUserCenter(func(fn UserCenter) error { + enabled = fn.Description().RankAgentEnabled + return nil + }) + return +} + +func GetUserCenter() (uc UserCenter, ok bool) { + _ = CallUserCenter(func(fn UserCenter) error { + uc = fn + ok = true + return nil + }) + return +} From eb0e54c72415d48dae0283170ccc1cb789780f98 Mon Sep 17 00:00:00 2001 From: LinkinStars Date: Mon, 3 Apr 2023 11:14:28 +0800 Subject: [PATCH 2/5] fix(activity): show the trigger user info when display timeline --- internal/service/activity/activity.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/internal/service/activity/activity.go b/internal/service/activity/activity.go index 4fe151b9..46fb82c1 100644 --- a/internal/service/activity/activity.go +++ b/internal/service/activity/activity.go @@ -3,6 +3,7 @@ package activity import ( "context" "encoding/json" + "fmt" "strings" "github.com/answerdev/answer/internal/base/constant" @@ -106,7 +107,11 @@ func (as *ActivityService) GetObjectTimeline(ctx context.Context, req *schema.Ge item.Username = "N/A" item.UserDisplayName = "N/A" } else { - item.UserID = act.UserID + if act.TriggerUserID > 0 { + item.UserID = fmt.Sprintf("%d", act.TriggerUserID) + } else { + item.UserID = act.UserID + } } item.Comment = as.getTimelineActivityComment(ctx, item.ObjectID, item.ObjectType, item.ActivityType, item.RevisionID) From 2a121c573ba6173258c85b99fca2f0f91e78fe56 Mon Sep 17 00:00:00 2001 From: LinkinStars Date: Mon, 3 Apr 2023 11:17:38 +0800 Subject: [PATCH 3/5] feat(router): add middleware for user router --- go.mod | 17 +++++- go.sum | 52 +++++++++++++++++-- i18n/en_US.yaml | 2 + .../middleware/user_center_plugin_auth.go | 19 +++++++ internal/base/reason/reason.go | 6 ++- internal/controller/question_controller.go | 3 ++ .../user_backyard_controller.go | 15 ++++++ internal/repo/rank/user_rank_repo.go | 5 ++ internal/repo/user/user_backyard_repo.go | 11 ++++ internal/repo/user/user_repo.go | 37 ++++++++----- internal/router/answer_api_router.go | 34 ++++++------ internal/service/rank/rank_service.go | 4 ++ plugin/user_center.go | 31 ++++++++--- 13 files changed, 193 insertions(+), 43 deletions(-) create mode 100644 internal/base/middleware/user_center_plugin_auth.go diff --git a/go.mod b/go.mod index 4007f17c..40de6032 100644 --- a/go.mod +++ b/go.mod @@ -21,6 +21,7 @@ require ( github.com/google/wire v0.5.0 github.com/gosimple/slug v1.13.1 github.com/grokify/html-strip-tags-go v0.0.1 + github.com/imroc/req/v3 v3.33.1 github.com/jinzhu/copier v0.3.5 github.com/jinzhu/now v1.1.5 github.com/lib/pq v1.10.7 @@ -41,8 +42,9 @@ require ( github.com/swaggo/swag v1.8.10 github.com/tidwall/gjson v1.14.4 github.com/yuin/goldmark v1.4.13 - golang.org/x/crypto v0.1.0 + golang.org/x/crypto v0.4.0 golang.org/x/net v0.5.0 + google.golang.org/grpc v1.46.2 gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df gopkg.in/yaml.v3 v3.0.1 modernc.org/sqlite v1.14.2 @@ -71,13 +73,18 @@ require ( github.com/go-openapi/jsonreference v0.20.0 // indirect github.com/go-openapi/spec v0.20.7 // indirect github.com/go-openapi/swag v0.22.3 // indirect + github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect + github.com/golang/protobuf v1.5.2 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/google/go-cmp v0.5.9 // indirect + github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/gorilla/css v1.0.0 // indirect github.com/gosimple/unidecode v1.0.1 // indirect + github.com/hashicorp/errwrap v1.1.0 // indirect + github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/imdario/mergo v0.3.12 // indirect github.com/inconshreveable/mousetrap v1.0.1 // indirect @@ -95,6 +102,7 @@ require ( github.com/moby/term v0.0.0-20201216013528-df9cb8a40635 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/onsi/ginkgo/v2 v2.2.0 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.0.2 // indirect github.com/opencontainers/runc v1.1.2 // indirect @@ -103,6 +111,11 @@ require ( github.com/pelletier/go-toml/v2 v2.0.5 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/quic-go/qpack v0.4.0 // indirect + github.com/quic-go/qtls-go1-18 v0.2.0 // indirect + github.com/quic-go/qtls-go1-19 v0.2.0 // indirect + github.com/quic-go/qtls-go1-20 v0.1.0 // indirect + github.com/quic-go/quic-go v0.32.0 // indirect github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect github.com/sirupsen/logrus v1.8.1 // indirect github.com/spf13/afero v1.9.2 // indirect @@ -121,11 +134,13 @@ require ( go.uber.org/atomic v1.10.0 // indirect go.uber.org/multierr v1.8.0 // indirect go.uber.org/zap v1.23.0 // indirect + golang.org/x/exp v0.0.0-20221205204356-47842c84f3db // indirect golang.org/x/image v0.1.0 // indirect golang.org/x/mod v0.6.0 // indirect golang.org/x/sys v0.4.0 // indirect golang.org/x/text v0.6.0 // indirect golang.org/x/tools v0.2.0 // indirect + google.golang.org/genproto v0.0.0-20220519153652-3a47de7e79bd // indirect google.golang.org/protobuf v1.28.1 // indirect gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect gopkg.in/ini.v1 v1.67.0 // indirect diff --git a/go.sum b/go.sum index 9e59aca6..0df6aa34 100644 --- a/go.sum +++ b/go.sum @@ -74,6 +74,7 @@ github.com/anargu/gin-brotli v0.0.0-20220116052358-12bf532d5267/go.mod h1:Yj3yPP github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY= github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= @@ -110,6 +111,10 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= @@ -157,6 +162,7 @@ github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.m github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= @@ -213,6 +219,8 @@ github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= +github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/goccy/go-json v0.8.1/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/goccy/go-json v0.9.11 h1:/pAaQDLHEoCq/5FFmSKBswWmK6H0e8g4159Kc/X/nqk= @@ -259,6 +267,8 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= @@ -276,6 +286,7 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -292,6 +303,8 @@ github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE= +github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= @@ -321,13 +334,18 @@ github.com/grokify/html-strip-tags-go v0.0.1/go.mod h1:2Su6romC5/1VXOQMaWL2yb618 github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= +github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= @@ -350,6 +368,8 @@ github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1: github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/imroc/req/v3 v3.33.1 h1:BZnyl+K0hXcJlZBHY2CqbPgmVc1pPJDzjn6aJfB6shI= +github.com/imroc/req/v3 v3.33.1/go.mod h1:cZ+7C3L/AYOr4tLGG16hZF90F1WzAdAdzt1xFSlizXY= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc= github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= @@ -524,8 +544,10 @@ github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:v github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU= +github.com/onsi/ginkgo/v2 v2.2.0 h1:3ZNA3L1c5FYDFTTxbFeVGGD8jYvjYauHD30YgLxVsNI= +github.com/onsi/ginkgo/v2 v2.2.0/go.mod h1:MEH45j8TBi6u9BMogfbp0stKC5cdGjumZj5Y7AG4VIk= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.20.1 h1:PA/3qinGoukvymdIDV8pii6tiZgC8kbmJO6Z5+b002Q= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= @@ -589,12 +611,23 @@ github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= +github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo= +github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A= +github.com/quic-go/qtls-go1-18 v0.2.0 h1:5ViXqBZ90wpUcZS0ge79rf029yx0dYB0McyPJwqqj7U= +github.com/quic-go/qtls-go1-18 v0.2.0/go.mod h1:moGulGHK7o6O8lSPSZNoOwcLvJKJ85vVNc7oJFD65bc= +github.com/quic-go/qtls-go1-19 v0.2.0 h1:Cvn2WdhyViFUHoOqK52i51k4nDX8EwIh5VJiVM4nttk= +github.com/quic-go/qtls-go1-19 v0.2.0/go.mod h1:ySOI96ew8lnoKPtSqx2BlI5wCpUVPT05RMAlajtnyOI= +github.com/quic-go/qtls-go1-20 v0.1.0 h1:d1PK3ErFy9t7zxKsG3NXBJXZjp/kMLoIb3y/kV54oAI= +github.com/quic-go/qtls-go1-20 v0.1.0/go.mod h1:JKtK6mjbAVcUTN/9jZpvLbGxvdWIKS8uT7EiStoU1SM= +github.com/quic-go/quic-go v0.32.0 h1:lY02md31s1JgPiiyfqJijpu/UX/Iun304FI3yUqX7tA= +github.com/quic-go/quic-go v0.32.0/go.mod h1:/fCsKANhQIeD5l76c2JFU+07gVE3KaA0FP+0zMWwfwo= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk= github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8= @@ -725,6 +758,7 @@ go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= @@ -765,8 +799,8 @@ golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU= -golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= +golang.org/x/crypto v0.4.0 h1:UVQgzMY87xqpKNgb+kDsll2Igd33HszWHFLmpaRMq/8= +golang.org/x/crypto v0.4.0/go.mod h1:3quD/ATkf6oY+rnes5c3ExXTbLc8mueNue5/DoinL80= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -777,6 +811,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/exp v0.0.0-20221205204356-47842c84f3db h1:D/cFflL63o2KSLJIwjlcIt8PR064j/xsmdEJL/YvY/o= +golang.org/x/exp v0.0.0-20221205204356-47842c84f3db/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190501045829-6d32002ffd75/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= @@ -961,6 +997,7 @@ golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3 golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= @@ -1094,6 +1131,7 @@ google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfG google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= @@ -1107,6 +1145,8 @@ google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20220519153652-3a47de7e79bd h1:e0TwkXOdbnH/1x5rc5MZ/VYyiZ4v+RdVfrGMqEwT68I= +google.golang.org/genproto v0.0.0-20220519153652-3a47de7e79bd/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= @@ -1125,9 +1165,14 @@ google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3Iji google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.46.2 h1:u+MLGgVf7vRdjEYZ8wDFhAVNmhkbJ5hmrA1LMWK1CAQ= +google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -1139,6 +1184,7 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= diff --git a/i18n/en_US.yaml b/i18n/en_US.yaml index b4d1dcc0..c255d9fb 100644 --- a/i18n/en_US.yaml +++ b/i18n/en_US.yaml @@ -12,6 +12,8 @@ backend: other: Unauthorized. database_error: other: Data server error. + forbidden_error: + other: Forbidden. role: name: user: diff --git a/internal/base/middleware/user_center_plugin_auth.go b/internal/base/middleware/user_center_plugin_auth.go new file mode 100644 index 00000000..c6a190a4 --- /dev/null +++ b/internal/base/middleware/user_center_plugin_auth.go @@ -0,0 +1,19 @@ +package middleware + +import ( + "github.com/answerdev/answer/internal/base/handler" + "github.com/answerdev/answer/internal/base/reason" + "github.com/answerdev/answer/plugin" + "github.com/gin-gonic/gin" + "github.com/segmentfault/pacman/errors" +) + +// BanAPIWhenUserCenterEnabled ban api when user center enabled +func BanAPIWhenUserCenterEnabled(ctx *gin.Context) { + if plugin.UserCenterEnabled() { + handler.HandleResponse(ctx, errors.Forbidden(reason.ForbiddenError), nil) + ctx.Abort() + return + } + ctx.Next() +} diff --git a/internal/base/reason/reason.go b/internal/base/reason/reason.go index b03ad55c..d155c4b5 100644 --- a/internal/base/reason/reason.go +++ b/internal/base/reason/reason.go @@ -11,6 +11,8 @@ const ( UnauthorizedError = "base.unauthorized_error" // DatabaseError database error DatabaseError = "base.database_error" + // ForbiddenError forbidden error + ForbiddenError = "base.forbidden_error" ) const ( @@ -25,7 +27,7 @@ const ( AnswerNotFound = "error.answer.not_found" AnswerCannotDeleted = "error.answer.cannot_deleted" AnswerCannotUpdate = "error.answer.cannot_update" - AnswerCannotAddByClosedQuestion = "error.answer.question_closed_cannot_add" + AnswerCannotAddByClosedQuestion = "error.answer.question_closed_cannot_add" CommentEditWithoutPermission = "error.comment.edit_without_permission" DisallowVote = "error.object.disallow_vote" DisallowFollow = "error.object.disallow_follow" @@ -46,7 +48,7 @@ const ( TagNotContainSynonym = "error.tag.not_contain_synonym_tags" TagCannotUpdate = "error.tag.cannot_update" TagIsUsedCannotDelete = "error.tag.is_used_cannot_delete" - TagAlreadyExist = "error.tag.already_exist" + TagAlreadyExist = "error.tag.already_exist" RankFailToMeetTheCondition = "error.rank.fail_to_meet_the_condition" ThemeNotFound = "error.theme.not_found" LangNotFound = "error.lang.not_found" diff --git a/internal/controller/question_controller.go b/internal/controller/question_controller.go index a12a7523..c882f422 100644 --- a/internal/controller/question_controller.go +++ b/internal/controller/question_controller.go @@ -515,6 +515,9 @@ func (qc *QuestionController) AdminSearchAnswerList(ctx *gin.Context) { return } req.QuestionID = uid.DeShortID(req.QuestionID) + if req.QuestionID == "0" { + req.QuestionID = "" + } userID := middleware.GetLoginUserIDFromContext(ctx) questionList, count, err := qc.questionService.AdminSearchAnswerList(ctx, req, userID) handler.HandleResponse(ctx, err, gin.H{ diff --git a/internal/controller_admin/user_backyard_controller.go b/internal/controller_admin/user_backyard_controller.go index eaa06f73..76fb8378 100644 --- a/internal/controller_admin/user_backyard_controller.go +++ b/internal/controller_admin/user_backyard_controller.go @@ -3,9 +3,12 @@ package controller_admin import ( "github.com/answerdev/answer/internal/base/handler" "github.com/answerdev/answer/internal/base/middleware" + "github.com/answerdev/answer/internal/base/reason" "github.com/answerdev/answer/internal/schema" "github.com/answerdev/answer/internal/service/user_admin" + "github.com/answerdev/answer/plugin" "github.com/gin-gonic/gin" + "github.com/segmentfault/pacman/errors" ) // UserAdminController user controller @@ -29,6 +32,10 @@ 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() { + handler.HandleResponse(ctx, errors.Forbidden(reason.ForbiddenError), nil) + return + } req := &schema.UpdateUserStatusReq{} if handler.BindAndCheck(ctx, req) { return @@ -73,6 +80,10 @@ func (uc *UserAdminController) UpdateUserRole(ctx *gin.Context) { // @Success 200 {object} handler.RespBody // @Router /answer/admin/api/user [post] func (uc *UserAdminController) AddUser(ctx *gin.Context) { + if plugin.UserCenterEnabled() { + handler.HandleResponse(ctx, errors.Forbidden(reason.ForbiddenError), nil) + return + } req := &schema.AddUserReq{} if handler.BindAndCheck(ctx, req) { return @@ -95,6 +106,10 @@ func (uc *UserAdminController) AddUser(ctx *gin.Context) { // @Success 200 {object} handler.RespBody // @Router /answer/admin/api/user/password [put] func (uc *UserAdminController) UpdateUserPassword(ctx *gin.Context) { + if plugin.UserCenterEnabled() { + handler.HandleResponse(ctx, errors.Forbidden(reason.ForbiddenError), nil) + return + } req := &schema.UpdateUserPasswordReq{} if handler.BindAndCheck(ctx, req) { return diff --git a/internal/repo/rank/user_rank_repo.go b/internal/repo/rank/user_rank_repo.go index 65174999..462f999e 100644 --- a/internal/repo/rank/user_rank_repo.go +++ b/internal/repo/rank/user_rank_repo.go @@ -9,6 +9,7 @@ import ( "github.com/answerdev/answer/internal/entity" "github.com/answerdev/answer/internal/service/config" "github.com/answerdev/answer/internal/service/rank" + "github.com/answerdev/answer/plugin" "github.com/jinzhu/now" "github.com/segmentfault/pacman/errors" "github.com/segmentfault/pacman/log" @@ -36,6 +37,10 @@ func NewUserRankRepo(data *data.Data, configRepo config.ConfigRepo) rank.UserRan func (ur *UserRankRepo) TriggerUserRank(ctx context.Context, session *xorm.Session, userID string, deltaRank int, activityType int, ) (isReachStandard bool, err error) { + // IMPORTANT: If user center enabled the rank agent, then we should not change user rank. + if plugin.RankAgentEnabled() { + return false, nil + } if deltaRank == 0 { return false, nil } diff --git a/internal/repo/user/user_backyard_repo.go b/internal/repo/user/user_backyard_repo.go index 1b6cb2f1..6c7efe3f 100644 --- a/internal/repo/user/user_backyard_repo.go +++ b/internal/repo/user/user_backyard_repo.go @@ -86,6 +86,10 @@ func (ur *userAdminRepo) GetUserInfo(ctx context.Context, userID string) (user * if err != nil { return nil, false, errors.InternalServer(reason.DatabaseError).WithError(err).WithStack() } + err = tryToDecorateUserInfoFromUserCenter(ctx, ur.data, user) + if err != nil { + return nil, false, err + } return } @@ -96,6 +100,11 @@ func (ur *userAdminRepo) GetUserInfoByEmail(ctx context.Context, email string) ( Where("status != ?", entity.UserStatusDeleted).Get(userInfo) if err != nil { err = errors.InternalServer(reason.DatabaseError).WithError(err).WithStack() + return + } + err = tryToDecorateUserInfoFromUserCenter(ctx, ur.data, user) + if err != nil { + return nil, false, err } return } @@ -127,6 +136,8 @@ func (ur *userAdminRepo) GetUserPage(ctx context.Context, page, pageSize int, us total, err = pager.Help(page, pageSize, &users, user, session) if err != nil { err = errors.InternalServer(reason.DatabaseError).WithError(err).WithStack() + return } + tryToDecorateUserListFromUserCenter(ctx, ur.data, users) return } diff --git a/internal/repo/user/user_repo.go b/internal/repo/user/user_repo.go index 0a553521..d0226a41 100644 --- a/internal/repo/user/user_repo.go +++ b/internal/repo/user/user_repo.go @@ -142,7 +142,10 @@ func (ur *userRepo) GetByUserID(ctx context.Context, userID string) (userInfo *e err = errors.InternalServer(reason.DatabaseError).WithError(err).WithStack() return } - ur.tryToDecorateUserInfoFromUserCenter(ctx, userInfo) + err = tryToDecorateUserInfoFromUserCenter(ctx, ur.data, userInfo) + if err != nil { + return nil, false, err + } return } @@ -152,7 +155,7 @@ func (ur *userRepo) BatchGetByID(ctx context.Context, ids []string) ([]*entity.U if err != nil { return nil, errors.InternalServer(reason.DatabaseError).WithError(err).WithStack() } - ur.tryToDecorateUserListFromUserCenter(ctx, list) + tryToDecorateUserListFromUserCenter(ctx, ur.data, list) return list, nil } @@ -164,7 +167,10 @@ func (ur *userRepo) GetByUsername(ctx context.Context, username string) (userInf err = errors.InternalServer(reason.DatabaseError).WithError(err).WithStack() return } - ur.tryToDecorateUserInfoFromUserCenter(ctx, userInfo) + err = tryToDecorateUserInfoFromUserCenter(ctx, ur.data, userInfo) + if err != nil { + return nil, false, err + } return } @@ -188,28 +194,27 @@ func (ur *userRepo) GetUserCount(ctx context.Context) (count int64, err error) { return } -func (ur *userRepo) tryToDecorateUserInfoFromUserCenter(ctx context.Context, original *entity.User) { +func tryToDecorateUserInfoFromUserCenter(ctx context.Context, data *data.Data, original *entity.User) (err error) { uc, ok := plugin.GetUserCenter() if !ok { - return + return nil } userInfo := &entity.UserExternalLogin{} - session := ur.data.DB.Where("user_id = ?", original.ID) + session := data.DB.Where("user_id = ?", original.ID) session.Where("provider = ?", uc.Info().SlugName) exist, err := session.Get(userInfo) if err != nil { - log.Error(err) - return + return errors.InternalServer(reason.DatabaseError).WithError(err).WithStack() } if !exist { - return + return nil } userCenterBasicUserInfo, err := uc.UserInfo(userInfo.ExternalID) if err != nil { log.Error(err) - return + return errors.BadRequest(reason.UserNotFound).WithError(err).WithStack() } // In general, usernames should be guaranteed unique by the User Center plugin, so there are no inconsistencies. @@ -217,10 +222,10 @@ func (ur *userRepo) tryToDecorateUserInfoFromUserCenter(ctx context.Context, ori log.Warnf("user %s username is inconsistent with user center", original.ID) } decorateByUserCenterUser(original, userCenterBasicUserInfo) + return nil } -func (ur *userRepo) tryToDecorateUserListFromUserCenter(ctx context.Context, original []*entity.User) { - log.Debugf("try to decorate user list from user center, original: %+v", original) +func tryToDecorateUserListFromUserCenter(ctx context.Context, data *data.Data, original []*entity.User) { uc, ok := plugin.GetUserCenter() if !ok { return @@ -234,7 +239,7 @@ func (ur *userRepo) tryToDecorateUserListFromUserCenter(ctx context.Context, ori } userExternalLoginList := make([]*entity.UserExternalLogin, 0) - session := ur.data.DB.Where("provider = ?", uc.Info().SlugName) + session := data.DB.Where("provider = ?", uc.Info().SlugName) session.In("user_id", ids) err := session.Find(&userExternalLoginList) if err != nil { @@ -248,10 +253,13 @@ func (ur *userRepo) tryToDecorateUserListFromUserCenter(ctx context.Context, ori originalExternalIDMapping[u.ExternalID] = originalUserIDMapping[u.UserID] userExternalIDs = append(userExternalIDs, u.ExternalID) } + if len(userExternalIDs) == 0 { + return + } ucUsers, err := uc.UserList(userExternalIDs) if err != nil { - log.Error(err) + log.Errorf("get user list from user center failed: %v, %v", err, userExternalIDs) return } @@ -277,4 +285,5 @@ func decorateByUserCenterUser(original *entity.User, ucUser *plugin.UserCenterBa if plugin.RankAgentEnabled() { original.Rank = ucUser.Rank } + original.Status = int(ucUser.Status) } diff --git a/internal/router/answer_api_router.go b/internal/router/answer_api_router.go index 22b11603..015bfcac 100644 --- a/internal/router/answer_api_router.go +++ b/internal/router/answer_api_router.go @@ -1,6 +1,7 @@ package router import ( + "github.com/answerdev/answer/internal/base/middleware" "github.com/answerdev/answer/internal/controller" "github.com/answerdev/answer/internal/controller_admin" "github.com/gin-gonic/gin" @@ -100,23 +101,24 @@ func (a *AnswerAPIRouter) RegisterMustUnAuthAnswerAPIRouter(r *gin.RouterGroup) r.GET("/siteinfo/legal", a.siteinfoController.GetSiteLegalInfo) // user - r.POST("/user/login/email", a.userController.UserEmailLogin) - r.POST("/user/register/email", a.userController.UserRegisterByEmail) - r.GET("/user/register/captcha", a.userController.UserRegisterCaptcha) - r.POST("/user/email/verification", a.userController.UserVerifyEmail) - r.PUT("/user/email", a.userController.UserChangeEmailVerify) - r.GET("/user/action/record", a.userController.ActionRecord) - r.POST("/user/password/reset", a.userController.RetrievePassWord) - r.POST("/user/password/replacement", a.userController.UseRePassWord) r.GET("/user/info", a.userController.GetUserInfoByUserID) - r.PUT("/user/email/notification", a.userController.UserUnsubscribeEmailNotification) + routerGroup := r.Group("", middleware.BanAPIWhenUserCenterEnabled) + routerGroup.POST("/user/login/email", a.userController.UserEmailLogin) + routerGroup.POST("/user/register/email", a.userController.UserRegisterByEmail) + routerGroup.GET("/user/register/captcha", a.userController.UserRegisterCaptcha) + routerGroup.POST("/user/email/verification", a.userController.UserVerifyEmail) + routerGroup.PUT("/user/email", a.userController.UserChangeEmailVerify) + routerGroup.GET("/user/action/record", a.userController.ActionRecord) + routerGroup.POST("/user/password/reset", a.userController.RetrievePassWord) + routerGroup.POST("/user/password/replacement", a.userController.UseRePassWord) + routerGroup.PUT("/user/email/notification", a.userController.UserUnsubscribeEmailNotification) } func (a *AnswerAPIRouter) RegisterUnAuthAnswerAPIRouter(r *gin.RouterGroup) { // user r.GET("/user/logout", a.userController.UserLogout) - r.POST("/user/email/change/code", a.userController.UserChangeEmailSendCode) - r.POST("/user/email/verification/send", a.userController.UserVerifyEmailSend) + r.POST("/user/email/change/code", middleware.BanAPIWhenUserCenterEnabled, a.userController.UserChangeEmailSendCode) + r.POST("/user/email/verification/send", middleware.BanAPIWhenUserCenterEnabled, a.userController.UserVerifyEmailSend) r.GET("/personal/user/info", a.userController.GetOtherUserInfoByUsername) r.GET("/user/ranking", a.userController.UserRanking) @@ -202,8 +204,8 @@ func (a *AnswerAPIRouter) RegisterAnswerAPIRouter(r *gin.RouterGroup) { r.DELETE("/answer", a.answerController.RemoveAnswer) // user - r.PUT("/user/password", a.userController.UserModifyPassWord) - r.PUT("/user/info", a.userController.UserUpdateInfo) + r.PUT("/user/password", middleware.BanAPIWhenUserCenterEnabled, a.userController.UserModifyPassWord) + r.PUT("/user/info", middleware.BanAPIWhenUserCenterEnabled, a.userController.UserUpdateInfo) r.PUT("/user/interface", a.userController.UserUpdateInterface) r.POST("/user/notice/set", a.userController.UserNoticeSet) @@ -242,10 +244,10 @@ func (a *AnswerAPIRouter) RegisterAnswerAdminAPIRouter(r *gin.RouterGroup) { // user r.GET("/users/page", a.adminUserController.GetUserPage) - r.PUT("/user/status", a.adminUserController.UpdateUserStatus) + r.PUT("/user/status", middleware.BanAPIWhenUserCenterEnabled, a.adminUserController.UpdateUserStatus) r.PUT("/user/role", a.adminUserController.UpdateUserRole) - r.POST("/user", a.adminUserController.AddUser) - r.PUT("/user/password", a.adminUserController.UpdateUserPassword) + r.POST("/user", middleware.BanAPIWhenUserCenterEnabled, a.adminUserController.AddUser) + r.PUT("/user/password", middleware.BanAPIWhenUserCenterEnabled, a.adminUserController.UpdateUserPassword) // reason r.GET("/reasons", a.reasonController.Reasons) diff --git a/internal/service/rank/rank_service.go b/internal/service/rank/rank_service.go index b079bed5..534913d8 100644 --- a/internal/service/rank/rank_service.go +++ b/internal/service/rank/rank_service.go @@ -16,6 +16,7 @@ import ( usercommon "github.com/answerdev/answer/internal/service/user_common" "github.com/answerdev/answer/pkg/htmltext" "github.com/answerdev/answer/pkg/uid" + "github.com/answerdev/answer/plugin" "github.com/segmentfault/pacman/errors" "github.com/segmentfault/pacman/log" "xorm.io/xorm" @@ -228,6 +229,9 @@ func (rs *RankService) checkUserRank(ctx context.Context, userID string, userRan // GetRankPersonalWithPage get personal comment list page func (rs *RankService) GetRankPersonalWithPage(ctx context.Context, req *schema.GetRankPersonalWithPageReq) ( pageModel *pager.PageModel, err error) { + if plugin.RankAgentEnabled() { + return pager.NewPageModel(0, []string{}), nil + } if len(req.Username) > 0 { userInfo, exist, err := rs.userCommon.GetUserBasicInfoByUserName(ctx, req.Username) if err != nil { diff --git a/plugin/user_center.go b/plugin/user_center.go index a32e6276..adc064f5 100644 --- a/plugin/user_center.go +++ b/plugin/user_center.go @@ -2,13 +2,21 @@ package plugin type UserCenter interface { Base + // Description returns the description of the user center, including the name, icon, url, etc. Description() UserCenterDesc + // ControlCenterItems returns the items that will be displayed in the control center ControlCenterItems() []ControlCenter + // LoginCallback is called when the user center login callback is called LoginCallback(ctx *GinContext) (userInfo *UserCenterBasicUserInfo, err error) + // SignUpCallback is called when the user center sign up callback is called SignUpCallback(ctx *GinContext) (userInfo *UserCenterBasicUserInfo, err error) + // UserInfo returns the user information UserInfo(externalID string) (userInfo *UserCenterBasicUserInfo, err error) + // UserList returns the user list information UserList(externalIDs []string) (userInfo []*UserCenterBasicUserInfo, err error) + // UserSettings returns the user settings UserSettings(externalID string) (userSettings *SettingInfo, err error) + // PersonalBranding returns the personal branding information PersonalBranding(externalID string) (branding []*PersonalBranding) } @@ -21,14 +29,23 @@ type UserCenterDesc struct { RankAgentEnabled bool `json:"rank_agent_enabled"` } +type UserStatus int + +const ( + UserStatusAvailable UserStatus = 1 + UserStatusSuspended UserStatus = 9 + UserStatusDeleted UserStatus = 10 +) + type UserCenterBasicUserInfo struct { - ExternalID string `json:"external_id"` - Username string `json:"username"` - DisplayName string `json:"display_name"` - Email string `json:"email"` - Rank int `json:"rank"` - Avatar string `json:"avatar"` - Mobile string `json:"mobile"` + ExternalID string `json:"external_id"` + Username string `json:"username"` + DisplayName string `json:"display_name"` + Email string `json:"email"` + Rank int `json:"rank"` + Avatar string `json:"avatar"` + Mobile string `json:"mobile"` + Status UserStatus `json:"status"` } type ControlCenter struct { From ad13b97eafa96279c10ca720e3ba63815d78cb87 Mon Sep 17 00:00:00 2001 From: LinkinStars Date: Mon, 3 Apr 2023 18:13:35 +0800 Subject: [PATCH 4/5] feat(rank): removed reputation-related leaderboard and notification content --- internal/service/comment/comment_service.go | 2 -- internal/service/notification_common/notification.go | 5 ++++- .../user_external_login/user_center_login_service.go | 3 +++ .../user_external_login/user_external_login_service.go | 6 ++++-- internal/service/user_service.go | 7 +++++++ 5 files changed, 18 insertions(+), 5 deletions(-) diff --git a/internal/service/comment/comment_service.go b/internal/service/comment/comment_service.go index 38595229..1b65c48d 100644 --- a/internal/service/comment/comment_service.go +++ b/internal/service/comment/comment_service.go @@ -20,7 +20,6 @@ import ( "github.com/answerdev/answer/pkg/encryption" "github.com/answerdev/answer/pkg/htmltext" "github.com/answerdev/answer/pkg/uid" - "github.com/davecgh/go-spew/spew" "github.com/jinzhu/copier" "github.com/segmentfault/pacman/errors" "github.com/segmentfault/pacman/log" @@ -448,7 +447,6 @@ func (cs *CommentService) GetCommentPersonalWithPage(ctx context.Context, req *s if err != nil { log.Error(err) } else { - spew.Dump("==", objInfo) commentResp.ObjectType = objInfo.ObjectType commentResp.Title = objInfo.Title commentResp.UrlTitle = htmltext.UrlTitle(objInfo.Title) diff --git a/internal/service/notification_common/notification.go b/internal/service/notification_common/notification.go index eb148cd4..ecc0a125 100644 --- a/internal/service/notification_common/notification.go +++ b/internal/service/notification_common/notification.go @@ -15,6 +15,7 @@ import ( "github.com/answerdev/answer/internal/service/object_info" usercommon "github.com/answerdev/answer/internal/service/user_common" "github.com/answerdev/answer/pkg/uid" + "github.com/answerdev/answer/plugin" "github.com/goccy/go-json" "github.com/jinzhu/copier" "github.com/segmentfault/pacman/errors" @@ -82,7 +83,9 @@ func (ns *NotificationCommon) HandleNotification() { // ObjectInfo.ObjectID // ObjectInfo.ObjectType func (ns *NotificationCommon) AddNotification(ctx context.Context, msg *schema.NotificationMsg) error { - + if msg.Type == schema.NotificationTypeAchievement && plugin.RankAgentEnabled() { + return nil + } req := &schema.NotificationContent{ TriggerUserID: msg.TriggerUserID, ReceiverUserID: msg.ReceiverUserID, 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 df48aa63..dff2be3f 100644 --- a/internal/service/user_external_login/user_center_login_service.go +++ b/internal/service/user_external_login/user_center_login_service.go @@ -53,6 +53,9 @@ func (us *UserCenterLoginService) ExternalLogin( return nil, err } if exist { + if err := us.userRepo.UpdateLastLoginDate(ctx, oldUserInfo.ID); err != nil { + log.Errorf("update user last login date failed: %v", err) + } accessToken, _, err := us.userCommonService.CacheLoginUserInfo( ctx, oldUserInfo.ID, oldUserInfo.MailStatus, oldUserInfo.Status) return &schema.UserExternalLoginResp{AccessToken: accessToken}, err 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 383491a1..d3288685 100644 --- a/internal/service/user_external_login/user_external_login_service.go +++ b/internal/service/user_external_login/user_external_login_service.go @@ -24,8 +24,7 @@ type UserExternalLoginRepo interface { AddUserExternalLogin(ctx context.Context, user *entity.UserExternalLogin) (err error) UpdateInfo(ctx context.Context, userInfo *entity.UserExternalLogin) (err error) GetByExternalID(ctx context.Context, provider, externalID string) (userInfo *entity.UserExternalLogin, exist bool, err error) - GetUserExternalLoginList(ctx context.Context, userID string) ( - resp []*entity.UserExternalLogin, err error) + GetUserExternalLoginList(ctx context.Context, userID string) (resp []*entity.UserExternalLogin, err error) DeleteUserExternalLogin(ctx context.Context, userID, externalID string) (err error) SetCacheUserExternalLoginInfo(ctx context.Context, key string, info *schema.ExternalLoginUserInfoCache) (err error) GetCacheUserExternalLoginInfo(ctx context.Context, key string) (info *schema.ExternalLoginUserInfoCache, err error) @@ -76,6 +75,9 @@ func (us *UserExternalLoginService) ExternalLogin( return nil, err } if exist && oldUserInfo.Status != entity.UserStatusDeleted { + if err := us.userRepo.UpdateLastLoginDate(ctx, oldUserInfo.ID); err != nil { + log.Errorf("update user last login date failed: %v", err) + } newMailStatus, err := us.activeUser(ctx, oldUserInfo, externalUserInfo) if err != nil { log.Error(err) diff --git a/internal/service/user_service.go b/internal/service/user_service.go index c4f9fa27..0af62656 100644 --- a/internal/service/user_service.go +++ b/internal/service/user_service.go @@ -22,6 +22,7 @@ import ( usercommon "github.com/answerdev/answer/internal/service/user_common" "github.com/answerdev/answer/internal/service/user_external_login" "github.com/answerdev/answer/pkg/checker" + "github.com/answerdev/answer/plugin" "github.com/google/uuid" "github.com/segmentfault/pacman/errors" "github.com/segmentfault/pacman/log" @@ -639,6 +640,9 @@ func (us *UserService) UserUnsubscribeEmailNotification( func (us *UserService) getActivityUserRankStat(ctx context.Context, startTime, endTime time.Time, limit int, userIDExist map[string]bool) (rankStat []*entity.ActivityUserRankStat, userIDs []string, err error) { + if plugin.RankAgentEnabled() { + return make([]*entity.ActivityUserRankStat, 0), make([]string, 0), nil + } rankStat, err = us.activityRepo.GetUsersWhoHasGainedTheMostReputation(ctx, startTime, endTime, limit) if err != nil { return nil, nil, err @@ -658,6 +662,9 @@ func (us *UserService) getActivityUserRankStat(ctx context.Context, startTime, e func (us *UserService) getActivityUserVoteStat(ctx context.Context, startTime, endTime time.Time, limit int, userIDExist map[string]bool) (voteStat []*entity.ActivityUserVoteStat, userIDs []string, err error) { + if plugin.RankAgentEnabled() { + return make([]*entity.ActivityUserVoteStat, 0), make([]string, 0), nil + } voteStat, err = us.activityRepo.GetUsersWhoHasVoteMost(ctx, startTime, endTime, limit) if err != nil { return nil, nil, err From e3ff77a8efcf36ead32086d4b309cb8567a65a78 Mon Sep 17 00:00:00 2001 From: LinkinStars Date: Tue, 4 Apr 2023 11:03:26 +0800 Subject: [PATCH 5/5] docs(i18n): update zh_CN.yaml --- i18n/zh_CN.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/i18n/zh_CN.yaml b/i18n/zh_CN.yaml index 23f9e782..04fd53e6 100644 --- a/i18n/zh_CN.yaml +++ b/i18n/zh_CN.yaml @@ -661,6 +661,7 @@ ui: label: 确认新密码 settings: page_title: 设置 + goto_modify: 前往修改 nav: profile: 我的资料 notification: 通知