package handler import ( "errors" "git.infra.nkode.tech/dkelly/nkode-core/api" "git.infra.nkode.tech/dkelly/nkode-core/config" "git.infra.nkode.tech/dkelly/nkode-core/entities" "git.infra.nkode.tech/dkelly/nkode-core/models" "git.infra.nkode.tech/dkelly/nkode-core/security" "github.com/gin-gonic/gin" "github.com/google/uuid" "log" "strings" ) type NkodeHandler struct { API api.NKodeAPI Logger *log.Logger } const ( malformedCustomerId = "malformed customer id" malformedUserEmail = "malformed user email" malformedSessionId = "malformed session id" invalidKeypadDimensions = "invalid keypad dimensions" ) func (h *NkodeHandler) RegisterRoutes(r *gin.Engine) { r.Group("/v1/nkode") { r.POST("/create-new-customer", h.CreateNewCustomerHandler) r.POST("/generate-signup-reset-interface", h.SignupResetHandler) r.POST("/set-nkode", h.SetNKodeHandler) r.POST("/confirm-nkode", h.ConfirmNKodeHandler) r.POST("/get-login-interface", h.GetLoginInterfaceHandler) r.POST("/login", h.LoginHandler) r.POST("/renew-attributes", h.RenewAttributesHandler) r.POST("/random-svg-interface", h.RandomSvgInterfaceHandler) r.POST("/refresh-token", h.RefreshTokenHandler) r.POST("/reset-nkode", h.ResetNKodeHandler) r.POST("/signout", h.SignoutHandler) } } func (h *NkodeHandler) CreateNewCustomerHandler(c *gin.Context) { var newCustomerPost entities.NKodePolicy if err := c.ShouldBind(&newCustomerPost); err != nil { handleError(c, err) return } customerId, err := h.API.CreateNewCustomer(newCustomerPost) if err != nil { c.String(500, "Internal Server Error") return } h.Logger.Println("create new customer") c.JSON(200, gin.H{"customer_id": customerId}) } func (h *NkodeHandler) SignupResetHandler(c *gin.Context) { h.Logger.Println("generate signup reset interface") var postBody models.SignupRestPostBody if err := c.ShouldBind(&postBody); err != nil { handleError(c, err) return } kp := entities.KeypadDimension{ AttrsPerKey: postBody.AttrsPerKey, NumbOfKeys: postBody.NumbOfKeys, } if err := kp.IsValidKeypadDimension(); err != nil { c.String(400, invalidKeypadDimensions) return } customerId, err := uuid.Parse(postBody.CustomerId) if err != nil { c.String(400, malformedCustomerId) return } userEmail, err := entities.ParseEmail(postBody.UserEmail) if err != nil { c.String(400, malformedUserEmail) return } resp, err := h.API.GenerateSignupResetInterface(userEmail, entities.CustomerId(customerId), kp, postBody.Reset) if err != nil { handleError(c, err) return } c.JSON(200, resp) } func (h *NkodeHandler) SetNKodeHandler(c *gin.Context) { h.Logger.Println("set nkode") var postBody models.SetNKodePost if err := c.ShouldBind(&postBody); err != nil { handleError(c, err) return } customerId, err := uuid.Parse(postBody.CustomerId) if err != nil { c.String(400, malformedCustomerId) return } sessionId, err := uuid.Parse(postBody.SessionId) if err != nil { c.String(400, malformedSessionId) return } confirmInterface, err := h.API.SetNKode(entities.CustomerId(customerId), entities.SessionId(sessionId), postBody.KeySelection) if err != nil { handleError(c, err) return } resp := models.SetNKodeResp{UserInterface: confirmInterface} c.JSON(200, resp) } func (h *NkodeHandler) ConfirmNKodeHandler(c *gin.Context) { h.Logger.Println("confirm nkode") var postBody models.ConfirmNKodePost if err := c.ShouldBind(&postBody); err != nil { handleError(c, err) return } customerId, err := uuid.Parse(postBody.CustomerId) if err != nil { c.String(400, malformedCustomerId) return } sessionId, err := uuid.Parse(postBody.SessionId) if err != nil { c.String(400, malformedSessionId) return } if err := h.API.ConfirmNKode(entities.CustomerId(customerId), entities.SessionId(sessionId), postBody.KeySelection); err != nil { handleError(c, err) return } c.Status(200) } func (h *NkodeHandler) GetLoginInterfaceHandler(c *gin.Context) { h.Logger.Println("get login interface") var loginInterfacePost models.LoginInterfacePost if err := c.ShouldBind(&loginInterfacePost); err != nil { handleError(c, err) return } customerId, err := uuid.Parse(loginInterfacePost.CustomerId) if err != nil { c.String(400, malformedCustomerId) return } userEmail, err := entities.ParseEmail(loginInterfacePost.UserEmail) if err != nil { c.String(400, malformedUserEmail) return } postBody, err := h.API.GetLoginInterface(userEmail, entities.CustomerId(customerId)) if err != nil { handleError(c, err) return } c.JSON(200, postBody) } func (h *NkodeHandler) LoginHandler(c *gin.Context) { h.Logger.Println("login") var loginPost models.LoginPost if err := c.ShouldBind(&loginPost); err != nil { handleError(c, err) return } customerId, err := uuid.Parse(loginPost.CustomerId) if err != nil { c.String(400, malformedCustomerId) return } userEmail, err := entities.ParseEmail(loginPost.UserEmail) if err != nil { c.String(400, malformedUserEmail) return } jwtToken, err := h.API.Login(entities.CustomerId(customerId), userEmail, loginPost.KeySelection) if err != nil { handleError(c, err) return } c.JSON(200, jwtToken) } func (h *NkodeHandler) RenewAttributesHandler(c *gin.Context) { h.Logger.Println("renew attributes") var renewAttributesPost models.RenewAttributesPost if err := c.ShouldBind(&renewAttributesPost); err != nil { handleError(c, err) return } customerId, err := uuid.Parse(renewAttributesPost.CustomerId) if err != nil { c.String(400, malformedCustomerId) return } if err := h.API.RenewAttributes(entities.CustomerId(customerId)); err != nil { handleError(c, err) return } c.Status(200) } func (h *NkodeHandler) RandomSvgInterfaceHandler(c *gin.Context) { h.Logger.Println("random svg interface") svgs, err := h.API.RandomSvgInterface() if err != nil { handleError(c, err) return } resp := models.RandomSvgInterfaceResp{Svgs: svgs} c.JSON(200, resp) } func (h *NkodeHandler) RefreshTokenHandler(c *gin.Context) { h.Logger.Println("refresh token") refreshToken, err := getBearerToken(c) if err != nil { c.String(403, "forbidden") return } refreshClaims, err := security.ParseRegisteredClaimToken(refreshToken) customerId, err := uuid.Parse(refreshClaims.Issuer) if err != nil { c.String(400, malformedCustomerId) return } userEmail, err := entities.ParseEmail(refreshClaims.Subject) if err != nil { c.String(400, malformedUserEmail) return } accessToken, err := h.API.RefreshToken(userEmail, entities.CustomerId(customerId), refreshToken) if err != nil { handleError(c, err) return } c.JSON(200, gin.H{"access_token": accessToken}) } func (h *NkodeHandler) ResetNKodeHandler(c *gin.Context) { h.Logger.Println("reset nkode") var resetNKodePost models.ResetNKodePost if err := c.ShouldBind(&resetNKodePost); err != nil { handleError(c, err) return } customerId, err := uuid.Parse(resetNKodePost.CustomerId) if err != nil { c.String(400, malformedCustomerId) return } userEmail, err := entities.ParseEmail(resetNKodePost.UserEmail) if err != nil { c.String(400, malformedUserEmail) return } if err := h.API.ResetNKode(userEmail, entities.CustomerId(customerId)); err != nil { handleError(c, err) return } c.Status(200) } func (h *NkodeHandler) SignoutHandler(c *gin.Context) { h.Logger.Println("signout") token, err := getBearerToken(c) if err != nil { c.String(403, "forbidden") return } accessClaims, err := security.ParseRegisteredClaimToken(token) if err != nil { handleError(c, err) return } customerId, err := uuid.Parse(accessClaims.Issuer) if err != nil { c.String(400, malformedCustomerId) return } userEmail, err := entities.ParseEmail(accessClaims.Subject) if err != nil { c.String(400, malformedUserEmail) return } if err = h.API.Signout(userEmail, entities.CustomerId(customerId)); err != nil { handleError(c, err) return } c.Status(200) } func handleError(c *gin.Context, err error) { log.Print("handling error: ", err) statusCode, _ := config.HttpErrMap[err] switch statusCode { case 400: c.String(400, err.Error()) case 403: c.String(403, err.Error()) case 500: c.String(500, "Internal Server Error") default: log.Print("unknown error: ", err) c.String(500, "Internal Server Error") } } func getBearerToken(c *gin.Context) (string, error) { authHeader := c.GetHeader("Authorization") // Check if the Authorization header is present and starts with "Bearer " if authHeader == "" || !strings.HasPrefix(authHeader, "Bearer ") { return "", errors.New("forbidden") } token := strings.TrimPrefix(authHeader, "Bearer ") return token, nil }