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 }