mirror of https://gitee.com/answerdev/answer.git
feat(usercenter): add user center plugin and agent
This commit is contained in:
parent
d641bdf11e
commit
a3cc0ac38a
|
@ -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)
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -65,6 +65,7 @@ const (
|
|||
SiteTypeLogin = "login"
|
||||
SiteTypeCustomCssHTML = "css-html"
|
||||
SiteTypeTheme = "theme"
|
||||
SiteTypeUsers = "users"
|
||||
)
|
||||
|
||||
func ExistInPathIgnore(name string) bool {
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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{
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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"`
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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{}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
)
|
|
@ -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 {
|
||||
|
|
|
@ -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"`
|
||||
}
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
Loading…
Reference in New Issue