first commit
All checks were successful
CI / backend (push) Successful in 13m19s
CI / frontend (push) Successful in 11m3s
CI / docker (push) Successful in 1m58s

This commit is contained in:
Cauê Faleiros
2026-06-03 16:31:42 -03:00
commit 8c7e5fbbe4
92 changed files with 18226 additions and 0 deletions

View File

@@ -0,0 +1,118 @@
package users
import (
"net/http"
"strings"
"time"
"github.com/gin-gonic/gin"
)
type Routes struct {
store Store
appPublicURL string
}
func NewRoutes(store Store, appPublicURL string) Routes {
return Routes{
store: store,
appPublicURL: strings.TrimRight(appPublicURL, "/"),
}
}
func (r Routes) Register(router *gin.RouterGroup, requireAuth gin.HandlerFunc) {
router.Use(requireAuth)
router.GET("", r.list)
router.GET("/invitations", r.listInvitations)
router.POST("/invitations", r.createInvitation)
}
func (r Routes) list(c *gin.Context) {
if c.GetString("auth_role") != RoleSuperAdmin {
c.JSON(http.StatusForbidden, gin.H{"message": "Permissao insuficiente."})
return
}
users, err := r.store.List(c.Request.Context())
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"message": "Nao foi possivel listar usuarios."})
return
}
c.JSON(http.StatusOK, gin.H{"users": users})
}
func (r Routes) listInvitations(c *gin.Context) {
if c.GetString("auth_role") != RoleSuperAdmin {
c.JSON(http.StatusForbidden, gin.H{"message": "Permissao insuficiente."})
return
}
invitations, err := r.store.ListInvitations(c.Request.Context())
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"message": "Nao foi possivel listar convites."})
return
}
c.JSON(http.StatusOK, gin.H{"invitations": invitations})
}
type createInvitationRequest struct {
Email string `json:"email" binding:"required,email"`
Role string `json:"role" binding:"required"`
ClientID string `json:"client_id"`
}
func (r Routes) createInvitation(c *gin.Context) {
if c.GetString("auth_role") != RoleSuperAdmin {
c.JSON(http.StatusForbidden, gin.H{"message": "Permissao insuficiente."})
return
}
var input createInvitationRequest
if err := c.ShouldBindJSON(&input); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"message": "Informe os dados do convite."})
return
}
role := strings.TrimSpace(input.Role)
if role != RoleAgencyUser && role != RoleClientViewer {
c.JSON(http.StatusBadRequest, gin.H{"message": "Perfil invalido para convite."})
return
}
if role == RoleClientViewer && strings.TrimSpace(input.ClientID) == "" {
c.JSON(http.StatusBadRequest, gin.H{"message": "Convites de cliente precisam de um cliente vinculado."})
return
}
token, tokenHash, err := NewInvitationToken()
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"message": "Nao foi possivel gerar o convite."})
return
}
invitation, err := r.store.CreateInvitation(c.Request.Context(), CreateInvitationInput{
Email: strings.TrimSpace(input.Email),
Role: role,
ClientID: strings.TrimSpace(input.ClientID),
TokenHash: tokenHash,
ExpiresAt: time.Now().Add(7 * 24 * time.Hour),
CreatedBy: c.GetString("auth_user_id"),
})
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"message": "Nao foi possivel criar o convite."})
return
}
invitation.InvitationURL = r.invitationURL(token)
c.JSON(http.StatusCreated, gin.H{"invitation": invitation})
}
func (r Routes) invitationURL(token string) string {
if r.appPublicURL == "" {
return "/accept-invitation?token=" + token
}
return r.appPublicURL + "/accept-invitation?token=" + token
}