From 0ce35645b243dd025396bde63b94f18c2359c12e Mon Sep 17 00:00:00 2001
From: eson <9673575+githubcontent@user.noreply.gitee.com>
Date: Thu, 27 Jul 2023 16:48:43 +0800
Subject: [PATCH] for  save

---
 model/gmodel/fs_user_logic.go                 |   2 +-
 server/auth/internal/logic/email_manager.go   |  37 +++---
 .../logic/useremailconfirmationlogic.go       |  78 +++++++++++++
 .../internal/logic/useremailregisterlogic.go  |  37 ++++++
 .../internal/logic/usergoogleloginlogic.go    | 106 +++++-------------
 server/auth/internal/types/types.go           |   5 +-
 server_api/auth.api                           |  13 ++-
 utils/auth/confirmation_link.go               |   6 +
 utils/auth/register.go                        |  12 +-
 utils/basic/basic.go                          |  11 +-
 10 files changed, 195 insertions(+), 112 deletions(-)

diff --git a/model/gmodel/fs_user_logic.go b/model/gmodel/fs_user_logic.go
index e80917e5..11ae443f 100644
--- a/model/gmodel/fs_user_logic.go
+++ b/model/gmodel/fs_user_logic.go
@@ -39,7 +39,7 @@ func (u *FsUserModel) FindUserByGoogleId(ctx context.Context, Id int64) (resp *F
 	return resp, err
 }
 
-func (u *FsUserModel) TransactionRegsterGoogle(ctx context.Context, fc func(tx *gorm.DB) error) (err error) {
+func (u *FsUserModel) Transaction(ctx context.Context, fc func(tx *gorm.DB) error) (err error) {
 	return u.db.WithContext(ctx).Transaction(fc)
 }
 
diff --git a/server/auth/internal/logic/email_manager.go b/server/auth/internal/logic/email_manager.go
index 14f3a002..522fae2f 100644
--- a/server/auth/internal/logic/email_manager.go
+++ b/server/auth/internal/logic/email_manager.go
@@ -11,40 +11,49 @@ import (
 
 var EmailManager *EmailSender
 
+type EmailFormat struct {
+	TargetEmail      string // 发送的目标email
+	CompanyName      string // fs公司名
+	ConfirmationLink string // fs确认连接
+	SenderName       string // 发送人
+	SenderTitle      string // 发送标题
+}
+
 // EmailSender
 type EmailSender struct {
 	lock            sync.Mutex
-	EmailTasks      chan string
+	EmailTasks      chan *EmailFormat
 	Auth            smtp.Auth
 	FromEmail       string
-	emailSending    map[string]*EmailTask
 	ResendTimeLimit time.Duration
-	semaphore       chan struct{}
+
+	emailSending map[string]*EmailTask
+	semaphore    chan struct{}
 }
 
 // EmailTask
 type EmailTask struct {
-	Email    string    // email
-	SendTime time.Time // 处理的任务时间
+	Email    *EmailFormat // email
+	SendTime time.Time    // 处理的任务时间
 }
 
 func (m *EmailSender) ProcessEmailTasks() {
 	for {
-		emailTarget, ok := <-m.EmailTasks
+		emailformat, ok := <-m.EmailTasks
 		if !ok {
 			log.Println("Email task channel closed")
 			break
 		}
 
 		m.lock.Lock()
-		_, isSending := m.emailSending[emailTarget]
+		_, isSending := m.emailSending[emailformat.TargetEmail]
 		if isSending {
 			m.lock.Unlock()
 			continue
 		}
 
-		m.emailSending[emailTarget] = &EmailTask{
-			Email:    emailTarget,
+		m.emailSending[emailformat.TargetEmail] = &EmailTask{
+			Email:    emailformat,
 			SendTime: time.Now(),
 		}
 		m.lock.Unlock()
@@ -55,11 +64,11 @@ func (m *EmailSender) ProcessEmailTasks() {
 		go func() {
 			defer func() { <-m.semaphore }() // Release a token
 
-			content := RenderEmailTemplate("fusen", "http://www.baidu.com", "fusen@gmail.com", "mail-valid")
-			err := smtp.SendMail("smtp.gmail.com:587", m.Auth, m.FromEmail, []string{emailTarget}, content)
+			content := RenderEmailTemplate(emailformat.CompanyName, emailformat.ConfirmationLink, emailformat.SenderName, emailformat.SenderTitle)
+			err := smtp.SendMail("smtp.gmail.com:587", m.Auth, m.FromEmail, []string{emailformat.TargetEmail}, content)
 			if err != nil {
-				log.Printf("Failed to send email to %s: %v\n", emailTarget, err)
-				m.Resend(emailTarget, content)
+				log.Printf("Failed to send email to %s: %v\n", emailformat, err)
+				m.Resend(emailformat.TargetEmail, content)
 			}
 		}()
 	}
@@ -105,7 +114,7 @@ func init() {
 
 	// Initialize the email manager
 	EmailManager = &EmailSender{
-		EmailTasks: make(chan string, 10),
+		EmailTasks: make(chan *EmailFormat, 10),
 		Auth: smtp.PlainAuth(
 			"",
 			"user@example.com",
diff --git a/server/auth/internal/logic/useremailconfirmationlogic.go b/server/auth/internal/logic/useremailconfirmationlogic.go
index d7436c14..8017e55e 100644
--- a/server/auth/internal/logic/useremailconfirmationlogic.go
+++ b/server/auth/internal/logic/useremailconfirmationlogic.go
@@ -1,8 +1,10 @@
 package logic
 
 import (
+	"fusenapi/model/gmodel"
 	"fusenapi/utils/auth"
 	"fusenapi/utils/basic"
+	"time"
 
 	"context"
 
@@ -10,6 +12,7 @@ import (
 	"fusenapi/server/auth/internal/types"
 
 	"github.com/zeromicro/go-zero/core/logx"
+	"gorm.io/gorm"
 )
 
 type UserEmailConfirmationLogic struct {
@@ -34,6 +37,43 @@ func (l *UserEmailConfirmationLogic) UserEmailConfirmation(req *types.RequestEma
 	// 返回值必须调用Set重新返回, resp可以空指针调用 resp.SetStatus(basic.CodeOK, data)
 	// userinfo 传入值时, 一定不为null
 
+	token, err := l.svcCtx.TokenManger.Decrypt(req.Token)
+	if err != nil {
+		logx.Error(err)
+		return resp.SetStatus(basic.CodeOAuthRegisterTokenErr)
+	}
+
+	switch token.OperateType {
+	case auth.OpTypeRegister:
+		if time.Since(token.CreateAt) >= 24*time.Hour {
+			return resp.SetStatus(basic.CodeOAuthConfirmationTimeoutErr)
+		}
+
+		switch token.Platform {
+		case "google":
+
+			user, err := l.OpGoogleRegister(token)
+			if err != nil {
+				logx.Error(err)
+				return resp.SetStatus(basic.CodeDbSqlErr)
+			}
+
+			jwtToken, err := auth.GenerateJwtTokenUint64(
+				auth.StringToHash(*user.PasswordHash),
+				l.svcCtx.Config.Auth.AccessExpire,
+				time.Now().Unix(),
+				user.Id,
+				0,
+			)
+
+		case "facebook":
+			l.OpGoogleRegister(token)
+		}
+
+	default:
+		return resp.SetStatus(basic.CodeOAuthRegisterTokenErr)
+	}
+
 	return resp.SetStatus(basic.CodeOK)
 }
 
@@ -41,3 +81,41 @@ func (l *UserEmailConfirmationLogic) UserEmailConfirmation(req *types.RequestEma
 // func (l *UserEmailConfirmationLogic) AfterLogic(w http.ResponseWriter, r *http.Request, resp *basic.Response) {
 // // httpx.OkJsonCtx(r.Context(), w, resp)
 // }
+
+// OpGoogleRegister 谷歌平台的注册流程
+func (l *UserEmailConfirmationLogic) OpGoogleRegister(token *auth.RegisterToken) (*gmodel.FsUser, error) {
+	user := &gmodel.FsUser{}
+
+	err := l.svcCtx.AllModels.FsUser.Transaction(context.TODO(), func(tx *gorm.DB) error {
+
+		err := tx.Model(user).Where("email = ?", token.Email).Take(user).Error
+		if err != nil {
+			// 没有找到在数据库就创建注册
+			if err == gorm.ErrRecordNotFound {
+				createAt := time.Now().Unix()
+				user.Email = &token.Email
+				user.CreatedAt = &createAt
+				user.GoogleId = &token.Id
+				user.PasswordHash = &token.Password
+				err = tx.Model(user).Create(user).Error
+				if err != nil {
+					return err
+				}
+
+				// 继承guest_id的资源表
+				// tx.Table("fs_resources").Model()
+
+				return nil
+			}
+			return err
+		}
+
+		user.GoogleId = &token.Id
+		return tx.Model(user).Update("google_id", user).Error
+	})
+
+	if err != nil {
+		return nil, err
+	}
+	return user, nil
+}
diff --git a/server/auth/internal/logic/useremailregisterlogic.go b/server/auth/internal/logic/useremailregisterlogic.go
index c8e4751f..b800ff0b 100644
--- a/server/auth/internal/logic/useremailregisterlogic.go
+++ b/server/auth/internal/logic/useremailregisterlogic.go
@@ -39,5 +39,42 @@ func (l *UserEmailRegisterLogic) UserEmailRegister(req *types.RequestEmailRegist
 	// 返回值必须调用Set重新返回, resp可以空指针调用 resp.SetStatus(basic.CodeOK, data)
 	// userinfo 传入值时, 一定不为null
 
+	// 进入邮件注册流程
+	// 这里是注册模块, 发邮件, 通过邮件注册确认邮箱存在
+
+	// 邮箱验证格式错误
+	if !auth.ValidateEmail(req.Email) {
+		return resp.SetStatus(basic.CodeOAuthEmailErr)
+	}
+
+	token, err := l.svcCtx.TokenManger.Decrypt(req.RegisterToken)
+	if err != nil {
+		logx.Error(err)
+		return resp.SetStatus(basic.CodeOAuthRegisterTokenErr)
+	}
+
+	if token.OperateType != auth.OpTypeRegister {
+		return resp.SetStatus(basic.CodeOAuthRegisterTokenErr)
+	}
+
+	// 确认email 重新序列化
+	token.Email = req.Email
+	token.WCId = req.WCId
+
+	clurl, err := l.svcCtx.TokenManger.Generate(token)
+	if err != nil {
+		logx.Error(err)
+		return resp.SetStatus(basic.CodeOAuthRegisterTokenErr)
+	}
+
+	// 进入发送邮箱的系统
+	EmailManager.EmailTasks <- &EmailFormat{
+		TargetEmail:      req.Email,
+		CompanyName:      "fusen",
+		ConfirmationLink: clurl,
+		SenderName:       "support@fusenpack.com",
+		SenderTitle:      "register-valid",
+	} // email进入队
+
 	return resp.SetStatus(basic.CodeOK)
 }
diff --git a/server/auth/internal/logic/usergoogleloginlogic.go b/server/auth/internal/logic/usergoogleloginlogic.go
index bc89e39d..7fe4f336 100644
--- a/server/auth/internal/logic/usergoogleloginlogic.go
+++ b/server/auth/internal/logic/usergoogleloginlogic.go
@@ -1,9 +1,12 @@
 package logic
 
 import (
+	"crypto/rand"
+	"encoding/base64"
 	"fmt"
 	"fusenapi/utils/auth"
 	"fusenapi/utils/basic"
+	"io"
 	"log"
 	"net/http"
 	"time"
@@ -46,52 +49,6 @@ func NewUserGoogleLoginLogic(ctx context.Context, svcCtx *svc.ServiceContext) *U
 // func (l *UserGoogleLoginLogic) BeforeLogic(w http.ResponseWriter, r *http.Request) {
 // }
 
-<<<<<<< HEAD
-// 处理逻辑后 w,r 如:重定向, resp 必须重新处理
-func (l *UserGoogleLoginLogic) AfterLogic(w http.ResponseWriter, r *http.Request, resp *basic.Response) {
-
-	if resp.Code == 200 {
-
-		if !l.isRegistered {
-
-			rtoken, err := l.svcCtx.TokenManger.Encrypt(l.registerInfo)
-			if err != nil {
-				resp.SetStatus(basic.CodeOAuthRegisterTokenErr)
-			}
-			l.registerToken = rtoken
-		}
-
-		rurl := fmt.Sprintf(
-			l.svcCtx.Config.MainAddress+"/oauth?token=%s&is_registered=%t&register_token=%s",
-			l.token,
-			l.isRegistered,
-			l.registerToken,
-		)
-
-		html := fmt.Sprintf(`
-		<!DOCTYPE html>
-		<html>
-		<head>
-			<title>Redirect</title>
-			<script type="text/javascript">
-				window.onload = function() {
-					window.location = "%s";
-				}
-			</script>
-		</head>
-		<body>
-		</body>
-		</html>
-		`, rurl)
-		fmt.Fprintln(w, html)
-	} else {
-		httpx.OkJson(w, resp)
-	}
-
-}
-
-=======
->>>>>>> 529a02ac7582babc634e6f9cddab126f5f962efb
 func (l *UserGoogleLoginLogic) UserGoogleLogin(req *types.RequestGoogleLogin, userinfo *auth.UserInfo) (resp *basic.Response) {
 	// 返回值必须调用Set重新返回, resp可以空指针调用 resp.SetStatus(basic.CodeOK, data)
 	// userinfo 传入值时, 一定不为null
@@ -141,30 +98,34 @@ func (l *UserGoogleLoginLogic) UserGoogleLogin(req *types.RequestGoogleLogin, us
 			return resp.SetStatus(basic.CodeDbSqlErr)
 		}
 
+		nonce := make([]byte, 16)
+		if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
+			logx.Error(err)
+			return resp.SetStatus(basic.CodeOK)
+		}
+
+		l.registerInfo = &auth.RegisterToken{
+			Id:          googleId,
+			Password:    base64.URLEncoding.EncodeToString(nonce),
+			Platform:    "google",
+			OperateType: auth.OpTypeRegister,
+			CreateAt:    time.Now(),
+		}
+
 		l.isRegistered = false
-		l.registerToken = // 
-
-		// 进入邮件注册流程
-		// if req.Email == "" {
-		// 	return resp.SetStatus(basic.CodeOK)
-		// }
-
-		// // 这里是注册模块, 发邮件, 通过邮件注册确认邮箱存在
-
-		// // 邮箱验证格式错误
-		// if !auth.ValidateEmail(req.Email) {
-		// 	return resp.SetStatus(basic.CodeOAuthEmailErr)
-		// }
-
-		// EmailManager.EmailTasks <- req.Email // email进入队
+		token, err := l.svcCtx.TokenManger.Encrypt(l.registerInfo)
+		if err != nil {
+			logx.Error(err)
+			return resp.SetStatus(basic.CodeOAuthRegisterTokenErr)
+		}
+		l.registerToken = token
 
 		return resp.SetStatus(basic.CodeOK)
 	}
 
+	l.isRegistered = true
 	// 如果密码匹配,则生成 JWT Token。
-	nowSec := time.Now().Unix()
-
-	jwtToken, err := auth.GenerateJwtTokenUint64(auth.StringToHash(*user.PasswordHash), l.svcCtx.Config.Auth.AccessExpire, nowSec, user.Id, 0)
+	jwtToken, err := auth.GenerateJwtTokenUint64(auth.StringToHash(*user.PasswordHash), l.svcCtx.Config.Auth.AccessExpire, time.Now().Unix(), user.Id, 0)
 
 	// 如果生成 JWT Token 失败,则抛出错误并返回未认证的状态码。
 	if err != nil {
@@ -182,23 +143,6 @@ func (l *UserGoogleLoginLogic) AfterLogic(w http.ResponseWriter, r *http.Request
 
 	if resp.Code == 200 {
 
-		if !l.isRegistered {
-			now := time.Now()
-			rtoken, err := auth.GenerateRegisterToken(
-				&l.svcCtx.Config.Auth.AccessSecret,
-				l.svcCtx.Config.Auth.AccessExpire,
-				now.Unix(),
-				l.oauthinfo.Id,
-				l.oauthinfo.Platform,
-			)
-
-			if err != nil {
-				resp.SetStatus(basic.CodeOAuthRegisterTokenErr)
-			}
-
-			l.registerToken = rtoken
-		}
-
 		rurl := fmt.Sprintf(
 			l.svcCtx.Config.MainAddress+"/oauth?token=%s&is_registered=%t&register_token=%s",
 			l.token,
diff --git a/server/auth/internal/types/types.go b/server/auth/internal/types/types.go
index 56bf3d43..6295f978 100644
--- a/server/auth/internal/types/types.go
+++ b/server/auth/internal/types/types.go
@@ -18,12 +18,13 @@ type RequestGoogleLogin struct {
 }
 
 type RequestEmailConfirmation struct {
-	Email string `json:"email"` // 要确认的email
-	Token string `json:"token"` // 操作Token
+	Token string `query:"token"` // 操作Token
 }
 
 type RequestEmailRegister struct {
 	Email         string `json:"email"`
+	WCId          uint64 `json:"wcid"`
+	GuestId       uint64 `json:"guest_id"`
 	RegisterToken string `json:"register_token"`
 }
 
diff --git a/server_api/auth.api b/server_api/auth.api
index 5003e8d9..663eef26 100644
--- a/server_api/auth.api
+++ b/server_api/auth.api
@@ -12,16 +12,16 @@ import "basic.api"
 service auth {
 	@handler UserLoginHandler
 	post /api/auth/login(RequestUserLogin) returns (response);
-
+	
 	@handler AcceptCookieHandler
 	post /api/auth/accept-cookie(request) returns (response);
-
+	
 	@handler UserGoogleLoginHandler
 	get /api/auth/oauth2/login/google(RequestGoogleLogin) returns (response);
-
+	
 	@handler UserEmailConfirmationHandler
 	get /api/auth/email/confirmation(RequestEmailConfirmation) returns (response);
-
+	
 	@handler UserEmailRegisterHandler
 	get /api/auth/oauth2/register(RequestEmailRegister) returns (response);
 }
@@ -40,12 +40,13 @@ type RequestGoogleLogin {
 }
 
 type RequestEmailConfirmation {
-	Email string `json:"email"` // 要确认的email
-	Token string `json:"token"` // 操作Token
+	Token string `query:"token"` // 操作Token
 }
 
 type RequestEmailRegister {
 	Email         string `json:"email"`
+	WCId          uint64 `json:"wcid"`
+	GuestId       uint64 `json:"guest_id"`
 	RegisterToken string `json:"register_token"`
 }
 
diff --git a/utils/auth/confirmation_link.go b/utils/auth/confirmation_link.go
index ef0caf49..29886165 100644
--- a/utils/auth/confirmation_link.go
+++ b/utils/auth/confirmation_link.go
@@ -10,6 +10,12 @@ import (
 	"net/url"
 )
 
+type OperateType int8
+
+const (
+	OpTypeRegister OperateType = 1 //注册的操作类型
+)
+
 type ConfirmationLink[T any] struct {
 	Secret          []byte
 	DefaultQueryKey string // 默认key 是 token
diff --git a/utils/auth/register.go b/utils/auth/register.go
index b817a158..3c3bafaa 100644
--- a/utils/auth/register.go
+++ b/utils/auth/register.go
@@ -13,10 +13,14 @@ import (
 )
 
 type RegisterToken struct {
-	Id       int64
-	Password string
-	Platform string
-	Expired  time.Time
+	OperateType           // 操作的类型, 验证的token 必须要继承这个
+	Id          int64     // 注册的 id
+	GuestId     uint64    // guest_id 需要继承
+	WCId        uint64    // websocket 通道id
+	Email       string    // email
+	Password    string    // 密码
+	Platform    string    // 平台
+	CreateAt    time.Time // 创建时间
 }
 
 func ParseJwtTokenUint64SecretByRequest(r *http.Request, AccessSecret uint64) (jwt.MapClaims, error) {
diff --git a/utils/basic/basic.go b/utils/basic/basic.go
index 16705eb9..d8a7aea8 100644
--- a/utils/basic/basic.go
+++ b/utils/basic/basic.go
@@ -39,9 +39,11 @@ var (
 	CodeServiceErr = &StatusResponse{510, "server logic error"} // 服务逻辑错误
 	CodeUnAuth     = &StatusResponse{401, "unauthorized"}       // 未授权
 
-	CodeOAuthGoogleApiErr     = &StatusResponse{5070, "oauth2 google api error"}
-	CodeOAuthRegisterTokenErr = &StatusResponse{5071, "oauth2 jwt token error"}
-	CodeOAuthEmailErr         = &StatusResponse{5071, "Invalid email format"}
+	CodeOAuthGoogleApiErr           = &StatusResponse{5070, "oauth2 google api error"}
+	CodeOAuthRegisterTokenErr       = &StatusResponse{5071, "oauth2 register create token error"}
+	CodeOAuthEmailErr               = &StatusResponse{5072, "Invalid email format"}
+	CodeOAuthRandReaderErr          = &StatusResponse{5073, "rand reader error"}
+	CodeOAuthConfirmationTimeoutErr = &StatusResponse{5074, "confirmation timeout error"}
 
 	CodeS3PutObjectRequestErr = &StatusResponse{5060, "s3 PutObjectRequest error"}    // s3 PutObjectRequest 错误
 	CodeS3PutSizeLimitErr     = &StatusResponse{5061, "s3 over limit size error"}     // s3 超过文件大小限制 错误
@@ -75,7 +77,8 @@ var (
 	CodeAesCbcEncryptionErr = &StatusResponse{5106, "encryption data err"} // 加密数据失败
 	CodeAesCbcDecryptionErr = &StatusResponse{5107, "decryption data err"} // 解密数据失败
 
-	CodeSharedStateErr = &StatusResponse{5201, "shared state server  err"} // 状态机错误
+	CodeSharedStateErr       = &StatusResponse{5201, "shared state server  err"} // 状态机错误
+	CodeEmailConfirmationErr = &StatusResponse{5202, "email confirmation   err"} // email 验证错误
 )
 
 type Response struct {