feat(plugin): add user center plugin

This commit is contained in:
LinkinStars 2023-03-29 17:12:45 +08:00
parent aa13657e8d
commit e5df07ad7c
14 changed files with 658 additions and 10 deletions

View File

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

View File

@ -25,4 +25,5 @@ var ProviderSetController = wire.NewSet(
NewActivityController,
NewTemplateController,
NewConnectorController,
NewUserCenterController,
)

View File

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

View File

@ -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
}
}

View File

@ -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()
}

View File

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

View File

@ -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"`
}

View File

@ -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"`
}

View File

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

View File

@ -82,5 +82,6 @@ var ProviderSetService = wire.NewSet(
role.NewUserRoleRelService,
role.NewRolePowerRelService,
user_external_login.NewUserExternalLoginService,
user_external_login.NewUserCenterLoginService,
plugin_common.NewPluginCommonService,
)

View File

@ -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
}

View File

@ -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
}

View File

@ -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 {

81
plugin/user_center.go Normal file
View File

@ -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
}