using System.Net; using System.Net.Mail; namespace JobTrackerApi.Services; public interface IAppEmailSender { Task SendAsync(string toEmail, string subject, string bodyText, CancellationToken cancellationToken = default); } public sealed class SmtpEmailSender : IAppEmailSender { private readonly IConfiguration _cfg; private readonly ILogger _logger; public SmtpEmailSender(IConfiguration cfg, ILogger logger) { _cfg = cfg; _logger = logger; } public async Task SendAsync(string toEmail, string subject, string bodyText, CancellationToken cancellationToken = default) { var host = (_cfg["Email:SmtpHost"] ?? "").Trim(); var user = (_cfg["Email:SmtpUser"] ?? "").Trim(); var pass = (_cfg["Email:SmtpPassword"] ?? "").Trim(); var from = (_cfg["Email:From"] ?? user).Trim(); var fromName = (_cfg["Email:FromName"] ?? "Jobbjakt").Trim(); var port = _cfg.GetValue("Email:SmtpPort", 587); if (port <= 0) port = 587; var enableSsl = _cfg.GetValue("Email:SmtpEnableSsl", true); var timeoutMs = _cfg.GetValue("Email:SmtpTimeoutMs", 15000); if (timeoutMs <= 0) timeoutMs = 15000; var enabled = _cfg.GetValue("Email:Enabled", false); if (!enabled) { _logger.LogWarning("Email sending is disabled (Email:Enabled=false). Suppressed email to {To} subject={Subject}", toEmail, subject); return; } if (string.IsNullOrWhiteSpace(host)) throw new InvalidOperationException("Email:SmtpHost is not configured."); if (string.IsNullOrWhiteSpace(from)) throw new InvalidOperationException("Email:From is not configured."); using var msg = new MailMessage(); msg.From = new MailAddress(from, string.IsNullOrWhiteSpace(fromName) ? null : fromName); msg.To.Add(new MailAddress(toEmail)); msg.Subject = subject; msg.Body = bodyText; msg.IsBodyHtml = false; using var smtp = new SmtpClient(host, port) { EnableSsl = enableSsl, DeliveryMethod = SmtpDeliveryMethod.Network, Timeout = timeoutMs, }; if (!string.IsNullOrWhiteSpace(user)) { smtp.Credentials = new NetworkCredential(user, pass); } // SmtpClient has no CancellationToken support; run on thread pool. await Task.Run(() => smtp.Send(msg), cancellationToken); } }