Skip to content

Commit

Permalink
Merge pull request #8 from frankiejun/main
Browse files Browse the repository at this point in the history
实现邮箱验证功能
  • Loading branch information
flycash authored Sep 15, 2023
2 parents 2ed6e78 + c4ac04a commit 4bae919
Show file tree
Hide file tree
Showing 25 changed files with 910 additions and 76 deletions.
9 changes: 8 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,11 @@ e2e_up:

.PHONY: e2e_down
e2e_down:
docker compose -f script/integration_test_compose.yml down
docker compose -f script/integration_test_compose.yml down

.PHONY: mock
mock:
@mockgen -source=internal/repository/dao/user.go -package=sqlmock -destination=internal/repository/dao/mocks/user.mock.go
@mockgen -source=internal/repository/user.go -package=repomocks -destination=internal/repository/mocks/user.mock.go
@mockgen -source=internal/service/user.go -package=svcmocks -destination=internal/service/mocks/user.mock.go
@mockgen -source=internal/service/email/email.go -package=evcmocks -destination=internal/service/email/mocks/email.mock.go
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ require (
github.com/DATA-DOG/go-sqlmock v1.5.0
github.com/dlclark/regexp2 v1.10.0
github.com/gin-gonic/gin v1.9.1
github.com/go-gomail/gomail v0.0.0-20160411212932-81ebce5c23df
github.com/go-sql-driver/mysql v1.7.1
github.com/golang-jwt/jwt/v5 v5.0.0
github.com/stretchr/testify v1.8.4
Expand Down Expand Up @@ -42,5 +43,7 @@ require (
golang.org/x/sys v0.8.0 // indirect
golang.org/x/text v0.9.0 // indirect
google.golang.org/protobuf v1.30.0 // indirect
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
6 changes: 6 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
github.com/go-gomail/gomail v0.0.0-20160411212932-81ebce5c23df h1:Bao6dhmbTA1KFVxmJ6nBoMuOJit2yjEgLJpIMYpop0E=
github.com/go-gomail/gomail v0.0.0-20160411212932-81ebce5c23df/go.mod h1:GJr+FCSXshIwgHBtLglIg9M2l2kQSi6QjVAngtzI08Y=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
Expand Down Expand Up @@ -95,8 +97,12 @@ golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8T
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk=
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AWRXxgwEyPp2z+p0+hgMuE=
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
Expand Down
11 changes: 6 additions & 5 deletions internal/domain/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@ package domain
import "time"

type User struct {
Id int64
Email string
Password string
CreateTime time.Time
UpdateTime time.Time
Id int64
Email string
EmailVerified bool
Password string
CreateTime time.Time
UpdateTime time.Time
}
80 changes: 78 additions & 2 deletions internal/integration/user_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ import (
"bytes"
"context"
"database/sql"
"github.com/ecodeclub/webook/internal/ioc"
"github.com/ecodeclub/webook/internal/service/email"
//"github.com/golang-jwt/jwt/v5"
"fmt"
"log"
"net/http"
Expand Down Expand Up @@ -175,14 +178,75 @@ func TestUserHandler_e2e_SignUp(t *testing.T) {
}
}

func TestUserHandler_e2e_EmailVerify(t *testing.T) {
r := initWebServer()
db := initDB()
u := initUser(db)
u.RegisterRoutes(r)
testCases := []struct {
name string
body string
before func(t *testing.T)
after func(t *testing.T)
token string
email string
wantCode int
wantBody string
}{
{
name: "验证成功!",
before: func(t *testing.T) {
},
after: func(t *testing.T) {

},
body: "",
token: genToken("[email protected]", 1),
email: "[email protected]",
wantCode: http.StatusOK,
wantBody: "验证成功!",
},
{
name: "验证失败!",
before: func(t *testing.T) {

},
after: func(t *testing.T) {

},
body: "",
email: "[email protected]",
wantCode: http.StatusOK,
wantBody: "验证失败!",
token: "eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJFbWFpbCI6Imp1bm55ZmVuZ0AxNjMuY29tIiwiZXhwIjoxNjk0NTIxODQzfQ.gwGcIDcaKuFG6DyyLnWfEn5poIZ3BMUk2lNQsDhyA3DMBSTo9HkEJ1eyIUQ0XDqp29XVme5dOOMuY2LgRfI60Q",
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
tc.before(t)
req, err := http.NewRequest(http.MethodPost, "/users/email/verify/"+tc.token, bytes.NewBuffer([]byte(tc.body)))
assert.NoError(t, err)
req.Header.Set("Content-Type", "application/json")

resp := httptest.NewRecorder()
r.ServeHTTP(resp, req)

assert.Equal(t, tc.wantCode, resp.Code)
assert.Equal(t, tc.wantBody, resp.Body.String())
tc.after(t)
})
}
}

func TestUserHandler_e2e_Login(t *testing.T) {
// server := InitWebServer()
server := gin.Default()
//db := initDB()
var db *gorm.DB
da := dao.NewUserInfoDAO(db)
repo := repository.NewUserInfoRepository(da)
svc := service.NewUserService(repo)
evc := email.NewEmailService(ioc.InitEmailCfg())
svc := service.NewUserService(repo, evc)

userHandle := web.NewUserHandler(svc)
userHandle.RegisterRoutes(server)
Expand Down Expand Up @@ -314,11 +378,23 @@ func initWebServer() *gin.Engine {
func initUser(db *gorm.DB) *web.UserHandler {
da := dao.NewUserInfoDAO(db)
repo := repository.NewUserInfoRepository(da)
svc := service.NewUserService(repo)
evc := email.NewEmailService(ioc.InitEmailCfg())
svc := service.NewUserService(repo, evc)
u := web.NewUserHandler(svc)
return u
}

func genToken(emailAddr string, timeout int) string {
token := jwt.NewWithClaims(jwt.SigningMethodHS512, service.EmailClaims{
Email: emailAddr,
RegisteredClaims: jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Minute * time.Duration(timeout))),
},
})
tokenStr, _ := token.SignedString([]byte(service.EmailJWTKey))
return tokenStr
}

func Decrypt(encryptString string, secret string) (interface{}, error) {
claims := &web.TokenClaims{}
token, err := jwt.ParseWithClaims(encryptString, claims, func(token *jwt.Token) (interface{}, error) {
Expand Down
29 changes: 29 additions & 0 deletions internal/ioc/email.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package ioc

import (
"crypto/tls"
"github.com/go-gomail/gomail"
"os"
"strconv"
)

func InitEmailCfg() *gomail.Dialer {
//服务邮箱地址
host := os.Getenv("EmailHost")
//服务邮箱端口
portstr := os.Getenv("EmailPort")
//发送者或邮箱名称
username := os.Getenv("EmailUsername")
//邮箱的授权密码
password := os.Getenv("EmailPassword")

port, err := strconv.Atoi(portstr)
if err != nil {
panic("读取邮箱端口失败!")
}

dialer := gomail.NewDialer(host, port, username, password)
dialer.TLSConfig = &tls.Config{InsecureSkipVerify: true}
return dialer

}
36 changes: 32 additions & 4 deletions internal/repository/dao/mocks/user.mock.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

24 changes: 19 additions & 5 deletions internal/repository/dao/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,17 @@ var (

type UserDAO interface {
Insert(ctx context.Context, u User) error
UpdateEmailVerified(ctx context.Context, email string) error
FindByEmail(ctx context.Context, email string) (User, error)
}

type User struct {
Id int64 `gorm:"primaryKey;autoIncrement"`
Email string `gorm:"unique"`
Password string
CreateTime int64
UpdateTime int64
Id int64 `gorm:"primaryKey;autoIncrement"`
Email string `gorm:"unique"`
EmailVerified bool
Password string
CreateTime int64
UpdateTime int64
}

type GormUserDAO struct {
Expand Down Expand Up @@ -54,3 +57,14 @@ func (dao *GormUserDAO) Insert(ctx context.Context, u User) error {
}
return err
}

func (dao *GormUserDAO) UpdateEmailVerified(ctx context.Context, email string) error {
return dao.db.WithContext(ctx).Model(&User{}).Where("email = ?", email).
UpdateColumns(map[string]interface{}{"email_verified": true, "update_time": time.Now().UnixMilli()}).Error
}

func (dao *GormUserDAO) FindByEmail(ctx context.Context, email string) (User, error) {
var u User
err := dao.db.WithContext(ctx).First(&u, "email = ?", email).Error
return u, err
}
Loading

0 comments on commit 4bae919

Please sign in to comment.