mirror of https://gitee.com/answerdev/answer.git
feat(plugin): add user center plugin
This commit is contained in:
parent
aa13657e8d
commit
e5df07ad7c
|
@ -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)
|
||||
|
|
|
@ -25,4 +25,5 @@ var ProviderSetController = wire.NewSet(
|
|||
NewActivityController,
|
||||
NewTemplateController,
|
||||
NewConnectorController,
|
||||
NewUserCenterController,
|
||||
)
|
||||
|
|
|
@ -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)
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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"`
|
||||
}
|
|
@ -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"`
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -82,5 +82,6 @@ var ProviderSetService = wire.NewSet(
|
|||
role.NewUserRoleRelService,
|
||||
role.NewRolePowerRelService,
|
||||
user_external_login.NewUserExternalLoginService,
|
||||
user_external_login.NewUserCenterLoginService,
|
||||
plugin_common.NewPluginCommonService,
|
||||
)
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
}
|
Loading…
Reference in New Issue