301 lines
9.0 KiB
Go
301 lines
9.0 KiB
Go
package controllers
|
|
|
|
import (
|
|
"io"
|
|
"math"
|
|
"net/http"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/gofiber/fiber/v2"
|
|
"github.com/sujit-baniya/fiber-boilerplate/pkg/models"
|
|
)
|
|
|
|
// ─── Panel: lista de configuraciones ─────────────────────────────────────────
|
|
|
|
// SaasApiIndex renderiza la vista del panel de integraciones SaaS.
|
|
func SaasApiIndex(c *fiber.Ctx) error {
|
|
data := fiber.Map{
|
|
"user": c.Locals("user").(map[string]interface{}),
|
|
"modules": c.Locals("userModules"),
|
|
}
|
|
if err := c.Render("saas_api", data, "layouts/main"); err != nil {
|
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// GetSaasApiConfigs devuelve la lista paginada en JSON.
|
|
func GetSaasApiConfigs(c *fiber.Ctx) error {
|
|
page, _ := strconv.Atoi(c.Query("page", "1"))
|
|
if page < 1 {
|
|
page = 1
|
|
}
|
|
limit := 20
|
|
offset := (page - 1) * limit
|
|
|
|
items, total, err := models.GetAllSaasApiConfigs(limit, offset)
|
|
if err != nil {
|
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
|
}
|
|
|
|
saasOpts, _ := models.GetAllSaasProductosSelect()
|
|
|
|
return c.JSON(fiber.Map{
|
|
"items": items,
|
|
"saas": saasOpts,
|
|
"total": total,
|
|
"totalPages": int(math.Ceil(float64(total) / float64(limit))),
|
|
"page": page,
|
|
"limit": limit,
|
|
})
|
|
}
|
|
|
|
// CreateSaasApiConfig crea una nueva configuración de integración.
|
|
func CreateSaasApiConfig(c *fiber.Ctx) error {
|
|
type Req struct {
|
|
SaasID uint `json:"saas_id"`
|
|
Nombre string `json:"nombre"`
|
|
Pasarela string `json:"pasarela"`
|
|
EndpointURL string `json:"endpoint_url"`
|
|
Metodo string `json:"metodo"`
|
|
ApiKeyHeader string `json:"api_key_header"`
|
|
ApiKeyValue string `json:"api_key_value"`
|
|
PayloadTemplate string `json:"payload_template"`
|
|
TimeoutSeg int `json:"timeout_seg"`
|
|
Activo bool `json:"activo"`
|
|
}
|
|
var req Req
|
|
if err := c.BodyParser(&req); err != nil {
|
|
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "body inválido"})
|
|
}
|
|
if req.SaasID == 0 {
|
|
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "saas_id es requerido"})
|
|
}
|
|
if strings.TrimSpace(req.Nombre) == "" {
|
|
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "nombre es requerido"})
|
|
}
|
|
if strings.TrimSpace(req.EndpointURL) == "" {
|
|
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "endpoint_url es requerido"})
|
|
}
|
|
metodo := strings.ToUpper(strings.TrimSpace(req.Metodo))
|
|
if metodo == "" {
|
|
metodo = "POST"
|
|
}
|
|
pasarela := strings.ToLower(strings.TrimSpace(req.Pasarela))
|
|
if pasarela == "" {
|
|
pasarela = "ambas"
|
|
}
|
|
timeout := req.TimeoutSeg
|
|
if timeout <= 0 {
|
|
timeout = 10
|
|
}
|
|
|
|
item := models.SaasApiConfig{
|
|
SaasID: req.SaasID,
|
|
Nombre: req.Nombre,
|
|
Pasarela: pasarela,
|
|
EndpointURL: req.EndpointURL,
|
|
Metodo: metodo,
|
|
ApiKeyHeader: req.ApiKeyHeader,
|
|
ApiKeyValue: req.ApiKeyValue,
|
|
PayloadTemplate: req.PayloadTemplate,
|
|
TimeoutSeg: timeout,
|
|
Activo: req.Activo,
|
|
}
|
|
if err := models.CreateSaasApiConfig(&item); err != nil {
|
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
|
}
|
|
return c.Status(fiber.StatusCreated).JSON(item)
|
|
}
|
|
|
|
// UpdateSaasApiConfig actualiza una configuración existente.
|
|
func UpdateSaasApiConfig(c *fiber.Ctx) error {
|
|
idParam := c.Params("id")
|
|
id64, err := strconv.ParseUint(idParam, 10, 64)
|
|
if err != nil {
|
|
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "id inválido"})
|
|
}
|
|
|
|
item, err := models.GetSaasApiConfigByID(uint(id64))
|
|
if err != nil {
|
|
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "no encontrado"})
|
|
}
|
|
|
|
type Req struct {
|
|
SaasID uint `json:"saas_id"`
|
|
Nombre string `json:"nombre"`
|
|
Pasarela string `json:"pasarela"`
|
|
EndpointURL string `json:"endpoint_url"`
|
|
Metodo string `json:"metodo"`
|
|
ApiKeyHeader string `json:"api_key_header"`
|
|
ApiKeyValue string `json:"api_key_value"`
|
|
PayloadTemplate string `json:"payload_template"`
|
|
TimeoutSeg int `json:"timeout_seg"`
|
|
Activo bool `json:"activo"`
|
|
}
|
|
var req Req
|
|
if err := c.BodyParser(&req); err != nil {
|
|
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "body inválido"})
|
|
}
|
|
|
|
if req.SaasID != 0 {
|
|
item.SaasID = req.SaasID
|
|
}
|
|
if strings.TrimSpace(req.Nombre) != "" {
|
|
item.Nombre = req.Nombre
|
|
}
|
|
pasarela := strings.ToLower(strings.TrimSpace(req.Pasarela))
|
|
if pasarela != "" {
|
|
item.Pasarela = pasarela
|
|
}
|
|
if strings.TrimSpace(req.EndpointURL) != "" {
|
|
item.EndpointURL = req.EndpointURL
|
|
}
|
|
metodo := strings.ToUpper(strings.TrimSpace(req.Metodo))
|
|
if metodo != "" {
|
|
item.Metodo = metodo
|
|
}
|
|
item.ApiKeyHeader = req.ApiKeyHeader
|
|
// Solo actualizar api_key_value si se envió un valor (para no borrar el secreto con "")
|
|
if req.ApiKeyValue != "" {
|
|
item.ApiKeyValue = req.ApiKeyValue
|
|
}
|
|
item.PayloadTemplate = req.PayloadTemplate
|
|
if req.TimeoutSeg > 0 {
|
|
item.TimeoutSeg = req.TimeoutSeg
|
|
}
|
|
item.Activo = req.Activo
|
|
|
|
if err := models.UpdateSaasApiConfig(item); err != nil {
|
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
|
}
|
|
return c.JSON(item)
|
|
}
|
|
|
|
// DeleteSaasApiConfig elimina una configuración.
|
|
func DeleteSaasApiConfig(c *fiber.Ctx) error {
|
|
idParam := c.Params("id")
|
|
id64, err := strconv.ParseUint(idParam, 10, 64)
|
|
if err != nil {
|
|
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "id inválido"})
|
|
}
|
|
if err := models.DeleteSaasApiConfig(uint(id64)); err != nil {
|
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
|
}
|
|
return c.JSON(fiber.Map{"ok": true})
|
|
}
|
|
|
|
// ProbaSaasApiConfig envía una petición de prueba al endpoint configurado.
|
|
// Body JSON opcional: { "payload": "..." } — si no se envía usa el payload_template tal cual.
|
|
func ProbaSaasApiConfig(c *fiber.Ctx) error {
|
|
idParam := c.Params("id")
|
|
id64, err := strconv.ParseUint(idParam, 10, 64)
|
|
if err != nil {
|
|
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "id inválido"})
|
|
}
|
|
cfg, err := models.GetSaasApiConfigByID(uint(id64))
|
|
if err != nil {
|
|
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "no encontrado"})
|
|
}
|
|
|
|
type Req struct {
|
|
Payload string `json:"payload"`
|
|
}
|
|
var req Req
|
|
_ = c.BodyParser(&req)
|
|
|
|
body := strings.TrimSpace(req.Payload)
|
|
if body == "" {
|
|
body = strings.TrimSpace(cfg.PayloadTemplate)
|
|
}
|
|
|
|
timeout := cfg.TimeoutSeg
|
|
if timeout <= 0 {
|
|
timeout = 10
|
|
}
|
|
client := &http.Client{Timeout: time.Duration(timeout) * time.Second}
|
|
|
|
var httpReq *http.Request
|
|
metodo := strings.ToUpper(cfg.Metodo)
|
|
if metodo == "" {
|
|
metodo = "POST"
|
|
}
|
|
|
|
if body != "" && metodo != "GET" {
|
|
httpReq, err = http.NewRequest(metodo, cfg.EndpointURL, strings.NewReader(body))
|
|
if err != nil {
|
|
return c.Status(fiber.StatusUnprocessableEntity).JSON(fiber.Map{"error": "URL inválida: " + err.Error()})
|
|
}
|
|
httpReq.Header.Set("Content-Type", "application/json")
|
|
} else {
|
|
httpReq, err = http.NewRequest(metodo, cfg.EndpointURL, nil)
|
|
if err != nil {
|
|
return c.Status(fiber.StatusUnprocessableEntity).JSON(fiber.Map{"error": "URL inválida: " + err.Error()})
|
|
}
|
|
}
|
|
|
|
if cfg.ApiKeyHeader != "" && cfg.ApiKeyValue != "" {
|
|
httpReq.Header.Set(cfg.ApiKeyHeader, cfg.ApiKeyValue)
|
|
}
|
|
httpReq.Header.Set("User-Agent", "u-site-tester/1.0")
|
|
|
|
start := time.Now()
|
|
resp, err := client.Do(httpReq)
|
|
latency := time.Since(start).Milliseconds()
|
|
|
|
if err != nil {
|
|
return c.JSON(fiber.Map{
|
|
"ok": false,
|
|
"error": err.Error(),
|
|
"latency_ms": latency,
|
|
})
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
respBytes, _ := io.ReadAll(io.LimitReader(resp.Body, 64*1024)) // máx 64 KB
|
|
return c.JSON(fiber.Map{
|
|
"ok": resp.StatusCode >= 200 && resp.StatusCode < 300,
|
|
"http_status": resp.StatusCode,
|
|
"body": string(respBytes),
|
|
"latency_ms": latency,
|
|
})
|
|
}
|
|
|
|
// ─── Panel: logs de despacho ──────────────────────────────────────────────────
|
|
|
|
// SaasDispatchLogIndex renderiza la vista de logs de despacho.
|
|
func SaasDispatchLogIndex(c *fiber.Ctx) error {
|
|
data := fiber.Map{
|
|
"user": c.Locals("user").(map[string]interface{}),
|
|
"modules": c.Locals("userModules"),
|
|
}
|
|
if err := c.Render("saas_dispatch_logs", data, "layouts/main"); err != nil {
|
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// GetSaasDispatchLogs devuelve los logs de despacho paginados.
|
|
func GetSaasDispatchLogs(c *fiber.Ctx) error {
|
|
page, _ := strconv.Atoi(c.Query("page", "1"))
|
|
if page < 1 {
|
|
page = 1
|
|
}
|
|
limit := 30
|
|
offset := (page - 1) * limit
|
|
|
|
items, total, err := models.GetSaasDispatchLogs(limit, offset)
|
|
if err != nil {
|
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
|
}
|
|
return c.JSON(fiber.Map{
|
|
"items": items,
|
|
"total": total,
|
|
"totalPages": int(math.Ceil(float64(total) / float64(limit))),
|
|
"page": page,
|
|
"limit": limit,
|
|
})
|
|
}
|