// Package notify delivers notifications using credentials persisted in the // notification_credentials table. It is the credential-backed counterpart to // the legacy secrets.yml driven senders in internal/sender and internal/tg. package notify import ( "crypto/tls" "fmt" "gopkg.in/gomail.v2" "rocketgit.ru/rsmon/worker/app/models" ) // Email sends an email using the given SMTP credential. bodyText is sent as // text/plain and, when non-empty, bodyHTML is added as a text/html alternative // so the recipient's MUA picks the best representation. func Email(cred *models.NotificationCredential, to, subject, bodyText, bodyHTML string) error { if cred == nil { return fmt.Errorf("credential is nil") } if cred.Kind != models.CredentialKindSMTP { return fmt.Errorf("credential %d is not smtp (kind=%s)", cred.ID, cred.Kind) } if cred.Server == nil || cred.Port == nil || cred.Login == nil || cred.FromAddr == nil { return fmt.Errorf("credential %d missing required smtp fields", cred.ID) } password, err := cred.GetSecret() if err != nil { return fmt.Errorf("decrypt smtp password: %w", err) } fromName := "RSMon" if cred.FromName != nil && *cred.FromName != "" { fromName = *cred.FromName } m := gomail.NewMessage() m.SetHeader("From", m.FormatAddress(*cred.FromAddr, fromName)) m.SetHeader("To", to) m.SetHeader("Subject", subject) m.AddAlternative("text/plain", bodyText) if bodyHTML != "" { m.AddAlternative("text/html", bodyHTML) } d := gomail.NewDialer(*cred.Server, *cred.Port, *cred.Login, password) if cred.InsecureSkipVerify { d.TLSConfig = &tls.Config{InsecureSkipVerify: true} } if err := d.DialAndSend(m); err != nil { return fmt.Errorf("send smtp via credential %d: %w", cred.ID, err) } return nil }