59 lines
2.0 KiB
Go
59 lines
2.0 KiB
Go
package repository
|
|
|
|
import (
|
|
"yingce/backend/internal/emailidentity"
|
|
"yingce/backend/internal/kernel"
|
|
"yingce/backend/internal/model"
|
|
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
// CheckEmailAvailable compares legacy rows without rewriting their login address.
|
|
func (r *Repository) CheckEmailAvailable(email, ownID string) error {
|
|
if email == "" {
|
|
return nil
|
|
}
|
|
value := "lower(trim(email))"
|
|
local := "split_part(" + value + ",'@',1)"
|
|
if r.Dialect() == "sqlite" {
|
|
local = "substr(" + value + ",1,instr(" + value + ",'@')-1)"
|
|
}
|
|
untagged := "split_part(" + local + ",'+',1)"
|
|
if r.Dialect() == "sqlite" {
|
|
untagged = "CASE WHEN instr(" + local + ",'+') > 0 THEN substr(" + local + ",1,instr(" + local + ",'+')-1) ELSE " + local + " END"
|
|
}
|
|
canonical := "CASE WHEN " + value + " LIKE '%@gmail.com' OR " + value + " LIKE '%@googlemail.com' THEN replace((" + untagged + "),'.','') || '@gmail.com' ELSE " + value + " END"
|
|
var count int64
|
|
if err := r.db.Model(&model.User{}).Where("id <> ? AND ("+canonical+") = ?", ownID, emailidentity.Canonical(email)).Count(&count).Error; err != nil {
|
|
return err
|
|
}
|
|
if count > 0 {
|
|
return kernel.BadAuthRequest("邮箱已被注册")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// All public registration paths lock before checking and writing, including
|
|
// registrations using different verification codes on different instances.
|
|
func lockRegistrationEmail(tx *gorm.DB, email string) error {
|
|
if tx.Dialector.Name() == "postgres" {
|
|
return tx.Exec("SELECT pg_advisory_xact_lock(hashtext(?)::bigint)", "registration-email:"+emailidentity.Canonical(email)).Error
|
|
}
|
|
if tx.Dialector.Name() == "sqlite" {
|
|
return tx.Exec("UPDATE users SET updated_at = updated_at WHERE 1 = 0").Error
|
|
}
|
|
return gorm.ErrInvalidData
|
|
}
|
|
|
|
func (r *Repository) CreateRegisteredUser(user *model.User) error {
|
|
return r.db.Transaction(func(tx *gorm.DB) error {
|
|
if err := lockRegistrationEmail(tx, user.Email); err != nil {
|
|
return err
|
|
}
|
|
if err := New(tx).CheckEmailAvailable(user.Email, ""); err != nil {
|
|
return err
|
|
}
|
|
return tx.Create(user).Error
|
|
})
|
|
}
|