// // login.go // Copyright (C) 2025 veypi // 2025-05-12 17:35 // Distributed under terms of the MIT license. // package user import ( "encoding/base64" "time" "github.com/golang-jwt/jwt/v5" "github.com/veypi/OneAuth/api/sms" "github.com/veypi/OneAuth/cfg" "github.com/veypi/OneAuth/libs/auth" "github.com/veypi/OneAuth/libs/utils" "github.com/veypi/OneAuth/models" "github.com/vyes-ai/vigo" "github.com/vyes-ai/vigo/contrib/limiter" "github.com/vyes-ai/vigo/logv" ) var publicLimits = limiter.NewAdvancedRequestLimiter(time.Minute*5, 20, time.Second*3, nil).Limit var _ = Router.Post("/login", "用户登录", vigo.SkipBefore, publicLimits, userLogin) type loginOpts struct { UserName string `json:"username" src:"json" desc:"用户名"` Code string `json:"code" src:"json" desc:"密码/授权码"` VerifyCode string `json:"verify_code" src:"json" desc:"验证码"` Region string `json:"region" src:"json" desc:"区域"` Phone string `json:"phone" src:"json" desc:"手机号"` Email string `json:"email" src:"json" desc:"邮箱"` Type *string `json:"type" src:"json" desc:"登录类型"` AppID *string `json:"app_id" src:"json" desc:"应用ID"` Device *string `json:"device" src:"json" desc:"设备信息"` } func userLogin(x *vigo.X, opts *loginOpts) (string, error) { // Implement login logic here // For example, validate user credentials and return a token user := &models.User{} query := cfg.DB() typ := "" if opts.Type != nil { typ = *opts.Type } switch typ { case "phone": query = query.Where("phone = ?", opts.Region+opts.Phone) case "email": query = query.Where("email = ?", opts.Email) default: query = query.Where("username = ?", opts.UserName) } err := query.First(user).Error if err != nil { return "", vigo.ErrNotFound } logv.Info().Str("user", user.ID).Msg("login") if opts.VerifyCode != "" { err = sms.VerifyCode(opts.Phone, opts.VerifyCode, opts.Region, "signin") if err != nil { logv.Warn().Msgf("verify code: %v", err) return "", vigo.ErrNotAuthorized.WithError(err) } } else { code, err := base64.URLEncoding.DecodeString(opts.Code) if err != nil { return "", vigo.ErrArgInvalid.WithArgs("code") } ncode, err := utils.AesDecrypt([]byte(user.Code), utils.PKCS7Padding(code, 32), []byte(user.Salt)) if err != nil || string(ncode) != user.ID { return "", vigo.ErrNotAuthorized } } aid := cfg.Config.ID if opts.AppID != nil && *opts.AppID != "" { aid = *opts.AppID } data := &models.Token{} data.UserID = user.ID data.AppID = aid data.ExpiredAt = time.Now().Add(time.Hour * 72) if opts.Device != nil { data.Device = *opts.Device } err = cfg.DB().Create(data).Error if err != nil { return "", err } claim := &auth.Claims{} claim.ID = data.ID claim.UID = user.ID claim.AID = aid claim.Name = user.Username claim.Icon = user.Icon claim.ExpiresAt = jwt.NewNumericDate(data.ExpiredAt) claim.IssuedAt = jwt.NewNumericDate(time.Now()) claim.Issuer = cfg.Config.ID return auth.GenJwt(claim) }