package auth import ( "net/http" "strings" "photum-backend/internal/profissionais" "photum-backend/internal/storage" "github.com/gin-gonic/gin" "github.com/google/uuid" ) type Handler struct { service *Service s3Service *storage.S3Service } func NewHandler(service *Service, s3Service *storage.S3Service) *Handler { return &Handler{service: service, s3Service: s3Service} } type uploadURLRequest struct { Filename string `json:"filename" binding:"required"` ContentType string `json:"content_type" binding:"required"` } // GetUploadURL godoc // @Summary Get S3 Presigned URL for upload // @Description Get a pre-signed URL to upload a file directly to S3/Civo // @Tags auth // @Accept json // @Produce json // @Param request body uploadURLRequest true "Upload URL Request" // @Success 200 {object} map[string]string // @Router /auth/upload-url [post] func (h *Handler) GetUploadURL(c *gin.Context) { var req uploadURLRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } uploadURL, publicURL, err := h.s3Service.GeneratePresignedURL(req.Filename, req.ContentType) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{ "upload_url": uploadURL, "public_url": publicURL, }) } type registerRequest struct { Email string `json:"email" binding:"required,email"` Senha string `json:"senha" binding:"required,min=6"` Role string `json:"role" binding:"required"` Nome string `json:"nome" binding:"required"` Telefone string `json:"telefone"` TipoProfissional string `json:"professional_type"` EmpresaID string `json:"empresa_id"` Regiao string `json:"regiao"` CpfCnpj string `json:"cpf_cnpj"` Cep string `json:"cep"` Endereco string `json:"endereco"` Numero string `json:"numero"` Complemento string `json:"complemento"` Bairro string `json:"bairro"` Cidade string `json:"cidade"` Estado string `json:"estado"` } // Register godoc // @Summary Register a new user // @Description Register a new user with email, password, name, phone, role and professional type // @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 } // Create professional data only if role is appropriate var profData *profissionais.CreateProfissionalInput if req.Role == "BUSINESS_OWNER" { profData = &profissionais.CreateProfissionalInput{ Nome: req.Nome, Whatsapp: &req.Telefone, } } var empresaIDPtr *string if req.EmpresaID != "" { empresaIDPtr = &req.EmpresaID } regiao := req.Regiao if regiao == "" { regiao = req.Estado } if regiao == "" { regiao = c.GetString("regiao") } if regiao == "" { regiao = c.GetHeader("x-regiao") } // Default to SP if still empty if regiao == "" { regiao = "SP" } user, err := h.service.Register(c.Request.Context(), req.Email, req.Senha, req.Role, req.Nome, req.Telefone, req.TipoProfissional, empresaIDPtr, profData, regiao, req.CpfCnpj, req.Cep, req.Endereco, req.Numero, req.Complemento, req.Bairro, req.Cidade, req.Estado) 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 } // Auto-login after registration tokenPair, _, _, err := h.service.Login(c.Request.Context(), req.Email, req.Senha) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "user created but failed to auto-login"}) 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, }) // Calculate MaxAge for access token cookie based on role accessMaxAge := 180 * 60 // Default 3 hours if req.Role == "RESEARCHER" { accessMaxAge = 30 * 24 * 60 * 60 // 30 days } // Set access_token cookie for fallback http.SetCookie(c.Writer, &http.Cookie{ Name: "access_token", Value: tokenPair.AccessToken, Path: "/", HttpOnly: true, Secure: false, SameSite: http.SameSiteStrictMode, MaxAge: accessMaxAge, }) c.JSON(http.StatusCreated, gin.H{ "message": "user created", "access_token": tokenPair.AccessToken, "user": gin.H{ "id": uuid.UUID(user.ID.Bytes).String(), "email": user.Email, "role": user.Role, "ativo": user.Ativo, }, }) } type loginRequest struct { Email string `json:"email" binding:"required,email" example:"admin@photum.com"` Senha string `json:"senha" binding:"required,min=6" example:"123456"` } 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"` Name string `json:"name,omitempty"` Phone string `json:"phone,omitempty"` CompanyID string `json:"company_id,omitempty"` CompanyName string `json:"company_name,omitempty"` AllowedRegions []string `json:"allowed_regions"` CpfCnpj string `json:"cpf_cnpj,omitempty"` Cep string `json:"cep,omitempty"` Endereco string `json:"endereco,omitempty"` Numero string `json:"numero,omitempty"` Complemento string `json:"complemento,omitempty"` Bairro string `json:"bairro,omitempty"` Cidade string `json:"cidade,omitempty"` Estado string `json:"estado,omitempty"` } // 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, }) // Calculate MaxAge for access token cookie based on role accessMaxAge := 180 * 60 // Default 3 hours if user.Role == "RESEARCHER" { accessMaxAge = 30 * 24 * 60 * 60 // 30 days } // Set access_token cookie for fallback http.SetCookie(c.Writer, &http.Cookie{ Name: "access_token", Value: tokenPair.AccessToken, Path: "/", HttpOnly: true, Secure: false, SameSite: http.SameSiteStrictMode, MaxAge: accessMaxAge, }) // Handle Nullable Fields var companyID, companyName string if user.EmpresaID.Valid { companyID = uuid.UUID(user.EmpresaID.Bytes).String() } if user.EmpresaNome.Valid { companyName = user.EmpresaNome.String } // Prepare response uResp := userResponse{ ID: uuid.UUID(user.ID.Bytes).String(), Email: user.Email, Role: user.Role, Ativo: user.Ativo, // Added this back from original Name: user.Nome, CompanyID: companyID, CompanyName: companyName, AllowedRegions: user.RegioesPermitidas, Phone: "", } // Helper to get phone from profData or Client Data if user.Role == "PHOTOGRAPHER" && profData != nil { if profData.Whatsapp.Valid { uResp.Phone = profData.Whatsapp.String } } else if user.Role == "EVENT_OWNER" { // Fetch Client Data clientData, err := h.service.GetClientData(c.Request.Context(), uResp.ID) if err == nil && clientData != nil { if clientData.Telefone.Valid { uResp.Phone = clientData.Telefone.String } if clientData.CpfCnpj.Valid { uResp.CpfCnpj = clientData.CpfCnpj.String } if clientData.Cep.Valid { uResp.Cep = clientData.Cep.String } if clientData.Endereco.Valid { uResp.Endereco = clientData.Endereco.String } if clientData.Numero.Valid { uResp.Numero = clientData.Numero.String } if clientData.Complemento.Valid { uResp.Complemento = clientData.Complemento.String } if clientData.Bairro.Valid { uResp.Bairro = clientData.Bairro.String } if clientData.Cidade.Valid { uResp.Cidade = clientData.Cidade.String } if clientData.Estado.Valid { uResp.Estado = clientData.Estado.String } // Use client name if available ?? Or user name is fine. // Usually user.Name comes from `usuarios` table, but `cadastro_clientes` also has nome. // Let's stick to user.Name for consistency, usually they should be same. } } resp := loginResponse{ AccessToken: tokenPair.AccessToken, ExpiresAt: "2025-...", User: uResp, } 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": "", // Deprecated/Removed from query "functions": profData.Functions, "equipamentos": profData.Equipamentos.String, "avatar_url": profData.AvatarUrl.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 } // Set new access_token cookie http.SetCookie(c.Writer, &http.Cookie{ Name: "access_token", Value: accessToken, Path: "/", HttpOnly: true, Secure: false, SameSite: http.SameSiteStrictMode, MaxAge: 180 * 60, // 3 hours }) 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"}) } // Me godoc // @Summary Get current user // @Description Get current authenticated user // @Tags auth // @Accept json // @Produce json // @Success 200 {object} loginResponse // @Router /api/me [get] func (h *Handler) Me(c *gin.Context) { // User ID comes from context (AuthMiddleware) userID, exists := c.Get("userID") if !exists { c.JSON(http.StatusUnauthorized, gin.H{"error": "unauthorized"}) return } // We can fetch fresh user data user, err := h.service.GetUser(c.Request.Context(), userID.(string)) if err != nil { c.JSON(http.StatusUnauthorized, gin.H{"error": "user not found"}) return } var empresaNome string if user.EmpresaNome.Valid { empresaNome = user.EmpresaNome.String } var empresaID string if user.EmpresaID.Valid { empresaID = uuid.UUID(user.EmpresaID.Bytes).String() } // Ensure valid slice for JSON response (avoid null) allowedRegions := make([]string, 0) if user.RegioesPermitidas != nil { allowedRegions = append(allowedRegions, user.RegioesPermitidas...) } uResp := userResponse{ ID: uuid.UUID(user.ID.Bytes).String(), Email: user.Email, Role: user.Role, Ativo: user.Ativo, Name: user.Nome, Phone: user.Whatsapp, // Default to user.Whatsapp CompanyName: empresaNome, CompanyID: empresaID, AllowedRegions: allowedRegions, } if user.Role == "EVENT_OWNER" { clientData, err := h.service.GetClientData(c.Request.Context(), uResp.ID) if err == nil && clientData != nil { if clientData.Telefone.Valid { uResp.Phone = clientData.Telefone.String } if clientData.CpfCnpj.Valid { uResp.CpfCnpj = clientData.CpfCnpj.String } if clientData.Cep.Valid { uResp.Cep = clientData.Cep.String } if clientData.Endereco.Valid { uResp.Endereco = clientData.Endereco.String } if clientData.Numero.Valid { uResp.Numero = clientData.Numero.String } if clientData.Complemento.Valid { uResp.Complemento = clientData.Complemento.String } if clientData.Bairro.Valid { uResp.Bairro = clientData.Bairro.String } if clientData.Cidade.Valid { uResp.Cidade = clientData.Cidade.String } if clientData.Estado.Valid { uResp.Estado = clientData.Estado.String } } } resp := loginResponse{ User: uResp, } if user.Role == "PHOTOGRAPHER" || user.Role == "BUSINESS_OWNER" { regiao := c.GetString("regiao") if regiao == "" { regiao = c.GetHeader("x-regiao") } profData, err := h.service.GetProfessionalByUserID(c.Request.Context(), uuid.UUID(user.ID.Bytes).String()) if err == nil && profData != nil { // Update phone from professional data if valid if profData.Whatsapp.Valid { resp.User.Phone = profData.Whatsapp.String } 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": "", // Deprecated "functions": profData.Functions, "equipamentos": profData.Equipamentos.String, "avatar_url": profData.AvatarUrl.String, } } } c.JSON(http.StatusOK, resp) } type updateMeRequest struct { Nome string `json:"name"` Telefone string `json:"phone"` CpfCnpj string `json:"cpf_cnpj"` Cep string `json:"cep"` Endereco string `json:"endereco"` Numero string `json:"numero"` Complemento string `json:"complemento"` Bairro string `json:"bairro"` Cidade string `json:"cidade"` Estado string `json:"estado"` } // UpdateMe godoc // @Summary Update current user profile // @Description Update profile information (Name, Phone, Address). Currently for EVENT_OWNER. // @Tags auth // @Accept json // @Produce json // @Success 200 {object} map[string]string // @Failure 400 {object} map[string]string // @Failure 500 {object} map[string]string // @Router /api/me [put] func (h *Handler) UpdateMe(c *gin.Context) { userID, exists := c.Get("userID") if !exists { c.JSON(http.StatusUnauthorized, gin.H{"error": "unauthorized"}) return } var req updateMeRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "invalid payload: " + err.Error()}) return } // For now, we assume this is mostly for Clients (EVENT_OWNER) // based on the fields provided (address, cpf, etc). // Future: Check role and call appropriate service if needed. // But UpdateClientData uses "UpdateCadastroCliente" which is linked to user_id. // If the user does not have a client record, it might fail or do nothing if query uses WHERE exists? // The query uses UPDATE, so if no row, no update. // We should probably check role or just try to update. // But to be safe, let's just call UpdateClientData. // If the user is a Photographer, they should use the /profissionais/me PUT (if exists) or similar. // But wait, the user complaint is about Clients. err := h.service.UpdateClientData(c.Request.Context(), userID.(string), UpdateClientInput{ Nome: req.Nome, Telefone: req.Telefone, CpfCnpj: req.CpfCnpj, Cep: req.Cep, Endereco: req.Endereco, Numero: req.Numero, Complemento: req.Complemento, Bairro: req.Bairro, Cidade: req.Cidade, Estado: req.Estado, }) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to update profile: " + err.Error()}) return } c.JSON(http.StatusOK, gin.H{"message": "profile updated successfully"}) } // ListPending godoc // @Summary List pending users // @Description List users with ativo=false // @Tags admin // @Accept json // @Produce json // @Success 200 {array} map[string]interface{} // @Failure 500 {object} map[string]string // @Security BearerAuth // @Router /api/admin/users/pending [get] func (h *Handler) ListPending(c *gin.Context) { regiao := c.GetString("regiao") if regiao == "" { regiao = c.GetHeader("x-regiao") } users, err := h.service.ListPendingUsers(c.Request.Context(), regiao) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } // Map to response // The generated type ListUsuariosPendingRow fields are: // ID pgtype.UUID // Email string // Role string // Ativo bool // CriadoEm pgtype.Timestamptz // Nome pgtype.Text // Whatsapp pgtype.Text resp := make([]map[string]interface{}, len(users)) for i, u := range users { nome := u.Nome whatsapp := u.Whatsapp var empresaNome string if u.EmpresaNome.Valid { empresaNome = u.EmpresaNome.String } resp[i] = map[string]interface{}{ "id": uuid.UUID(u.ID.Bytes).String(), "email": u.Email, "role": u.Role, "ativo": u.Ativo, "created_at": u.CriadoEm.Time, "name": nome, // Mapped to name for frontend compatibility "phone": whatsapp, "company_name": empresaNome, "professional_type": u.TipoProfissional.String, // Add this } } c.JSON(http.StatusOK, resp) } // Approve godoc // @Summary Approve user // @Description Set user ativo=true // @Tags admin // @Accept json // @Produce json // @Param id path string true "User ID" // @Success 200 {object} map[string]string // @Failure 400 {object} map[string]string // @Failure 500 {object} map[string]string // @Security BearerAuth // @Router /api/admin/users/{id}/approve [patch] func (h *Handler) Approve(c *gin.Context) { id := c.Param("id") if id == "" { c.JSON(http.StatusBadRequest, gin.H{"error": "id required"}) return } err := h.service.ApproveUser(c.Request.Context(), id) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"message": "user approved"}) } // AdminCreateUser godoc // @Summary Create user (Admin) // @Description Create a new user with specific role (Admin only) // @Tags admin // @Accept json // @Produce json // @Param request body registerRequest true "Create User Request" // @Success 201 {object} map[string]string // @Failure 400 {object} map[string]string // @Failure 500 {object} map[string]string // @Security BearerAuth // @Router /api/admin/users [post] func (h *Handler) AdminCreateUser(c *gin.Context) { var req registerRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } // Just reuse the request struct but call AdminCreateUser service regiao := c.GetString("regiao") // If Admin explicitly sends regiao in body, use it (override context) if req.Regiao != "" { regiao = req.Regiao } if regiao == "" { // Fallback or Error? Admin creation usually implies target region. // Let's assume header is present or default. regiao = "SP" // Default for now if missing? Or error? } user, err := h.service.AdminCreateUser(c.Request.Context(), req.Email, req.Senha, req.Role, req.Nome, req.TipoProfissional, true, regiao, req.EmpresaID, req.Telefone, req.CpfCnpj, req.Cep, req.Endereco, req.Numero, req.Complemento, req.Bairro, req.Cidade, req.Estado) 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", "id": uuid.UUID(user.ID.Bytes).String(), "email": user.Email, }) } type updateRoleRequest struct { Role string `json:"role" binding:"required"` } // UpdateRole godoc // @Summary Update user role // @Description Update user role (Admin only) // @Tags admin // @Accept json // @Produce json // @Param id path string true "User ID" // @Param request body updateRoleRequest true "Update Role Request" // @Success 200 {object} map[string]string // @Failure 400 {object} map[string]string // @Failure 500 {object} map[string]string // @Security BearerAuth // @Router /api/admin/users/{id}/role [patch] func (h *Handler) UpdateRole(c *gin.Context) { id := c.Param("id") if id == "" { c.JSON(http.StatusBadRequest, gin.H{"error": "id required"}) return } var req updateRoleRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } err := h.service.UpdateUserRole(c.Request.Context(), id, req.Role) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"message": "role updated"}) } // DeleteUser godoc // @Summary Delete user // @Description Delete user (Admin only) // @Tags admin // @Accept json // @Produce json // @Param id path string true "User ID" // @Success 200 {object} map[string]string // @Failure 400 {object} map[string]string // @Failure 500 {object} map[string]string // @Security BearerAuth // @Router /api/admin/users/{id} [delete] func (h *Handler) DeleteUser(c *gin.Context) { id := c.Param("id") if id == "" { c.JSON(http.StatusBadRequest, gin.H{"error": "id required"}) return } err := h.service.DeleteUser(c.Request.Context(), id) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"message": "user deleted"}) } // ListUsers godoc // @Summary List all users // @Description List all users (Admin only) // @Tags admin // @Accept json // @Produce json // @Success 200 {array} map[string]interface{} // @Failure 500 {object} map[string]string // @Security BearerAuth // @Router /api/admin/users [get] func (h *Handler) ListUsers(c *gin.Context) { regiao := c.GetString("regiao") if regiao == "" { regiao = c.GetHeader("x-regiao") } users, err := h.service.ListUsers(c.Request.Context(), regiao) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } resp := make([]map[string]interface{}, len(users)) for i, u := range users { empresaId := "" if u.EmpresaID.Valid { empresaId = uuid.UUID(u.EmpresaID.Bytes).String() } resp[i] = map[string]interface{}{ "id": uuid.UUID(u.ID.Bytes).String(), "email": u.Email, "role": u.Role, "ativo": u.Ativo, "created_at": u.CriadoEm.Time, "name": u.Nome, "phone": u.Whatsapp, "company_name": u.EmpresaNome.String, "company_id": empresaId, "professional_type": u.TipoProfissional.String, } } c.JSON(http.StatusOK, resp) } // GetUser godoc // @Summary Get user by ID // @Description Get user details by ID (Admin only) // @Tags admin // @Accept json // @Produce json // @Param id path string true "User ID" // @Success 200 {object} map[string]interface{} // @Failure 400 {object} map[string]string // @Failure 404 {object} map[string]string // @Failure 500 {object} map[string]string // @Security BearerAuth // @Router /api/admin/users/{id} [get] func (h *Handler) GetUser(c *gin.Context) { id := c.Param("id") if id == "" { c.JSON(http.StatusBadRequest, gin.H{"error": "id required"}) return } user, err := h.service.GetUser(c.Request.Context(), id) if err != nil { if strings.Contains(err.Error(), "no rows") { c.JSON(http.StatusNotFound, gin.H{"error": "user not found"}) return } c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } var empresaNome string if user.EmpresaNome.Valid { empresaNome = user.EmpresaNome.String } var empresaID string if user.EmpresaID.Valid { empresaID = uuid.UUID(user.EmpresaID.Bytes).String() } resp := loginResponse{ User: userResponse{ ID: uuid.UUID(user.ID.Bytes).String(), Email: user.Email, Role: user.Role, Ativo: user.Ativo, Name: user.Nome, Phone: user.Whatsapp, CompanyName: empresaNome, CompanyID: empresaID, AllowedRegions: user.RegioesPermitidas, }, } c.JSON(http.StatusOK, resp) }