fix: seguridad del webhook de soporte, pagos en contabilidad y Telegram para tareas

- soporte: el webhook de correo entrante era público sin ninguna validación;
  ahora exige una API key (query ?key= o header) comparada en tiempo constante.
  Además evita tickets duplicados por reintentos del proveedor (dedup por
  Message-Id) y enhebra respuestas del mismo remitente en vez de abrir un
  ticket nuevo por cada correo.
- contabilidad: marcar una cuenta por cobrar/pagar como pagada ahora crea y
  vincula la Transaccion correspondiente (antes el dashboard de ingresos/
  egresos nunca reflejaba esos pagos). Se corrige además que actualizar una
  cuenta por cobrar borraba su transaccion_id en cada PUT.
- tareas: se activa por defecto el canal Telegram para tarea_asignada (estaba
  apagado desde el seed original) y se agrega un flujo real de vinculación de
  Telegram para el staff interno (código temporal + verificación), igual al
  que ya existía para los usuarios del portal — sin esto el chat_id de cada
  usuario había que pegarlo a mano y la notificación nunca llegaba.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Lizandro GD
2026-08-03 01:25:17 +00:00
co-authored by Claude Sonnet 5
parent 1127d944c7
commit d2bf699b60
14 changed files with 612 additions and 100 deletions
+14 -1
View File
@@ -109,6 +109,8 @@ func Migrate() {
&models.Tarifa{},
&models.DocumentoGenerado{},
&models.Arquitectura{},
// Vinculación de Telegram para staff interno
&models.TelegramStaffToken{},
); err != nil {
log.Fatalf("Error during main migration: %v", err)
}
@@ -963,7 +965,7 @@ func SeedNotifDefaults() {
{Evento: "servidor_vence_pronto", Destinatario: "admin", CanalEmail: true, CanalTelegram: true, CanalSistema: true, Descripcion: "Admin recibe cuando un VPS está próximo a vencer"},
{Evento: "servidor_recurso_alto", Destinatario: "admin", CanalEmail: false, CanalTelegram: true, CanalSistema: true, Descripcion: "Admin recibe cuando CPU/RAM/Disco supera el umbral"},
// Tareas
{Evento: "tarea_asignada", Destinatario: "admin", CanalEmail: true, CanalTelegram: false, CanalSistema: true, Descripcion: "Notifica cuando se asigna una tarea a un usuario"},
{Evento: "tarea_asignada", Destinatario: "admin", CanalEmail: true, CanalTelegram: true, CanalSistema: true, Descripcion: "Notifica cuando se asigna una tarea a un usuario"},
{Evento: "tarea_estado", Destinatario: "admin", CanalEmail: false, CanalTelegram: false, CanalSistema: true, Descripcion: "Notifica cuando cambia el estado de una tarea"},
{Evento: "tarea_comentario", Destinatario: "admin", CanalEmail: false, CanalTelegram: false, CanalSistema: true, Descripcion: "Notifica cuando hay un nuevo comentario en una tarea"},
}
@@ -976,6 +978,17 @@ func SeedNotifDefaults() {
}
}
log.Printf("[SEED] SeedNotifDefaults: %d nuevas configuraciones creadas.", created)
// Corrección puntual (una sola vez): tarea_asignada/admin se sembró originalmente
// con canal_telegram=false, dejando la notificación muerta desde su creación.
// Si nadie tocó el registro desde entonces, se activa por defecto.
var taRow models.NotifEventoConfig
if err := db.Where("evento = ? AND destinatario = ?", "tarea_asignada", "admin").First(&taRow).Error; err == nil {
if !taRow.CanalTelegram && taRow.UpdatedAt.Equal(taRow.CreatedAt) {
db.Model(&taRow).Update("canal_telegram", true)
log.Println("[SEED] tarea_asignada/admin: canal_telegram activado (corrección de default)")
}
}
}
// SeedShield es un stub idempotente para la configuración de Shield.