182 lines
5.3 KiB
Go
182 lines
5.3 KiB
Go
package liveclient
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
"time"
|
|
|
|
kratoserrors "github.com/go-kratos/kratos/v3/errors"
|
|
"golang.org/x/crypto/bcrypt"
|
|
|
|
"kra/internal/biz/system"
|
|
)
|
|
|
|
const (
|
|
ClientAuthorityID uint = 1000
|
|
InitialDiamonds int64 = 100
|
|
)
|
|
|
|
var (
|
|
ErrInvalidAccount = kratoserrors.BadRequest("LIVE_CLIENT_INVALID", "invalid client account data")
|
|
ErrUsernameExists = kratoserrors.BadRequest("LIVE_CLIENT_USERNAME_EXISTS", "username already exists")
|
|
ErrAccountNotFound = kratoserrors.NotFound("LIVE_CLIENT_NOT_FOUND", "client account not found")
|
|
)
|
|
|
|
type Account struct {
|
|
UserID uint
|
|
UUID string
|
|
Username string
|
|
Nickname string
|
|
Avatar string
|
|
AuthorityID uint
|
|
Enabled bool
|
|
DiamondBalance int64
|
|
CreatedAt time.Time
|
|
PasswordUpdatedAt *time.Time
|
|
}
|
|
|
|
type RegisterInput struct {
|
|
Username, Nickname, Password, ConfirmPassword string
|
|
}
|
|
|
|
type LoginInput struct {
|
|
Username, Password, IP, Agent string
|
|
}
|
|
|
|
type ProfileInput struct {
|
|
UserID uint
|
|
Nickname, Avatar string
|
|
}
|
|
|
|
type PasswordInput struct {
|
|
UserID uint
|
|
OldPassword, NewPassword, ConfirmPassword string
|
|
IP, Agent string
|
|
}
|
|
|
|
type AuthResult struct {
|
|
Account *Account
|
|
Auth *system.AuthenticationResult
|
|
}
|
|
|
|
type Repo interface {
|
|
Register(context.Context, *Account, string, int64) (*Account, error)
|
|
Get(context.Context, uint) (*Account, error)
|
|
UpdateProfile(context.Context, *ProfileInput) (*Account, error)
|
|
}
|
|
|
|
type Usecase struct {
|
|
repo Repo
|
|
security *system.SecurityUsecase
|
|
auth *system.AuthenticationUsecase
|
|
users *system.UserUsecase
|
|
}
|
|
|
|
func NewUsecase(repo Repo, security *system.SecurityUsecase, auth *system.AuthenticationUsecase, users *system.UserUsecase) *Usecase {
|
|
return &Usecase{repo: repo, security: security, auth: auth, users: users}
|
|
}
|
|
|
|
func (uc *Usecase) Register(ctx context.Context, in *RegisterInput, ip, agent string) (*AuthResult, error) {
|
|
if in == nil {
|
|
return nil, ErrInvalidAccount
|
|
}
|
|
in.Username = strings.TrimSpace(in.Username)
|
|
if n := len([]rune(in.Username)); n < 3 || n > 32 || in.Password == "" || in.Password != in.ConfirmPassword {
|
|
return nil, ErrInvalidAccount
|
|
}
|
|
in.Nickname = strings.TrimSpace(in.Nickname)
|
|
if in.Nickname == "" {
|
|
in.Nickname = in.Username
|
|
} else if n := len([]rune(in.Nickname)); n < 2 || n > 16 {
|
|
return nil, ErrInvalidAccount
|
|
}
|
|
config, err := uc.security.Current(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if err = uc.security.ValidatePassword(config, in.Password); err != nil {
|
|
return nil, err
|
|
}
|
|
hash, err := bcrypt.GenerateFromPassword([]byte(in.Password), bcrypt.DefaultCost)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
account, err := uc.repo.Register(ctx, &Account{Username: in.Username, Nickname: in.Nickname, AuthorityID: ClientAuthorityID, Enabled: true}, string(hash), InitialDiamonds)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
auth, err := uc.auth.AuthenticateRegisteredUser(ctx, accountUser(account), ip, agent)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &AuthResult{Account: account, Auth: auth}, nil
|
|
}
|
|
|
|
func (uc *Usecase) Login(ctx context.Context, in *LoginInput) (*AuthResult, error) {
|
|
if in == nil || strings.TrimSpace(in.Username) == "" || in.Password == "" {
|
|
return nil, ErrInvalidAccount
|
|
}
|
|
auth, err := uc.auth.LoginClient(ctx, &system.LoginAttempt{Username: strings.TrimSpace(in.Username), Password: in.Password, IP: in.IP, Agent: in.Agent})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
account, err := uc.repo.Get(ctx, auth.User.ID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &AuthResult{Account: account, Auth: auth}, nil
|
|
}
|
|
|
|
func (uc *Usecase) Get(ctx context.Context, userID uint) (*Account, error) {
|
|
if userID == 0 {
|
|
return nil, ErrAccountNotFound
|
|
}
|
|
return uc.repo.Get(ctx, userID)
|
|
}
|
|
|
|
func (uc *Usecase) UpdateProfile(ctx context.Context, in *ProfileInput) (*Account, error) {
|
|
if in == nil || in.UserID == 0 {
|
|
return nil, ErrInvalidAccount
|
|
}
|
|
in.Nickname = strings.TrimSpace(in.Nickname)
|
|
in.Avatar = strings.TrimSpace(in.Avatar)
|
|
if in.Nickname != "" {
|
|
if n := len([]rune(in.Nickname)); n < 2 || n > 16 {
|
|
return nil, ErrInvalidAccount
|
|
}
|
|
}
|
|
if len([]rune(in.Avatar)) > 1000 {
|
|
return nil, ErrInvalidAccount
|
|
}
|
|
return uc.repo.UpdateProfile(ctx, in)
|
|
}
|
|
|
|
func (uc *Usecase) ChangePassword(ctx context.Context, in *PasswordInput) (*AuthResult, error) {
|
|
if in == nil || in.UserID == 0 || in.OldPassword == "" || in.NewPassword == "" || in.NewPassword != in.ConfirmPassword {
|
|
return nil, ErrInvalidAccount
|
|
}
|
|
config, err := uc.security.Current(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if err = uc.security.ValidatePassword(config, in.NewPassword); err != nil {
|
|
return nil, err
|
|
}
|
|
if err = uc.users.ChangePassword(ctx, in.UserID, in.OldPassword, in.NewPassword); err != nil {
|
|
return nil, err
|
|
}
|
|
account, err := uc.repo.Get(ctx, in.UserID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
auth, err := uc.auth.AuthenticateRegisteredUser(ctx, accountUser(account), in.IP, in.Agent)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &AuthResult{Account: account, Auth: auth}, nil
|
|
}
|
|
|
|
func accountUser(account *Account) *system.User {
|
|
return &system.User{ID: account.UserID, UUID: account.UUID, Username: account.Username, NickName: account.Nickname, HeaderImg: account.Avatar, AuthorityID: account.AuthorityID, Enable: 1, PasswordUpdatedAt: account.PasswordUpdatedAt}
|
|
}
|