diff --git a/AsbCloudApp/Services/IEmailService.cs b/AsbCloudApp/Services/IEmailService.cs new file mode 100644 index 00000000..8e50b13c --- /dev/null +++ b/AsbCloudApp/Services/IEmailService.cs @@ -0,0 +1,10 @@ +using System; +using System.Collections.Generic; + +namespace AsbCloudApp.Services +{ + public interface IEmailService + { + void EnqueueSend(IEnumerable addresses, string subject, string htmlBody); + } +} diff --git a/AsbCloudInfrastructure/DependencyInjection.cs b/AsbCloudInfrastructure/DependencyInjection.cs index ec09d446..15ecdc2b 100644 --- a/AsbCloudInfrastructure/DependencyInjection.cs +++ b/AsbCloudInfrastructure/DependencyInjection.cs @@ -50,6 +50,7 @@ namespace AsbCloudInfrastructure services.AddScoped(provider => provider.GetService()); services.AddScoped(); + services.AddScoped(); services.AddHostedService();// replace by BackgroundWorkerService diff --git a/AsbCloudInfrastructure/Services/BackgroundWorkerService.cs b/AsbCloudInfrastructure/Services/BackgroundWorkerService.cs index 30aee4b2..4906471d 100644 --- a/AsbCloudInfrastructure/Services/BackgroundWorkerService.cs +++ b/AsbCloudInfrastructure/Services/BackgroundWorkerService.cs @@ -136,7 +136,8 @@ namespace AsbCloudInfrastructure.Services { try { - await work.ActionAsync(work.Id, cts.Token).ConfigureAwait(false); + var actionTask = work.ActionAsync(work.Id, cts.Token); + await actionTask.WaitAsync(TimeSpan.FromMinutes(2), cts.Token); } catch (Exception ex) { diff --git a/AsbCloudInfrastructure/Services/EmailService.cs b/AsbCloudInfrastructure/Services/EmailService.cs new file mode 100644 index 00000000..e9915a36 --- /dev/null +++ b/AsbCloudInfrastructure/Services/EmailService.cs @@ -0,0 +1,115 @@ +using AsbCloudApp.Exceptions; +using AsbCloudApp.Services; +using Microsoft.Extensions.Configuration; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Net.Mail; +using System.Text.RegularExpressions; +using System.Threading; +using System.Threading.Tasks; + +namespace AsbCloudInfrastructure.Services +{ + public class EmailService : IEmailService + { + private readonly IBackgroundWorkerService backgroundWorker; + private readonly bool IsConfigured; + private readonly string sender; + private readonly string smtpServer; + private readonly string smtpPassword; + + public EmailService(IBackgroundWorkerService backgroundWorker, IConfiguration configuration) + { + sender = configuration.GetValue("email:sender", null); + smtpPassword = configuration.GetValue("email:password", null); + smtpServer = configuration.GetValue("email:smtpServer", null); + + var configError = (string.IsNullOrEmpty(sender) || + string.IsNullOrEmpty(smtpPassword) || + string.IsNullOrEmpty(smtpServer)); + + IsConfigured = !configError; + + this.backgroundWorker = backgroundWorker; + } + + public void EnqueueSend(IEnumerable addresses, string subject, string htmlBody) + { + if(!IsConfigured) + { + Trace.TraceWarning("smtp is not configured"); + return; + } + var jobId = CalcJobId(addresses, subject, htmlBody); + if (!backgroundWorker.Contains(jobId)) + { + var action = MakeEmailSendJobAsync(addresses, subject, htmlBody); + backgroundWorker.Enqueue(jobId, action); + } + } + + private Func MakeEmailSendJobAsync(IEnumerable addresses, string subject, string htmlBody) + { + var mailAddresses = new List(); + foreach (var address in addresses) + { + if (MailAddress.TryCreate(address, out MailAddress mailAddress)) + mailAddresses.Add(mailAddress); + else + Trace.TraceWarning($"Mail {address} is not correct."); + } + + if (!mailAddresses.Any()) + throw new ArgumentException($"No valid email found. List:[{string.Join(',', addresses)}]", nameof(addresses)); + + if (string.IsNullOrEmpty(subject)) + throw new ArgumentInvalidException($"{nameof(subject)} should be set", nameof(subject)); + + var func = async (string id, CancellationToken token) => + { + var from = new MailAddress(sender); + + var message = new MailMessage(); + message.From = from; + + foreach (var mailAddress in mailAddresses) + message.To.Add(mailAddress); + + message.Subject = subject; + message.Body = htmlBody; + message.IsBodyHtml = true; + + var client = new SmtpClient(smtpServer); + client.EnableSsl = true; + client.UseDefaultCredentials = false; + client.Credentials = new System.Net.NetworkCredential(sender, smtpPassword); + + // TODO: uncomment next when tested + //await client.SendMailAsync(message, token); + await Task.Delay(0); + Trace.TraceInformation($"Send email to {string.Join(',', addresses)} subj:{subject}"); + }; + return func; + } + + private string CalcJobId(IEnumerable addresses, string subject, string content) + { + var hash = GetHashCode(addresses); + hash ^= subject.GetHashCode(); + hash ^= content.GetHashCode(); + return hash.ToString("x"); + } + + private static int GetHashCode(IEnumerable strings) + { + int hash = -1397075115; + var enumerator = strings.GetEnumerator(); + + while(enumerator.MoveNext()) + hash ^= enumerator.Current.GetHashCode(); + return hash; + } + } +} diff --git a/AsbCloudWebApi/Docs/Mail Templates.odt b/AsbCloudWebApi/Docs/Mail Templates.odt new file mode 100644 index 00000000..faeccff0 Binary files /dev/null and b/AsbCloudWebApi/Docs/Mail Templates.odt differ diff --git a/AsbCloudWebApi/appsettings.json b/AsbCloudWebApi/appsettings.json index 9e02897a..b82232b2 100644 --- a/AsbCloudWebApi/appsettings.json +++ b/AsbCloudWebApi/appsettings.json @@ -13,7 +13,12 @@ "LocalConnection": "Host=localhost;Database=postgres;Username=postgres;Password=q;Persist Security Info=True" }, "AllowedHosts": "*", - "Urls": "http://0.0.0.0:5000",//;https://0.0.0.0:5001" //, + "email": { + "smtpServer": "smtp.timeweb.ru", + "sender": "bot@autodrilling.ru", + "password": "xHhgwZ4D" + }, + "Urls": "http://0.0.0.0:5000" //;https://0.0.0.0:5001" //, // See https man: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/servers/kestrel/endpoints?view=aspnetcore-6.0 //"Kestrel": { // "Endpoints": { diff --git a/ConsoleApp1/Program.cs b/ConsoleApp1/Program.cs index 1f653d09..48a4eb54 100644 --- a/ConsoleApp1/Program.cs +++ b/ConsoleApp1/Program.cs @@ -8,6 +8,7 @@ using AsbCloudDb; using Google.Apis.Drive.v3.Data; using Microsoft.EntityFrameworkCore; using System.Net.Mail; +using System.Text.RegularExpressions; namespace ConsoleApp1 { @@ -18,9 +19,14 @@ namespace ConsoleApp1 class Program { + + static void Main(/*string[] args*/) { - SendMail.Main(); + var regex = new Regex(@"<[a-zA-Z0-9]+.*>.*<[a-zA-Z]+/*>"); + var testHtml = "aa

AAA

asdasd"; + var t = regex.IsMatch(testHtml); + //SendMail.Main(); //DbDemoDataService.AddDemoData(); //.GetAwaiter().GetResult(); diff --git a/ConsoleApp1/SendMail.cs b/ConsoleApp1/SendMail.cs index d3607bb7..f72b659c 100644 --- a/ConsoleApp1/SendMail.cs +++ b/ConsoleApp1/SendMail.cs @@ -14,6 +14,7 @@ namespace ConsoleApp1 MailAddress to = new MailAddress("ng.frolov@autodrilling.ru"); MailAddress from = new MailAddress("bot@autodrilling.ru"); MailMessage message = new MailMessage(from, to); + message.Subject = "Using the new SMTP client."; message.Body = "

this is a test text

"; message.IsBodyHtml = true;