Skip to content

Commit 9bc7d44

Browse files
authored
Add send grid as alternative mailing option (#1162)
* Include .ico in theme favicon upload. Configure JWT not to block debug when no profile pic * Use SendGrid as Alternative mailing Client * Handle TenantThemeSate restore after login. * Update as per request * Added ArgumentNullException on settings of null
1 parent b26d2d6 commit 9bc7d44

11 files changed

Lines changed: 214 additions & 48 deletions

File tree

src/BuildingBlocks/Blazor.UI/Components/Theme/FshThemeCustomizer.razor

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,36 +220,42 @@
220220

221221
await OnAssetUpload.InvokeAsync((args.File, args.AssetType, setAsset));
222222
StateHasChanged();
223+
if (ThemeState != null) ThemeState.UpdateSettings(_settings);
223224
}
224225

225226
private void OnLightPaletteChanged(PaletteSettings palette)
226227
{
227228
_settings.LightPalette = palette;
228229
StateHasChanged();
230+
if (ThemeState != null) ThemeState.UpdateSettings(_settings);
229231
}
230232

231233
private void OnDarkPaletteChanged(PaletteSettings palette)
232234
{
233235
_settings.DarkPalette = palette;
234236
StateHasChanged();
237+
if (ThemeState != null) ThemeState.UpdateSettings(_settings);
235238
}
236239

237240
private void OnBrandAssetsChanged(BrandAssets assets)
238241
{
239242
_settings.BrandAssets = assets;
240243
StateHasChanged();
244+
if (ThemeState != null) ThemeState.UpdateSettings(_settings);
241245
}
242246

243247
private void OnTypographyChanged(TypographySettings typography)
244248
{
245249
_settings.Typography = typography;
246250
StateHasChanged();
251+
if (ThemeState != null) ThemeState.UpdateSettings(_settings);
247252
}
248253

249254
private void OnLayoutChanged(LayoutSettings layout)
250255
{
251256
_settings.Layout = layout;
252257
StateHasChanged();
258+
if (ThemeState != null) ThemeState.UpdateSettings(_settings);
253259
}
254260

255261
private static TenantThemeSettings CloneSettings(TenantThemeSettings source)

src/BuildingBlocks/Mailing/Extensions.cs

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,19 @@ public static class Extensions
88
{
99
public static IServiceCollection AddHeroMailing(this IServiceCollection services)
1010
{
11-
services.AddTransient<IMailService, SmtpMailService>();
12-
services
13-
.AddOptions<MailOptions>()
11+
services.AddOptions<MailOptions>()
1412
.BindConfiguration(nameof(MailOptions))
15-
.Validate(o => !string.IsNullOrWhiteSpace(o.From), "MailOptions: From is required.")
16-
.Validate(o => !string.IsNullOrWhiteSpace(o.Host), "MailOptions: Host is required.")
17-
.Validate(o => o.Port > 0, "MailOptions: Port must be greater than zero.")
18-
.Validate(o => !string.IsNullOrWhiteSpace(o.UserName), "MailOptions: UserName is required.")
19-
.Validate(o => !string.IsNullOrWhiteSpace(o.Password), "MailOptions: Password is required.")
2013
.ValidateOnStart();
14+
15+
services.AddTransient<IMailService>(sp =>
16+
{
17+
var options = sp.GetRequiredService<IOptions<MailOptions>>().Value;
18+
if (options.UseSendGrid)
19+
{
20+
return new SendGridMailService(sp.GetRequiredService<IOptions<MailOptions>>());
21+
}
22+
return new SmtpMailService(sp.GetRequiredService<IOptions<MailOptions>>(), sp.GetRequiredService<Microsoft.Extensions.Logging.ILogger<SmtpMailService>>());
23+
});
2124
return services;
2225
}
2326
}

src/BuildingBlocks/Mailing/MailOptions.cs

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,24 @@
22

33
public class MailOptions
44
{
5+
public bool UseSendGrid { get; set; }
56
public string? From { get; set; }
7+
public string? DisplayName { get; set; }
8+
public SmtpOptions? Smtp { get; set; }
9+
public SendGridOptions? SendGrid { get; set; }
10+
}
611

12+
public class SmtpOptions
13+
{
714
public string? Host { get; set; }
8-
915
public int Port { get; set; }
10-
1116
public string? UserName { get; set; }
12-
1317
public string? Password { get; set; }
18+
}
1419

20+
public class SendGridOptions
21+
{
22+
public string? ApiKey { get; set; }
23+
public string? From { get; set; }
1524
public string? DisplayName { get; set; }
16-
}
25+
}

src/BuildingBlocks/Mailing/Mailing.csproj

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,12 @@
77
</PropertyGroup>
88

99
<ItemGroup>
10-
<PackageReference Include="MailKit" />
11-
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" />
12-
<PackageReference Include="Microsoft.Extensions.Options" />
13-
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" />
14-
<PackageReference Include="MimeKit" />
10+
<PackageReference Include="MailKit" />
11+
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" />
12+
<PackageReference Include="Microsoft.Extensions.Options" />
13+
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" />
14+
<PackageReference Include="MimeKit" />
15+
<PackageReference Include="SendGrid" />
1516
</ItemGroup>
1617

1718
<ItemGroup>
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
using Microsoft.Extensions.Options;
2+
using SendGrid;
3+
using SendGrid.Helpers.Mail;
4+
using System.Threading;
5+
using System.Threading.Tasks;
6+
7+
namespace FSH.Framework.Mailing.Services;
8+
9+
public class SendGridMailService : IMailService
10+
{
11+
private readonly MailOptions _settings;
12+
13+
public SendGridMailService(IOptions<MailOptions> settings)
14+
{
15+
ArgumentNullException.ThrowIfNull(settings);
16+
_settings = settings.Value;
17+
}
18+
19+
public async Task SendAsync(MailRequest request, CancellationToken ct)
20+
{
21+
ArgumentNullException.ThrowIfNull(request);
22+
if (_settings.SendGrid?.ApiKey is null)
23+
throw new InvalidOperationException("SendGrid ApiKey is not configured.");
24+
25+
var client = new SendGridClient(_settings.SendGrid.ApiKey);
26+
var from = new EmailAddress(request.From ?? _settings.SendGrid.From ?? _settings.From, request.DisplayName ?? _settings.SendGrid.DisplayName ?? _settings.DisplayName);
27+
var msg = MailHelper.CreateSingleEmail(
28+
from,
29+
new EmailAddress(request.To[0]),
30+
request.Subject,
31+
request.Body,
32+
request.Body);
33+
34+
if (request.Cc.Count > 0)
35+
msg.AddCcs(request.Cc.Select(cc => new EmailAddress(cc)).ToList());
36+
if (request.Bcc.Count > 0)
37+
msg.AddBccs(request.Bcc.Select(bcc => new EmailAddress(bcc)).ToList());
38+
if (request.ReplyTo != null)
39+
msg.ReplyTo = new EmailAddress(request.ReplyTo, request.ReplyToName);
40+
41+
// Attachments
42+
if (request.AttachmentData.Count > 0)
43+
{
44+
foreach (var att in request.AttachmentData)
45+
{
46+
msg.AddAttachment(att.Key, Convert.ToBase64String(att.Value));
47+
}
48+
}
49+
50+
await client.SendEmailAsync(msg, ct);
51+
}
52+
}

src/BuildingBlocks/Mailing/Services/SmtpMailService.cs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ public async Task SendAsync(MailRequest request, CancellationToken ct)
1515
{
1616
ArgumentNullException.ThrowIfNull(request);
1717

18+
if (_settings.Smtp?.Host is null)
19+
throw new InvalidOperationException("SMTP Host is not configured.");
20+
1821
using var email = new MimeMessage();
1922

2023
// From
@@ -29,14 +32,14 @@ public async Task SendAsync(MailRequest request, CancellationToken ct)
2932
email.ReplyTo.Add(new MailboxAddress(request.ReplyToName, request.ReplyTo));
3033

3134
// Bcc
32-
if (request.Bcc != null)
35+
if (request.Bcc != null && request.Bcc.Count > 0)
3336
{
3437
foreach (string address in request.Bcc.Where(bccValue => !string.IsNullOrWhiteSpace(bccValue)))
3538
email.Bcc.Add(MailboxAddress.Parse(address.Trim()));
3639
}
3740

3841
// Cc
39-
if (request.Cc != null)
42+
if (request.Cc != null && request.Cc.Count > 0)
4043
{
4144
foreach (string? address in request.Cc.Where(ccValue => !string.IsNullOrWhiteSpace(ccValue)))
4245
email.Cc.Add(MailboxAddress.Parse(address.Trim()));
@@ -72,8 +75,8 @@ public async Task SendAsync(MailRequest request, CancellationToken ct)
7275
using var client = new SmtpClient();
7376
try
7477
{
75-
await client.ConnectAsync(_settings.Host, _settings.Port, SecureSocketOptions.StartTls, ct);
76-
await client.AuthenticateAsync(_settings.UserName, _settings.Password, ct);
78+
await client.ConnectAsync(_settings.Smtp.Host, _settings.Smtp.Port, SecureSocketOptions.StartTls, ct);
79+
await client.AuthenticateAsync(_settings.Smtp.UserName, _settings.Smtp.Password, ct);
7780
await client.SendAsync(email, ct);
7881
}
7982
catch (Exception ex)

src/Directory.Packages.props

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@
8787
<PackageVersion Include="Serilog" Version="4.3.1-dev-02395" />
8888
<PackageVersion Include="Serilog.AspNetCore" Version="10.0.0" />
8989
<PackageVersion Include="Serilog.Enrichers.CorrelationId" Version="3.0.1" />
90+
<PackageVersion Include="SendGrid" Version="9.29.3" />
9091
<PackageVersion Include="SonarAnalyzer.CSharp" Version="10.18.0.131500" />
9192
<PackageVersion Include="UAParser" Version="3.1.47" />
9293
<PackageVersion Include="Microsoft.AspNetCore.OpenApi" Version="10.0.2" />

src/Playground/Playground.Api/appsettings.json

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -125,12 +125,20 @@
125125
"StyleSources": []
126126
},
127127
"MailOptions": {
128+
"UseSendGrid": false,
128129
"From": "mukesh@fullstackhero.net",
129-
"Host": "smtp.ethereal.email",
130-
"Port": 587,
131-
"UserName": "anderson22@ethereal.email",
132-
"Password": "rqD44sq5P6U2UDCqD1",
133-
"DisplayName": "Mukesh Murugan"
130+
"DisplayName": "Mukesh Murugan",
131+
"SMTP": {
132+
"Host": "smtp.ethereal.email",
133+
"Port": 587,
134+
"UserName": "anderson22@ethereal.email",
135+
"Password": "rqD44sq5P6U2UDCqD1"
136+
},
137+
"SendGrid": {
138+
"ApiKey": "your-sendgrid-api-key",
139+
"From": "sendgrid@fullstackhero.net",
140+
"DisplayName": "FSH SendGrid"
141+
}
134142
},
135143
"RateLimitingOptions": {
136144
"Enabled": false,

src/Playground/Playground.Blazor/Components/App.razor

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
<Routes @rendermode="InteractiveServer" />
2020
<script src="_framework/blazor.web.js"></script>
2121
<script src=@Assets["_content/MudBlazor/MudBlazor.min.js"]></script>
22+
<script src="@Assets["fsh-theme.js"]"></script>
2223
</body>
2324

2425
</html>

0 commit comments

Comments
 (0)