diff --git a/src/Elektrifikatsiya/Elektrifikatsiya/Elektrifikatsiya.csproj b/src/Elektrifikatsiya/Elektrifikatsiya/Elektrifikatsiya.csproj index 89ebdd6..87cca3b 100644 --- a/src/Elektrifikatsiya/Elektrifikatsiya/Elektrifikatsiya.csproj +++ b/src/Elektrifikatsiya/Elektrifikatsiya/Elektrifikatsiya.csproj @@ -42,7 +42,9 @@ + + @@ -50,6 +52,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/src/Elektrifikatsiya/Elektrifikatsiya/Program.cs b/src/Elektrifikatsiya/Elektrifikatsiya/Program.cs index 6615bf5..adb054c 100644 --- a/src/Elektrifikatsiya/Elektrifikatsiya/Program.cs +++ b/src/Elektrifikatsiya/Elektrifikatsiya/Program.cs @@ -7,17 +7,22 @@ using Elektrifikatsiya.Database; using Elektrifikatsiya.Models; using Elektrifikatsiya.Services; using Elektrifikatsiya.Services.Implementations; - +using Elektrifikatsiya.Utilities; using HiveMQtt.Client; using HiveMQtt.Client.Options; using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Options; WebApplicationBuilder builder = WebApplication.CreateBuilder(args); +// Read configuration +builder.Services.Configure(builder.Configuration.GetRequiredSection("EmailSettings")); + // Add services to the container. builder.Services.AddRazorPages(); builder.Services.AddServerSideBlazor(); +builder.Services.AddOptions(); builder.Services.AddHttpContextAccessor(); builder.Services.AddHostedService(); builder.Services.AddSingleton(); @@ -38,6 +43,7 @@ builder.Services.AddTransient() builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); +builder.Services.AddScoped(); builder.Services.AddDbContext(options => options.UseSqlite("Data Source=./MainDatabase.sqlite")); builder.Services.AddBootstrapProviders(); builder.Services.AddHttpClient(); @@ -46,6 +52,7 @@ builder.Services.AddBlazorise(options => options.Immediate = true; }); + AddBlazorise(builder.Services); WebApplication app = builder.Build(); @@ -67,6 +74,8 @@ app.MapBlazorHub(); app.MapFallbackToPage("/_Host"); app.MapControllers(); +app.Services.GetRequiredService>().Value.CompileTemplates(); + IServiceScope serviceScope = app.Services.GetRequiredService().CreateScope(); MainDatabaseContext mainDatabase = serviceScope.ServiceProvider.GetRequiredService(); diff --git a/src/Elektrifikatsiya/Elektrifikatsiya/Services/IEmailService.cs b/src/Elektrifikatsiya/Elektrifikatsiya/Services/IEmailService.cs new file mode 100644 index 0000000..01caf9d --- /dev/null +++ b/src/Elektrifikatsiya/Elektrifikatsiya/Services/IEmailService.cs @@ -0,0 +1,8 @@ +namespace Elektrifikatsiya.Services; + +public interface IEmailService +{ + Task SendAsync(string to, string subject, string html, string? from = null); + + Task SendWithTemeplateAsync(string to, string subject, string templateKey, string? from = null, Dictionary? templateParameters = null); +} \ No newline at end of file diff --git a/src/Elektrifikatsiya/Elektrifikatsiya/Services/Implementations/EmailService.cs b/src/Elektrifikatsiya/Elektrifikatsiya/Services/Implementations/EmailService.cs new file mode 100644 index 0000000..8198e8f --- /dev/null +++ b/src/Elektrifikatsiya/Elektrifikatsiya/Services/Implementations/EmailService.cs @@ -0,0 +1,44 @@ +using Elektrifikatsiya.Utilities; +using HandlebarsDotNet; +using MailKit.Net.Smtp; +using MailKit.Security; +using Microsoft.Extensions.Options; +using MimeKit; +using MimeKit.Text; + +namespace Elektrifikatsiya.Services.Implementations +{ + public class EmailService : IEmailService + { + private readonly EmailSettings emailSettings; + + public EmailService(IOptions emailSettings) + { + this.emailSettings = emailSettings.Value; + } + + public async Task SendAsync(string to, string subject, string html, string? from = null) + { + from ??= emailSettings.DefaultEmail; + + MimeMessage message = new MimeMessage(); + message.From.Add(MailboxAddress.Parse(from)); + message.To.Add(MailboxAddress.Parse(to)); + message.Subject = subject; + message.Body = new TextPart(TextFormat.Html) { Text = html }; + + using SmtpClient smtp = new SmtpClient(); + await smtp.ConnectAsync(emailSettings.SmtpServer, emailSettings.SmtpPort, SecureSocketOptions.StartTls); + await smtp.AuthenticateAsync(emailSettings.User, emailSettings.Key); + await smtp.SendAsync(message); + await smtp.DisconnectAsync(true); + } + + public Task SendWithTemeplateAsync(string to, string subject, string templateKey, string? from = null, Dictionary? templateParameters = null) + { + string content = emailSettings.CompiledTemplates[templateKey](templateParameters ?? new()); + return SendAsync(to, subject, content, from); + + } + } +} diff --git a/src/Elektrifikatsiya/Elektrifikatsiya/Utilities/EmailSettings.cs b/src/Elektrifikatsiya/Elektrifikatsiya/Utilities/EmailSettings.cs new file mode 100644 index 0000000..4e53927 --- /dev/null +++ b/src/Elektrifikatsiya/Elektrifikatsiya/Utilities/EmailSettings.cs @@ -0,0 +1,23 @@ +using HandlebarsDotNet; + +namespace Elektrifikatsiya.Utilities; + +public class EmailSettings +{ + public string User { get; set; } + public string DefaultEmail { get; set; } + public string Key { get; set; } + public Dictionary Templates { get; set; } + public string SmtpServer { get; set; } + public int SmtpPort { get; set; } + + public Dictionary> CompiledTemplates = new(); + + public void CompileTemplates() + { + foreach ((string templateName, string templatePath) in Templates) + { + CompiledTemplates.Add(templateName, Handlebars.Compile(File.ReadAllText(templatePath))); + } + } +} \ No newline at end of file diff --git a/src/Elektrifikatsiya/Elektrifikatsiya/appsettings.Development.json b/src/Elektrifikatsiya/Elektrifikatsiya/appsettings.Development.json index 770d3e9..995b937 100644 --- a/src/Elektrifikatsiya/Elektrifikatsiya/appsettings.Development.json +++ b/src/Elektrifikatsiya/Elektrifikatsiya/appsettings.Development.json @@ -1,9 +1,19 @@ { - "DetailedErrors": true, - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft.AspNetCore": "Warning" + "DetailedErrors": true, + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "EmailSettings": { + "User": "", + "DefaultEmail": "", + "Key": "", + "Templates": { + + }, + "SmtpServer": "", + "SmtpPort": 0 } - } } diff --git a/src/Elektrifikatsiya/assets/DailyNotif.html b/src/Elektrifikatsiya/assets/DailyNotif.html new file mode 100644 index 0000000..ab13c0d --- /dev/null +++ b/src/Elektrifikatsiya/assets/DailyNotif.html @@ -0,0 +1,26 @@ +
Daily Notification
Energy Consumption yesterday: {{yesterday}}
Energy Consumption since the start of the year: {{yearly}}
\ No newline at end of file diff --git a/src/Elektrifikatsiya/assets/DailyNotif.mjml b/src/Elektrifikatsiya/assets/DailyNotif.mjml new file mode 100644 index 0000000..a4cfbb6 --- /dev/null +++ b/src/Elektrifikatsiya/assets/DailyNotif.mjml @@ -0,0 +1,49 @@ + + + + + + + @font-face { + font-family: Nunito; + src: url(https://fonts.gstatic.com/s/nunito/v16/XRXV3I6Li01BKofINeaB.woff2) + format('truetype'); + } + + + + + + + + + + Daily Notification + + + + + + + Energy Consumption yesterday: {{yesterday}} + + + + + Energy Consumption since the start of the year: {{yearly}} + + + + + + + + Goto Dashboard + + + + + + + + \ No newline at end of file