From 4135850e5b09249d813420858133ddb2e57e7517 Mon Sep 17 00:00:00 2001 From: Lizandro Guarnizo <77708265+lizandrogd@users.noreply.github.com> Date: Mon, 17 Aug 2026 20:44:35 -0500 Subject: [PATCH] =?UTF-8?q?feat(api-v2):=20endpoint=20de=20generaci=C3=B3n?= =?UTF-8?q?=20de=20texto=20para=20la=20integraci=C3=B3n=20de=20VCard?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit El sistema de vCard necesitaba usar la IA configurada en /app/ai-config y el único camino era /api/v1/ia/ollama-generate, que autentica con cookie de sesión: incómodo para una llamada entre servidores y la razón de los 401. Ahora hay POST /api/v2/vcard/ia, bajo el mismo scope y la misma API key (Bearer + IP) que ya usa esa integración para los QR y los VCF. Manda prompt, recibe texto. La clave del proveedor no sale del servidor. La alternativa —devolverle la configuración con la api_key adentro, como hace el Landing Generator— reparte la credencial de OpenAI/Anthropic a cada aplicación que la quiera usar. El módulo va fijo en "ia": si lo eligiera quien llama, una llave con scope vcard podría usar la configuración del agente o la del Landing Generator. Co-Authored-By: Claude Opus 5 --- rest/controllers/api/ia_v2_controller.go | 39 ++++++++++++++++++++++ rest/controllers/hermes_spec_controller.go | 1 + rest/routes/hermes.go | 4 +++ rest/routes/hermes_vcard_test.go | 1 + 4 files changed, 45 insertions(+) create mode 100644 rest/controllers/api/ia_v2_controller.go diff --git a/rest/controllers/api/ia_v2_controller.go b/rest/controllers/api/ia_v2_controller.go new file mode 100644 index 0000000..ee6ec53 --- /dev/null +++ b/rest/controllers/api/ia_v2_controller.go @@ -0,0 +1,39 @@ +package controllers + +import ( + "github.com/gofiber/fiber/v2" + "github.com/sujit-baniya/fiber-boilerplate/pkg/services" +) + +// GenerarTextoIA corre una generación con la configuración de IA que el staff +// eligió en /app/ai-config para el módulo "ia" (el que la pantalla llama +// "IA / vCard"). +// +// La clave del proveedor no sale de acá: la integración manda su prompt con su +// propia API key de /api/v2 y recibe el texto. Es la diferencia con devolverle +// la configuración —incluida la clave de OpenAI o Anthropic— a otra aplicación. +// +// POST /api/v2/vcard/ia {"prompt": "...", "sistema": "..."} +func GenerarTextoIA(c *fiber.Ctx) error { + var body struct { + Prompt string `json:"prompt"` + Sistema string `json:"sistema"` + } + if err := c.BodyParser(&body); err != nil { + return c.Status(400).JSON(fiber.Map{"error": "body inválido"}) + } + if body.Prompt == "" { + return c.Status(400).JSON(fiber.Map{"error": "prompt requerido"}) + } + if body.Sistema == "" { + body.Sistema = "Respondé de forma clara y breve." + } + + // El módulo va fijo: si lo eligiera quien llama, una llave con scope vcard + // podría usar la configuración del agente o la del Landing Generator. + texto, err := services.CompletarTextoIA("ia", body.Sistema, body.Prompt) + if err != nil { + return c.Status(502).JSON(fiber.Map{"error": err.Error()}) + } + return c.JSON(fiber.Map{"texto": texto}) +} diff --git a/rest/controllers/hermes_spec_controller.go b/rest/controllers/hermes_spec_controller.go index 65fa887..766260a 100644 --- a/rest/controllers/hermes_spec_controller.go +++ b/rest/controllers/hermes_spec_controller.go @@ -441,6 +441,7 @@ func AdminApiSpec(c *fiber.Ctx) error { {"method": "POST", "path": "/api/v2/vcard/qr", "desc": "QR de contacto — devuelve imagen image/webp, NO JSON"}, {"method": "POST", "path": "/api/v2/vcard/qr-url", "desc": "QR de una URL → {\"url\": \"...\"}"}, {"method": "POST", "path": "/api/v2/vcard/vcf", "desc": "Generar archivo .vcf → {\"success\": true, \"url\": \"...\"}"}, + {"method": "POST", "path": "/api/v2/vcard/ia", "desc": "Generar texto con la IA configurada en /app/ai-config (módulo 'ia') → {\"texto\": \"...\"}"}, {"method": "POST", "path": "/api/v2/vcard/dlocal/planes", "desc": "Crear plan de suscripción"}, {"method": "GET", "path": "/api/v2/vcard/dlocal/planes", "desc": "Listar planes"}, {"method": "GET", "path": "/api/v2/vcard/dlocal/planes/:planID", "desc": "Ver un plan"}, diff --git a/rest/routes/hermes.go b/rest/routes/hermes.go index 9a384ea..b5a1b28 100644 --- a/rest/routes/hermes.go +++ b/rest/routes/hermes.go @@ -394,6 +394,10 @@ func AdminApiRoutes(api fiber.Router) { vcard.Post("/qr-url", apiControllers.CreateUrlQr) vcard.Post("/vcf", apiControllers.CreateVcf) + // Generación de texto con la configuración de IA del panel. La integración + // manda el prompt y recibe el texto: la clave del proveedor no sale de acá. + vcard.Post("/ia", apiControllers.GenerarTextoIA) + // dLocal y Rapyd bajo el mismo scope: son las operaciones de cobro que // acompañan a la VCard, y quien integra una necesita la otra. vcard.Post("/dlocal/planes", apiControllers.CreatePlan) diff --git a/rest/routes/hermes_vcard_test.go b/rest/routes/hermes_vcard_test.go index e813539..945cfcb 100644 --- a/rest/routes/hermes_vcard_test.go +++ b/rest/routes/hermes_vcard_test.go @@ -26,6 +26,7 @@ func TestRutasVcardRegistradas(t *testing.T) { "POST /v2/vcard/qr", "POST /v2/vcard/qr-url", "POST /v2/vcard/vcf", + "POST /v2/vcard/ia", "POST /v2/vcard/dlocal/planes", "GET /v2/vcard/dlocal/planes", "GET /v2/vcard/dlocal/planes/:planID",