github.com/e154/smart-home@v0.17.2-0.20240311175135-e530a6e5cd45/db/user.go (about)

     1  // This file is part of the Smart Home
     2  // Program complex distribution https://github.com/e154/smart-home
     3  // Copyright (C) 2016-2023, Filippov Alex
     4  //
     5  // This library is free software: you can redistribute it and/or
     6  // modify it under the terms of the GNU Lesser General Public
     7  // License as published by the Free Software Foundation; either
     8  // version 3 of the License, or (at your option) any later version.
     9  //
    10  // This library is distributed in the hope that it will be useful,
    11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    13  // Library General Public License for more details.
    14  //
    15  // You should have received a copy of the GNU Lesser General Public
    16  // License along with this library.  If not, see
    17  // <https://www.gnu.org/licenses/>.
    18  
    19  package db
    20  
    21  import (
    22  	"context"
    23  	"database/sql"
    24  	"encoding/json"
    25  	"fmt"
    26  	"strings"
    27  	"time"
    28  
    29  	"github.com/jackc/pgerrcode"
    30  	"github.com/jackc/pgx/v5/pgconn"
    31  	"github.com/pkg/errors"
    32  	"gorm.io/gorm"
    33  
    34  	"github.com/e154/smart-home/common/apperr"
    35  )
    36  
    37  // Users ...
    38  type Users struct {
    39  	Db *gorm.DB
    40  }
    41  
    42  // User ...
    43  type User struct {
    44  	Id                  int64 `gorm:"primary_key"`
    45  	Nickname            string
    46  	FirstName           string
    47  	LastName            string
    48  	EncryptedPassword   string
    49  	Email               string
    50  	Status              string
    51  	ResetPasswordToken  string
    52  	AuthenticationToken *string
    53  	Image               *Image
    54  	ImageId             sql.NullInt64
    55  	SignInCount         int64
    56  	CurrentSignInIp     string
    57  	LastSignInIp        string
    58  	Lang                string
    59  	User                *User
    60  	UserId              sql.NullInt64
    61  	Role                *Role
    62  	RoleName            string
    63  	Meta                []*UserMeta `gorm:"foreignKey:UserId;constraint:OnUpdate:CASCADE;"`
    64  	Devices             []*UserDevice
    65  	ResetPasswordSentAt *time.Time
    66  	CurrentSignInAt     *time.Time
    67  	LastSignInAt        *time.Time
    68  	CreatedAt           time.Time `gorm:"<-:create"`
    69  	UpdatedAt           time.Time
    70  	DeletedAt           *time.Time
    71  	History             json.RawMessage `gorm:"type:jsonb;not null"`
    72  }
    73  
    74  // TableName ...
    75  func (u *User) TableName() string {
    76  	return "users"
    77  }
    78  
    79  // Add ...
    80  func (u *Users) Add(ctx context.Context, user *User) (id int64, err error) {
    81  
    82  	if err = u.Db.WithContext(ctx).Create(&user).Error; err != nil {
    83  		var pgErr *pgconn.PgError
    84  		if errors.As(err, &pgErr) {
    85  			switch pgErr.Code {
    86  			case pgerrcode.UniqueViolation:
    87  				if strings.Contains(pgErr.Message, "nickname_2_users_unq") {
    88  					err = errors.Wrap(apperr.ErrUserAdd, fmt.Sprintf("user name \"%s\" not unique", user.Nickname))
    89  					return
    90  				}
    91  				if strings.Contains(pgErr.Message, "email_2_users_unq") {
    92  					err = errors.Wrap(apperr.ErrUserAdd, fmt.Sprintf("user email \"%s\" not unique", user.Email))
    93  					return
    94  				}
    95  			default:
    96  				fmt.Printf("unknown code \"%s\"\n", pgErr.Code)
    97  			}
    98  		}
    99  		err = errors.Wrap(apperr.ErrUserAdd, err.Error())
   100  		return
   101  	}
   102  	id = user.Id
   103  	return
   104  }
   105  
   106  // GetById ...
   107  func (u *Users) GetById(ctx context.Context, userId int64) (user *User, err error) {
   108  
   109  	user = &User{}
   110  	err = u.Db.WithContext(ctx).Model(user).
   111  		Where("id = ? and deleted_at isnull", userId).
   112  		Preload("Image").
   113  		Preload("Meta").
   114  		Preload("Role").
   115  		Preload("User").
   116  		Preload("Devices").
   117  		Find(user).
   118  		Error
   119  	if err != nil {
   120  		if errors.Is(err, gorm.ErrRecordNotFound) {
   121  			err = errors.Wrap(apperr.ErrUserNotFound, fmt.Sprintf("id \"%d\"", userId))
   122  			return
   123  		}
   124  		err = errors.Wrap(apperr.ErrUserGet, err.Error())
   125  	}
   126  	return
   127  }
   128  
   129  // GetByEmail ...
   130  func (u *Users) GetByEmail(ctx context.Context, email string) (user *User, err error) {
   131  
   132  	user = &User{}
   133  	err = u.Db.WithContext(ctx).Model(user).
   134  		Where("email = ?", email).
   135  		Preload("Image").
   136  		Preload("Meta").
   137  		Preload("Role").
   138  		Preload("User").
   139  		Preload("Devices").
   140  		Find(user).
   141  		Error
   142  	if err != nil {
   143  		err = errors.Wrap(apperr.ErrUserGet, err.Error())
   144  	}
   145  	return
   146  }
   147  
   148  // GetByNickname ...
   149  func (u *Users) GetByNickname(ctx context.Context, nickname string) (user *User, err error) {
   150  
   151  	user = &User{}
   152  	err = u.Db.WithContext(ctx).Model(user).
   153  		Where("nickname = ?", nickname).
   154  		Preload("Image").
   155  		Preload("Meta").
   156  		Preload("Role").
   157  		Preload("User").
   158  		Preload("Devices").
   159  		Find(user).
   160  		Error
   161  	if err != nil {
   162  		err = errors.Wrap(apperr.ErrUserGet, err.Error())
   163  	}
   164  	return
   165  }
   166  
   167  // GetByAuthenticationToken ...
   168  func (u *Users) GetByAuthenticationToken(ctx context.Context, token string) (user *User, err error) {
   169  
   170  	user = &User{}
   171  	err = u.Db.WithContext(ctx).Model(user).
   172  		Where("authentication_token = ?", token).
   173  		Preload("Image").
   174  		Preload("Meta").
   175  		Preload("Role").
   176  		Preload("User").
   177  		Preload("Devices").
   178  		Find(user).
   179  		Error
   180  	if err != nil {
   181  		err = errors.Wrap(apperr.ErrUserGet, err.Error())
   182  	}
   183  	return
   184  }
   185  
   186  // GetByResetPassToken ...
   187  func (u *Users) GetByResetPassToken(ctx context.Context, token string) (user *User, err error) {
   188  
   189  	user = &User{}
   190  	err = u.Db.WithContext(ctx).Model(user).
   191  		Where("reset_password_token = ?", token).
   192  		Preload("Image").
   193  		Preload("Meta").
   194  		Preload("Role").
   195  		Preload("User").
   196  		Preload("Devices").
   197  		Find(user).
   198  		Error
   199  	if err != nil {
   200  		err = errors.Wrap(apperr.ErrUserGet, err.Error())
   201  	}
   202  	return
   203  }
   204  
   205  // Update ...
   206  func (u *Users) Update(ctx context.Context, user *User) (err error) {
   207  
   208  	q := map[string]interface{}{
   209  		"nickname":               user.Nickname,
   210  		"first_name":             user.FirstName,
   211  		"last_name":              user.LastName,
   212  		"email":                  user.Email,
   213  		"status":                 user.Status,
   214  		"reset_password_token":   user.ResetPasswordToken,
   215  		"authentication_token":   user.AuthenticationToken,
   216  		"image_id":               user.ImageId,
   217  		"sign_in_count":          user.SignInCount,
   218  		"current_sign_in_ip":     user.CurrentSignInIp,
   219  		"last_sign_in_ip":        user.LastSignInIp,
   220  		"lang":                   user.Lang,
   221  		"user_id":                user.UserId,
   222  		"role_name":              user.RoleName,
   223  		"reset_password_sent_at": user.ResetPasswordSentAt,
   224  		"current_sign_in_at":     user.CurrentSignInAt,
   225  		"last_sign_in_at":        user.LastSignInAt,
   226  		"history":                user.History,
   227  		//"meta":                   user.Meta, //todo fix
   228  	}
   229  	if user.EncryptedPassword != "" {
   230  		q["encrypted_password"] = user.EncryptedPassword
   231  	}
   232  	if err = u.Db.WithContext(ctx).Model(&User{Id: user.Id}).Updates(q).Error; err != nil {
   233  		var pgErr *pgconn.PgError
   234  		if errors.As(err, &pgErr) {
   235  			switch pgErr.Code {
   236  			case pgerrcode.UniqueViolation:
   237  				if strings.Contains(pgErr.Message, "nickname_2_users_unq") {
   238  					err = errors.Wrap(apperr.ErrUserUpdate, fmt.Sprintf("user name \"%s\" not unique", user.Nickname))
   239  					return
   240  				}
   241  				if strings.Contains(pgErr.Message, "email_2_users_unq") {
   242  					err = errors.Wrap(apperr.ErrUserUpdate, fmt.Sprintf("user email \"%s\" not unique", user.Email))
   243  					return
   244  				}
   245  			default:
   246  				fmt.Printf("unknown code \"%s\"\n", pgErr.Code)
   247  			}
   248  		}
   249  		err = errors.Wrap(apperr.ErrUserUpdate, err.Error())
   250  	}
   251  	return
   252  }
   253  
   254  // NewResetPassToken ...
   255  func (u *Users) NewResetPassToken(ctx context.Context, userId int64, token string) (err error) {
   256  	err = u.Db.WithContext(ctx).Model(&User{Id: userId}).Updates(map[string]interface{}{
   257  		"reset_password_token":   token,
   258  		"reset_password_sent_at": time.Now(),
   259  	}).Error
   260  	if err != nil {
   261  		err = errors.Wrap(apperr.ErrUserUpdate, err.Error())
   262  	}
   263  	return
   264  }
   265  
   266  // ClearResetPassToken ...
   267  func (u *Users) ClearResetPassToken(ctx context.Context, userId int64) (err error) {
   268  	err = u.Db.WithContext(ctx).Model(&User{Id: userId}).Updates(map[string]interface{}{
   269  		"reset_password_token":   "",
   270  		"reset_password_sent_at": nil,
   271  	}).Error
   272  	if err != nil {
   273  		err = errors.Wrap(apperr.ErrUserUpdate, err.Error())
   274  	}
   275  	return
   276  }
   277  
   278  // ClearToken ...
   279  func (u *Users) ClearToken(ctx context.Context, userId int64) (err error) {
   280  	err = u.Db.WithContext(ctx).Model(&User{Id: userId}).Updates(map[string]interface{}{
   281  		"authentication_token": "",
   282  	}).Error
   283  	if err != nil {
   284  		err = errors.Wrap(apperr.ErrUserUpdate, err.Error())
   285  	}
   286  	return
   287  }
   288  
   289  // UpdateAuthenticationToken ...
   290  func (u *Users) UpdateAuthenticationToken(ctx context.Context, userId int64, token string) (err error) {
   291  	err = u.Db.WithContext(ctx).Model(&User{Id: userId}).Updates(map[string]interface{}{
   292  		"authentication_token": token,
   293  	}).Error
   294  	if err != nil {
   295  		err = errors.Wrap(apperr.ErrUserUpdate, err.Error())
   296  	}
   297  	return
   298  }
   299  
   300  // Delete ...
   301  func (u *Users) Delete(ctx context.Context, userId int64) (err error) {
   302  	err = u.Db.WithContext(ctx).Model(&User{Id: userId}).Updates(map[string]interface{}{
   303  		"deleted_at": time.Now(),
   304  	}).Error
   305  	if err != nil {
   306  		err = errors.Wrap(apperr.ErrUserDelete, err.Error())
   307  	}
   308  	return
   309  }
   310  
   311  // List ...
   312  func (n *Users) List(ctx context.Context, limit, offset int, orderBy, sort string) (list []*User, total int64, err error) {
   313  
   314  	if err = n.Db.WithContext(ctx).Model(User{}).Count(&total).Error; err != nil {
   315  		err = errors.Wrap(apperr.ErrUserList, err.Error())
   316  		return
   317  	}
   318  
   319  	list = make([]*User, 0)
   320  	err = n.Db.WithContext(ctx).
   321  		Limit(limit).
   322  		Offset(offset).
   323  		Order(fmt.Sprintf("%s %s", sort, orderBy)).
   324  		Preload("Image").
   325  		Preload("Meta").
   326  		Preload("Role").
   327  		Preload("User").
   328  		Where("deleted_at isnull").
   329  		Find(&list).
   330  		Error
   331  	if err != nil {
   332  		err = errors.Wrap(apperr.ErrUserList, err.Error())
   333  	}
   334  	return
   335  }