package auth import ( "net/http" "strings" "photum-backend/internal/profissionais" "github.com/gin-gonic/gin" "github.com/google/uuid" ) type Handler struct { service *Service } func NewHandler(service *Service) *Handler { return &Handler{service: service} } type registerRequest struct { Email string `json:"email" binding:"required,email"` Senha string `json:"senha" binding:"required,min=6"` Nome string `json:"nome" binding:"required"` Telefone string `json:"telefone"` } // Register godoc // @Summary Register a new user // @Description Register a new user (defaults to 'profissional' role) with email, password, name and phone // @Tags auth // @Accept json // @Produce json // @Param request body registerRequest true "Register Request" // @Success 201 {object} map[string]string // @Failure 400 {object} map[string]string // @Failure 500 {object} map[string]string // @Router /auth/register [post] func (h *Handler) Register(c *gin.Context) { var req registerRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } // Default simplified registration: Role="profissional" role := "profissional" // Create professional data from input profData := &profissionais.CreateProfissionalInput{ Nome: req.Nome, Whatsapp: &req.Telefone, // Map Telefone to Whatsapp // FuncaoProfissionalID is left empty intentionally } _, err := h.service.Register(c.Request.Context(), req.Email, req.Senha, role, profData) if err != nil { if strings.Contains(err.Error(), "duplicate key") { c.JSON(http.StatusConflict, gin.H{"error": "email already registered"}) return } c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusCreated, gin.H{"message": "user created"}) } type loginRequest struct { Email string `json:"email" binding:"required,email"` Senha string `json:"senha" binding:"required,min=6"` } type loginResponse struct { AccessToken string `json:"access_token"` ExpiresAt string `json:"expires_at"` User userResponse `json:"user"` Profissional interface{} `json:"profissional,omitempty"` } type userResponse struct { ID string `json:"id"` Email string `json:"email"` Role string `json:"role"` Ativo bool `json:"ativo"` } // Login godoc // @Summary Login // @Description Login with email and password // @Tags auth // @Accept json // @Produce json // @Param request body loginRequest true "Login Request" // @Success 200 {object} loginResponse // @Failure 401 {object} map[string]string // @Failure 500 {object} map[string]string // @Router /auth/login [post] func (h *Handler) Login(c *gin.Context) { var req loginRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } tokenPair, user, profData, err := h.service.Login(c.Request.Context(), req.Email, req.Senha) if err != nil { c.JSON(http.StatusUnauthorized, gin.H{"error": "invalid credentials"}) return } http.SetCookie(c.Writer, &http.Cookie{ Name: "refresh_token", Value: tokenPair.RefreshToken, Path: "/auth/refresh", HttpOnly: true, Secure: false, SameSite: http.SameSiteStrictMode, MaxAge: 30 * 24 * 60 * 60, }) resp := loginResponse{ AccessToken: tokenPair.AccessToken, ExpiresAt: "2025-...", // logic to calculate if needed, or remove field User: userResponse{ ID: uuid.UUID(user.ID.Bytes).String(), Email: user.Email, Role: user.Role, Ativo: user.Ativo, }, } if profData != nil { resp.Profissional = map[string]interface{}{ "id": uuid.UUID(profData.ID.Bytes).String(), "nome": profData.Nome, "funcao_profissional_id": uuid.UUID(profData.FuncaoProfissionalID.Bytes).String(), "funcao_profissional": profData.FuncaoNome.String, "equipamentos": profData.Equipamentos.String, } } c.JSON(http.StatusOK, resp) } // Refresh godoc // @Summary Refresh access token // @Description Get a new access token using a valid refresh token // @Tags auth // @Accept json // @Produce json // @Param refresh_token body string false "Refresh Token" // @Success 200 {object} map[string]interface{} // @Failure 401 {object} map[string]string // @Router /auth/refresh [post] func (h *Handler) Refresh(c *gin.Context) { refreshToken, err := c.Cookie("refresh_token") if err != nil { var req struct { RefreshToken string `json:"refresh_token"` } if err := c.ShouldBindJSON(&req); err == nil { refreshToken = req.RefreshToken } } if refreshToken == "" { c.JSON(http.StatusUnauthorized, gin.H{"error": "refresh token required"}) return } accessToken, accessExp, err := h.service.Refresh(c.Request.Context(), refreshToken) if err != nil { c.JSON(http.StatusUnauthorized, gin.H{"error": "invalid refresh token"}) return } c.JSON(http.StatusOK, gin.H{ "access_token": accessToken, "expires_at": accessExp, }) } // Logout godoc // @Summary Logout user // @Description Revoke refresh token and clear cookie // @Tags auth // @Accept json // @Produce json // @Param refresh_token body string false "Refresh Token" // @Success 200 {object} map[string]string // @Router /auth/logout [post] func (h *Handler) Logout(c *gin.Context) { refreshToken, err := c.Cookie("refresh_token") if err != nil { var req struct { RefreshToken string `json:"refresh_token"` } if err := c.ShouldBindJSON(&req); err == nil { refreshToken = req.RefreshToken } } if refreshToken != "" { _ = h.service.Logout(c.Request.Context(), refreshToken) } c.SetCookie("refresh_token", "", -1, "/", "", false, true) c.JSON(http.StatusOK, gin.H{"message": "logged out"}) }