This commit is contained in:
Lizandro Guarnizo
2026-05-19 13:33:39 -05:00
parent 762ebc2afc
commit b5e6a9285f
4 changed files with 74 additions and 50 deletions
+19 -12
View File
@@ -180,14 +180,14 @@ func PortalGetProyectoData(c *fiber.Ctx) error {
documentos, _ := models.GetDocumentosByProyecto(proy.ID)
return c.JSON(fiber.Map{
"proyecto": proy,
"fases": fases,
"avances": avances,
"entregables": entregables,
"documentos": documentos,
"tickets": tickets,
"facturas": facturas,
"facturasNoLeidas": facturasNoLeidas,
"proyecto": proy,
"fases": fases,
"avances": avances,
"entregables": entregables,
"documentos": documentos,
"tickets": tickets,
"facturas": facturas,
"facturasNoLeidas": facturasNoLeidas,
})
}
@@ -252,7 +252,9 @@ func PortalResponderTicket(c *fiber.Ctx) error {
return c.Status(401).JSON(fiber.Map{"error": "no autenticado"})
}
ticketID, _ := c.ParamsInt("id")
type Req struct{ Contenido string `json:"contenido"` }
type Req struct {
Contenido string `json:"contenido"`
}
var req Req
if err := c.BodyParser(&req); err != nil {
return c.Status(400).JSON(fiber.Map{"error": err.Error()})
@@ -382,7 +384,7 @@ func PortalDownloadDocumento(c *fiber.Ctx) error {
if !hasAccess {
return c.Status(403).JSON(fiber.Map{"error": "Sin acceso"})
}
clean := doc.Archivo
clean := filepath.Clean(doc.Archivo)
if !strings.HasPrefix(clean, "uploads/") {
return c.Status(403).JSON(fiber.Map{"error": "Acceso denegado"})
}
@@ -390,7 +392,13 @@ func PortalDownloadDocumento(c *fiber.Ctx) error {
if nombre == "" {
nombre = doc.Nombre
}
c.Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"`, nombre))
safe := strings.Map(func(r rune) rune {
if r > 127 || r == '"' || r == '\\' || r == '/' || r == '\n' || r == '\r' {
return '_'
}
return r
}, nombre)
c.Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"; filename*=UTF-8''%s`, safe, url.PathEscape(nombre)))
return c.SendFile(clean)
}
@@ -792,4 +800,3 @@ func searchTokenInUpdates(botToken, token string) (int64, bool) {
}
return 0, false
}
+17 -1
View File
@@ -4,6 +4,7 @@ import (
"fmt"
"math"
"mime/multipart"
"net/url"
"os"
"path/filepath"
"strconv"
@@ -491,10 +492,25 @@ func DownloadDocumento(c *fiber.Ctx) error {
if !strings.HasPrefix(clean, "uploads/") {
return c.Status(403).JSON(fiber.Map{"error": "Acceso denegado"})
}
c.Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"`, item.OriginalName))
nombre := item.OriginalName
if nombre == "" {
nombre = item.Nombre
}
c.Set("Content-Disposition", attachmentDisposition(nombre))
return c.SendFile(clean)
}
// attachmentDisposition genera un header Content-Disposition con fallback ASCII y encoding RFC 5987.
func attachmentDisposition(name string) string {
safe := strings.Map(func(r rune) rune {
if r > 127 || r == '"' || r == '\\' || r == '/' || r == '\n' || r == '\r' {
return '_'
}
return r
}, name)
return fmt.Sprintf(`attachment; filename="%s"; filename*=UTF-8''%s`, safe, url.PathEscape(name))
}
// ─── Tickets (admin) ──────────────────────────────────────────────────────────
func GetTickets(c *fiber.Ctx) error {