diff --git a/pkg/models/websms_config.go b/pkg/models/websms_config.go index d440622..6b2ffbd 100644 --- a/pkg/models/websms_config.go +++ b/pkg/models/websms_config.go @@ -1,6 +1,9 @@ package models import ( + "crypto/rand" + "encoding/hex" + "github.com/sujit-baniya/fiber-boilerplate/app" "gorm.io/gorm" ) @@ -12,6 +15,13 @@ type WebSmsConfig struct { Sender string `json:"sender" gorm:"column:sender;size:30"` Notas string `json:"notas" gorm:"column:notas;type:text"` Activo bool `json:"activo" gorm:"column:activo;default:true"` + SecretKey string `json:"secret_key" gorm:"column:secret_key;size:64;uniqueIndex"` +} + +func generateWebSmsSecret() string { + b := make([]byte, 24) + rand.Read(b) + return "sms_" + hex.EncodeToString(b) } func (WebSmsConfig) TableName() string { return "websms_config" } @@ -30,14 +40,22 @@ func SaveWebSmsConfig(s WebSmsConfig) error { Update("activo", false) s.Activo = true if s.ID > 0 { + // preserve existing secret_key + var existing WebSmsConfig + app.Http.Database.DB.First(&existing, s.ID) + if existing.SecretKey == "" { + existing.SecretKey = generateWebSmsSecret() + } return app.Http.Database.DB.Model(&s).Updates(map[string]interface{}{ - "username": s.Username, - "api_token": s.ApiToken, - "sender": s.Sender, - "notas": s.Notas, - "activo": true, + "username": s.Username, + "api_token": s.ApiToken, + "sender": s.Sender, + "notas": s.Notas, + "activo": true, + "secret_key": existing.SecretKey, }).Error } + s.SecretKey = generateWebSmsSecret() return app.Http.Database.DB.Create(&s).Error } diff --git a/rest/controllers/websms_controller.go b/rest/controllers/websms_controller.go index ab11b88..3d4cd1f 100644 --- a/rest/controllers/websms_controller.go +++ b/rest/controllers/websms_controller.go @@ -1,6 +1,8 @@ package controllers import ( + "strings" + "github.com/gofiber/fiber/v2" "github.com/sujit-baniya/fiber-boilerplate/pkg/models" "github.com/sujit-baniya/fiber-boilerplate/pkg/services" @@ -146,6 +148,57 @@ func WebSmsClickWebhook(c *fiber.Ctx) error { return c.SendStatus(200) } +// ─── API pública ──────────────────────────────────────────────────────────── + +// ApiSendSms envía un SMS. Requiere Authorization: Bearer . +// POST /api/sms/send { "numero": "573001234567", "mensaje": "Hola" } +func ApiSendSms(c *fiber.Ctx) error { + bearer := strings.TrimPrefix(c.Get("Authorization"), "Bearer ") + if bearer == "" { + return c.Status(401).JSON(fiber.Map{"error": "token requerido"}) + } + + cfg, err := models.GetWebSmsConfig() + if err != nil { + return c.Status(503).JSON(fiber.Map{"error": "WebSMS no configurado"}) + } + if cfg.SecretKey == "" || cfg.SecretKey != bearer { + return c.Status(401).JSON(fiber.Map{"error": "token inválido"}) + } + + type body struct { + Numero string `json:"numero"` + Mensaje string `json:"mensaje"` + } + var b body + if err := c.BodyParser(&b); err != nil || b.Numero == "" || b.Mensaje == "" { + return c.Status(400).JSON(fiber.Map{"error": "numero y mensaje son requeridos"}) + } + + resp, err := services.SendWebSms(cfg, b.Numero, b.Mensaje) + errStr := "" + status := "ok" + if err != nil { + errStr = err.Error() + status = "error" + } else if resp != nil { + status = resp.Code + } + + models.CreateWebSmsLog(&models.WebSmsLog{ + Para: b.Numero, + Mensaje: b.Mensaje, + Status: status, + MsgID: func() string { if resp != nil { return resp.ID }; return "" }(), + Error: errStr, + }) + + if err != nil { + return c.Status(500).JSON(fiber.Map{"error": errStr}) + } + return c.JSON(fiber.Map{"ok": true, "id": resp.ID}) +} + func WebSmsIncomingWebhook(c *fiber.Ctx) error { var payload services.WebSmsIncomingPayload if err := c.BodyParser(&payload); err != nil { diff --git a/rest/routes/publicas.go b/rest/routes/publicas.go index 449f50a..945c675 100755 --- a/rest/routes/publicas.go +++ b/rest/routes/publicas.go @@ -37,7 +37,8 @@ func RutasPublicas(web fiber.Router) { // Configurar en el bot: POST https://api.telegram.org/bot{TOKEN}/setWebhook?url={HOST}/webhooks/telegram-portal web.Post("/webhooks/telegram-portal", controllers.TelegramPortalWebhook) - // ─── WebSMS (LabsMobile) — ACK de entrega, clics y mensajes entrantes ── + // ─── WebSMS (LabsMobile) — API de envío + webhooks ────────────────── + web.Post("/api/sms/send", controllers.ApiSendSms) web.Post("/webhooks/websms/delivery", controllers.WebSmsDeliveryWebhook) web.Post("/webhooks/websms/click", controllers.WebSmsClickWebhook) web.Post("/webhooks/websms/incoming", controllers.WebSmsIncomingWebhook)