fix(vcard): decir por qué falla la subida a OSS, y no armar la clave con texto crudo del cliente

El sistema que consume /v2/vcard/qr-url solo recibía "Error subiendo archivo a
OSS": el motivo quedaba en nuestro log y del otro lado no había nada que hacer
con eso. Ahora la respuesta incluye el error real del proveedor —es una API
entre servidores y el que llama ya conoce la config de OSS que mandó— y el log
dice además qué clave se intentó subir.

La clave del objeto se armaba con el nombre tal como venía en el body. Un
nombre con "/" o con caracteres que OSS no acepta producía justamente ese
error genérico. Ahora se limpia.

El mismo nombre crudo se usaba para el archivo temporal (/tmp/<nombre>.webp),
o sea escritura de archivos en una ruta que elegía quien llamaba. Se fue
entero: los tres endpoints suben desde memoria con UploadFromReader, que
además saca el paso a disco y su limpieza.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Lizandro Guarnizo
2026-08-17 20:04:34 -05:00
co-authored by Claude Opus 5
parent 4674413257
commit 17ed57cb56
4 changed files with 83 additions and 55 deletions
+21 -38
View File
@@ -7,7 +7,6 @@ import (
"fmt"
"image/png"
"log"
"os"
"strings"
"github.com/chai2010/webp"
@@ -53,29 +52,24 @@ func CreateQr(c *fiber.Ctx) error {
}
random := helpers.RandomString(4)
fileName := fmt.Sprintf("qrs/qr-%s-%s.webp", strings.ReplaceAll(data.FirstName, " ", "_"), random)
tmpPath := fmt.Sprintf("/tmp/%s.webp", data.FirstName)
fileName := fmt.Sprintf("qrs/qr-%s-%s.webp", claveObjetoSegura(data.FirstName), random)
// 4. Guardar archivo temporal
if err := os.WriteFile(tmpPath, webpBuf.Bytes(), 0644); err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
"error": "No se pudo guardar el QR temporalmente",
})
}
defer os.Remove(tmpPath)
// 5. Subir a OSS
// 4. Subir a OSS directo desde memoria: el archivo temporal solo agregaba
// una forma más de fallar (y el nombre venía del cliente, sin limpiar).
ossProvider, err := getOSSFromBody(c)
if err != nil {
log.Printf("[QR] No se pudo resolver la config de OSS: %v", err)
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
"error": "Error de conexión con OSS",
"error": "Error de conexión con OSS", "detalle": err.Error(),
})
}
if err := ossProvider.UploadFile(fileName, tmpPath); err != nil {
log.Printf("Error subiendo archivo a OSS: %v", err)
if err := ossProvider.UploadFromReader(fileName, "image/webp", bytes.NewReader(webpBuf.Bytes())); err != nil {
log.Printf("Error subiendo archivo a OSS (%s): %v", fileName, err)
// El detalle viaja al que llama: es una API entre servidores y sin esto
// el otro lado solo ve "error subiendo a OSS" y no puede hacer nada.
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
"error": "Error subiendo archivo a OSS",
"error": "Error subiendo archivo a OSS", "detalle": err.Error(),
})
}
@@ -172,7 +166,7 @@ func CreateQrTmp(c *fiber.Ctx) error {
func CreateUrlQr(c *fiber.Ctx) error {
var data struct {
URL string `json:"url"`
URL string `json:"url"`
Nombre string `json:"unico"`
}
if err := json.Unmarshal(c.Body(), &data); err != nil {
@@ -187,14 +181,14 @@ func CreateUrlQr(c *fiber.Ctx) error {
"error": "No se pudo generar el QR",
})
}
img, err := png.Decode(bytes.NewReader(qrBytes))
if err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
"error": "No se pudo procesar la imagen",
})
}
// Codificar a WebP
var webpBuffer bytes.Buffer
if err := webp.Encode(&webpBuffer, img, &webp.Options{Lossless: true}); err != nil {
@@ -202,7 +196,7 @@ func CreateUrlQr(c *fiber.Ctx) error {
"error": "No se pudo convertir a WebP",
})
}
// 3. Convertir a WebP
var webpBuf bytes.Buffer
if err := webp.Encode(&webpBuf, img, nil); err != nil {
@@ -211,40 +205,29 @@ func CreateUrlQr(c *fiber.Ctx) error {
})
}
fileName := fmt.Sprintf("qrs/url/qr-%s.webp", claveObjetoSegura(data.Nombre))
fileName := fmt.Sprintf("qrs/url/qr-%s.webp", strings.ReplaceAll(data.Nombre, " ", "_") )
tmpPath := fmt.Sprintf("/tmp/%s.webp", data.Nombre)
// 4. Guardar archivo temporal
if err := os.WriteFile(tmpPath, webpBuf.Bytes(), 0644); err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
"error": "No se pudo guardar el QR temporalmente",
})
}
defer os.Remove(tmpPath)
// 5. Subir a OSS
// 4. Subir a OSS directo desde memoria (ver el comentario en CreateQr).
ossProvider, err := getOSSFromBody(c)
if err != nil {
log.Printf("[QR URL] No se pudo resolver la config de OSS: %v", err)
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
"error": "Error de conexión con OSS",
"error": "Error de conexión con OSS", "detalle": err.Error(),
})
}
if err := ossProvider.UploadFile(fileName, tmpPath); err != nil {
log.Printf("Error subiendo archivo a OSS: %v", err)
if err := ossProvider.UploadFromReader(fileName, "image/webp", bytes.NewReader(webpBuf.Bytes())); err != nil {
log.Printf("Error subiendo archivo a OSS (%s): %v", fileName, err)
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
"error": "Error subiendo archivo a OSS",
"error": "Error subiendo archivo a OSS", "detalle": err.Error(),
})
}
// 6. URL del archivo
url := ossProvider.PublicURL(fileName)
// 8. Retornar la URL
return c.JSON(fiber.Map{
"url": url,
})
}