package auth import ( "strings" "github.com/gofiber/fiber/v2" "github.com/jackc/pgx/v5/pgxpool" "github.com/techxcar/backend/pkg/database" ) func RequireAuth(secret string) fiber.Handler { return func(c *fiber.Ctx) error { authHeader := c.Get("Authorization") if authHeader == "" { return fiber.NewError(401, "autenticação necessária") } parts := strings.SplitN(authHeader, " ", 2) if len(parts) != 2 || parts[0] != "Bearer" { return fiber.NewError(401, "formato de autorização inválido") } claims, err := ValidateToken(parts[1], secret) if err != nil { return fiber.NewError(401, "token inválido ou expirado") } c.Locals("claims", claims) return c.Next() } } func RequireRole(roles ...string) fiber.Handler { return func(c *fiber.Ctx) error { claims, ok := c.Locals("claims").(*Claims) if !ok { return fiber.NewError(401, "autenticação necessária") } for _, role := range roles { if claims.Role == role { return c.Next() } } return fiber.NewError(403, "acesso não autorizado") } } // TenantMiddleware acquires a dedicated pgxpool connection per request, // sets the tenant search_path, stores the connection in c.Locals("conn"), // and releases the connection after the handler chain completes. func TenantMiddleware(db *database.DB) fiber.Handler { return func(c *fiber.Ctx) error { claims, ok := c.Locals("claims").(*Claims) if !ok || claims.TenantID == "" { return c.Next() } conn, err := db.Pool.Acquire(c.Context()) if err != nil { return fiber.NewError(500, "erro interno ao adquirir conexão") } schema := `"tenant_` + strings.ReplaceAll(claims.TenantID, "-", "_") + `"` if _, err := conn.Exec(c.Context(), "SET search_path = "+schema+", public"); err != nil { conn.Release() return fiber.NewError(500, "erro interno ao definir schema") } c.Locals("conn", conn) err = c.Next() conn.Release() return err } } // GetConn returns the tenant-scoped connection stored by TenantMiddleware. func GetConn(c *fiber.Ctx) *pgxpool.Conn { conn, _ := c.Locals("conn").(*pgxpool.Conn) return conn }