SPA nueva en /orchestrator (Vue 3 + Vite, servida por el mismo binario Go bajo /orchestrator para que la cookie de sesión funcione sin tocar CORS), reemplaza al panel Alpine.js como punto de entrada del menú. Backend, todo aditivo sobre el motor de uMind ya existente: - UmindHerramienta: tools custom por tenant que llaman un webhook HTTP, integradas al loop de function-calling existente. Cliente HTTP con guardas SSRF (bloqueo de IPs privadas/loopback/link-local resuelto en el momento de conectar, no antes, para cerrar la ventana de DNS rebinding) que no existían en el proyecto. - UmindCanal: Telegram y WhatsApp Business Cloud API como canales adicionales del mismo agente que ya atiende el widget web, ambos reusando ProcessWidgetMessage. WhatsApp valida X-Hub-Signature-256. Credenciales cifradas en reposo con el mismo AES-GCM+APP_KEY que ya usa el proyecto para la contraseña SMTP (primer uso para secretos de uMind). - Se conecta middlewares.Limit() (rate limiter que existía pero no se usaba en ningún lado) al widget público y a los webhooks nuevos. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
62 lines
1.9 KiB
Go
62 lines
1.9 KiB
Go
package services
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
|
|
"github.com/sujit-baniya/fiber-boilerplate/app"
|
|
"github.com/sujit-baniya/fiber-boilerplate/utils"
|
|
)
|
|
|
|
// CifrarSecretoUmind / DescifrarSecretoUmind protegen en reposo los
|
|
// secretos de terceros de uMind (headers de auth de tools, tokens de
|
|
// Telegram/WhatsApp) — mismo patrón AES-GCM + APP_KEY que ya usa el proyecto
|
|
// para la contraseña SMTP (utils.Encrypt/Decrypt, ver
|
|
// rest/controllers/smtp_config_controller.go), no uno nuevo.
|
|
func CifrarSecretoUmind(valor string) (string, error) {
|
|
if valor == "" {
|
|
return "", nil
|
|
}
|
|
if app.Http.Server.Key == "" {
|
|
return "", fmt.Errorf("APP_KEY no está configurada, no se puede cifrar el secreto")
|
|
}
|
|
return utils.Encrypt(valor, app.Http.Server.Key), nil
|
|
}
|
|
|
|
func DescifrarSecretoUmind(valorCifrado string) (string, error) {
|
|
if valorCifrado == "" {
|
|
return "", nil
|
|
}
|
|
if app.Http.Server.Key == "" {
|
|
return "", fmt.Errorf("APP_KEY no está configurada, no se puede descifrar el secreto")
|
|
}
|
|
return utils.Decrypt(valorCifrado, app.Http.Server.Key), nil
|
|
}
|
|
|
|
// CifrarCredencialesCanal / DescifrarCredencialesCanal empaquetan el mapa de
|
|
// credenciales de un UmindCanal (bot_token de Telegram; access_token,
|
|
// phone_number_id, app_secret y verify_token de WhatsApp) como un único blob
|
|
// cifrado en UmindCanal.CredencialesEnc.
|
|
func CifrarCredencialesCanal(credenciales map[string]string) (string, error) {
|
|
b, err := json.Marshal(credenciales)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return CifrarSecretoUmind(string(b))
|
|
}
|
|
|
|
func DescifrarCredencialesCanal(credencialesEnc string) (map[string]string, error) {
|
|
plano, err := DescifrarSecretoUmind(credencialesEnc)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if plano == "" {
|
|
return map[string]string{}, nil
|
|
}
|
|
var credenciales map[string]string
|
|
if err := json.Unmarshal([]byte(plano), &credenciales); err != nil {
|
|
return nil, fmt.Errorf("credenciales del canal corruptas: %w", err)
|
|
}
|
|
return credenciales, nil
|
|
}
|