You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
OneAuth/api/user/create.go

115 lines
3.3 KiB
Go

//
// create.go
// Copyright (C) 2025 veypi <i@veypi.com>
// 2025-05-06 15:05
// Distributed under terms of the MIT license.
//
package user
import (
"encoding/base64"
"fmt"
"math/rand"
"strings"
"time"
"github.com/google/uuid"
"github.com/veypi/vbase/api/sms"
"github.com/veypi/vbase/cfg"
"github.com/veypi/vbase/libs/utils"
"github.com/veypi/vbase/models"
"github.com/veypi/vigo"
"gorm.io/gorm"
)
var _ = Router.Post("/", "用户注册", vigo.SkipBefore, publicLimits, userPost)
type postOpts struct {
Username string `json:"username" gorm:"varchar(100);unique;default:not null" src:"json" desc:"用户名"`
Code string `json:"code" gorm:"varchar(128)" src:"json" desc:"授权码/密码"`
Phone string `json:"phone" gorm:"varchar(50);unique;default:null" src:"json" desc:"手机号"`
VerifyCode string `json:"verify_code" src:"json" desc:"验证码"`
Region string `json:"region" src:"json" desc:"区域"`
Nickname *string `json:"nickname" src:"json" desc:"昵称"`
Icon *string `json:"icon" src:"json" desc:"头像"`
Email *string `json:"email" gorm:"varchar(20);unique;default:null" src:"json" desc:"邮箱"`
}
func userPost(x *vigo.X, opts *postOpts) (*models.User, error) {
data := &models.User{}
if cfg.Config.SMS.Enable {
data.Phone = opts.Region + opts.Phone
data.Region = opts.Region
err := sms.VerifyCode(opts.Phone, opts.VerifyCode, opts.Region, "signup")
if err != nil {
return nil, vigo.ErrArgInvalid.WithArgs("verify code").WithString(err.Error())
}
}
data.Username = opts.Username
data.Code = opts.Code
data.Salt = utils.RandSeq(16)
if len(data.Username) < 2 {
return nil, vigo.ErrArgInvalid.WithArgs("username length")
}
code, err := base64.URLEncoding.DecodeString(opts.Code)
if err != nil || len(code) < 8 {
return nil, vigo.ErrArgInvalid.WithArgs("code")
}
code = utils.PKCS7Padding(code, 32)
// We need ID for encryption, but it's not generated yet.
// We can generate it manually here since vigo.Model doesn't auto-generate it before Create
data.ID = strings.ReplaceAll(uuid.New().String(), "-", "")
data.Code, err = utils.AesEncrypt([]byte(data.ID), code, []byte(data.Salt))
if err != nil {
return nil, vigo.ErrArgInvalid.WithArgs("code")
}
ncode, err := utils.AesDecrypt([]byte(data.Code), code, []byte(data.Salt))
if err != nil || string(ncode) != data.ID {
return nil, vigo.ErrInternalServer.WithString("code decrypt failed")
}
if opts.Nickname != nil {
data.Nickname = *opts.Nickname
}
if opts.Icon != nil {
data.Icon = *opts.Icon
} else {
data.Icon = fmt.Sprintf("https://public.veypi.com/img/avatar/%04d.jpg", rand.New(rand.NewSource(time.Now().UnixNano())).Intn(220))
}
if opts.Email != nil {
data.Email = *opts.Email
}
data.Status = 1
err = cfg.DB().Transaction(func(tx *gorm.DB) error {
err := tx.Create(data).Error
if err != nil {
return err
}
app := &models.App{}
err = tx.Where("id = ?", cfg.Config.ID).First(app).Error
if err != nil {
return err
}
status := "ok"
switch app.Typ {
case "private":
return vigo.ErrNotPermitted.WithArgs("not enable register")
case "apply":
status = "applying"
case "public":
}
if app.Typ != "public" {
}
return tx.Create(&models.AppUser{
UserID: data.ID,
AppID: cfg.Config.ID,
Status: status,
}).Error
})
return data, err
}