feat(usercenter): add user center plugin and agent

This commit is contained in:
LinkinStars 2023-04-14 17:33:22 +08:00
parent d641bdf11e
commit a3cc0ac38a
24 changed files with 298 additions and 53 deletions

View File

@ -82,6 +82,10 @@ import (
"github.com/segmentfault/pacman/log"
)
import (
_ "git.backyard.segmentfault.com/segmentfault/answer-private-plugins/user_center/wecom"
)
// Injectors from wire.go:
// initApplication init application.
@ -220,7 +224,7 @@ 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)
userCenterLoginService := user_external_login2.NewUserCenterLoginService(userRepo, userCommon, userExternalLoginRepo, userActiveActivityRepo)
userCenterLoginService := user_external_login2.NewUserCenterLoginService(userRepo, userCommon, userExternalLoginRepo, userActiveActivityRepo, siteInfoCommonService)
userCenterController := controller.NewUserCenterController(userCenterLoginService, siteInfoCommonService)
pluginAPIRouter := router.NewPluginAPIRouter(connectorController, userCenterController)
ginEngine := server.NewHTTPServer(debug, staticRouter, answerAPIRouter, swaggerRouter, uiRouter, authUserMiddleware, avatarMiddleware, templateRouter, pluginAPIRouter)

View File

@ -66,6 +66,8 @@ backend:
other: Email should be verified.
verify_url_expired:
other: Email verified URL has expired, please resend the email.
illegal_email_domain_error:
other: The domain name of the current email address cannot be registered.
lang:
not_found:
other: Language file not found.

View File

@ -65,6 +65,7 @@ const (
SiteTypeLogin = "login"
SiteTypeCustomCssHTML = "css-html"
SiteTypeTheme = "theme"
SiteTypeUsers = "users"
)
func ExistInPathIgnore(name string) bool {

View File

@ -42,6 +42,7 @@ const (
EmailDuplicate = "error.email.duplicate"
EmailVerifyURLExpired = "error.email.verify_url_expired"
EmailNeedToBeVerified = "error.email.need_to_be_verified"
EmailIllegalDomainError = "error.email.illegal_email_domain_error"
UserSuspended = "error.user.suspended"
ObjectNotFound = "error.object.not_found"
TagNotFound = "error.tag.not_found"

View File

@ -7,6 +7,7 @@ import (
brotli "github.com/anargu/gin-brotli"
"github.com/answerdev/answer/internal/base/middleware"
"github.com/answerdev/answer/internal/router"
"github.com/answerdev/answer/plugin"
"github.com/answerdev/answer/ui"
"github.com/gin-gonic/gin"
)
@ -67,5 +68,12 @@ func NewHTTPServer(debug bool,
// plugin routes
pluginAPIRouter.RegisterUnAuthConnectorRouter(mustUnAuthV1)
pluginAPIRouter.RegisterAuthConnectorRouter(authV1)
_ = plugin.CallAgent(func(agent plugin.Agent) error {
agent.RegisterUnAuthRouter(unAuthV1)
agent.RegisterAuthUserRouter(authV1)
agent.RegisterAuthAdminRouter(adminauthV1)
return nil
})
return r
}

View File

@ -3,6 +3,8 @@ package controller
import (
"fmt"
"net/http"
"net/url"
"strings"
"github.com/answerdev/answer/internal/base/handler"
"github.com/answerdev/answer/internal/base/middleware"
@ -90,8 +92,8 @@ func (uc *UserCenterController) UserCenterPersonalBranding(ctx *gin.Context) {
func (uc *UserCenterController) UserCenterLoginRedirect(ctx *gin.Context) {
var redirectURL string
_ = plugin.CallUserCenter(func(uc plugin.UserCenter) error {
info := uc.Description()
_ = plugin.CallUserCenter(func(userCenter plugin.UserCenter) error {
info := userCenter.Description()
redirectURL = info.LoginRedirectURL
return nil
})
@ -100,9 +102,9 @@ func (uc *UserCenterController) UserCenterLoginRedirect(ctx *gin.Context) {
func (uc *UserCenterController) UserCenterSignUpRedirect(ctx *gin.Context) {
var redirectURL string
_ = plugin.CallUserCenter(func(uc plugin.UserCenter) error {
info := uc.Description()
redirectURL = info.SignUpRedirectURL
_ = plugin.CallUserCenter(func(userCenter plugin.UserCenter) error {
info := userCenter.Description()
redirectURL = info.LoginRedirectURL
return nil
})
ctx.Redirect(http.StatusFound, redirectURL)
@ -128,12 +130,17 @@ func (uc *UserCenterController) UserCenterLoginCallback(ctx *gin.Context) {
return
}
resp, err := uc.userCenterLoginService.ExternalLogin(ctx, userCenter.Info().SlugName, userInfo)
resp, err := uc.userCenterLoginService.ExternalLogin(ctx, userCenter, userInfo)
if err != nil {
log.Errorf("external login failed: %v", err)
ctx.Redirect(http.StatusFound, "/50x")
return
}
if len(resp.ErrMsg) > 0 {
ctx.String(http.StatusOK, resp.ErrMsg)
return
}
userCenter.AfterLogin(userInfo.ExternalID, resp.AccessToken)
ctx.Redirect(http.StatusFound, fmt.Sprintf("%s/users/oauth?access_token=%s",
siteGeneral.SiteUrl, resp.AccessToken))
}
@ -158,12 +165,17 @@ func (uc *UserCenterController) UserCenterSignUpCallback(ctx *gin.Context) {
return
}
resp, err := uc.userCenterLoginService.ExternalLogin(ctx, userCenter.Info().SlugName, userInfo)
resp, err := uc.userCenterLoginService.ExternalLogin(ctx, userCenter, userInfo)
if err != nil {
log.Errorf("external login failed: %v", err)
ctx.Redirect(http.StatusFound, "/50x")
return
}
if len(resp.ErrMsg) > 0 {
ctx.String(http.StatusOK, resp.ErrMsg)
return
}
userCenter.AfterLogin(userInfo.ExternalID, resp.AccessToken)
ctx.Redirect(http.StatusFound, fmt.Sprintf("%s/users/oauth?access_token=%s",
siteGeneral.SiteUrl, resp.AccessToken))
}
@ -174,3 +186,17 @@ func (uc *UserCenterController) UserCenterUserSettings(ctx *gin.Context) {
resp, err := uc.userCenterLoginService.UserCenterUserSettings(ctx, userID)
handler.HandleResponse(ctx, err, resp)
}
func (uc *UserCenterController) formatRedirectURL(ctx *gin.Context, redirectURL string) string {
if !strings.Contains(redirectURL, "CALLBACK_URL") {
return redirectURL
}
general, err := uc.siteInfoService.GetSiteGeneral(ctx)
if err != nil {
log.Error(err)
ctx.Redirect(http.StatusFound, "/50x")
return ""
}
callbackURL := fmt.Sprintf("%s%s%s", general.SiteUrl, commonRouterPrefix, "/user-center/login/callback")
return strings.ReplaceAll(redirectURL, "CALLBACK_URL", url.QueryEscape(callbackURL))
}

View File

@ -13,6 +13,7 @@ import (
"github.com/answerdev/answer/internal/service/export"
"github.com/answerdev/answer/internal/service/siteinfo_common"
"github.com/answerdev/answer/internal/service/uploader"
"github.com/answerdev/answer/pkg/checker"
"github.com/gin-gonic/gin"
"github.com/segmentfault/pacman/errors"
"github.com/segmentfault/pacman/log"
@ -223,7 +224,7 @@ func (uc *UserController) UserRegisterByEmail(ctx *gin.Context) {
handler.HandleResponse(ctx, err, nil)
return
}
if !siteInfo.AllowNewRegistrations {
if !siteInfo.AllowNewRegistrations || !siteInfo.AllowEmailRegistrations {
handler.HandleResponse(ctx, errors.BadRequest(reason.NotAllowedRegistration), nil)
return
}
@ -232,6 +233,10 @@ func (uc *UserController) UserRegisterByEmail(ctx *gin.Context) {
if handler.BindAndCheck(ctx, req) {
return
}
if !checker.EmailInAllowEmailDomain(req.Email, siteInfo.AllowEmailDomains) {
handler.HandleResponse(ctx, errors.BadRequest(reason.EmailIllegalDomainError), nil)
return
}
req.IP = ctx.ClientIP()
captchaPass := uc.actionService.UserRegisterVerifyCaptcha(ctx, req.CaptchaID, req.CaptchaCode)
if !captchaPass {
@ -488,6 +493,16 @@ func (uc *UserController) UserChangeEmailSendCode(ctx *gin.Context) {
handler.HandleResponse(ctx, errors.Unauthorized(reason.UnauthorizedError), nil)
return
}
// check whether email allow register or not
siteInfo, err := uc.siteInfoCommonService.GetSiteLogin(ctx)
if err != nil {
handler.HandleResponse(ctx, err, nil)
return
}
if !checker.EmailInAllowEmailDomain(req.Email, siteInfo.AllowEmailDomains) {
handler.HandleResponse(ctx, errors.BadRequest(reason.EmailIllegalDomainError), nil)
return
}
captchaPass := uc.actionService.ActionRecordVerifyCaptcha(ctx, schema.ActionRecordTypeEmail, ctx.ClientIP(), req.CaptchaID, req.CaptchaCode)
if !captchaPass {

View File

@ -139,6 +139,19 @@ func (sc *SiteInfoController) GetSiteTheme(ctx *gin.Context) {
handler.HandleResponse(ctx, err, resp)
}
// GetSiteUsers get site user config
// @Summary get site user config
// @Description get site user config
// @Security ApiKeyAuth
// @Tags admin
// @Produce json
// @Success 200 {object} handler.RespBody{data=schema.SiteUsersResp}
// @Router /answer/admin/api/siteinfo/users [get]
func (sc *SiteInfoController) GetSiteUsers(ctx *gin.Context) {
resp, err := sc.siteInfoService.GetSiteUsers(ctx)
handler.HandleResponse(ctx, err, resp)
}
// GetRobots get site robots information
// @Summary get site robots information
// @Description get site robots information
@ -336,6 +349,24 @@ func (sc *SiteInfoController) SaveSiteTheme(ctx *gin.Context) {
handler.HandleResponse(ctx, err, nil)
}
// UpdateSiteUsers update site config about users
// @Summary update site info config about users
// @Description update site info config about users
// @Security ApiKeyAuth
// @Tags admin
// @Produce json
// @Param data body schema.SiteBrandingReq true "users info"
// @Success 200 {object} handler.RespBody{}
// @Router /answer/admin/api/siteinfo/users [put]
func (sc *SiteInfoController) UpdateSiteUsers(ctx *gin.Context) {
req := &schema.SiteUsersReq{}
if handler.BindAndCheck(ctx, req) {
return
}
err := sc.siteInfoService.SaveSiteUsers(ctx, req)
handler.HandleResponse(ctx, err, nil)
}
// GetSMTPConfig get smtp config
// @Summary GetSMTPConfig get smtp config
// @Description GetSMTPConfig get smtp config

View File

@ -143,8 +143,9 @@ func initSiteInfo(engine *xorm.Engine, language, siteName, siteURL, contactEmail
}
loginConfig := map[string]bool{
"allow_new_registrations": true,
"login_required": false,
"allow_new_registrations": true,
"allow_email_registrations": true,
"login_required": false,
}
loginConfigDataBytes, _ := json.Marshal(loginConfig)
_, err = engine.InsertOne(&entity.SiteInfo{

View File

@ -57,6 +57,7 @@ var migrations = []Migration{
NewMigration("add theme and private mode", addThemeAndPrivateMode, true),
NewMigration("add new answer notification", addNewAnswerNotification, true),
NewMigration("add plugin", addPlugin, false),
NewMigration("add login limitations", addLoginLimitations, true),
}
// GetCurrentDBVersion returns the current db version

31
internal/migrations/v9.go Normal file
View File

@ -0,0 +1,31 @@
package migrations
import (
"encoding/json"
"fmt"
"github.com/answerdev/answer/internal/base/constant"
"github.com/answerdev/answer/internal/entity"
"github.com/answerdev/answer/internal/schema"
"xorm.io/xorm"
)
func addLoginLimitations(x *xorm.Engine) error {
loginSiteInfo := &entity.SiteInfo{
Type: constant.SiteTypeLogin,
}
exist, err := x.Get(loginSiteInfo)
if err != nil {
return fmt.Errorf("get config failed: %w", err)
}
if exist {
content := &schema.SiteLoginReq{}
_ = json.Unmarshal([]byte(loginSiteInfo.Content), content)
content.AllowEmailRegistrations = true
_, err = x.ID(loginSiteInfo.ID).Cols("content").Update(loginSiteInfo)
if err != nil {
return fmt.Errorf("update site info failed: %w", err)
}
}
return nil
}

View File

@ -10,6 +10,7 @@ import (
"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/pkg/converter"
"github.com/answerdev/answer/plugin"
"github.com/segmentfault/pacman/errors"
"github.com/segmentfault/pacman/log"
@ -276,14 +277,28 @@ func decorateByUserCenterUser(original *entity.User, ucUser *plugin.UserCenterBa
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 len(ucUser.DisplayName) > 0 {
original.DisplayName = ucUser.DisplayName
}
if len(ucUser.Email) > 0 {
original.EMail = ucUser.Email
}
if len(ucUser.Avatar) > 0 {
original.Avatar = schema.CustomAvatar(ucUser.Avatar).ToJsonString()
}
if len(ucUser.Mobile) > 0 {
original.Mobile = ucUser.Mobile
}
if len(ucUser.Bio) > 0 {
original.Bio = ucUser.Bio
original.BioHTML = converter.Markdown2HTML(ucUser.Bio)
}
// If plugin enable rank agent, use rank from user center.
if plugin.RankAgentEnabled() {
original.Rank = ucUser.Rank
}
original.Status = int(ucUser.Status)
if ucUser.Status != plugin.UserStatusAvailable {
original.Status = int(ucUser.Status)
}
}

View File

@ -244,7 +244,7 @@ func (a *AnswerAPIRouter) RegisterAnswerAdminAPIRouter(r *gin.RouterGroup) {
// user
r.GET("/users/page", a.adminUserController.GetUserPage)
r.PUT("/user/status", middleware.BanAPIWhenUserCenterEnabled, a.adminUserController.UpdateUserStatus)
r.PUT("/user/status", a.adminUserController.UpdateUserStatus)
r.PUT("/user/role", a.adminUserController.UpdateUserRole)
r.POST("/user", middleware.BanAPIWhenUserCenterEnabled, a.adminUserController.AddUser)
r.PUT("/user/password", middleware.BanAPIWhenUserCenterEnabled, a.adminUserController.UpdateUserPassword)
@ -260,23 +260,25 @@ func (a *AnswerAPIRouter) RegisterAnswerAdminAPIRouter(r *gin.RouterGroup) {
// siteinfo
r.GET("/siteinfo/general", a.siteInfoController.GetGeneral)
r.GET("/siteinfo/interface", a.siteInfoController.GetInterface)
r.GET("/siteinfo/branding", a.siteInfoController.GetSiteBranding)
r.GET("/siteinfo/write", a.siteInfoController.GetSiteWrite)
r.GET("/siteinfo/legal", a.siteInfoController.GetSiteLegal)
r.GET("/siteinfo/seo", a.siteInfoController.GetSeo)
r.GET("/siteinfo/login", a.siteInfoController.GetSiteLogin)
r.GET("/siteinfo/custom-css-html", a.siteInfoController.GetSiteCustomCssHTML)
r.GET("/siteinfo/theme", a.siteInfoController.GetSiteTheme)
r.PUT("/siteinfo/general", a.siteInfoController.UpdateGeneral)
r.GET("/siteinfo/interface", a.siteInfoController.GetInterface)
r.PUT("/siteinfo/interface", a.siteInfoController.UpdateInterface)
r.GET("/siteinfo/branding", a.siteInfoController.GetSiteBranding)
r.PUT("/siteinfo/branding", a.siteInfoController.UpdateBranding)
r.GET("/siteinfo/write", a.siteInfoController.GetSiteWrite)
r.PUT("/siteinfo/write", a.siteInfoController.UpdateSiteWrite)
r.GET("/siteinfo/legal", a.siteInfoController.GetSiteLegal)
r.PUT("/siteinfo/legal", a.siteInfoController.UpdateSiteLegal)
r.PUT("/siteinfo/login", a.siteInfoController.UpdateSiteLogin)
r.PUT("/siteinfo/custom-css-html", a.siteInfoController.UpdateSiteCustomCssHTML)
r.PUT("/siteinfo/theme", a.siteInfoController.SaveSiteTheme)
r.GET("/siteinfo/seo", a.siteInfoController.GetSeo)
r.PUT("/siteinfo/seo", a.siteInfoController.UpdateSeo)
r.GET("/siteinfo/login", a.siteInfoController.GetSiteLogin)
r.PUT("/siteinfo/login", a.siteInfoController.UpdateSiteLogin)
r.GET("/siteinfo/custom-css-html", a.siteInfoController.GetSiteCustomCssHTML)
r.PUT("/siteinfo/custom-css-html", a.siteInfoController.UpdateSiteCustomCssHTML)
r.GET("/siteinfo/theme", a.siteInfoController.GetSiteTheme)
r.PUT("/siteinfo/theme", a.siteInfoController.SaveSiteTheme)
r.GET("/siteinfo/users", a.siteInfoController.GetSiteUsers)
r.PUT("/siteinfo/users", a.siteInfoController.UpdateSiteUsers)
r.GET("/setting/smtp", a.siteInfoController.GetSMTPConfig)
r.PUT("/setting/smtp", a.siteInfoController.UpdateSMTPConfig)

View File

@ -92,10 +92,17 @@ type GetSiteLegalInfoResp struct {
PrivacyPolicyParsedText string `json:"privacy_policy_parsed_text,omitempty"`
}
// SiteUsersReq site users config request
type SiteUsersReq struct {
DefaultAvatar string `validate:"required,oneof=system gravatar" form:"default_avatar" json:"default_avatar"`
}
// SiteLoginReq site login request
type SiteLoginReq struct {
AllowNewRegistrations bool `json:"allow_new_registrations"`
LoginRequired bool `json:"login_required"`
AllowNewRegistrations bool `json:"allow_new_registrations"`
AllowEmailRegistrations bool `json:"allow_email_registrations"`
LoginRequired bool `json:"login_required"`
AllowEmailDomains []string `json:"allow_email_domains"`
}
// SiteCustomCssHTMLReq site custom css html
@ -127,6 +134,9 @@ type SiteLoginResp SiteLoginReq
// SiteCustomCssHTMLResp site custom css html response
type SiteCustomCssHTMLResp SiteCustomCssHTMLReq
// SiteUsersResp site users response
type SiteUsersResp SiteUsersReq
// SiteThemeResp site theme response
type SiteThemeResp struct {
ThemeOptions []*ThemeOption `json:"theme_options"`

View File

@ -4,6 +4,8 @@ package schema
type UserExternalLoginResp struct {
BindingKey string `json:"binding_key"`
AccessToken string `json:"access_token"`
// ErrMsg error message, if not empty, means login failed and this message should be displayed.
ErrMsg string `json:"-"`
}
// ExternalLoginBindingUserSendEmailReq external login binding user request
@ -49,6 +51,8 @@ type ExternalLoginUserInfoCache struct {
Avatar string
// optional. The original user information provided by the third-party login platform
MetaInfo string
// optional. The bio provided by the third-party login platform
Bio string
}
// ExternalLoginUnbindingReq external login unbinding user

View File

@ -61,6 +61,11 @@ func (s *SiteInfoService) GetSiteBranding(ctx context.Context) (resp *schema.Sit
return s.siteInfoCommonService.GetSiteBranding(ctx)
}
// GetSiteUsers get site info about users
func (s *SiteInfoService) GetSiteUsers(ctx context.Context) (resp *schema.SiteUsersResp, err error) {
return s.siteInfoCommonService.GetSiteUsers(ctx)
}
// GetSiteWrite get site info write
func (s *SiteInfoService) GetSiteWrite(ctx context.Context) (resp *schema.SiteWriteResp, err error) {
resp = &schema.SiteWriteResp{}
@ -218,6 +223,17 @@ func (s *SiteInfoService) SaveSiteTheme(ctx context.Context, req *schema.SiteThe
return s.siteInfoRepo.SaveByType(ctx, constant.SiteTypeTheme, data)
}
// SaveSiteUsers save site users
func (s *SiteInfoService) SaveSiteUsers(ctx context.Context, req *schema.SiteUsersReq) (err error) {
content, _ := json.Marshal(req)
data := &entity.SiteInfo{
Type: constant.SiteTypeUsers,
Content: string(content),
Status: 1,
}
return s.siteInfoRepo.SaveByType(ctx, constant.SiteTypeUsers, data)
}
// GetSMTPConfig get smtp config
func (s *SiteInfoService) GetSMTPConfig(ctx context.Context) (
resp *schema.GetSMTPConfigResp, err error,

View File

@ -67,6 +67,15 @@ func (s *SiteInfoCommonService) GetSiteBranding(ctx context.Context) (resp *sche
return resp, nil
}
// GetSiteUsers get site info about users
func (s *SiteInfoCommonService) GetSiteUsers(ctx context.Context) (resp *schema.SiteUsersResp, err error) {
resp = &schema.SiteUsersResp{}
if err = s.getSiteInfoByType(ctx, constant.SiteTypeUsers, resp); err != nil {
return nil, err
}
return resp, nil
}
// GetSiteWrite get site info write
func (s *SiteInfoCommonService) GetSiteWrite(ctx context.Context) (resp *schema.SiteWriteResp, err error) {
resp = &schema.SiteWriteResp{}

View File

@ -5,10 +5,15 @@ import (
"encoding/json"
"time"
"github.com/answerdev/answer/internal/base/handler"
"github.com/answerdev/answer/internal/base/reason"
"github.com/answerdev/answer/internal/base/translator"
"github.com/answerdev/answer/internal/entity"
"github.com/answerdev/answer/internal/schema"
"github.com/answerdev/answer/internal/service/activity"
"github.com/answerdev/answer/internal/service/siteinfo_common"
usercommon "github.com/answerdev/answer/internal/service/user_common"
"github.com/answerdev/answer/pkg/checker"
"github.com/answerdev/answer/pkg/random"
"github.com/answerdev/answer/plugin"
"github.com/segmentfault/pacman/log"
@ -20,6 +25,7 @@ type UserCenterLoginService struct {
userExternalLoginRepo UserExternalLoginRepo
userCommonService *usercommon.UserCommon
userActivity activity.UserActiveActivityRepo
siteInfoCommonService *siteinfo_common.SiteInfoCommonService
}
// NewUserCenterLoginService new user external login service
@ -28,21 +34,36 @@ func NewUserCenterLoginService(
userCommonService *usercommon.UserCommon,
userExternalLoginRepo UserExternalLoginRepo,
userActivity activity.UserActiveActivityRepo,
siteInfoCommonService *siteinfo_common.SiteInfoCommonService,
) *UserCenterLoginService {
return &UserCenterLoginService{
userRepo: userRepo,
userCommonService: userCommonService,
userExternalLoginRepo: userExternalLoginRepo,
userActivity: userActivity,
siteInfoCommonService: siteInfoCommonService,
}
}
func (us *UserCenterLoginService) ExternalLogin(
ctx context.Context, provider string, basicUserInfo *plugin.UserCenterBasicUserInfo) (
ctx context.Context, userCenter plugin.UserCenter, basicUserInfo *plugin.UserCenterBasicUserInfo) (
resp *schema.UserExternalLoginResp, err error) {
if len(basicUserInfo.Email) > 0 {
// check whether site allow register or not
siteInfo, err := us.siteInfoCommonService.GetSiteLogin(ctx)
if err != nil {
return nil, err
}
if !checker.EmailInAllowEmailDomain(basicUserInfo.Email, siteInfo.AllowEmailDomains) {
log.Debugf("email domain not allowed: %s", basicUserInfo.Email)
return &schema.UserExternalLoginResp{
ErrMsg: translator.Tr(handler.GetLangByCtx(ctx), reason.EmailIllegalDomainError)}, nil
}
}
oldExternalLoginUserInfo, exist, err := us.userExternalLoginRepo.GetByExternalID(ctx,
provider, basicUserInfo.ExternalID)
userCenter.Info().SlugName, basicUserInfo.ExternalID)
if err != nil {
return nil, err
}
@ -62,7 +83,13 @@ func (us *UserCenterLoginService) ExternalLogin(
}
}
oldUserInfo, err := us.registerNewUser(ctx, provider, basicUserInfo)
// cache external user info, waiting for user enter email address.
if userCenter.Description().MustAuthEmailEnabled && len(basicUserInfo.Email) == 0 {
// TODO: check
return &schema.UserExternalLoginResp{ErrMsg: "Requires authorized email to login"}, nil
}
oldUserInfo, err := us.registerNewUser(ctx, userCenter.Info().SlugName, basicUserInfo)
if err != nil {
return nil, err
}
@ -98,6 +125,8 @@ func (us *UserCenterLoginService) registerNewUser(ctx context.Context, provider
userInfo.MailStatus = entity.EmailStatusAvailable
userInfo.Status = entity.UserStatusAvailable
userInfo.LastLoginDate = time.Now()
userInfo.Bio = basicUserInfo.Bio
userInfo.BioHTML = basicUserInfo.Bio
err = us.userRepo.AddUser(ctx, userInfo)
if err != nil {
return nil, err

View File

@ -150,6 +150,8 @@ func (us *UserExternalLoginService) registerNewUser(ctx context.Context,
userInfo.MailStatus = entity.EmailStatusToBeVerified
userInfo.Status = entity.UserStatusAvailable
userInfo.LastLoginDate = time.Now()
userInfo.Bio = externalUserInfo.Bio
userInfo.BioHTML = externalUserInfo.Bio
err = us.userRepo.AddUser(ctx, userInfo)
if err != nil {
return nil, err

17
pkg/checker/email.go Normal file
View File

@ -0,0 +1,17 @@
package checker
import "strings"
func EmailInAllowEmailDomain(email string, allowEmailDomains []string) bool {
if len(allowEmailDomains) == 0 {
return true
}
for _, domain := range allowEmailDomains {
if strings.HasSuffix(email, domain) {
return true
}
}
return false
}

17
plugin/agent.go Normal file
View File

@ -0,0 +1,17 @@
package plugin
import (
"github.com/gin-gonic/gin"
)
type Agent interface {
Base
RegisterUnAuthRouter(r *gin.RouterGroup)
RegisterAuthUserRouter(r *gin.RouterGroup)
RegisterAuthAdminRouter(r *gin.RouterGroup)
}
var (
CallAgent,
registerAgent = MakePlugin[Agent](true)
)

View File

@ -52,6 +52,10 @@ func Register(p Base) {
if _, ok := p.(UserCenter); ok {
registerUserCenter(p.(UserCenter))
}
if _, ok := p.(Agent); ok {
registerAgent(p.(Agent))
}
}
type Stack[T Base] struct {

View File

@ -18,15 +18,18 @@ type UserCenter interface {
UserSettings(externalID string) (userSettings *SettingInfo, err error)
// PersonalBranding returns the personal branding information
PersonalBranding(externalID string) (branding []*PersonalBranding)
// AfterLogin is called after the user logs in
AfterLogin(externalID, accessToken string)
}
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"`
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"`
MustAuthEmailEnabled bool `json:"must_auth_email_enabled"`
}
type UserStatus int
@ -45,6 +48,7 @@ type UserCenterBasicUserInfo struct {
Rank int `json:"rank"`
Avatar string `json:"avatar"`
Mobile string `json:"mobile"`
Bio string `json:"bio"`
Status UserStatus `json:"status"`
}

View File

@ -1,26 +1,21 @@
#!/bin/bash
set -e
echo "begin build plugin"
plugin_file=./script/plugin_list
if [ ! -f "$plugin_file" ]; then
echo "plugin_list is not exist"
exit 0
fi
num=0
for line in `cat $plugin_file`
do
account=$line
accounts[$num]=$account
((num++))
done
if [ $num -eq 0 ]; then
echo "plugin_list is null"
exit 0
fi
echo "plugin_list exist"
cmd="./answer build "
for repo in ${accounts[@]}
for repo in `cat $plugin_file`
do
echo ${repo}
cmd=$cmd" --with "${repo}
echo ${repo}
cmd=$cmd" --with "${repo}
done
echo "cmd is "$cmd
$cmd
if [ ! -f "./new_answer" ]; then
echo "new_answer is not exist build failed"