diff --git a/server/Controllers/AuthController.cs b/server/Controllers/AuthController.cs index 6da82b0..437f8c8 100644 --- a/server/Controllers/AuthController.cs +++ b/server/Controllers/AuthController.cs @@ -9,6 +9,7 @@ using Microsoft.Extensions.Options; using Newtonsoft.Json; using PodNoms.Api.Models; using PodNoms.Api.Models.ViewModels; +using PodNoms.Api.Services; using PodNoms.Api.Services.Auth; using PodNoms.Api.Utils; @@ -17,15 +18,15 @@ namespace PodNoms.Api.Controllers { public class AuthController : Controller { private readonly UserManager _userManager; private readonly IJwtFactory _jwtFactory; - private readonly IEmailSender _emailSender; + private readonly IMailSender _emailSender; private readonly AppSettings _appSettings; private readonly JwtIssuerOptions _jwtOptions; public AuthController(UserManager userManager, IJwtFactory jwtFactory, IOptions jwtOptions, - IOptions appSettings, IEmailSender emailSender) { + IOptions appSettings, IMailSender mailSender) { _userManager = userManager; _jwtFactory = jwtFactory; - _emailSender = emailSender; + _emailSender = mailSender; _appSettings = appSettings.Value; _jwtOptions = jwtOptions.Value; } @@ -77,7 +78,7 @@ namespace PodNoms.Api.Controllers { var code = await _userManager.GeneratePasswordResetTokenAsync(user); var callbackUrl = $"{_appSettings.SiteUrl}/reset?token={WebUtility.UrlEncode(code)}&email={WebUtility.UrlEncode(user.Email)}"; await _emailSender.SendEmailAsync(model.Email, "Reset Password", - "Please reset your password by clicking here: link"); + new { resetLink = callbackUrl }, "forgot_password.html"); return Ok(model); } return BadRequest(model); diff --git a/server/Resources/email.html b/server/Resources/email.html new file mode 100644 index 0000000..6123154 --- /dev/null +++ b/server/Resources/email.html @@ -0,0 +1,142 @@ + + + + + + + Forgot password + + + + + + + + + +
  +
+ + + + + + + + + +
+ + +
+

Hi there,

+

+ {{message}} +

+
+
+
+ + + + + +   + + + + + \ No newline at end of file diff --git a/server/Resources/forgot_password.html b/server/Resources/forgot_password.html new file mode 100644 index 0000000..983dd6e --- /dev/null +++ b/server/Resources/forgot_password.html @@ -0,0 +1,156 @@ + + + + + + + Forgot password + + + + + + + + + + +
  +
+ + + + + + + + + +
+ + + + +
+

Hi there,

+

+ Someone told us you forgot your password? Don't worry, it happens. +

+ + + + + + +
+ + + + + + +
+ Reset my password +
+
+
+
+ + + +
+
 
+ + + \ No newline at end of file diff --git a/server/Services/Auth/EmailSender.cs b/server/Services/Auth/EmailSender.cs deleted file mode 100644 index b4df98c..0000000 --- a/server/Services/Auth/EmailSender.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Threading.Tasks; -using Microsoft.AspNetCore.Identity.UI.Services; -using PodNoms.Api.Services; - -namespace PodNoms.Api.Services.Auth { - public class EmailSender : IEmailSender { - private readonly IMailSender _mailSender; - - public EmailSender(IMailSender mailSender) { - this._mailSender = mailSender; - } - - public async Task SendEmailAsync(string email, string subject, string htmlMessage) { - await this._mailSender.SendEmail(email, subject, htmlMessage); - } - } -} \ No newline at end of file diff --git a/server/Services/Auth/PodNomsUserManager.cs b/server/Services/Auth/PodNomsUserManager.cs index 6e856a1..b70301e 100644 --- a/server/Services/Auth/PodNomsUserManager.cs +++ b/server/Services/Auth/PodNomsUserManager.cs @@ -37,7 +37,7 @@ namespace PodNoms.Api.Services.Auth { _checkName(user); await _imageify(user); try { - await _mailSender.SendEmail("fergal.moran@gmail.com", "New user signup", $"{user.Email}\n{user.FirstName} {user.LastName}"); + await _mailSender.SendEmailAsync("fergal.moran@gmail.com", "New user signup", $"{user.Email}\n{user.FirstName} {user.LastName}"); } catch (Exception) { } diff --git a/server/Services/IMailSender.cs b/server/Services/IMailSender.cs index 88ba954..321e631 100644 --- a/server/Services/IMailSender.cs +++ b/server/Services/IMailSender.cs @@ -2,6 +2,7 @@ using System.Threading.Tasks; namespace PodNoms.Api.Services { public interface IMailSender { - Task SendEmail(string email, string subject, string message); + Task SendEmailAsync(string email, string subject, string message, string template = "email.html"); + Task SendEmailAsync(string email, string subject, dynamic viewModel, string template = "email.html"); } } diff --git a/server/Services/Jobs/ClearOrphanAudioJob.cs b/server/Services/Jobs/ClearOrphanAudioJob.cs index 1a9d4f1..ab0b496 100644 --- a/server/Services/Jobs/ClearOrphanAudioJob.cs +++ b/server/Services/Jobs/ClearOrphanAudioJob.cs @@ -47,7 +47,7 @@ namespace PodNoms.Api.Services.Jobs { _logger.LogWarning($"Error processing blob {blob.Uri}\n{e.Message}"); } } - await this._mailSender.SendEmail("fergal.moran@gmail.com", $"ClearOrphanAudioJob: Complete {blobCount}", string.Empty); + await this._mailSender.SendEmailAsync("fergal.moran@gmail.com", $"ClearOrphanAudioJob: Complete {blobCount}", string.Empty); } catch (Exception ex) { _logger.LogError($"Error clearing orphans\n{ex.Message}"); } diff --git a/server/Services/Jobs/UpdateYouTubeDlJob.cs b/server/Services/Jobs/UpdateYouTubeDlJob.cs index 17b69aa..789dfd8 100644 --- a/server/Services/Jobs/UpdateYouTubeDlJob.cs +++ b/server/Services/Jobs/UpdateYouTubeDlJob.cs @@ -22,7 +22,7 @@ namespace PodNoms.Api.Services.Jobs { yt.Options.GeneralOptions.Update = true; yt.Download("https://www.youtube.com/watch?v=OJ2wOKDzKyI"); - var results = await _sender.SendEmail("fergal.moran@gmail.com", "PodNoms: UpdateYouTubeDlJob completed", "As you were"); + var results = await _sender.SendEmailAsync("fergal.moran@gmail.com", "PodNoms: UpdateYouTubeDlJob completed", "As you were"); _logger.LogInformation($"{results}"); } } diff --git a/server/Services/MailgunSender.cs b/server/Services/MailgunSender.cs index 7d89cce..b60d104 100644 --- a/server/Services/MailgunSender.cs +++ b/server/Services/MailgunSender.cs @@ -8,30 +8,42 @@ using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using PodNoms.Api.Models; using System.Net; +using PodNoms.Api.Utils; +using HandlebarsDotNet; namespace PodNoms.Api.Services { public class MailgunSender : IMailSender { private readonly EmailSettings _emailSettings; private readonly ILogger _logger; - public MailgunSender(IOptions emailSettings, ILogger logger){ + public MailgunSender(IOptions emailSettings, ILogger logger) { _emailSettings = emailSettings.Value; _logger = logger; } - public async Task SendEmail(string email, string subject, string message){ + public async Task SendEmailAsync(string email, string subject, string message, string template = "email.html") { + return await SendEmailAsync(email, subject, new { message = message }, template); + } + public async Task SendEmailAsync(string email, string subject, dynamic objectModel = null, string template = "email.html") { using (var client = new HttpClient { BaseAddress = new Uri(_emailSettings.ApiBaseUri) }) { client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(Encoding.ASCII.GetBytes(_emailSettings.ApiKey))); _logger.LogInformation($"From: {_emailSettings.From}\nTo: {email}\nApi key: {_emailSettings.ApiKey}"); - + var t = await ResourceReader.ReadResource(template); + string mailBody; + if (objectModel != null) { + var parser = Handlebars.Compile(t); + mailBody = parser(objectModel); + } else { + mailBody = t; + } var content = new FormUrlEncodedContent(new[] { new KeyValuePair("from", _emailSettings.From), new KeyValuePair("to", email), new KeyValuePair("subject", subject), - new KeyValuePair("html", message) + new KeyValuePair("html", mailBody) }); var result = await client.PostAsync(_emailSettings.RequestUri, content).ConfigureAwait(false); diff --git a/server/Startup.cs b/server/Startup.cs index ccd7f09..ad27d16 100644 --- a/server/Startup.cs +++ b/server/Startup.cs @@ -219,7 +219,6 @@ namespace PodNoms.Api { services.AddScoped(); services.AddScoped(); services.AddScoped(); - services.AddScoped(); services.AddScoped(); services.AddHttpClient();