package user
import (
"OneAuth/cfg"
"OneAuth/libs/app"
"OneAuth/libs/base"
"OneAuth/libs/oerr"
"OneAuth/libs/token"
"OneAuth/models"
"errors"
//"OneAuth/ws"
"encoding/base64"
"fmt"
"github.com/veypi/OneBD"
"github.com/veypi/OneBD/rfc"
"github.com/veypi/utils/log"
"gorm.io/gorm"
"math/rand"
"strconv"
"time"
)
func Router ( r OneBD . Router ) {
pool := OneBD . NewHandlerPool ( func ( ) OneBD . Handler {
h := & handler { }
h . Ignore ( rfc . MethodHead , rfc . MethodPost )
return h
} )
r . Set ( "/" , pool , rfc . MethodGet , rfc . MethodPost ) // list
r . Set ( "/:user_id" , pool , rfc . MethodGet , rfc . MethodPatch , rfc . MethodHead , rfc . MethodDelete )
r . Set ( "/:user_id/role/" , userRoleP , rfc . MethodPost )
r . Set ( "/:user_id/role/:role_id" , userRoleP , rfc . MethodDelete )
//r.WS("/ws", func(m OneBD.Meta) (conn OneBD.WebsocketConn, err error) {
//return ws.User.Upgrade(m.ResponseWriter(), m.Request())
//})
}
type handler struct {
base . ApiHandler
User * models . User
}
// Get get user data
func ( h * handler ) Get ( ) ( interface { } , error ) {
username := h . Meta ( ) . Query ( "username" )
if username != "" {
users := make ( [ ] * models . User , 0 , 10 )
err := cfg . DB ( ) . Preload ( "Scores" ) . Preload ( "Roles.Auths" ) . Where ( "username LIKE ? OR nickname LIKE ?" , "%" + username + "%" , "%" + username + "%" ) . Find ( & users ) . Error
if err != nil {
return nil , err
}
return users , nil
}
userID := h . Meta ( ) . ParamsInt ( "user_id" )
if userID != 0 {
user := & models . User { }
user . ID = uint ( userID )
return user , cfg . DB ( ) . Where ( user ) . Preload ( "Scores" ) . Preload ( "Roles.Auths" ) . Preload ( "Favorites" ) . First ( user ) . Error
} else {
users := make ( [ ] models . User , 10 )
skip , err := strconv . Atoi ( h . Meta ( ) . Query ( "skip" ) )
if err != nil || skip < 0 {
skip = 0
}
if err := cfg . DB ( ) . Preload ( "Scores" ) . Preload ( "Roles.Auths" ) . Offset ( skip ) . Find ( & users ) . Error ; err != nil {
return nil , err
}
return users , nil
}
}
// Post register user
func ( h * handler ) Post ( ) ( interface { } , error ) {
self := & models . App { }
self . ID = cfg . CFG . APPID
err := cfg . DB ( ) . Where ( self ) . First ( self ) . Error
if err != nil {
return nil , oerr . DBErr . Attach ( err )
}
if ! self . EnableRegister {
return nil , oerr . NoAuth . AttachStr ( "register disabled" )
}
var userdata = struct {
UUID string ` json:"uuid" `
Username string ` json:"username" `
Password string ` json:"password" `
Nickname string ` json:"nickname" `
Phone string ` json:"phone" `
Email string ` json:"email" `
Domain string ` json:"domain" `
Title string ` json:"title" `
Position string ` json:"position" `
} { }
if err := h . Meta ( ) . ReadJson ( & userdata ) ; err != nil {
return nil , err
}
pass , err := base64 . StdEncoding . DecodeString ( userdata . Password )
if err != nil {
return nil , err
}
if len ( pass ) > 32 || len ( pass ) < 6 {
return nil , oerr . PassError
}
r := rand . New ( rand . NewSource ( time . Now ( ) . UnixNano ( ) ) )
h . User = new ( models . User )
h . User . Icon = fmt . Sprintf ( "/media/icon/default/%04d.jpg" , r . Intn ( 230 ) )
h . User . Nickname = userdata . Nickname
h . User . Phone = userdata . Phone
h . User . Username = userdata . Username
h . User . Email = userdata . Email
h . User . Position = userdata . Position
if err := h . User . UpdatePass ( string ( pass ) ) ; err != nil {
log . HandlerErrs ( err )
return nil , oerr . ResourceCreatedFailed
}
err = cfg . DB ( ) . Transaction ( func ( tx * gorm . DB ) error {
if err := tx . Create ( & h . User ) . Error ; err != nil {
return oerr . ResourceDuplicated
}
err := app . AddUser ( tx , self . ID , h . User . ID , self . InitRoleID )
if err != nil {
return err
}
if userdata . UUID != self . UUID && userdata . UUID != "" {
target := & models . App { }
target . UUID = userdata . UUID
err = tx . Where ( target ) . First ( target ) . Error
if err != nil {
return err
}
if target . EnableRegister {
err := app . AddUser ( tx , target . ID , h . User . ID , target . InitRoleID )
if err != nil {
return err
}
} else {
// TODO
}
return err
}
return nil
} )
if err != nil {
return nil , err
}
return h . User , nil
}
// Patch update user data
func ( h * handler ) Patch ( ) ( interface { } , error ) {
uid := h . Meta ( ) . Params ( "user_id" )
opts := struct {
Password string ` json:"password" `
Nickname string ` json:"nickname" `
Phone string ` json:"phone" gorm:"type:varchar(20);unique;default:null" json:",omitempty" `
Email string ` json:"email" gorm:"type:varchar(50);unique;default:null" json:",omitempty" `
Status string ` json:"status" `
Position string ` json:"position" `
} { }
if err := h . Meta ( ) . ReadJson ( & opts ) ; err != nil {
return nil , err
}
target := models . User { }
if tempID , err := strconv . Atoi ( uid ) ; err != nil || tempID <= 0 {
return nil , oerr . ApiArgsError . Attach ( err )
} else {
target . ID = uint ( tempID )
}
tx := cfg . DB ( ) . Begin ( )
if err := cfg . DB ( ) . Where ( & target ) . First ( & target ) . Error ; err != nil {
return nil , err
}
if target . ID != h . Payload . ID {
return nil , oerr . NoAuth
}
if len ( opts . Password ) >= 6 {
if err := target . UpdatePass ( opts . Password ) ; err != nil {
log . HandlerErrs ( err )
return nil , oerr . ApiArgsError . AttachStr ( err . Error ( ) )
}
}
if opts . Nickname != "" {
target . Nickname = opts . Nickname
}
if opts . Position != "" {
target . Position = opts . Position
}
if opts . Phone != "" {
target . Phone = opts . Phone
}
if opts . Email != "" {
target . Email = opts . Email
}
if opts . Status != "" {
target . Status = opts . Status
}
if err := tx . Updates ( & target ) . Error ; err != nil {
tx . Rollback ( )
return nil , err
}
tx . Commit ( )
return nil , nil
}
// Delete delete user
func ( h * handler ) Delete ( ) ( interface { } , error ) {
// TODO::
return nil , nil
}
// Head user login
func ( h * handler ) Head ( ) ( interface { } , error ) {
uid := h . Meta ( ) . Params ( "user_id" )
pass , err := base64 . StdEncoding . DecodeString ( h . Meta ( ) . Query ( "password" ) )
if err != nil {
return nil , oerr . ApiArgsError . Attach ( err )
}
password := string ( pass )
if len ( uid ) == 0 || len ( password ) == 0 {
return nil , oerr . ApiArgsError
}
appUUID := h . Meta ( ) . Query ( "uuid" )
if appUUID == "" {
return nil , oerr . ApiArgsMissing . AttachStr ( "uuid" )
}
h . User = new ( models . User )
uidType := h . Meta ( ) . Query ( "uid_type" )
switch uidType {
case "username" :
h . User . Username = uid
case "phone" :
h . User . Phone = uid
case "email" :
h . User . Email = uid
default :
h . User . Username = uid
}
target := & models . App { }
target . UUID = appUUID
err = cfg . DB ( ) . Where ( target ) . Find ( target ) . Error
if err != nil {
return nil , oerr . DBErr . Attach ( err )
}
if err := cfg . DB ( ) . Preload ( "Roles.Auths" ) . Preload ( "Auths" ) . Where ( h . User ) . First ( h . User ) . Error ; err != nil {
if err . Error ( ) == gorm . ErrRecordNotFound . Error ( ) {
return nil , oerr . AccountNotExist
} else {
log . HandlerErrs ( err )
return nil , oerr . DBErr . Attach ( err )
}
}
isAuth , err := h . User . CheckLogin ( password )
if err != nil || ! isAuth {
return nil , oerr . PassError . Attach ( err )
}
au := & models . AppUser { }
au . UserID = h . User . ID
au . AppID = target . ID
err = cfg . DB ( ) . Where ( au ) . First ( au ) . Error
appID := target . ID
h . Meta ( ) . SetHeader ( "content" , target . UserRefreshUrl )
if err != nil {
if ! errors . Is ( err , gorm . ErrRecordNotFound ) {
return nil , err
}
appID = cfg . CFG . APPID
h . Meta ( ) . SetHeader ( "content" , "/app/" + target . UUID )
} else if au . Disabled {
return nil , oerr . DisableLogin
}
tokenStr , err := token . GetToken ( h . User , appID )
if err != nil {
log . HandlerErrs ( err )
return nil , oerr . Unknown . Attach ( err )
}
h . Meta ( ) . SetHeader ( "auth_token" , tokenStr )
log . Info ( ) . Msg ( h . User . Username + " login" )
return nil , nil
}