Projekt

Obecné

Profil

« Předchozí | Další » 

Revize 331310c6

Přidáno uživatelem Dominik Chlouba před téměř 4 roky(ů)

Implementatio of SMTP and SendGrid email services, email templating, base template value object

Zobrazit rozdíly:

Leuze.sln
53 53
EndProject
54 54
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Leuze.Modules.Goal.Infrastructure.Persistence", "src\Modules\Goal\Infrastructure\Leuze.Modules.Goal.Infrastructure.Persistence\Leuze.Modules.Goal.Infrastructure.Persistence.csproj", "{BEBFBA58-BCB7-4F44-9247-EAAAE748176A}"
55 55
EndProject
56
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Leuze.Core.Infrastructure.Email", "src\Core\Infrastructure\Leuze.Core.Infrastructure.Email\Leuze.Core.Infrastructure.Email.csproj", "{A54445C7-26EC-4314-B64E-A08B02A46B6E}"
57
EndProject
56 58
Global
57 59
	GlobalSection(SolutionConfigurationPlatforms) = preSolution
58 60
		Debug|Any CPU = Debug|Any CPU
......
111 113
		{BEBFBA58-BCB7-4F44-9247-EAAAE748176A}.Debug|Any CPU.Build.0 = Debug|Any CPU
112 114
		{BEBFBA58-BCB7-4F44-9247-EAAAE748176A}.Release|Any CPU.ActiveCfg = Release|Any CPU
113 115
		{BEBFBA58-BCB7-4F44-9247-EAAAE748176A}.Release|Any CPU.Build.0 = Release|Any CPU
116
		{A54445C7-26EC-4314-B64E-A08B02A46B6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
117
		{A54445C7-26EC-4314-B64E-A08B02A46B6E}.Debug|Any CPU.Build.0 = Debug|Any CPU
118
		{A54445C7-26EC-4314-B64E-A08B02A46B6E}.Release|Any CPU.ActiveCfg = Release|Any CPU
119
		{A54445C7-26EC-4314-B64E-A08B02A46B6E}.Release|Any CPU.Build.0 = Release|Any CPU
114 120
	EndGlobalSection
115 121
	GlobalSection(SolutionProperties) = preSolution
116 122
		HideSolutionNode = FALSE
......
137 143
		{C6F9EFF2-30DA-4FA9-AEFF-4957F0260267} = {1AEE9C09-804F-4AF7-B966-80830B39C85F}
138 144
		{BAAC1AB6-C671-408C-94EF-96459D5403DD} = {4A441C7D-2659-4FB5-8886-420728C229BF}
139 145
		{BEBFBA58-BCB7-4F44-9247-EAAAE748176A} = {E4134FEF-ECBC-4EEF-A65B-8E8D318DA8DA}
146
		{A54445C7-26EC-4314-B64E-A08B02A46B6E} = {6A0F1F61-D0EB-4132-B722-5787D7046CC0}
140 147
	EndGlobalSection
141 148
	GlobalSection(ExtensibilityGlobals) = postSolution
142 149
		SolutionGuid = {849C3668-1CB9-4F98-ADFA-A71F5629384C}
src/Core/Application/Leuze.Core.Application.UI/Components/Login/Form.razor
1 1
@inject IMediator _mediator
2 2
@inject NavigationManager _navManager
3
@inject IToastService toastService
3 4
@inject IStringLocalizer<Form> T
4 5

  
5 6
<Styled @bind-Classname="@_form">
......
132 133

  
133 134
    private async Task Change()
134 135
    {
135
        /*var result = await _mediator.Send(new VerifyLocalUser.Command(Email, Password));
136
        _navManager.TryGetQueryString<string>("token", out var token);
137
        _navManager.TryGetQueryString<string>("user", out var user);
138
        token = token.Replace(" ", "+");
139
        var result = await _mediator.Send(new ResetLocalUserPassword.Command(user, token, Password));
136 140

  
137 141
        if (!result.IsSuccess) Error = result.Errors.First();
138
        else _navManager.NavigateTo("/");*/
139

  
140
        //TODO: Change password
142
        else
143
        {
144
            toastService.ShowSuccess("Heslo úspěšně nastaveno");
145
            _navManager.NavigateTo("/");
146
        }
141 147

  
142 148
    }
143 149

  
src/Core/Application/Leuze.Core.Application.UI/Extensions/NavigationManagerExtensions.cs
1
using Microsoft.AspNetCore.Components;
2
using Microsoft.AspNetCore.WebUtilities;
3
using System;
4
using System.Collections.Generic;
5
using System.Linq;
6
using System.Threading.Tasks;
7

  
8
namespace Leuze.Core.Application.UI.Extensions
9
{
10
    /// <summary>
11
    /// 
12
    /// </summary>
13
    public static class NavigationManagerExtensions
14
    {
15
        public static bool TryGetQueryString<T>(this NavigationManager navManager, string key, out T value)
16
        {
17
            var uri = navManager.ToAbsoluteUri(navManager.Uri);
18

  
19
            if (QueryHelpers.ParseQuery(uri.Query).TryGetValue(key, out var valueFromQueryString))
20
            {
21
                if (typeof(T) == typeof(int) && int.TryParse(valueFromQueryString, out var valueAsInt))
22
                {
23
                    value = (T)(object)valueAsInt;
24
                    return true;
25
                }
26

  
27
                if (typeof(T) == typeof(string))
28
                {
29
                    value = (T)(object)valueFromQueryString.ToString();
30
                    return true;
31
                }
32

  
33
                if (typeof(T) == typeof(decimal) && decimal.TryParse(valueFromQueryString, out var valueAsDecimal))
34
                {
35
                    value = (T)(object)valueAsDecimal;
36
                    return true;
37
                }
38
            }
39

  
40
            value = default;
41
            return false;
42
        }
43
    }
44
}
src/Core/Application/Leuze.Core.Application.UI/_Imports.razor
36 36
@using ChartJs.Blazor.Common.Handlers
37 37
@using ChartJs.Blazor.Common.Time
38 38
@using ChartJs.Blazor.Util
39
@using Leuze.Core.Application.UI.Extensions
39 40
@using ChartJs.Blazor.Interop
src/Core/Application/Leuze.Core.Application/CQRS/Users/Commands/CreateLocalUser.cs
2 2
using Leuze.Core.Application.Identity;
3 3
using Leuze.Core.Domain.Domains.Users;
4 4
using Leuze.Core.Domain.Repositories;
5
using Leuze.Core.Domain.Services;
6
using Leuze.Core.Domain.Services.Models.Email;
5 7
using Microsoft.AspNetCore.Http;
6 8
using Microsoft.AspNetCore.Identity;
7
using Microsoft.AspNetCore.Identity.UI.Services;
8 9
using Microsoft.EntityFrameworkCore;
9 10
using Microsoft.Extensions.Options;
10 11
using System;
11 12
using System.Collections.Generic;
12 13
using System.Linq;
14
using System.Net.Mail;
13 15
using System.Threading;
14 16
using System.Threading.Tasks;
15 17

  
......
41 43
            private readonly RoleManager<ApplicationRole> _roleManager;
42 44
            private readonly AppSettings _appSettings;
43 45
            private readonly IHttpContextAccessor _httpContextAccessor;
46
            private readonly IEmailSender _emailSender;
44 47

  
45 48
            /// <summary>
46 49
            /// 
47 50
            /// </summary>
48 51
            /// <param name="domainUserRepository"></param>
49 52
            /// <param name="userManager"></param>
53
            /// <param name="roleManager"></param>
50 54
            /// <param name="options"></param>
51 55
            /// <param name="httpContextAccessor"></param>
52 56
            /// <param name="userRepository"></param>
57
            /// <param name="emailSender"></param>
53 58
            public Handler(IDomainUserRepository domainUserRepository, UserManager<ApplicationUser> userManager, RoleManager<ApplicationRole> roleManager,
54
                IOptions<AppSettings> options, IHttpContextAccessor httpContextAccessor, IUserRepository userRepository)
55
                => (_domainUserRepository, _userManager, _appSettings, _httpContextAccessor, _userRepository, _roleManager)
56
                        = (domainUserRepository, userManager, options.Value, httpContextAccessor, userRepository, roleManager);
59
                IOptions<AppSettings> options, IHttpContextAccessor httpContextAccessor, IUserRepository userRepository, IEmailSender emailSender)
60
                => (_domainUserRepository, _userManager, _appSettings, _httpContextAccessor, _userRepository, _roleManager, _emailSender)
61
                        = (domainUserRepository, userManager, options.Value, httpContextAccessor, userRepository, roleManager, emailSender);
57 62

  
58 63
            /// <summary>
59 64
            /// 
......
85 90

  
86 91
                var resetToken = await _userManager.GeneratePasswordResetTokenAsync(user);
87 92

  
88
                /*await _emailSender.SendAsync(new NewUserTemplate(
93
                await _emailSender.SendAsync(new NewUserTemplate(
89 94
                    null!,
90
                    new MailAddress(domainUser.Email.GetEmailAddress()),
95
                    new MailAddress(domainUser.EmailAddress.Email),
91 96
                    "Vytvořen účet - Leuze",
92
                    new() { ResetLink = $"{_httpContextAccessor.HttpContext!.Request.Scheme}://{_httpContextAccessor.HttpContext!.Request.Host}/{_appSettings.Authentication.ResetUrl}/?token={resetToken}&user={user.Email}" }
93
                ));*/
97
                    new() { ResetLink = $"{_httpContextAccessor.HttpContext!.Request.Scheme}://{_httpContextAccessor.HttpContext!.Request.Host}/{_appSettings.ResetUrl}/?token={resetToken}&user={user.Email}" }
98
                ));
94 99

  
95 100
                return RequestResponse<Response>.CreateSuccessResponse(new(resetToken));
96 101
            }
src/Core/Application/Leuze.Core.Application/Configuration/AppSettings.cs
16 16
        /// </summary>
17 17
        public EncryptionSection Encryption { get; set; } = new();
18 18

  
19
        /// <summary>
20
        /// 
21
        /// </summary>
22
        public string ResetUrl { get; set; } = null!;
23

  
19 24
        /// <summary>
20 25
        /// 
21 26
        /// </summary>
src/Core/Domain/Leuze.Core.Domain/Leuze.Core.Domain.csproj
22 22
    <PackageReference Include="ExtCore.Data.Abstractions" Version="6.0.0" />
23 23
    <PackageReference Include="ExtCore.Data.Entities.Abstractions" Version="6.0.0" />
24 24
    <PackageReference Include="MediatR" Version="9.0.0" />
25
    <PackageReference Include="FluentEmail.Razor" Version="3.0.0" />
26
    <PackageReference Include="FluentEmail.SendGrid" Version="3.0.0" />
27
    <PackageReference Include="FluentEmail.Smtp" Version="3.0.0" />
25 28
  </ItemGroup>
26 29

  
27 30
  <ItemGroup>
28 31
    <ProjectReference Include="..\..\Application\Leuze.Core.Application.Identity\Leuze.Core.Application.Identity.csproj" />
29 32
  </ItemGroup>
30 33

  
31
  <ItemGroup>
32
    <Folder Include="Services\" />
33
  </ItemGroup>
34

  
35 34
</Project>
src/Core/Domain/Leuze.Core.Domain/Services/Models/Email/NewUserTemplate.cs
1
using System.Net.Mail;
2

  
3
namespace Leuze.Core.Domain.Services.Models.Email
4
{
5
    /// <summary>
6
    /// 
7
    /// </summary>
8
    public class NewUserTemplate : BaseEmailTemplate<NewUserTemplateValues>
9
    {
10

  
11
        private static string TEMPLATE_FILE = "NewUserTemplate.cshtml";
12

  
13
        /// <summary>
14
        /// 
15
        /// </summary>
16
        /// <param name="from"></param>
17
        /// <param name="to"></param>
18
        /// <param name="subject"></param>
19
        /// <param name="values"></param>
20
        public NewUserTemplate(
21
            MailAddress from,
22
            MailAddress to,
23
            string subject,
24
            NewUserTemplateValues values
25
        ) : base(from, to, subject, values, TEMPLATE_FILE)
26
        {
27
        }
28
    }
29

  
30
    /// <summary>
31
    /// 
32
    /// </summary>
33
    public sealed record NewUserTemplateValues
34
    {
35
        /// <summary>
36
        /// /
37
        /// </summary>
38
        public string ResetLink { get; set; } = null!;
39

  
40
    }
41
}
src/Core/Domain/Leuze.Core.Domain/Services/Models/Email/ResetPasswordTemplate.cs
1
using System.Net.Mail;
2

  
3
namespace Leuze.Core.Domain.Services.Models.Email
4
{
5
    public class ResetPasswordTemplate : BaseEmailTemplate<ResetPasswordTemplateValues>
6
    {
7

  
8
        public static string WIZARD_SHORT_LINK_TEMPLATE_FILE = "ResetPasswordTemplate.cshtml";
9

  
10
        public ResetPasswordTemplate(
11
            MailAddress from,
12
            MailAddress to,
13
            string subject,
14
            ResetPasswordTemplateValues values
15
        ) : base(from, to, subject, values, WIZARD_SHORT_LINK_TEMPLATE_FILE)
16
        {
17
        }
18
    }
19

  
20
    public sealed record ResetPasswordTemplateValues
21
    {
22

  
23
        public string ResetLink { get; set; } = null!;
24

  
25
    }
26
}
src/Core/Infrastructure/Leuze.Core.Infrastructure.Email/Configuration/EmailSettings.cs
10 10
        /// </summary>
11 11
        public SmtpSection Smtp { get; set; } = new();
12 12

  
13
        /// <summary>
14
        /// 
15
        /// </summary>
16
        public SendGridSection SendGrid { get; set; } = new();
17

  
13 18
        /// <summary>
14 19
        ///     Specifies email template root path.
15 20
        /// </summary>
16 21
        public string AdminEmailAddress { get; set; } = null!;
17 22

  
23
        /// <summary>
24
        /// 
25
        /// </summary>
26
        public bool UseSendGrid { get; set; } = false;
27

  
18 28
        /// <summary>
19 29
        ///     A wrapper class for <see cref="EmailSettings" /> Smtp configuration section.
20 30
        /// </summary>
......
40 50
            /// </summary>
41 51
            public int Port { get; set; }
42 52
        }
53
        
54
        /// <summary>
55
        /// 
56
        /// </summary>
57
        public sealed class SendGridSection
58
        {
59

  
60
            /// <summary>
61
            /// 
62
            /// </summary>
63
            public string ApiKey { get; set; } = null!;
64

  
65
        }
43 66
    }
44 67
}
src/Core/Infrastructure/Leuze.Core.Infrastructure.Email/Extensions/EmailProviderExtension.cs
17 17
        /// <param name="emailSettings"></param>
18 18
        public static void AddEmailProvider(this IServiceCollection services, EmailSettings emailSettings)
19 19
        {
20
            services
21
                .AddFluentEmail(emailSettings.AdminEmailAddress)
22
                .AddSmtpSender(new SmtpClient()
20
            services.AddScoped((services) =>
21
            {
22
                return new SmtpClient()
23 23
                {
24 24
                    Host = emailSettings.Smtp.Host,
25 25
                    Port = emailSettings.Smtp.Port,
26 26
                    EnableSsl = true,
27 27
                    Credentials = new NetworkCredential(emailSettings.Smtp.User, emailSettings.Smtp.Password)
28
                })
28
                };
29
            });
30

  
31
            services
32
                .AddFluentEmail(emailSettings.AdminEmailAddress)
33
                .AddSendGridSender(emailSettings.SendGrid.ApiKey, false)
29 34
                .AddRazorRenderer($"{Directory.GetCurrentDirectory()}/");
30 35

  
31
            services.AddScoped<IEmailSender, SmtpSender>();
36
            if(emailSettings.UseSendGrid) services.AddScoped<IEmailSender, SendGridSender>();
37
            else services.AddScoped<IEmailSender, SmtpSender>();
32 38
        }
33 39

  
34 40
    }
src/Core/Infrastructure/Leuze.Core.Infrastructure.Email/Leuze.Core.Infrastructure.Email.csproj
21 21
  <ItemGroup>
22 22
    <PackageReference Include="ExtCore.Infrastructure" Version="6.0.0" />
23 23
    <PackageReference Include="FluentEmail.Razor" Version="3.0.0" />
24
    <PackageReference Include="FluentEmail.SendGrid" Version="3.0.0" />
24 25
    <PackageReference Include="FluentEmail.Smtp" Version="3.0.0" />
25 26
  </ItemGroup>
26 27
  
src/Core/Infrastructure/Leuze.Core.Infrastructure.Email/Services/SendGridSender.cs
1
using FluentEmail.Core;
2
using Leuze.Core.Domain.Services;
3
using Leuze.Core.Domain.Services.Models.Email;
4
using Leuze.Core.Infrastructure.Email.Configuration;
5
using Microsoft.Extensions.Options;
6
using System;
7
using System.Collections.Generic;
8
using System.Linq;
9
using System.Net.Mail;
10
using System.Text;
11
using System.Threading.Tasks;
12

  
13
namespace Leuze.Core.Infrastructure.Email.Services
14
{
15
    public class SendGridSender : IEmailSender
16
    {
17
        private readonly IFluentEmail _fluentEmail;
18

  
19
        /// <summary>
20
        /// 
21
        /// </summary>
22
        private readonly EmailSettings _emailSettings;
23

  
24
        /// <summary>
25
        /// 
26
        /// </summary>
27
        /// <param name="options"></param>
28
        /// <param name="fluentEmail"></param>
29
        public SendGridSender(
30
            IOptions<EmailSettings> options,
31
            IFluentEmail fluentEmail
32
        )
33
        {
34
            _fluentEmail = fluentEmail;
35
            _emailSettings = options.Value;
36
        }
37
        
38
        /// <summary>
39
        /// 
40
        /// </summary>
41
        /// <returns></returns>
42
        public MailAddress GetAdminEmailAddress() => new MailAddress(_emailSettings.AdminEmailAddress);
43

  
44
        /// <summary>
45
        /// 
46
        /// </summary>
47
        /// <typeparam name="TValuesType"></typeparam>
48
        /// <param name="template"></param>
49
        /// <returns></returns>
50
        public async Task SendAsync<TValuesType>(BaseEmailTemplate<TValuesType> template)
51
        {
52
            if (template.To is not null)
53
            {
54
                _fluentEmail.To(template.To.Address);
55
                _fluentEmail.Subject(template.Subject);
56
                _fluentEmail.UsingTemplateFromFile($"Templates/{template.TemplatePath}", template.Values);
57

  
58
                await _fluentEmail.SendAsync();
59
            }
60
        }
61
    }
62
}
src/Core/Infrastructure/Leuze.Core.Infrastructure.Email/Services/SmtpSender.cs
27 27
        /// 
28 28
        /// </summary>
29 29
        /// <param name="smtpClient"></param>
30
        /// <param name="renderer"></param>
31 30
        /// <param name="options"></param>
32
        public SmtpSender(SmtpClient smtpClient, RazorRenderer renderer, IOptions<EmailSettings> options) : base(smtpClient) 
33
            => (FluentEmail.Core.Email.DefaultSender, FluentEmail.Core.Email.DefaultRenderer, _emailSettings) 
34
                = (this, renderer, options.Value);
31
        public SmtpSender(
32
            SmtpClient smtpClient,
33
            IOptions<EmailSettings> options
34
        ) : base(smtpClient)
35
        {
36
            FluentEmail.Core.Email.DefaultSender = this;
37
            FluentEmail.Core.Email.DefaultRenderer = new RazorRenderer();
38
            _emailSettings = options.Value;
39
        }
35 40

  
36 41
        /// <summary>
37 42
        /// 
......
49 54
            await FluentEmail.Core.Email.From(template.From is not null ? template.From.Address : _emailSettings.AdminEmailAddress)
50 55
                .To(template.To is not null ? template.To.Address : _emailSettings.AdminEmailAddress)
51 56
                .Subject(template.Subject)
52
                .UsingTemplateFromFile($"Templates/{template.TemplatePath}", template.Values)
57
                .UsingTemplateFromFile($"{Directory.GetCurrentDirectory()}/Templates/{template.TemplatePath}", template.Values)
53 58
                .SendAsync();
54 59
        }
55 60

  
src/Presentation/Leuze.App/Leuze.App.csproj
12 12
    <Nullable>enable</Nullable>
13 13
    <GenerateDocumentationFile>true</GenerateDocumentationFile>
14 14
    <UserSecretsId>61d7eb1b-7e84-4d33-bf02-87517697a10c</UserSecretsId>
15
    <PreserveCompilationReferences>true</PreserveCompilationReferences>
16
    <PreserveCompilationContext>true</PreserveCompilationContext>
15 17
  </PropertyGroup>
16 18

  
17 19
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
src/Presentation/Leuze.App/Templates/NewUserTemplate.cshtml
1
@{
2
    <p>Nový účet vytvořen. Nastavte si heslo: </p>
3
    <p>@Model.ResetLink</p>
4

  
5
}
src/Presentation/Leuze.App/Templates/ResetPasswordTemplate.cshtml
1
@{
2

  
3
    <p>@Model.ResetLink</p>
4

  
5
}
src/Presentation/Leuze.App/appsettings.Development.json
20 20
      "IV": "GY1dGVtY9bKReyFi"
21 21
    }
22 22
  },
23
  "Email": {
24
    "Smtp": {
25
      "Host": "",
26
      "User": "",
27
      "Password": "",
28
      "Port": 587
29
    },
30
    "AdminEmailAddress": ""
31
  },
32 23
  "Serilog": {
33 24
    "Using": [],
34 25
    "MinimumLevel": {
src/Presentation/Leuze.App/appsettings.json
18 18
    "Encryption": {
19 19
      "Key": "GY1dGVtY9bKReyFiGap1Ba7zKbZGvZWZ",
20 20
      "IV": "GY1dGVtY9bKReyFi"
21
    }
21
    },
22
    "ResetUrl": "Identity/Account/ResetPassword"
22 23
  },
23 24
  "Email": {
24 25
    "Smtp": {
......
27 28
      "Password": "",
28 29
      "Port": 587
29 30
    },
30
    "AdminEmailAddress": ""
31
    "SendGrid": {
32
      "ApiKey": ""
33
    },
34
    "UseSendGrid": false,
35
    "AdminEmailAddress": "hello@leuze.com"
31 36
  },
32 37
  "Serilog": {
33 38
    "Using": [],

Také k dispozici: Unified diff