Acá es donde uMind deja de ser una herramienta interna: el cliente entra a /portal/studio con su sesión de portal y gestiona lo suyo. - UmindScopePortal/UmindScopeStaff es el ÚNICO punto donde se decide el alcance. El del cliente sale de GetClienteIDsForPortalUser, el mismo que ya autoriza el resto del portal. nil = staff sin restricción, slice vacío = no ve nada; una ruta sin scope también cae en "no ve nada" para que olvidarse el middleware falle visible y no abra todo. - Un solo set de handlers montado bajo /app/umind y /portal/umind (RegistrarRutasUmind). Duplicarlos sería duplicar las chances de olvidar un chequeo. - Guarda de acceso en TODOS los handlers, incluidos los sub-recursos que llegan por :id (documento, tool, canal, conexión): hay que cargarlos para saber de quién son, si no un cliente podría borrar el canal de otro adivinando el id. Responden 404, no 403: un 403 confirmaría que el recurso existe. - Cierra un bug preexistente: las lecturas GET /app/umind/* no tenían SoloAdmin ni pasaban por MenuMiddleware, así que cualquier usuario de staff podía leer los tenants de todos los clientes. - Límite de agentes por plan (409 con mensaje claro). Un tenant sin plan no tiene límite: cortarles de golpe sería peor que dejarlos como estaban. - /umind/ai-configs reemplaza con alcance a /app/api/ai-config/select, que devolvía TODAS las configs del sistema. - El SPA deduce por la URL si es staff o cliente (base del router, prefijo de API y URL de login) y oculta lo que es solo de staff. - Test de aislamiento entre clientes: 7 casos, incluido que un scope vacío no se confunda con staff. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
67 lines
3.0 KiB
Go
67 lines
3.0 KiB
Go
package routes
|
|
|
|
import (
|
|
"github.com/gofiber/fiber/v2"
|
|
"github.com/sujit-baniya/fiber-boilerplate/rest/controllers"
|
|
)
|
|
|
|
// RegistrarRutasUmind monta los mismos handlers de uMind bajo dos prefijos:
|
|
// /app/umind para el staff y /portal/umind para el cliente. Una sola
|
|
// implementación, dos historias de autenticación — duplicar los handlers sería
|
|
// duplicar también las chances de que uno se olvide un chequeo de alcance.
|
|
//
|
|
// scope decide QUÉ tenants ve el que llama (UmindScopeStaff/UmindScopePortal).
|
|
// escritura middleware extra para POST/PUT/DELETE (SoloAdmin del lado staff;
|
|
// nil del lado del cliente, que ya está acotado por su scope).
|
|
//
|
|
// El CRUD de tenants NO se registra acá a propósito: crear/borrar tenants y
|
|
// asignarles cliente y plan es del staff, el cliente solo administra lo que
|
|
// hay adentro.
|
|
func RegistrarRutasUmind(g fiber.Router, scope fiber.Handler, escritura fiber.Handler) {
|
|
// Concatena el scope con el middleware de escritura, si lo hay. El scope
|
|
// va SIEMPRE, también en las lecturas.
|
|
w := func(h ...fiber.Handler) []fiber.Handler {
|
|
out := []fiber.Handler{scope}
|
|
if escritura != nil {
|
|
out = append(out, escritura)
|
|
}
|
|
return append(out, h...)
|
|
}
|
|
r := func(h ...fiber.Handler) []fiber.Handler {
|
|
return append([]fiber.Handler{scope}, h...)
|
|
}
|
|
|
|
g.Get("/umind/tenants", r(controllers.GetUmindTenants)...)
|
|
g.Get("/umind/agentes", r(controllers.GetUmindAgentesHandler)...)
|
|
g.Post("/umind/agentes", w(controllers.CreateUmindAgenteHandler)...)
|
|
g.Put("/umind/agentes/:id", w(controllers.UpdateUmindAgenteHandler)...)
|
|
g.Delete("/umind/agentes/:id", w(controllers.DeleteUmindAgenteHandler)...)
|
|
|
|
g.Get("/umind/documentos", r(controllers.GetUmindDocumentosHandler)...)
|
|
g.Post("/umind/documentos", w(controllers.CreateUmindDocumentoHandler)...)
|
|
g.Delete("/umind/documentos/:id", w(controllers.DeleteUmindDocumentoHandler)...)
|
|
|
|
g.Get("/umind/sesiones", r(controllers.GetUmindSesionesHandler)...)
|
|
g.Get("/umind/historial", r(controllers.GetUmindHistorialHandler)...)
|
|
|
|
g.Get("/umind/tools", r(controllers.GetUmindHerramientasHandler)...)
|
|
g.Post("/umind/tools", w(controllers.CreateUmindHerramientaHandler)...)
|
|
g.Put("/umind/tools/:id", w(controllers.UpdateUmindHerramientaHandler)...)
|
|
g.Delete("/umind/tools/:id", w(controllers.DeleteUmindHerramientaHandler)...)
|
|
|
|
g.Get("/umind/canales", r(controllers.GetUmindCanalesHandler)...)
|
|
g.Post("/umind/canales", w(controllers.CreateUmindCanalHandler)...)
|
|
g.Put("/umind/canales/:id", w(controllers.UpdateUmindCanalHandler)...)
|
|
g.Delete("/umind/canales/:id", w(controllers.DeleteUmindCanalHandler)...)
|
|
|
|
g.Post("/umind/chat", r(controllers.UmindChatPruebaHandler)...)
|
|
|
|
g.Get("/umind/conexiones", r(controllers.GetUmindConexionesHandler)...)
|
|
g.Get("/umind/conexiones/conectar", w(controllers.UmindConectarHandler)...)
|
|
g.Delete("/umind/conexiones/:id", w(controllers.DeleteUmindConexionHandler)...)
|
|
|
|
g.Get("/umind/eventos", r(controllers.GetUmindEventosHandler)...)
|
|
g.Get("/umind/uso", r(controllers.GetUmindUsoHandler)...)
|
|
g.Get("/umind/ai-configs", r(controllers.GetUmindAiConfigsHandler)...)
|
|
}
|