package clients import ( "errors" "net/http" "regexp" "strings" "mira/backend/internal/auth" "mira/backend/internal/users" "github.com/gin-gonic/gin" ) type Routes struct { store Store } func NewRoutes(store Store) Routes { return Routes{store: store} } func (r Routes) Register(router *gin.RouterGroup, requireAuth gin.HandlerFunc) { router.Use(requireAuth) router.GET("", r.list) router.POST("", r.create) router.GET("/:clientId", r.get) router.PATCH("/:clientId", r.update) router.DELETE("/:clientId", r.delete) } func (r Routes) list(c *gin.Context) { claims, ok := auth.ClaimsFromContext(c) if !ok { c.JSON(http.StatusUnauthorized, gin.H{"message": "Autenticacao obrigatoria."}) return } var clients []Client var err error if claims.Role == users.RoleSuperAdmin || claims.Role == users.RoleAgencyUser { clients, err = r.store.List(c.Request.Context()) } else { clients, err = r.store.ListForUser(c.Request.Context(), claims.UserID) } if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"message": "Nao foi possivel listar clientes."}) return } c.JSON(http.StatusOK, gin.H{"clients": clients}) } type createClientRequest struct { Name string `json:"name" binding:"required,min=2"` } func (r Routes) create(c *gin.Context) { claims, ok := auth.ClaimsFromContext(c) if !ok || claims.Role != users.RoleSuperAdmin { c.JSON(http.StatusForbidden, gin.H{"message": "Permissao insuficiente."}) return } var input createClientRequest if err := c.ShouldBindJSON(&input); err != nil { c.JSON(http.StatusBadRequest, gin.H{"message": "Informe um nome de cliente valido."}) return } client, err := r.store.Create(c.Request.Context(), input.Name) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"message": "Nao foi possivel criar o cliente."}) return } c.JSON(http.StatusCreated, gin.H{"client": client}) } func (r Routes) get(c *gin.Context) { if !r.canReadClient(c, c.Param("clientId")) { c.JSON(http.StatusForbidden, gin.H{"message": "Permissao insuficiente."}) return } client, err := r.store.FindByID(c.Request.Context(), c.Param("clientId")) if errors.Is(err, ErrNotFound) { c.JSON(http.StatusNotFound, gin.H{"message": "Cliente nao encontrado."}) return } if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"message": "Nao foi possivel carregar o cliente."}) return } c.JSON(http.StatusOK, gin.H{"client": client}) } type updateClientRequest struct { Name string `json:"name"` Status string `json:"status"` WebsiteURL string `json:"website_url"` InstagramURL string `json:"instagram_url"` FacebookURL string `json:"facebook_url"` LinkedinURL string `json:"linkedin_url"` Color string `json:"color"` Notes string `json:"notes"` } func (r Routes) update(c *gin.Context) { claims, ok := auth.ClaimsFromContext(c) if !ok || claims.Role != users.RoleSuperAdmin { c.JSON(http.StatusForbidden, gin.H{"message": "Permissao insuficiente."}) return } var input updateClientRequest if err := c.ShouldBindJSON(&input); err != nil { c.JSON(http.StatusBadRequest, gin.H{"message": "Dados invalidos."}) return } color := strings.TrimSpace(input.Color) if color != "" && !hexColorPattern.MatchString(color) { c.JSON(http.StatusBadRequest, gin.H{"message": "Cor invalida."}) return } client, err := r.store.Update(c.Request.Context(), c.Param("clientId"), input.Name, input.Status, input.WebsiteURL, input.InstagramURL, input.FacebookURL, input.LinkedinURL, color, input.Notes) if errors.Is(err, ErrNotFound) { c.JSON(http.StatusNotFound, gin.H{"message": "Cliente nao encontrado."}) return } if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"message": "Nao foi possivel atualizar o cliente."}) return } c.JSON(http.StatusOK, gin.H{"client": client}) } var hexColorPattern = regexp.MustCompile(`^#[0-9a-fA-F]{6}$`) func (r Routes) archive(c *gin.Context) { claims, ok := auth.ClaimsFromContext(c) if !ok || claims.Role != users.RoleSuperAdmin { c.JSON(http.StatusForbidden, gin.H{"message": "Permissao insuficiente."}) return } client, err := r.store.Archive(c.Request.Context(), c.Param("clientId")) if errors.Is(err, ErrNotFound) { c.JSON(http.StatusNotFound, gin.H{"message": "Cliente nao encontrado."}) return } if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"message": "Nao foi possivel arquivar o cliente."}) return } c.JSON(http.StatusOK, gin.H{"client": client}) } func (r Routes) delete(c *gin.Context) { claims, ok := auth.ClaimsFromContext(c) if !ok || claims.Role != users.RoleSuperAdmin { c.JSON(http.StatusForbidden, gin.H{"message": "Permissao insuficiente."}) return } err := r.store.Delete(c.Request.Context(), c.Param("clientId")) if errors.Is(err, ErrNotFound) { c.JSON(http.StatusNotFound, gin.H{"message": "Cliente nao encontrado."}) return } if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"message": "Nao foi possivel excluir o cliente."}) return } c.JSON(http.StatusOK, gin.H{"message": "Cliente excluido."}) } func (r Routes) canReadClient(c *gin.Context, clientID string) bool { claims, ok := auth.ClaimsFromContext(c) if !ok { return false } if claims.Role == users.RoleSuperAdmin || claims.Role == users.RoleAgencyUser { return true } allowed, err := r.store.UserCanAccess(c.Request.Context(), claims.UserID, clientID) return err == nil && allowed }