photum/backend/internal/auth/handler.go

203 lines
5.9 KiB
Go

package auth
import (
"net/http"
"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"`
Role string `json:"role" binding:"required,oneof=profissional empresa"`
ProfissionalData *profissionais.CreateProfissionalInput `json:"profissional_data"`
}
// Register godoc
// @Summary Register a new user
// @Description Register a new user with optional professional profile
// @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
}
if req.Role == "profissional" && req.ProfissionalData == nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "profissional_data is required for role 'profissional'"})
return
}
_, err := h.service.Register(c.Request.Context(), req.Email, req.Senha, req.Role, req.ProfissionalData)
if err != nil {
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"`
}
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"`
}
// 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": err.Error()})
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-...",
User: userResponse{
ID: uuid.UUID(user.ID.Bytes).String(),
Email: user.Email,
Role: user.Role,
},
}
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 and Logout handlers should be kept or restored if they were lost.
// I will assume they are needed and add them back in a subsequent edit if missing,
// or include them here if I can fit them.
// The previous content had them. I'll add them to be safe.
// 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"})
}