Los clientes escriben a soporte@ desde su correo de siempre. Hasta ahora eso solo llegaba si un proveedor (SendGrid/Mailgun) nos hacía POST; si nadie lo configuraba, los correos quedaban sin leer en el buzón. Ahora el cron entra al buzón cada 2 minutos, baja los no leídos, abre ticket (o los engancha al hilo si son respuesta) y los marca como leídos. La lógica de ingesta se movió a services para que webhook e IMAP se comporten igual. La contraseña del buzón se guarda cifrada (AES-GCM con APP_KEY) y no vuelve al navegador. De paso, dos bugs que impedían guardar la configuración: el formulario mandaba id=0 (gorm.Model serializa "ID"), así que cada guardado creaba una fila nueva en vez de editar la que se usa; y Updates con struct ignoraba los booleanos en false, así que desactivar algo no tenía efecto. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
70 lines
3.5 KiB
Go
70 lines
3.5 KiB
Go
package models
|
|
|
|
import (
|
|
"github.com/sujit-baniya/fiber-boilerplate/app"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
type SoporteWebhookConfig struct {
|
|
gorm.Model
|
|
Activo bool `json:"activo" gorm:"column:activo;default:true"`
|
|
Nombre string `json:"nombre" gorm:"column:nombre;size:100"`
|
|
Provider string `json:"provider" gorm:"column:provider;size:50;default:'sendgrid'"` // sendgrid|mailgun|generic
|
|
ApiKey string `json:"api_key" gorm:"column:api_key;size:255"`
|
|
EmailDestino string `json:"email_destino" gorm:"column:email_destino;size:255"` // ej: soporte@u-s.app
|
|
ResponderAuto bool `json:"responder_auto" gorm:"column:responder_auto;default:true"`
|
|
MensajeAuto string `json:"mensaje_auto" gorm:"column:mensaje_auto;type:text"`
|
|
AsignarA *uint `json:"asignar_a" gorm:"column:asignar_a;index"` // auto-asignar tickets a este user
|
|
|
|
// SMTP salida para notificaciones y auto-respuesta
|
|
SmtpHost string `json:"smtp_host" gorm:"column:smtp_host;size:255"`
|
|
SmtpPort int `json:"smtp_port" gorm:"column:smtp_port;default:587"`
|
|
SmtpUsername string `json:"smtp_username" gorm:"column:smtp_username;size:255"`
|
|
SmtpPassword string `json:"smtp_password" gorm:"column:smtp_password;size:255"` // cifrado AES
|
|
SmtpEncryption string `json:"smtp_encryption" gorm:"column:smtp_encryption;size:20;default:'tls'"` // tls|starttls|none
|
|
SmtpFromAddr string `json:"smtp_from_addr" gorm:"column:smtp_from_addr;size:255"`
|
|
SmtpFromName string `json:"smtp_from_name" gorm:"column:smtp_from_name;size:255"`
|
|
|
|
// IMAP entrante: leer el buzón directamente en vez de depender de que un
|
|
// proveedor nos haga POST. Es lo único que hace falta para responderle a un
|
|
// cliente que escribe a soporte@ desde su correo de siempre.
|
|
ImapActivo bool `json:"imap_activo" gorm:"column:imap_activo;default:false"`
|
|
ImapHost string `json:"imap_host" gorm:"column:imap_host;size:255"`
|
|
ImapPort int `json:"imap_port" gorm:"column:imap_port;default:993"`
|
|
ImapUsername string `json:"imap_username" gorm:"column:imap_username;size:255"`
|
|
ImapPasswordEnc string `json:"-" gorm:"column:imap_password_enc;type:text"` // AES-GCM con APP_KEY
|
|
ImapEncryption string `json:"imap_encryption" gorm:"column:imap_encryption;size:20;default:'ssl'"` // ssl|starttls
|
|
ImapCarpeta string `json:"imap_carpeta" gorm:"column:imap_carpeta;size:100;default:'INBOX'"`
|
|
|
|
// Solo para la vista: dice si ya hay contraseña guardada sin exponerla, para
|
|
// que el formulario sepa que puede mandar el campo vacío sin borrarla.
|
|
TieneImapPassword bool `json:"tiene_imap_password" gorm:"-"`
|
|
}
|
|
|
|
func (SoporteWebhookConfig) TableName() string { return "soporte_webhook_config" }
|
|
|
|
func GetSoporteWebhookActivo() (*SoporteWebhookConfig, error) {
|
|
var item SoporteWebhookConfig
|
|
err := app.Http.Database.DB.Where("activo = ?", true).First(&item).Error
|
|
return &item, err
|
|
}
|
|
|
|
func GetAllSoporteWebhookConfigs() ([]SoporteWebhookConfig, error) {
|
|
var items []SoporteWebhookConfig
|
|
err := app.Http.Database.DB.Order("created_at DESC").Find(&items).Error
|
|
return items, err
|
|
}
|
|
|
|
func SaveSoporteWebhookConfig(s *SoporteWebhookConfig) error {
|
|
if s.ID > 0 {
|
|
// Select("*") para que los booleanos en false (activo, responder_auto,
|
|
// imap_activo) también se guarden: Updates con struct ignora los ceros,
|
|
// así que desactivar algo no tenía efecto.
|
|
return app.Http.Database.DB.Model(&SoporteWebhookConfig{}).
|
|
Where("id = ?", s.ID).
|
|
Select("*").Omit("id", "created_at", "deleted_at").
|
|
Updates(s).Error
|
|
}
|
|
return app.Http.Database.DB.Create(s).Error
|
|
}
|