package controllers import ( "bytes" "encoding/json" "errors" "fmt" "image/png" "log" "os" "strings" "github.com/chai2010/webp" "github.com/gofiber/fiber/v2" "github.com/sujit-baniya/fiber-boilerplate/app" "github.com/sujit-baniya/fiber-boilerplate/pkg/helpers" "github.com/sujit-baniya/fiber-boilerplate/pkg/models" "github.com/sujit-baniya/fiber-boilerplate/pkg/services" "gorm.io/gorm" // Importa el repositorio ) func CreateQr(c *fiber.Ctx) error { var data services.ContactData if err := json.Unmarshal(c.Body(), &data); err != nil { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ "error": "Datos inválidos", }) } // 1. Generar el QR en formato PNG qrBytes, err := services.GenerateVCardQR(data) if err != nil { return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ "error": "No se pudo generar el QR", }) } // 2. Decodificar PNG img, err := png.Decode(bytes.NewReader(qrBytes)) if err != nil { return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ "error": "Error al decodificar QR PNG", }) } // 3. Convertir a WebP var webpBuf bytes.Buffer if err := webp.Encode(&webpBuf, img, nil); err != nil { return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ "error": "Error al convertir QR a WebP", }) } 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) // 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 ossService, err := services.NewOSSServiceFromDB() if err != nil { return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ "error": "Error de conexión con OSS", }) } if err := ossService.UploadFile(fileName, tmpPath); err != nil { log.Printf("Error subiendo archivo a OSS: %v", err) return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ "error": "Error subiendo archivo a OSS", }) } // 6. URL del archivo url := fmt.Sprintf("https://%s.%s/%s", ossService.BucketName, ossService.Endpoint, fileName) // 7. Buscar si ya existe un registro con ese vcard_id var existing models.QrVcard db := app.Http.Database.DB err = db.Where("vcard_id = ?", data.VcardID).First(&existing).Error if err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { // No existe, crear nuevo newQr := models.QrVcard{ Qr: url, UsuarioID: data.FirstName, Email: data.Email, VcardID: data.VcardID, } if err := models.CreateQrVcard(&newQr); err != nil { return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ "error": "Error al guardar nuevo QR en la base de datos", }) } } else { // Otro error return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ "error": "Error al consultar la base de datos", }) } } else { // Ya existe, eliminar el QR anterior en OSS oldFilePath := strings.Replace(existing.Qr, fmt.Sprintf("https://%s.%s/", ossService.BucketName, ossService.Endpoint), "", 1) if err := ossService.DeleteFile(oldFilePath); err != nil { log.Printf("Error al eliminar QR anterior en OSS: %v", err) // No retornamos error, porque queremos continuar de todas formas } // Actualizar el registro existing.Qr = url existing.UsuarioID = data.FirstName existing.Email = data.Email if err := models.UpdateQrVcard(&existing); err != nil { return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ "error": "Error al actualizar el QR en la base de datos", }) } } // 8. Retornar la URL return c.JSON(fiber.Map{ "url": url, }) } func CreateQrTmp(c *fiber.Ctx) error { var data services.ContactData if err := json.Unmarshal(c.Body(), &data); err != nil { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ "error": "Datos inválidos", }) } qrBytes, err := services.GenerateVCardQR(data) if err != nil { return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ "error": "No se pudo generar el QR", }) } // Decodificar PNG 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 { return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ "error": "No se pudo convertir a WebP", }) } c.Type("webp") return c.Send(webpBuffer.Bytes()) } func CreateUrlQr(c *fiber.Ctx) error { var data struct { URL string `json:"url"` Nombre string `json:"unico"` } if err := json.Unmarshal(c.Body(), &data); err != nil { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ "error": "Datos inválidos", }) } qrBytes, err := services.GenerateURlQR(data.URL) if err != nil { return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ "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 { return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ "error": "No se pudo convertir a WebP", }) } // 3. Convertir a WebP var webpBuf bytes.Buffer if err := webp.Encode(&webpBuf, img, nil); err != nil { return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ "error": "Error al convertir QR a WebP", }) } 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 ossService, err := services.NewOSSServiceFromDB() if err != nil { return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ "error": "Error de conexión con OSS", }) } if err := ossService.UploadFile(fileName, tmpPath); err != nil { log.Printf("Error subiendo archivo a OSS: %v", err) return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ "error": "Error subiendo archivo a OSS", }) } // 6. URL del archivo url := fmt.Sprintf("https://%s.%s/%s", ossService.BucketName, ossService.Endpoint, fileName) // 8. Retornar la URL return c.JSON(fiber.Map{ "url": url, }) }