package auth import ( "errors" "net/http" "strings" "mira/backend/internal/users" "github.com/gin-gonic/gin" ) type Routes struct { users users.Store tokens TokenService } func NewRoutes(users users.Store, tokens TokenService) Routes { return Routes{ users: users, tokens: tokens, } } func (r Routes) Register(router *gin.RouterGroup) { router.POST("/login", r.login) router.POST("/invitations/accept", r.acceptInvitation) router.POST("/logout", func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{"message": "Sessao encerrada."}) }) router.POST("/refresh", func(c *gin.Context) { c.JSON(http.StatusNotImplemented, gin.H{"message": "Renovacao de sessao ainda nao implementada."}) }) } type loginRequest struct { Email string `json:"email" binding:"required,email"` Password string `json:"password" binding:"required"` } func (r Routes) login(c *gin.Context) { var input loginRequest if err := c.ShouldBindJSON(&input); err != nil { c.JSON(http.StatusBadRequest, gin.H{"message": "Informe e-mail e senha validos."}) return } user, err := r.users.FindByEmail(c.Request.Context(), strings.TrimSpace(input.Email)) if errors.Is(err, users.ErrNotFound) { c.JSON(http.StatusUnauthorized, gin.H{"message": "E-mail ou senha invalidos."}) return } if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"message": "Nao foi possivel fazer login."}) return } if !CheckPassword(input.Password, user.PasswordHash) || user.Status != "active" { c.JSON(http.StatusUnauthorized, gin.H{"message": "E-mail ou senha invalidos."}) return } token, err := r.tokens.IssueAccessToken(user.ID, user.Email, user.Role) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"message": "Nao foi possivel criar a sessao."}) return } c.JSON(http.StatusOK, gin.H{ "access_token": token, "token_type": "Bearer", "user": gin.H{ "id": user.ID, "email": user.Email, "name": user.Name, "role": user.Role, "status": user.Status, }, }) } type acceptInvitationRequest struct { Token string `json:"token" binding:"required"` Name string `json:"name" binding:"required,min=2"` Password string `json:"password" binding:"required,min=12"` } func (r Routes) acceptInvitation(c *gin.Context) { var input acceptInvitationRequest if err := c.ShouldBindJSON(&input); err != nil { c.JSON(http.StatusBadRequest, gin.H{"message": "Informe nome, senha e token validos."}) return } passwordHash, err := HashPassword(input.Password) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"message": "Nao foi possivel proteger a senha."}) return } user, err := r.users.AcceptInvitation( c.Request.Context(), users.HashInvitationToken(strings.TrimSpace(input.Token)), strings.TrimSpace(input.Name), passwordHash, ) if errors.Is(err, users.ErrInvitationNotFound) { c.JSON(http.StatusBadRequest, gin.H{"message": "Convite invalido ou expirado."}) return } if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"message": "Nao foi possivel aceitar o convite."}) return } token, err := r.tokens.IssueAccessToken(user.ID, user.Email, user.Role) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"message": "Nao foi possivel criar a sessao."}) return } c.JSON(http.StatusOK, gin.H{ "access_token": token, "token_type": "Bearer", "user": gin.H{ "id": user.ID, "email": user.Email, "name": user.Name, "role": user.Role, "status": user.Status, }, }) }