Files
ttstd-infinite-canvas/backend/internal/repository/email_identity.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
})
}