package controllers import ( "math" "strconv" "strings" "github.com/gofiber/fiber/v2" "github.com/sujit-baniya/fiber-boilerplate/pkg/models" "github.com/sujit-baniya/fiber-boilerplate/pkg/services" ) // UmindIndex renderiza el panel de administración de uMind. func UmindIndex(c *fiber.Ctx) error { return c.Render("umind", fiber.Map{ "user": c.Locals("user"), "modules": c.Locals("userModules"), }, "layouts/main") } // ─── Tenants ───────────────────────────────────────────────────────────────── func GetUmindTenants(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.GetAllUmindTenants(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, }) } type umindTenantReq struct { Nombre string `json:"nombre"` DominiosPermitidos []string `json:"dominios_permitidos"` AiConfigID *uint `json:"ai_config_id"` Tono string `json:"tono"` MensajeBienvenida string `json:"mensaje_bienvenida"` Activo bool `json:"activo"` } func (r umindTenantReq) dominiosLimpios() []string { var out []string for _, d := range r.DominiosPermitidos { d = strings.ToLower(strings.TrimSpace(d)) if d != "" { out = append(out, d) } } return out } func CreateUmindTenantHandler(c *fiber.Ctx) error { var req umindTenantReq if err := c.BodyParser(&req); err != nil { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "body inválido"}) } if strings.TrimSpace(req.Nombre) == "" { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "nombre es requerido"}) } dominios := req.dominiosLimpios() if len(dominios) == 0 { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "agrega al menos un dominio permitido"}) } tenant := &models.UmindTenant{ Nombre: strings.TrimSpace(req.Nombre), DominiosPermitidos: strings.Join(dominios, ","), AiConfigID: req.AiConfigID, Tono: req.Tono, MensajeBienvenida: req.MensajeBienvenida, Activo: true, CreadoPorID: extraerUserID(c), } if err := models.CreateUmindTenant(tenant); err != nil { return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()}) } return c.Status(fiber.StatusCreated).JSON(fiber.Map{"ok": true, "id": tenant.ID, "site_key": tenant.SiteKey}) } func UpdateUmindTenantHandler(c *fiber.Ctx) error { id, err := strconv.ParseUint(c.Params("id"), 10, 64) if err != nil { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "id inválido"}) } var req umindTenantReq if err := c.BodyParser(&req); err != nil { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "body inválido"}) } dominios := req.dominiosLimpios() updates := map[string]interface{}{ "nombre": strings.TrimSpace(req.Nombre), "dominios_permitidos": strings.Join(dominios, ","), "ai_config_id": req.AiConfigID, "tono": req.Tono, "mensaje_bienvenida": req.MensajeBienvenida, "activo": req.Activo, } if err := models.UpdateUmindTenant(uint(id), updates); err != nil { return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()}) } return c.JSON(fiber.Map{"ok": true}) } func DeleteUmindTenantHandler(c *fiber.Ctx) error { id, err := strconv.ParseUint(c.Params("id"), 10, 64) if err != nil { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "id inválido"}) } if err := models.DeleteUmindTenant(uint(id)); err != nil { return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()}) } return c.JSON(fiber.Map{"ok": true}) } // ─── Documentos / ingesta ──────────────────────────────────────────────────── func GetUmindDocumentosHandler(c *fiber.Ctx) error { tenantID, err := strconv.ParseUint(c.Query("tenant_id"), 10, 64) if err != nil || tenantID == 0 { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "tenant_id requerido"}) } items, err := models.GetUmindDocumentosByTenant(uint(tenantID)) if err != nil { return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()}) } return c.JSON(fiber.Map{"items": items}) } // CreateUmindDocumentoHandler registra una nueva fuente (por ahora solo URL a // crawlear) y dispara la ingesta en segundo plano — puede tardar varios // segundos/minutos según cuántas páginas tenga el sitio. func CreateUmindDocumentoHandler(c *fiber.Ctx) error { var req struct { TenantID uint `json:"tenant_id"` URL string `json:"url"` MaxPaginas int `json:"max_paginas"` } if err := c.BodyParser(&req); err != nil { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "body inválido"}) } if req.TenantID == 0 { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "tenant_id requerido"}) } if strings.TrimSpace(req.URL) == "" { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "url requerida"}) } if _, err := models.GetUmindTenantByID(req.TenantID); err != nil { return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "tenant no encontrado"}) } doc := &models.UmindDocumento{ TenantID: req.TenantID, Tipo: "url", Origen: strings.TrimSpace(req.URL), Estado: "procesando", } if err := models.CreateUmindDocumento(doc); err != nil { return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()}) } go services.IngestarTenant(req.TenantID, doc.ID, doc.Origen, req.MaxPaginas) return c.Status(fiber.StatusAccepted).JSON(fiber.Map{"ok": true, "id": doc.ID, "estado": "procesando"}) } func DeleteUmindDocumentoHandler(c *fiber.Ctx) error { id, err := strconv.ParseUint(c.Params("id"), 10, 64) if err != nil { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "id inválido"}) } if err := models.DeleteUmindDocumento(uint(id)); err != nil { return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()}) } return c.JSON(fiber.Map{"ok": true}) } // ─── Conversaciones ─────────────────────────────────────────────────────────── func GetUmindSesionesHandler(c *fiber.Ctx) error { tenantID, err := strconv.ParseUint(c.Query("tenant_id"), 10, 64) if err != nil || tenantID == 0 { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "tenant_id requerido"}) } items, err := models.GetUmindSesiones(uint(tenantID), 50) if err != nil { return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()}) } return c.JSON(fiber.Map{"items": items}) } func GetUmindHistorialHandler(c *fiber.Ctx) error { tenantID, err := strconv.ParseUint(c.Query("tenant_id"), 10, 64) if err != nil || tenantID == 0 { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "tenant_id requerido"}) } sessionID := c.Query("session_id") if sessionID == "" { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "session_id requerido"}) } items, err := models.GetUmindHistorial(uint(tenantID), sessionID, 200) if err != nil { return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()}) } return c.JSON(fiber.Map{"items": items}) }