add user model / service and database maintenance command
This commit is contained in:
@@ -0,0 +1,37 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/urfave/cli"
|
||||
|
||||
"github.com/mayswind/lab/pkg/datastore"
|
||||
"github.com/mayswind/lab/pkg/log"
|
||||
"github.com/mayswind/lab/pkg/models"
|
||||
)
|
||||
|
||||
var Database = cli.Command{
|
||||
Name: "database",
|
||||
Usage: "lab database maintenance",
|
||||
Subcommands: []cli.Command{
|
||||
{
|
||||
Name: "update",
|
||||
Usage: "Update database structure",
|
||||
Action: updateDatabaseStructure,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func updateDatabaseStructure(c *cli.Context) error {
|
||||
_, err := initializeSystem(c)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.BootInfof("[database.updateDatabaseStructure] starting maintaining")
|
||||
|
||||
_ = datastore.Container.UserStore.SyncStructs(new(models.User))
|
||||
|
||||
log.BootInfof("[database.updateDatabaseStructure] maintained successfully")
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
|
||||
"github.com/mayswind/lab/pkg/datastore"
|
||||
"github.com/mayswind/lab/pkg/log"
|
||||
"github.com/mayswind/lab/pkg/settings"
|
||||
"github.com/mayswind/lab/pkg/uuid"
|
||||
)
|
||||
|
||||
func initializeSystem(c *cli.Context) (*settings.Config, error) {
|
||||
var err error
|
||||
configFilePath := c.GlobalString("conf-path")
|
||||
|
||||
if configFilePath != "" {
|
||||
if _, err = os.Stat(configFilePath); err != nil {
|
||||
log.BootErrorf("[initializer.initializeSystem] cannot load configuration from custom config path %s, because file not exists", configFilePath)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.BootInfof("[initializer.initializeSystem] will loading configuration from custom config path %s", configFilePath)
|
||||
} else {
|
||||
configFilePath, err = settings.GetDefaultConfigFilePath()
|
||||
|
||||
if err != nil {
|
||||
log.BootErrorf("[initializer.initializeSystem] cannot get default configuration path, because %s", err.Error())
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.BootInfof("[initializer.initializeSystem] will load configuration from default config path %s", configFilePath)
|
||||
}
|
||||
|
||||
config, err := settings.LoadConfiguration(configFilePath)
|
||||
|
||||
if err != nil {
|
||||
log.BootErrorf("[initializer.initializeSystem] cannot load configuration, because %s", err.Error())
|
||||
return nil, err
|
||||
}
|
||||
|
||||
settings.SetCurrentConfig(config)
|
||||
|
||||
err = datastore.InitializeDataStore(config)
|
||||
|
||||
if err != nil {
|
||||
log.BootErrorf("[initializer.initializeSystem] initializes data store failed, because %s", err.Error())
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = log.SetLoggerConfiguration(config)
|
||||
|
||||
if err != nil {
|
||||
log.BootErrorf("[initializer.initializeSystem] sets logger configuration failed, because %s", err.Error())
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = uuid.InitializeUuidGenerator(config)
|
||||
|
||||
if err != nil {
|
||||
log.BootErrorf("[initializer.initializeSystem] initializes uuid generator failed, because %s", err.Error())
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cfgJson, _ := json.Marshal(config)
|
||||
log.BootInfof("[initializer.initializeSystem] has loaded configuration %s", cfgJson)
|
||||
|
||||
return config, nil
|
||||
}
|
||||
+3
-2
@@ -13,6 +13,7 @@ var (
|
||||
ErrUserPasswordWrong = NewNormalError(NORMAL_SUBCATEGORY_USER, 5, http.StatusBadRequest, "password is wrong")
|
||||
ErrUsernameAlreadyExists = NewNormalError(NORMAL_SUBCATEGORY_USER, 6, http.StatusBadRequest, "username already exists")
|
||||
ErrUserEmailAlreadyExists = NewNormalError(NORMAL_SUBCATEGORY_USER, 7, http.StatusBadRequest, "email already exists")
|
||||
ErrLoginNameOrPasswordInvalid = NewNormalError(NORMAL_SUBCATEGORY_USER, 8, http.StatusUnauthorized, "login name or password is invalid")
|
||||
ErrLoginNameOrPasswordWrong = NewNormalError(NORMAL_SUBCATEGORY_USER, 9, http.StatusUnauthorized, "login name or password is wrong")
|
||||
ErrLoginNameInvalid = NewNormalError(NORMAL_SUBCATEGORY_USER, 8, http.StatusUnauthorized, "login name is invalid")
|
||||
ErrLoginNameOrPasswordInvalid = NewNormalError(NORMAL_SUBCATEGORY_USER, 9, http.StatusUnauthorized, "login name or password is invalid")
|
||||
ErrLoginNameOrPasswordWrong = NewNormalError(NORMAL_SUBCATEGORY_USER, 10, http.StatusUnauthorized, "login name or password is wrong")
|
||||
)
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
package models
|
||||
|
||||
type UserType byte
|
||||
|
||||
const (
|
||||
USER_TYPE_NORMAL UserType = 0
|
||||
USER_TYPE_ADMIN UserType = 63
|
||||
USER_TYPE_SUPER_ADMIN UserType = 127
|
||||
)
|
||||
|
||||
type User struct {
|
||||
Uid int64 `xorm:"PK"`
|
||||
Username string `xorm:"VARCHAR(32) UNIQUE NOT NULL"`
|
||||
Email string `xorm:"VARCHAR(100) UNIQUE NOT NULL"`
|
||||
Nickname string `xorm:"VARCHAR(64) NOT NULL"`
|
||||
Password string `xorm:"VARCHAR(64) NOT NULL"`
|
||||
Salt string `xorm:"VARCHAR(10) NOT NULL"`
|
||||
Rands string `xorm:"VARCHAR(10) NOT NULL"`
|
||||
Type UserType `xorm:"TINYINT NOT NULL"`
|
||||
IsAdmin bool `xorm:"NOT NULL"`
|
||||
Deleted bool `xorm:"NOT NULL"`
|
||||
EmailVerified bool `xorm:"NOT NULL"`
|
||||
CreatedUnixTime int64
|
||||
UpdatedUnixTime int64
|
||||
DeletedUnixTime int64
|
||||
LastLoginUnixTime int64
|
||||
}
|
||||
|
||||
type UserLoginRequest struct {
|
||||
LoginName string `json:"loginName" binding:"required,notBlank,max=100,validUsername|validEmail"`
|
||||
Password string `json:"password" binding:"required,min=6,max=128"`
|
||||
}
|
||||
|
||||
type UserRegisterRequest struct {
|
||||
Username string `json:"username" binding:"required,notBlank,max=32,validUsername"`
|
||||
Email string `json:"email" binding:"required,notBlank,max=100,validEmail"`
|
||||
Nickname string `json:"nickname" binding:"required,notBlank,max=64"`
|
||||
Password string `json:"password" binding:"required,min=6,max=128"`
|
||||
}
|
||||
|
||||
type UserProfileUpdateRequest struct {
|
||||
Email string `json:"email" binding:"omitempty,notBlank,max=100,validEmail"`
|
||||
Nickname string `json:"nickname" binding:"omitempty,notBlank,max=64"`
|
||||
Password string `json:"password" binding:"omitempty,min=6,max=128"`
|
||||
}
|
||||
|
||||
type UserProfileResponse struct {
|
||||
Uid string `json:"uid"`
|
||||
Username string `json:"username"`
|
||||
Email string `json:"email"`
|
||||
Nickname string `json:"nickname"`
|
||||
Type UserType `json:"type"`
|
||||
CreatedAt int64 `json:"createdAt"`
|
||||
UpdatedAt int64 `json:"updatedAt"`
|
||||
LastLoginAt int64 `json:"lastLoginAt"`
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"github.com/mayswind/lab/pkg/datastore"
|
||||
"github.com/mayswind/lab/pkg/settings"
|
||||
"github.com/mayswind/lab/pkg/uuid"
|
||||
)
|
||||
|
||||
type ServiceUsingDB struct {
|
||||
container *datastore.DataStoreContainer
|
||||
}
|
||||
|
||||
func (s *ServiceUsingDB) UserDB() *datastore.Database {
|
||||
return s.container.UserStore.Choose(0)
|
||||
}
|
||||
|
||||
func (s *ServiceUsingDB) TokenDB(uid int64) *datastore.Database {
|
||||
return s.container.TokenStore.Choose(uid)
|
||||
}
|
||||
|
||||
func (s *ServiceUsingDB) UserDataDB(uid int64) *datastore.Database {
|
||||
return s.container.UserDataStore.Choose(uid)
|
||||
}
|
||||
|
||||
type ServiceUsingConfig struct {
|
||||
container *settings.ConfigContainer
|
||||
}
|
||||
|
||||
func (s *ServiceUsingConfig) CurrentConfig() *settings.Config {
|
||||
return s.container.Current
|
||||
}
|
||||
|
||||
type ServiceUsingUuid struct {
|
||||
container *uuid.UuidContainer
|
||||
}
|
||||
|
||||
func (s *ServiceUsingUuid) GenerateUuid(uuidType uuid.UuidType) int64 {
|
||||
return s.container.GenerateUuid(uuidType)
|
||||
}
|
||||
@@ -0,0 +1,226 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"xorm.io/xorm"
|
||||
|
||||
"github.com/mayswind/lab/pkg/datastore"
|
||||
"github.com/mayswind/lab/pkg/errs"
|
||||
"github.com/mayswind/lab/pkg/models"
|
||||
"github.com/mayswind/lab/pkg/utils"
|
||||
"github.com/mayswind/lab/pkg/uuid"
|
||||
)
|
||||
|
||||
type UserService struct {
|
||||
ServiceUsingDB
|
||||
ServiceUsingUuid
|
||||
}
|
||||
|
||||
var (
|
||||
Users = &UserService{
|
||||
ServiceUsingDB: ServiceUsingDB{
|
||||
container: datastore.Container,
|
||||
},
|
||||
ServiceUsingUuid: ServiceUsingUuid{
|
||||
container: uuid.Container,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func (s *UserService) GetUserByUsernameOrEmailAndPassword(loginname string, password string) (*models.User, error) {
|
||||
var user *models.User
|
||||
var err error
|
||||
|
||||
if utils.IsValidUsername(loginname) {
|
||||
user, err = s.GetUserByUsername(loginname)
|
||||
} else if utils.IsValidEmail(loginname) {
|
||||
user, err = s.GetUserByEmail(loginname)
|
||||
} else {
|
||||
err = errs.ErrLoginNameInvalid
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !s.IsPasswordEqualsUserPassword(password, user) {
|
||||
return nil, errs.ErrUserPasswordWrong
|
||||
}
|
||||
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func (s *UserService) GetUserById(uid int64) (*models.User, error) {
|
||||
if uid <= 0 {
|
||||
return nil, errs.ErrUserIdInvalid
|
||||
}
|
||||
|
||||
user := &models.User{}
|
||||
has, err := s.UserDB().ID(uid).Where("deleted=?", false).Get(user)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if !has {
|
||||
return nil, errs.ErrUserNotFound
|
||||
}
|
||||
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func (s *UserService) GetUserByUsername(username string) (*models.User, error) {
|
||||
if username == "" {
|
||||
return nil, errs.ErrUsernameIsEmpty
|
||||
}
|
||||
|
||||
user := &models.User{}
|
||||
has, err := s.UserDB().Where("username=? AND deleted=?", username, false).Get(user)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if !has {
|
||||
return nil, errs.ErrUserNotFound
|
||||
}
|
||||
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func (s *UserService) GetUserByEmail(email string) (*models.User, error) {
|
||||
if email == "" {
|
||||
return nil, errs.ErrEmailIsEmpty
|
||||
}
|
||||
|
||||
user := &models.User{}
|
||||
has, err := s.UserDB().Where("email=? AND deleted=?", email, false).Get(user)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if !has {
|
||||
return nil, errs.ErrUserNotFound
|
||||
}
|
||||
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func (s *UserService) CreateUser(user *models.User) error {
|
||||
exists, err := s.ExistsUsername(user.Username)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
} else if exists {
|
||||
return errs.ErrUsernameAlreadyExists
|
||||
}
|
||||
|
||||
exists, err = s.ExistsEmail(user.Email)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
} else if exists {
|
||||
return errs.ErrUserEmailAlreadyExists
|
||||
}
|
||||
|
||||
if user.Password == "" {
|
||||
return errs.ErrPasswordIsEmpty
|
||||
}
|
||||
|
||||
if user.Salt, err = utils.GetRandomString(10); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if user.Rands, err = utils.GetRandomString(10); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
user.Uid = s.GenerateUuid(uuid.UUID_TYPE_USER)
|
||||
user.Password = utils.EncodePassword(user.Password, user.Salt)
|
||||
|
||||
user.Deleted = false
|
||||
|
||||
user.CreatedUnixTime = time.Now().Unix()
|
||||
user.UpdatedUnixTime = time.Now().Unix()
|
||||
user.LastLoginUnixTime = time.Now().Unix()
|
||||
|
||||
return s.UserDB().DoTranscation(func(sess *xorm.Session) error {
|
||||
_, err := sess.Insert(user)
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
func (s *UserService) UpdateUser(user *models.User) (keyProfileUpdated bool, err error) {
|
||||
if user.Uid <= 0 {
|
||||
return false, errs.ErrUserIdInvalid
|
||||
}
|
||||
|
||||
var updateCols []string
|
||||
|
||||
now := time.Now().Unix()
|
||||
keyProfileUpdated = false
|
||||
|
||||
if user.Email != "" {
|
||||
user.EmailVerified = false
|
||||
|
||||
updateCols = append(updateCols, "email")
|
||||
updateCols = append(updateCols, "email_verified")
|
||||
}
|
||||
|
||||
if user.Password != "" {
|
||||
user.Password = utils.EncodePassword(user.Password, user.Salt)
|
||||
|
||||
keyProfileUpdated = true
|
||||
updateCols = append(updateCols, "password")
|
||||
}
|
||||
|
||||
if user.Nickname != "" {
|
||||
updateCols = append(updateCols, "nickname")
|
||||
}
|
||||
|
||||
user.UpdatedUnixTime = now
|
||||
updateCols = append(updateCols, "updated_unix_time")
|
||||
|
||||
err = s.UserDB().DoTranscation(func(sess *xorm.Session) error {
|
||||
updatedRows, err := sess.ID(user.Uid).Where("deleted=?", false).Cols(updateCols...).Update(user)
|
||||
|
||||
if updatedRows < 1 {
|
||||
return errs.ErrUserNotFound
|
||||
}
|
||||
|
||||
return err
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return keyProfileUpdated, nil
|
||||
}
|
||||
|
||||
func (s *UserService) UpdateUserLastLoginTime(uid int64) error {
|
||||
if uid <= 0 {
|
||||
return errs.ErrUserIdInvalid
|
||||
}
|
||||
|
||||
return s.UserDB().DoTranscation(func(sess *xorm.Session) error {
|
||||
_, err := sess.ID(uid).Where("deleted=?", false).Cols("last_login_unix_time").Update(&models.User{LastLoginUnixTime: time.Now().Unix()})
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
func (s *UserService) ExistsUsername(username string) (bool, error) {
|
||||
if username == "" {
|
||||
return false, errs.ErrUsernameIsEmpty
|
||||
}
|
||||
|
||||
return s.UserDB().Cols("username").Where("username=? AND deleted=?", username, false).Exist(&models.User{})
|
||||
}
|
||||
|
||||
func (s *UserService) ExistsEmail(email string) (bool, error) {
|
||||
if email == "" {
|
||||
return false, errs.ErrEmailIsEmpty
|
||||
}
|
||||
|
||||
return s.UserDB().Cols("email").Where("email=? AND deleted=?", email, false).Exist(&models.User{})
|
||||
}
|
||||
|
||||
func (s *UserService) IsPasswordEqualsUserPassword(password string, user *models.User) bool {
|
||||
return user.Password == utils.EncodePassword(password, user.Salt)
|
||||
}
|
||||
Reference in New Issue
Block a user