package user import ( "encoding/json" "fmt" "github.com/golang-jwt/jwt/v4" "github.com/google/uuid" "nCovTrack-Backend/global" "nCovTrack-Backend/models" "nCovTrack-Backend/utils" "strings" "time" ) const ( EMAIL_CODE_REDIS_KEY = "verify_code_" ) // Login if login success, will return token func Login(user map[string]interface{}) (token string) { account := user["account"].(string) var queryMap []map[string]interface{} if strings.Contains(account, "@") { queryMap = append(queryMap, map[string]interface{}{"email": account}) } else { queryMap = append(queryMap, map[string]interface{}{"phone": account}) } userInfo := models.Get[models.BackUser](queryMap) if userInfo == nil { return "" } if !utils.PasswordCompare(user["password"].(string), userInfo["password"].(string)) { return "" } claims := jwt.MapClaims{ "id": userInfo["id"], "username": userInfo["username"], "role": userInfo["role"], "email": userInfo["email"], } return utils.GenerateToken(claims) } // Register user register, user can use account after approved func Register(user map[string]interface{}) { user["password"] = utils.PasswordEncrypt(user["password"].(string)) userStr, _ := json.Marshal(user) // insert into redis, wait for approve cmd := global.Redis.HMSet(global.REGISTER_REDIS_KEY, map[string]interface{}{user["email"].(string): userStr}) if cmd.Err() != nil { panic(cmd.Err()) } } // ListRegister list the registers in the redis to be approved func ListRegister() *[]map[string]interface{} { applyStrMap := global.Redis.HGetAll(global.REGISTER_REDIS_KEY).Val() var applies []map[string]interface{} for _, v := range applyStrMap { var apply map[string]interface{} _ = json.Unmarshal([]byte(v), &apply) applies = append(applies, apply) } if applies == nil { applies = []map[string]interface{}{} } return &applies } // ApproveRegister approve a register func ApproveRegister(email string, pass bool) bool { if !pass { rowsAffected := global.Redis.HDel(global.REGISTER_REDIS_KEY, email).Val() return rowsAffected != 0 } // if pass, will get the register info from redis, and the insert into mysql, this mean user is register success applyStr := global.Redis.HGet(global.REGISTER_REDIS_KEY, email).Val() rowsAffected := global.Redis.HDel(global.REGISTER_REDIS_KEY, email).Val() if rowsAffected == 0 { return false } var apply map[string]interface{} _ = json.Unmarshal([]byte(applyStr), &apply) if !NoDuplicatePhoneOrEmail(apply["phone"].(string), apply["email"].(string)) { return false } colMap := models.MapJ2c[models.BackUser](apply, true) ok, rowsAffected := models.Upsert[models.BackUser](colMap) return ok && rowsAffected != 0 } // ChangePassword user change password, or user forgot password func ChangePassword(changePwd map[string]interface{}) bool { match := VerifyEmailCode(changePwd["email"].(string), changePwd["code"].(string))["match"].(bool) if !match { return false } newPassword := utils.PasswordEncrypt(changePwd["newPassword"].(string)) colMap := map[string]interface{}{ "id": 1, "password": newPassword, } models.BeforeSave(colMap, -1) delete(colMap, "id") rowAffected := global.Db.Model(models.BackUser{}).Where("email = ?", changePwd["email"]).Updates(colMap).RowsAffected if rowAffected == 0 { return false } now := time.Now().Unix() global.Redis.HSet(global.CHANGEPWD_REDIS_KEY, changePwd["email"].(string), now) return true } // NoDuplicatePhoneOrEmail detect the phone or email is registered or not func NoDuplicatePhoneOrEmail(phone string, email string) bool { var queryMap []map[string]interface{} if phone != "" { queryMap = append(queryMap, map[string]interface{}{"phone": phone}) } if email != "" { queryMap = append(queryMap, map[string]interface{}{"email": email}) } return len(queryMap) != 0 && models.Count[models.BackUser](queryMap) == 0 } // SendEmailCode used to send email verify code func SendEmailCode(email string) bool { code := uuid.New().String()[0:6] text := fmt.Sprintf("Your Verify Code is :%s, Will Expire After 10 Minutes", code) subject := "nCovTrack Verify" // only set expired, not limit the frequency of use global.Redis.Set(EMAIL_CODE_REDIS_KEY+email, code, 10*time.Minute) return utils.SendEmail(subject, text, email) } // VerifyEmailCode use to verify user's verify code is correct or not func VerifyEmailCode(email string, code string) map[string]interface{} { verifyCode := global.Redis.Get(EMAIL_CODE_REDIS_KEY + email).Val() return map[string]interface{}{"match": verifyCode == code} }