diff --git a/SushiBar/SushiBar/Program.cs b/SushiBar/SushiBar/Program.cs index 7bff625..066c6c9 100644 --- a/SushiBar/SushiBar/Program.cs +++ b/SushiBar/SushiBar/Program.cs @@ -2,8 +2,10 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using NLog.Extensions.Logging; using SushiBarBusinessLogic.BusinessLogics; +using SushiBarBusinessLogic.MailWorker; using SushiBarBusinessLogic.OfficePackage; using SushiBarBusinessLogic.OfficePackage.Implements; +using SushiBarContracts.BindingModels; using SushiBarContracts.BusinessLogicsContracts; using SushiBarContracts.StoragesContracts; using SushiBarDatabaseImplement.Implements; @@ -22,9 +24,38 @@ namespace SushiBar var services = new ServiceCollection(); ConfigureServices(services); _serviceProvider = services.BuildServiceProvider(); + try + { + var mailSender = _serviceProvider.GetService(); + mailSender?.MailConfig(new MailConfigBindingModel + { + MailLogin = + System.Configuration.ConfigurationManager.AppSettings["MailLogin"] ?? + string.Empty, + MailPassword = + System.Configuration.ConfigurationManager.AppSettings["MailPassword"] ?? + string.Empty, + SmtpClientHost = + System.Configuration.ConfigurationManager.AppSettings["SmtpClientHost"] ?? + string.Empty, + SmtpClientPort = Convert + .ToInt32(System.Configuration.ConfigurationManager.AppSettings["SmtpClientPort"]), + PopHost = + System.Configuration.ConfigurationManager.AppSettings["PopHost"] ?? + string.Empty, + PopPort = Convert.ToInt32(System.Configuration.ConfigurationManager.AppSettings["PopPort"]) + }); + var timer = new System.Threading.Timer(MailCheck!, null, 0, 100000); + } + catch(Exception ex) + { + var logger = _serviceProvider.GetService(); + logger?.LogError(ex, "Error on working w/ email"); + } + Application.Run(_serviceProvider.GetRequiredService()); } - private static void ConfigureServices(ServiceCollection services) + private static void ConfigureServices(IServiceCollection services) { services.AddLogging(option => { @@ -43,7 +74,10 @@ namespace SushiBar services.AddTransient(); services.AddTransient(); services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddSingleton(); services.AddTransient(); services.AddTransient(); @@ -61,5 +95,6 @@ namespace SushiBar services.AddTransient(); services.AddTransient(); } + private static void MailCheck(object obj) => ServiceProvider?.GetService()?.MailCheck(); } } \ No newline at end of file diff --git a/SushiBar/SushiBarBusinessLogic/BusinessLogics/MessageInfoLogic.cs b/SushiBar/SushiBarBusinessLogic/BusinessLogics/MessageInfoLogic.cs new file mode 100644 index 0000000..31233cb --- /dev/null +++ b/SushiBar/SushiBarBusinessLogic/BusinessLogics/MessageInfoLogic.cs @@ -0,0 +1,41 @@ +using Microsoft.Extensions.Logging; +using SushiBarContracts.BindingModels; +using SushiBarContracts.BusinessLogicsContracts; +using SushiBarContracts.SearchModels; +using SushiBarContracts.StoragesContracts; +using SushiBarContracts.ViewModels; + +namespace SushiBarBusinessLogic.BusinessLogics; + +public class MessageInfoLogic : IMessageInfoLogic +{ + private readonly ILogger _logger; + private readonly IMessageInfoStorage _messageStorage; + + public MessageInfoLogic(ILogger logger, IMessageInfoStorage messageStorage) + { + _logger = logger; + _messageStorage = messageStorage; + } + + public bool Create(MessageInfoBindingModel model) + { + if (_messageStorage.Insert(model) != null) return true; + _logger.LogWarning("Insert operation failed"); + + return false; + + } + + public List? ReadList(MessageInfoSearchModel? model) + { + _logger.LogInformation("ReadList. MessageId:{MessageId}.ClientId:{ClientId} ", + model?.MessageId, + model?.ClientId); + var list = model == null ? _messageStorage.GetFullList() : _messageStorage.GetFilteredList(model); + + _logger.LogInformation("ReadList. Count: {Count}", list.Count); + + return list; + } +} \ No newline at end of file diff --git a/SushiBar/SushiBarBusinessLogic/MailWorker/AbstractMailWorker.cs b/SushiBar/SushiBarBusinessLogic/MailWorker/AbstractMailWorker.cs new file mode 100644 index 0000000..a21a3e4 --- /dev/null +++ b/SushiBar/SushiBarBusinessLogic/MailWorker/AbstractMailWorker.cs @@ -0,0 +1,80 @@ +using Microsoft.Extensions.Logging; +using SushiBarContracts.BindingModels; +using SushiBarContracts.BusinessLogicsContracts; + +namespace SushiBarBusinessLogic.MailWorker; + +public abstract class AbstractMailWorker +{ + protected string _mailLogin = string.Empty; + protected string _mailPassword = string.Empty; + protected string _smtpClientHost = string.Empty; + protected int _smtpClientPort; + protected string _popHost = string.Empty; + protected int _popPort; + private readonly IMessageInfoLogic _messageInfoLogic; + private readonly ILogger _logger; + + public AbstractMailWorker(ILogger logger, + IMessageInfoLogic messageInfoLogic) + { + _logger = logger; + _messageInfoLogic = messageInfoLogic; + } + public void MailConfig(MailConfigBindingModel config) + { + _mailLogin = config.MailLogin; + _mailPassword = config.MailPassword; + _smtpClientHost = config.SmtpClientHost; + _smtpClientPort = config.SmtpClientPort; + _popHost = config.PopHost; + _popPort = config.PopPort; + _logger.LogDebug("Config: {login}, {password}, {clientHost}, {clientPOrt}, {popHost}, {popPort}", + _mailLogin, _mailPassword, _smtpClientHost, + _smtpClientPort, _popHost, _popPort); + } + public async void MailSendAsync(MailSendInfoBindingModel info) + { + if (string.IsNullOrEmpty(_mailLogin) || + string.IsNullOrEmpty(_mailPassword)) + { + return; + } + if (string.IsNullOrEmpty(_smtpClientHost) || + _smtpClientPort == 0) + { + return; + } + if (string.IsNullOrEmpty(info.MailAddress) || + string.IsNullOrEmpty(info.Subject) || + string.IsNullOrEmpty(info.Text)) + { + return; + } + + _logger.LogDebug("Send Mail: {To}, {Subject}", info.MailAddress, + info.Subject); + await SendMailAsync(info); + } + public async void MailCheck() + { + if (string.IsNullOrEmpty(_mailLogin) || + string.IsNullOrEmpty(_mailPassword)) + { + return; + } + if (string.IsNullOrEmpty(_popHost) || _popPort == 0) + { + return; + } + + var list = await ReceiveMailAsync(); + _logger.LogDebug("Check Mail: {Count} new mails", list.Count); + foreach (var mail in list) + { + _messageInfoLogic.Create(mail); + } + } + protected abstract Task SendMailAsync(MailSendInfoBindingModel info); + protected abstract Task> ReceiveMailAsync(); +} \ No newline at end of file diff --git a/SushiBar/SushiBarBusinessLogic/MailWorker/MailKitWorker.cs b/SushiBar/SushiBarBusinessLogic/MailWorker/MailKitWorker.cs new file mode 100644 index 0000000..f7ab7c4 --- /dev/null +++ b/SushiBar/SushiBarBusinessLogic/MailWorker/MailKitWorker.cs @@ -0,0 +1,69 @@ +using System.Net; +using System.Net.Mail; +using System.Text; +using MailKit.Net.Pop3; +using MailKit.Security; +using Microsoft.Extensions.Logging; +using SushiBarContracts.BindingModels; +using SushiBarContracts.BusinessLogicsContracts; +using AuthenticationException = System.Security.Authentication.AuthenticationException; + +namespace SushiBarBusinessLogic.MailWorker; + +public class MailKitWorker : AbstractMailWorker +{ + public MailKitWorker(ILogger logger, + IMessageInfoLogic messageInfoLogic) + : base(logger, messageInfoLogic) {} + + protected override async Task SendMailAsync(MailSendInfoBindingModel info) + { + using var objMailMessage = new MailMessage(); + using var objSmtpClient = new SmtpClient(_smtpClientHost, _smtpClientPort); + objMailMessage.From = new MailAddress(_mailLogin); + objMailMessage.To.Add(new MailAddress(info.MailAddress)); + objMailMessage.Subject = info.Subject; + objMailMessage.Body = info.Text; + objMailMessage.SubjectEncoding = Encoding.UTF8; + objMailMessage.BodyEncoding = Encoding.UTF8; + objSmtpClient.UseDefaultCredentials = false; + objSmtpClient.EnableSsl = true; + objSmtpClient.DeliveryMethod = SmtpDeliveryMethod.Network; + objSmtpClient.Credentials = new NetworkCredential(_mailLogin, _mailPassword); + await Task.Run(() => objSmtpClient.Send(objMailMessage)); + } + protected override async Task> ReceiveMailAsync() + { + var list = new List(); + using var client = new Pop3Client(); + await Task.Run(() => + { + try + { + client.Connect(_popHost, _popPort, SecureSocketOptions.SslOnConnect); + client.Authenticate(_mailLogin, _mailPassword); + for (var i = 0; i < client.Count; i++) + { + var message = client.GetMessage(i); + list.AddRange(message.From.Mailboxes.Select(mail => new MessageInfoBindingModel + { + DateDelivery = message.Date.DateTime, + MessageId = message.MessageId, + SenderName = mail.Address, + Subject = message.Subject, + Body = message.TextBody + })); + } + } + catch (AuthenticationException) + { + + } + finally + { + client.Disconnect(true); + } + }); + return list; + } +} \ No newline at end of file diff --git a/SushiBar/SushiBarBusinessLogic/SushiBarBusinessLogic.csproj b/SushiBar/SushiBarBusinessLogic/SushiBarBusinessLogic.csproj index e78c618..27ad21c 100644 --- a/SushiBar/SushiBarBusinessLogic/SushiBarBusinessLogic.csproj +++ b/SushiBar/SushiBarBusinessLogic/SushiBarBusinessLogic.csproj @@ -8,6 +8,7 @@ + diff --git a/SushiBar/SushiBarContracts/BindingModels/MailConfigBindingModel.cs b/SushiBar/SushiBarContracts/BindingModels/MailConfigBindingModel.cs new file mode 100644 index 0000000..9e4a9e6 --- /dev/null +++ b/SushiBar/SushiBarContracts/BindingModels/MailConfigBindingModel.cs @@ -0,0 +1,11 @@ +namespace SushiBarContracts.BindingModels; + +public class MailConfigBindingModel +{ + public string MailLogin { get; set; } = string.Empty; + public string MailPassword { get; set; } = string.Empty; + public string SmtpClientHost { get; set; } = string.Empty; + public int SmtpClientPort { get; set; } + public string PopHost { get; set; } = string.Empty; + public int PopPort { get; set; } +} \ No newline at end of file diff --git a/SushiBar/SushiBarContracts/BindingModels/MailSendInfoBindingModel.cs b/SushiBar/SushiBarContracts/BindingModels/MailSendInfoBindingModel.cs new file mode 100644 index 0000000..8d28547 --- /dev/null +++ b/SushiBar/SushiBarContracts/BindingModels/MailSendInfoBindingModel.cs @@ -0,0 +1,8 @@ +namespace SushiBarContracts.BindingModels; + +public class MailSendInfoBindingModel +{ + public string MailAddress { get; set; } = string.Empty; + public string Subject { get; set; } = string.Empty; + public string Text { get; set; } = string.Empty; +} \ No newline at end of file diff --git a/SushiBar/SushiBarContracts/BindingModels/MessageInfoBindingModel.cs b/SushiBar/SushiBarContracts/BindingModels/MessageInfoBindingModel.cs index cedc9b7..8ceba17 100644 --- a/SushiBar/SushiBarContracts/BindingModels/MessageInfoBindingModel.cs +++ b/SushiBar/SushiBarContracts/BindingModels/MessageInfoBindingModel.cs @@ -4,7 +4,6 @@ namespace SushiBarContracts.BindingModels; public class MessageInfoBindingModel : IMessageInfoModel { - public int Id { get; set; } public string MessageId { get; set; } = string.Empty; public int? ClientId { get; set; } public string SenderName { get; set; } = string.Empty; diff --git a/SushiBar/SushiBarContracts/SearchModels/MessageInfoSearchModel.cs b/SushiBar/SushiBarContracts/SearchModels/MessageInfoSearchModel.cs index cccc018..10351bd 100644 --- a/SushiBar/SushiBarContracts/SearchModels/MessageInfoSearchModel.cs +++ b/SushiBar/SushiBarContracts/SearchModels/MessageInfoSearchModel.cs @@ -2,6 +2,6 @@ public class MessageInfoSearchModel { - public int? Id { get; set; } + public string? MessageId { get; set; } public int? ClientId { get; set; } } \ No newline at end of file diff --git a/SushiBar/SushiBarContracts/ViewModels/MessageInfoViewModel.cs b/SushiBar/SushiBarContracts/ViewModels/MessageInfoViewModel.cs index 5189e85..5ec60fb 100644 --- a/SushiBar/SushiBarContracts/ViewModels/MessageInfoViewModel.cs +++ b/SushiBar/SushiBarContracts/ViewModels/MessageInfoViewModel.cs @@ -5,13 +5,12 @@ namespace SushiBarContracts.ViewModels; public class MessageInfoViewModel : IMessageInfoModel { - public int Id { get; } - public string MessageId { get; } - public int? ClientId { get; } + public string MessageId { get; set; } + public int? ClientId { get; set; } [DisplayName("Sender name")] - public string SenderName { get; } + public string SenderName { get; set; } [DisplayName("Date delivery")] - public DateTime DateDelivery { get; } - public string Subject { get; } - public string Body { get; } + public DateTime DateDelivery { get; set; } + public string Subject { get; set; } + public string Body { get; set; } } \ No newline at end of file diff --git a/SushiBar/SushiBarDatabaseImplement/Implements/MessageStorage.cs b/SushiBar/SushiBarDatabaseImplement/Implements/MessageStorage.cs new file mode 100644 index 0000000..765b11e --- /dev/null +++ b/SushiBar/SushiBarDatabaseImplement/Implements/MessageStorage.cs @@ -0,0 +1,50 @@ +using SushiBarContracts.BindingModels; +using SushiBarContracts.SearchModels; +using SushiBarContracts.StoragesContracts; +using SushiBarContracts.ViewModels; +using SushiBarDatabaseImplement.Models; + +namespace SushiBarDatabaseImplement.Implements; + +public class MessageStorage : IMessageInfoStorage +{ + public MessageInfoViewModel? GetElement(MessageInfoSearchModel model) + { + if (model.MessageId == null) + return null; + using var context = new SushiBarDatabase(); + return context.Messages.FirstOrDefault(x => x.MessageId == model.MessageId)?.GetViewModel; + } + + public List GetFilteredList(MessageInfoSearchModel model) + { + if (!model.ClientId.HasValue) + return new List(); + using var context = new SushiBarDatabase(); + return context.Messages + .Where(x => x.ClientId == model.ClientId) + .Select(x => x.GetViewModel) + .ToList(); + } + + public List GetFullList() + { + using var context = new SushiBarDatabase(); + return context.Messages + .Select(x => x.GetViewModel) + .ToList(); + } + + public MessageInfoViewModel? Insert(MessageInfoBindingModel model) + { + var newMessage = Message.Create(model); + if (newMessage == null) + { + return null; + } + using var context = new SushiBarDatabase(); + context.Messages.Add(newMessage); + context.SaveChanges(); + return newMessage.GetViewModel; + } +} \ No newline at end of file diff --git a/SushiBar/SushiBarDatabaseImplement/Models/Message.cs b/SushiBar/SushiBarDatabaseImplement/Models/Message.cs new file mode 100644 index 0000000..8f70c2e --- /dev/null +++ b/SushiBar/SushiBarDatabaseImplement/Models/Message.cs @@ -0,0 +1,50 @@ +using System.ComponentModel.DataAnnotations; +using SushiBarContracts.BindingModels; +using SushiBarContracts.ViewModels; +using SushiBarDataModels.Models; + +namespace SushiBarDatabaseImplement.Models; + +public class Message : IMessageInfoModel +{ + [Key] + public string MessageId { get; private set; } = string.Empty; + public int? ClientId { get; private set; } + [Required] + public string SenderName { get; private set; } = string.Empty; + [Required] + public DateTime DateDelivery { get; private set; } = DateTime.Now; + [Required] + public string Subject { get; private set; } = string.Empty; + [Required] + public string Body { get; private set; } = string.Empty; + + public virtual Client Client { get; set; } + + public static Message? Create(MessageInfoBindingModel? model) + { + if (model == null) + { + return null; + } + return new Message + { + Body = model.Body, + Subject = model.Subject, + ClientId = model.ClientId, + MessageId = model.MessageId, + SenderName = model.SenderName, + DateDelivery = model.DateDelivery, + }; + } + + public MessageInfoViewModel GetViewModel => new() + { + Body = Body, + Subject = Subject, + ClientId = ClientId, + MessageId = MessageId, + SenderName = SenderName, + DateDelivery = DateDelivery, + }; +} \ No newline at end of file diff --git a/SushiBar/SushiBarDatabaseImplement/SushiBarDatabase.cs b/SushiBar/SushiBarDatabaseImplement/SushiBarDatabase.cs index 6af51fc..ca3a322 100644 --- a/SushiBar/SushiBarDatabaseImplement/SushiBarDatabase.cs +++ b/SushiBar/SushiBarDatabaseImplement/SushiBarDatabase.cs @@ -19,5 +19,6 @@ namespace SushiBarDatabaseImplement public virtual DbSet Orders { set; get; } public virtual DbSet Clients { set; get; } public virtual DbSet Implementers { get; set; } + public virtual DbSet Messages { get; set; } } } diff --git a/SushiBar/SushiBarModels/Models/IMessageInfoModel.cs b/SushiBar/SushiBarModels/Models/IMessageInfoModel.cs index 0eada2c..360e36d 100644 --- a/SushiBar/SushiBarModels/Models/IMessageInfoModel.cs +++ b/SushiBar/SushiBarModels/Models/IMessageInfoModel.cs @@ -1,6 +1,6 @@ namespace SushiBarDataModels.Models; -public interface IMessageInfoModel : IId +public interface IMessageInfoModel { string MessageId { get; } int? ClientId { get; }