Files
soft_usite/config/config.go
T
Lizandro GuarnizoandClaude Sonnet 5 5ba41786d6 feat: conexiones OAuth (Gmail/Outlook) para el agente + rediseño del orquestador
Backend:
- UmindConexion: cuenta de correo conectada por tenant vía OAuth2
  (golang.org/x/oauth2, promovida de indirecta a directa), tokens cifrados
  en reposo con el mismo AES-GCM+APP_KEY que ya usan tools/canales.
- Flujo completo: /app/umind/conexiones/conectar redirige a Google/Microsoft,
  /callback/:proveedor intercambia el code (state autoverificable por HMAC,
  sin tabla de estados pendientes), refresh on-demand antes de cada uso.
- Dos tools nuevas para el agente (enviar_correo/leer_bandeja) que aparecen
  solo si el tenant tiene una conexión activa, vía Gmail API / Microsoft
  Graph directo (sin el SDK pesado de Google).
- Requiere que el dueño del proyecto cree las apps OAuth en Google Cloud
  Console / Azure y cargue GOOGLE_OAUTH_CLIENT_ID/SECRET y
  MS_OAUTH_CLIENT_ID/SECRET — sin eso los botones de conectar fallan con un
  mensaje claro, no en silencio.

Frontend: rediseño del orquestador — layout de sidebar fijo (reemplaza el
navbar + lista de página completa), modo oscuro vía prefers-color-scheme,
tabs en pill, y la nueva tab "Conexiones".

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-12 10:25:13 -05:00

114 lines
3.5 KiB
Go
Executable File

package config
import (
"fmt"
"os"
"path/filepath"
"time"
"github.com/gofiber/fiber/v2"
"github.com/ilyakaznacheev/cleanenv"
"github.com/joho/godotenv"
"github.com/oarkflow/log"
"github.com/sujit-baniya/flash"
)
// Config is a application configuration structure
type AppConfig struct {
Auth AuthConfig `yaml:"auth"`
Mail Mail `yaml:"mail"`
Hash Hash
View ViewConfig `yaml:"view"`
Cache CacheConfig `yaml:"cache"`
Database DatabaseConfig `yaml:"database"`
Session SessionConfig `yaml:"session"`
Queue QueueConfig `yaml:"queue"`
JwtSecrets JwtSecrets `yaml:"jwt"`
Storage StorageConfig `yaml:"storage"`
Server ServerConfig `yaml:"server"`
Log LogConfig `yaml:"log"`
Token Token `yaml:"token"`
OAuth OAuthConfig `yaml:"oauth"`
Profiler ProfilerConfig `yaml:"profiler"`
Flash *flash.Flash
ConfigFile string
}
func (cfg *AppConfig) Setup() {
err := godotenv.Load()
// read configuration from the file and environment variables
if err = cleanenv.ReadConfig(cfg.ConfigFile, cfg); err != nil {
fmt.Println(err)
os.Exit(2)
}
cfg.VerificarSecretos()
cfg.Server.LoadPath()
cfg.View.Load(cfg.Server.Path)
cfg.Mail.View = &cfg.View
cfg.Server.TemplateEngine = cfg.View.Template.TemplateEngine
cfg.Server.Setup()
cfg.LoadComponents()
if cfg.Auth.Type == "casbin" {
modelFile := filepath.Join(cfg.Server.AssetPath, "rbac_model.conf")
cfg.Auth.Setup(cfg.Database.DB, modelFile)
}
}
func (cfg *AppConfig) PrepareLog() {
writer := &log.MultiWriter{}
path := MakeDir(filepath.Join(cfg.Server.Path, cfg.Log.InfoLevel.Path))
writer.InfoWriter = &log.FileWriter{Filename: filepath.Join(path, "INFO.log"), EnsureFolder: true, TimeFormat: cfg.Log.InfoLevel.TimeFormat}
path = MakeDir(filepath.Join(cfg.Server.Path, cfg.Log.WarnLevel.Path))
writer.WarnWriter = &log.FileWriter{Filename: filepath.Join(cfg.Server.Path, cfg.Log.WarnLevel.Path, "WARN.log"), EnsureFolder: true, TimeFormat: cfg.Log.WarnLevel.TimeFormat}
path = MakeDir(filepath.Join(cfg.Server.Path, cfg.Log.ErrorLevel.Path))
writer.ErrorWriter = &log.FileWriter{Filename: filepath.Join(cfg.Server.Path, cfg.Log.ErrorLevel.Path, "ERROR.log"), EnsureFolder: true, TimeFormat: cfg.Log.ErrorLevel.TimeFormat}
if cfg.Log.ConsoleLog.Show {
writer.ConsoleWriter = &log.IOWriter{Writer: os.Stderr}
writer.ConsoleLevel = log.InfoLevel
}
log.DefaultLogger = log.Logger{
TimeField: cfg.Log.TimeField,
TimeFormat: cfg.Log.TimeFormat,
Writer: writer,
}
}
func (cfg *AppConfig) Route404() {
cfg.Server.Use(func(c *fiber.Ctx) error {
return c.Status(fiber.StatusNotFound).SendString("Page not found")
})
}
func (cfg *AppConfig) LoadComponents() {
cfg.Flash = flash.New(flash.Config{
Name: "fiber",
HTTPOnly: true,
})
cfg.LoadStatic()
cfg.PrepareLog()
_ = cfg.Database.Setup()
_ = cfg.Session.Setup(cfg.Database.Default)
cfg.Cache.Setup()
cfg.Storage.Setup()
}
func (cfg *AppConfig) LoadStatic() {
cfg.Server.Static("/websocket", "./resources/views/websocket.html")
// El guard se registra antes que el estático para que corra primero.
cfg.Server.Use("/uploads", uploadsProtegidos)
cfg.Server.Static("/uploads", "./uploads", fiber.Static{
ByteRange: true,
})
cfg.Server.Static("/", filepath.Join(cfg.Server.Path, cfg.Server.PublicPath), fiber.Static{
Compress: true,
ByteRange: true,
CacheDuration: 24 * time.Hour,
})
}
func (cfg *AppConfig) LoadSpamDetectionEngine() {
}