func (s *State) CreateUser(c *gin.Context) {
	db, err := gorm.Open("sqlite3", "test.db")
	if err != nil {
		c.AbortWithStatusJSON(500, gin.H{"message": serverError.Error()})
		return
	}
	defer db.Close()
	u := s.UserModel
	if err := s.Db.AutoMigrate(&u).Error; err != nil {
		c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"message": err.Error()})
		return
	}
	err = c.ShouldBindBodyWith(&u, binding.JSON)
	if err != nil {
		c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"message": err.Error()})
		return
	}
	err = u.HashPassword()
	if err != nil {
		c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"message": err.Error()})
	}
	u.SanitizeName()
	if err := db.Create(&u).Error; err != nil {
		c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"message": err.Error(), "code": "duplicate_username"})
		return
	}
	s.Redis.Set(u.Mobile, u.Username, 0)
	ip := c.ClientIP()
	s.Redis.HSet(u.Username+":ips_count", "first_ip", ip)
	c.JSON(http.StatusCreated, gin.H{"ok": "object was successfully created", "details": u})
}
FIXME issue #61
package consumer
import (
	"fmt"
	"net/http"
	"strconv"
	"strings"
	"time"
	gateway "github.com/adonese/noebs/apigateway"
	"github.com/dgrijalva/jwt-go"
	"github.com/gin-gonic/gin"
	"github.com/gin-gonic/gin/binding"
	"github.com/go-redis/redis/v7"
	"github.com/jinzhu/gorm"
	"golang.org/x/crypto/bcrypt"
)
type State struct {
	Db        *gorm.DB
	Redis     *redis.Client
	Auth      Auther
	UserModel gateway.UserModel
	UserLogin gateway.UserLogin
}
type Auther interface {
	VerifyJWT(token string) (*gateway.TokenClaims, error)
	GenerateJWT(token string) (string, error)
}
func (s *State) GenerateAPIKey(c *gin.Context) {
	var m map[string]string
	if err := c.ShouldBindJSON(&m); err != nil {
		if _, ok := m["email"]; ok {
			k, _ := gateway.GenerateAPIKey()
			s.Redis.SAdd("apikeys", k)
			c.JSON(http.StatusOK, gin.H{"result": k})
			return
		} else {
			c.JSON(http.StatusBadRequest, gin.H{"message": "missing_field"})
			return
		}
	} else {
		c.JSON(http.StatusBadRequest, gin.H{"message": "error in email"})
	}
}
func (s *State) ApiKeyMiddleware(c *gin.Context) {
	email := c.GetHeader("X-Email")
	key := c.GetHeader("X-API-Key")
	if email == "" || key == "" {
		c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"message": "unauthorized"})
		return
	}
	res, err := s.Redis.HGet("api_keys", email).Result()
	if err != redis.Nil {
		c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"message": "unauthorized"})
		return
	}
	if key == res {
		c.Next()
	} else {
		c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"message": "unauthorized"})
		return
	}
}
func (s *State) IpFilterMiddleware(c *gin.Context) {
	ip := c.ClientIP()
	if u := c.GetString("username"); u != "" {
		s.Redis.HIncrBy(u+":ips_count", ip, 1)
		c.Next()
	} else {
		c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"message": "unauthorized_access"})
	}
}
func (s *State) LoginHandler(c *gin.Context) {
	req := s.UserLogin
	if err := c.ShouldBindWith(&req, binding.JSON); err != nil {
		log.Printf("The request is wrong. %v", err)
		c.JSON(http.StatusBadRequest, gin.H{"message": err.Error(), "code": "bad_request"})
		return
	}
	s.Db.AutoMigrate(&gateway.Service{}, &gateway.UserModel{}, &gateway.UserLogin{})
	log.Printf("the processed request is: %v\n", req)
	u := s.UserModel
	if notFound := s.Db.Preload("jwt").Where("username = ?", strings.ToLower(req.Username)).First(&u).RecordNotFound(); notFound {
		log.Printf("User with service_id %s is not found.", req.Username)
		c.JSON(http.StatusBadRequest, gin.H{"message": notFound, "code": "not_found"})
		return
	}
	lCount, err := s.Redis.Get(req.Username + ":logged_in_devices").Result()
	num, _ := strconv.Atoi(lCount)
	if err != redis.Nil && num > 1 {
		log.Print("The user is logging from a different location")
	}
	res, err := s.Redis.Get(req.Username + ":login_counts").Result()
	if err == redis.Nil {
		s.Redis.Set(req.Username+":login_counts", 0, time.Hour)
	} else if err == nil {
		count, _ := strconv.Atoi(res)
		if count >= 5 {
			s.Redis.HIncrBy(req.Username, "suspicious_behavior", 1)
			ttl, _ := s.Redis.TTL(req.Username + ":login_counts").Result()
			c.JSON(http.StatusBadRequest, gin.H{"message": "Too many wrong login attempts",
				"code": "maximum_login", "ttl_minutes": ttl.Minutes()})
			return
		}
	}
	if err := bcrypt.CompareHashAndPassword([]byte(u.Password), []byte(req.Password)); err != nil {
		log.Printf("there is an error in the password %v", err)
		s.Redis.Incr(req.Username + ":login_counts")
		c.JSON(http.StatusBadRequest, gin.H{"message": "wrong password entered", "code": "wrong_password"})
		return
	}
	s.Redis.Del(req.Username + ":login_counts")
	token, err := s.Auth.GenerateJWT(u.Username)
	if err != nil {
		c.JSON(http.StatusInternalServerError, err)
		return
	}
	err = s.Db.Save(&u).Error
	if err != nil {
		c.JSON(http.StatusInternalServerError, err.Error())
		return
	}
	c.Writer.Header().Set("Authorization", token)
	s.Redis.Incr(req.Username + ":logged_in_devices")
	c.JSON(http.StatusOK, gin.H{"authorization": token, "user": u})
}
func (s *State) RefreshHandler(c *gin.Context) {
	h := c.GetHeader("Authorization")
	if h == "" {
		c.JSON(http.StatusUnauthorized, gin.H{"message": "empty header was sent", "code": "unauthorized"})
		return
	}
	claims, err := s.Auth.VerifyJWT(h)
	if e, ok := err.(*jwt.ValidationError); ok {
		if e.Errors&jwt.ValidationErrorExpired != 0 {
			log.Printf("the username is: %s", claims.Username)
			auth, _ := s.Auth.GenerateJWT(claims.Username)
			c.Writer.Header().Set("Authorization", auth)
			c.JSON(http.StatusOK, gin.H{"authorization": auth})
		} else {
			c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"message": "Malformed token", "code": "jwt_malformed"})
			return
		}
	} else if err == nil {
		log.Printf("the username is: %s", claims.Username)
		auth, _ := s.Auth.GenerateJWT(claims.Username)
		c.Writer.Header().Set("Authorization", auth)
		c.JSON(http.StatusOK, gin.H{"authorization": auth})
	}
}
func (s *State) LogOut(c *gin.Context) {
	h := c.GetHeader("Authorization")
	if h == "" {
		c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"message": "empty header was sent",
			"code": "unauthorized"})
		return
	}
	claims, err := s.Auth.VerifyJWT(h)
	if err != nil {
		c.JSON(http.StatusUnauthorized, gin.H{"message": err.Error(), "code": "malformed_jwt_token"})
		return
	}
	username := claims.Username
	if username != "" {
		s.Redis.Decr(username + ":logged_in_devices")
		c.JSON(http.StatusNoContent, gin.H{"message": "Device Successfully Logged Out"})
		return
	} else {
		c.JSON(http.StatusUnauthorized, gin.H{"message": "Unauthorized", "code": "unauthorized"})
		return
	}
}