✨ Account confirm
This commit is contained in:
@@ -5,7 +5,10 @@ import (
|
||||
"code.smartsheep.studio/hydrogen/passport/pkg/models"
|
||||
"code.smartsheep.studio/hydrogen/passport/pkg/security"
|
||||
"fmt"
|
||||
"github.com/samber/lo"
|
||||
"gorm.io/datatypes"
|
||||
"gorm.io/gorm"
|
||||
"time"
|
||||
)
|
||||
|
||||
func GetAccount(id uint) (models.Account, error) {
|
||||
@@ -65,7 +68,45 @@ func CreateAccount(name, nick, email, password string) (models.Account, error) {
|
||||
|
||||
if err := database.C.Create(&user).Error; err != nil {
|
||||
return user, err
|
||||
} else {
|
||||
return user, nil
|
||||
}
|
||||
|
||||
if tk, err := NewMagicToken(models.ConfirmMagicToken, &user, nil); err != nil {
|
||||
return user, err
|
||||
} else if err := NotifyMagicToken(tk); err != nil {
|
||||
return user, err
|
||||
}
|
||||
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func ConfirmAccount(code string) error {
|
||||
var token models.MagicToken
|
||||
if err := database.C.Where(&models.MagicToken{
|
||||
Code: code,
|
||||
Type: models.ConfirmMagicToken,
|
||||
}).First(&token).Error; err != nil {
|
||||
return err
|
||||
} else if token.AssignTo == nil {
|
||||
return fmt.Errorf("account was not found")
|
||||
}
|
||||
|
||||
var user models.Account
|
||||
if err := database.C.Where(&models.Account{
|
||||
BaseModel: models.BaseModel{ID: *token.AssignTo},
|
||||
}).First(&user).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return database.C.Transaction(func(tx *gorm.DB) error {
|
||||
user.ConfirmedAt = lo.ToPtr(time.Now())
|
||||
|
||||
if err := database.C.Delete(&token).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if err := database.C.Save(&user).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
51
pkg/services/mailer.go
Normal file
51
pkg/services/mailer.go
Normal file
@@ -0,0 +1,51 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"net/smtp"
|
||||
"net/textproto"
|
||||
|
||||
"github.com/jordan-wright/email"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
func SendMail(target string, subject string, content string) error {
|
||||
mail := &email.Email{
|
||||
To: []string{target},
|
||||
From: viper.GetString("mailer.name"),
|
||||
Subject: subject,
|
||||
Text: []byte(content),
|
||||
Headers: textproto.MIMEHeader{},
|
||||
}
|
||||
return mail.SendWithTLS(
|
||||
fmt.Sprintf("%s:%d", viper.GetString("mailer.smtp_host"), viper.GetInt("mailer.smtp_port")),
|
||||
smtp.PlainAuth(
|
||||
"",
|
||||
viper.GetString("mailer.username"),
|
||||
viper.GetString("mailer.password"),
|
||||
viper.GetString("mailer.smtp_host"),
|
||||
),
|
||||
&tls.Config{ServerName: viper.GetString("mailer.smtp_host")},
|
||||
)
|
||||
}
|
||||
|
||||
func SendMailHTML(target string, subject string, content string) error {
|
||||
mail := &email.Email{
|
||||
To: []string{target},
|
||||
From: viper.GetString("mailer.name"),
|
||||
Subject: subject,
|
||||
HTML: []byte(content),
|
||||
Headers: textproto.MIMEHeader{},
|
||||
}
|
||||
return mail.SendWithTLS(
|
||||
fmt.Sprintf("%s:%d", viper.GetString("mailer.smtp_host"), viper.GetInt("mailer.smtp_port")),
|
||||
smtp.PlainAuth(
|
||||
"",
|
||||
viper.GetString("mailer.username"),
|
||||
viper.GetString("mailer.password"),
|
||||
viper.GetString("mailer.smtp_host"),
|
||||
),
|
||||
&tls.Config{ServerName: viper.GetString("mailer.smtp_host")},
|
||||
)
|
||||
}
|
63
pkg/services/tokens.go
Normal file
63
pkg/services/tokens.go
Normal file
@@ -0,0 +1,63 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"code.smartsheep.studio/hydrogen/passport/pkg/database"
|
||||
"code.smartsheep.studio/hydrogen/passport/pkg/models"
|
||||
"fmt"
|
||||
"github.com/google/uuid"
|
||||
"github.com/spf13/viper"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func NewMagicToken(mode models.MagicTokenType, assignTo *models.Account, expiredAt *time.Time) (models.MagicToken, error) {
|
||||
var uid uint
|
||||
if assignTo != nil {
|
||||
uid = assignTo.ID
|
||||
}
|
||||
|
||||
token := models.MagicToken{
|
||||
Code: strings.Replace(uuid.NewString(), "-", "", -1),
|
||||
Type: mode,
|
||||
AssignTo: &uid,
|
||||
ExpiredAt: expiredAt,
|
||||
}
|
||||
|
||||
if err := database.C.Save(&token).Error; err != nil {
|
||||
return token, err
|
||||
} else {
|
||||
return token, nil
|
||||
}
|
||||
}
|
||||
|
||||
func NotifyMagicToken(token models.MagicToken) error {
|
||||
if token.AssignTo == nil {
|
||||
return fmt.Errorf("could notify a non-assign magic token")
|
||||
}
|
||||
|
||||
var user models.Account
|
||||
if err := database.C.Where(&models.MagicToken{
|
||||
AssignTo: token.AssignTo,
|
||||
}).Preload("Contacts").First(&user).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var subject string
|
||||
var content string
|
||||
switch token.Type {
|
||||
case models.ConfirmMagicToken:
|
||||
link := fmt.Sprintf("%s/users/me/confirm?tk=%s", viper.GetString("domain"), token.Code)
|
||||
subject = fmt.Sprintf("[%s] Confirm your registration", viper.GetString("name"))
|
||||
content = fmt.Sprintf("We got a create account request with this email recently.\n"+
|
||||
"So we need you to click the link below to confirm your registeration.\n"+
|
||||
"Confirmnation Link: %s\n"+
|
||||
"If you didn't do that, you can ignore this email.\n\n"+
|
||||
"%s\n"+
|
||||
"Best wishes",
|
||||
link, viper.GetString("maintainer"))
|
||||
default:
|
||||
return fmt.Errorf("unsupported magic token type to notify")
|
||||
}
|
||||
|
||||
return SendMail(user.GetPrimaryEmail().Content, subject, content)
|
||||
}
|
Reference in New Issue
Block a user