mirror of https://github.com/Squidex/squidex.git
26 changed files with 466 additions and 23 deletions
@ -0,0 +1,19 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System.Threading.Tasks; |
|||
using Squidex.Shared.Users; |
|||
|
|||
namespace Squidex.Domain.Apps.Entities.Apps.Invitiation |
|||
{ |
|||
public interface IInvitationEmailSender |
|||
{ |
|||
Task SendNewUserEmailAsync(IUser assigner, IUser assignee, string appName); |
|||
|
|||
Task SendExistingUserEmailAsync(IUser assigner, IUser assignee, string appName); |
|||
} |
|||
} |
|||
@ -0,0 +1,97 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System.Threading.Tasks; |
|||
using Squidex.Domain.Apps.Events.Apps; |
|||
using Squidex.Infrastructure; |
|||
using Squidex.Infrastructure.EventSourcing; |
|||
using Squidex.Infrastructure.Log; |
|||
using Squidex.Infrastructure.Tasks; |
|||
using Squidex.Shared.Users; |
|||
|
|||
namespace Squidex.Domain.Apps.Entities.Apps.Invitiation |
|||
{ |
|||
public sealed class InvitationEmailEventConsumer : IEventConsumer |
|||
{ |
|||
private readonly IInvitationEmailSender emailSender; |
|||
private readonly IUserResolver userResolver; |
|||
private readonly ISemanticLog log; |
|||
|
|||
public string Name |
|||
{ |
|||
get { return "InvitationEmailSender"; } |
|||
} |
|||
|
|||
public string EventsFilter |
|||
{ |
|||
get { return "^app-"; } |
|||
} |
|||
|
|||
public InvitationEmailEventConsumer(IInvitationEmailSender emailSender, IUserResolver userResolver, ISemanticLog log) |
|||
{ |
|||
Guard.NotNull(emailSender, nameof(emailSender)); |
|||
Guard.NotNull(userResolver, nameof(userResolver)); |
|||
Guard.NotNull(log, nameof(log)); |
|||
|
|||
this.emailSender = emailSender; |
|||
this.userResolver = userResolver; |
|||
|
|||
this.log = log; |
|||
} |
|||
|
|||
public bool Handles(StoredEvent @event) |
|||
{ |
|||
return true; |
|||
} |
|||
|
|||
public Task ClearAsync() |
|||
{ |
|||
return TaskHelper.Done; |
|||
} |
|||
|
|||
public async Task On(Envelope<IEvent> @event) |
|||
{ |
|||
if (@event.Payload is AppContributorAssigned appContributorAssigned && appContributorAssigned.Actor.IsClient) |
|||
{ |
|||
var assigner = await userResolver.FindByIdOrEmailAsync(appContributorAssigned.Actor.Identifier); |
|||
|
|||
if (assigner == null) |
|||
{ |
|||
LogWarning($"Assigner {appContributorAssigned.Actor.Identifier} not found"); |
|||
return; |
|||
} |
|||
|
|||
var assignee = await userResolver.FindByIdOrEmailAsync(appContributorAssigned.ContributorId); |
|||
|
|||
if (assignee == null) |
|||
{ |
|||
LogWarning($"Assignee {appContributorAssigned.ContributorId} not found"); |
|||
return; |
|||
} |
|||
|
|||
var appName = appContributorAssigned.AppId.Name; |
|||
|
|||
if (appContributorAssigned.IsCreated) |
|||
{ |
|||
await emailSender.SendNewUserEmailAsync(assigner, assignee, appName); |
|||
} |
|||
else |
|||
{ |
|||
await emailSender.SendExistingUserEmailAsync(assigner, assignee, appName); |
|||
} |
|||
} |
|||
} |
|||
|
|||
private void LogWarning(string reason) |
|||
{ |
|||
log.LogWarning(w => w |
|||
.WriteProperty("action", "InviteUser") |
|||
.WriteProperty("status", "Failed") |
|||
.WriteProperty("reason", reason)); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,102 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System.Threading.Tasks; |
|||
using Microsoft.Extensions.Options; |
|||
using Squidex.Infrastructure; |
|||
using Squidex.Infrastructure.Email; |
|||
using Squidex.Infrastructure.Log; |
|||
using Squidex.Shared.Users; |
|||
|
|||
namespace Squidex.Domain.Apps.Entities.Apps.Invitiation |
|||
{ |
|||
public sealed class InvitationEmailSender : IInvitationEmailSender |
|||
{ |
|||
private readonly IEmailSender emailSender; |
|||
private readonly IEmailUrlGenerator emailUrlGenerator; |
|||
private readonly ISemanticLog log; |
|||
private readonly InvitationEmailTextOptions texts; |
|||
|
|||
public InvitationEmailSender( |
|||
IOptions<InvitationEmailTextOptions> texts, |
|||
IEmailSender emailSender, |
|||
IEmailUrlGenerator emailUrlGenerator, |
|||
ISemanticLog log) |
|||
{ |
|||
Guard.NotNull(texts, nameof(texts)); |
|||
Guard.NotNull(emailSender, nameof(emailSender)); |
|||
Guard.NotNull(emailUrlGenerator, nameof(emailUrlGenerator)); |
|||
Guard.NotNull(log, nameof(log)); |
|||
|
|||
this.texts = texts.Value; |
|||
this.emailSender = emailSender; |
|||
this.emailUrlGenerator = emailUrlGenerator; |
|||
this.log = log; |
|||
} |
|||
|
|||
public Task SendExistingUserEmailAsync(IUser assigner, IUser assignee, string appName) |
|||
{ |
|||
return SendEmailAsync(texts.NewUserSubject, texts.NewUserBody, assigner, assignee, appName); |
|||
} |
|||
|
|||
public Task SendNewUserEmailAsync(IUser assigner, IUser assignee, string appName) |
|||
{ |
|||
return SendEmailAsync(texts.ExistingUserBody, texts.ExistingUserSubject, assigner, assignee, appName); |
|||
} |
|||
|
|||
private async Task SendEmailAsync(string emailBody, string emailSubj, IUser assigner, IUser assignee, string appName) |
|||
{ |
|||
if (string.IsNullOrWhiteSpace(texts.NewUserSubject)) |
|||
{ |
|||
LogWarning("No email subject configured for new users"); |
|||
return; |
|||
} |
|||
|
|||
if (string.IsNullOrWhiteSpace(texts.NewUserBody)) |
|||
{ |
|||
LogWarning("No email body configured for new users"); |
|||
return; |
|||
} |
|||
|
|||
var appUrl = emailUrlGenerator.GenerateUIUrl(); |
|||
|
|||
emailSubj = Format(emailSubj, assigner, assignee, appUrl, appName); |
|||
emailBody = Format(emailBody, assigner, assignee, appUrl, appName); |
|||
|
|||
await emailSender.SendAsync(assignee.Email, emailSubj, emailBody); |
|||
} |
|||
|
|||
private void LogWarning(string reason) |
|||
{ |
|||
log.LogWarning(w => w |
|||
.WriteProperty("action", "InviteUser") |
|||
.WriteProperty("status", "Failed") |
|||
.WriteProperty("reason", reason)); |
|||
} |
|||
|
|||
private string Format(string text, IUser assigner, IUser assignee, string uiUrl, string appName) |
|||
{ |
|||
text = text.Replace("$APP_NAME", appName); |
|||
|
|||
text = text.Replace("$UI_URL", uiUrl); |
|||
|
|||
if (assigner != null) |
|||
{ |
|||
text = text.Replace("$ASSIGNER_EMAIL", assigner.Email); |
|||
text = text.Replace("$ASSIGNER_NAME", assigner.DisplayName()); |
|||
} |
|||
|
|||
if (assignee != null) |
|||
{ |
|||
text = text.Replace("$ASSIGNEE_EMAIL", assignee.Email); |
|||
text = text.Replace("$ASSIGNEE_NAME", assignee.DisplayName()); |
|||
} |
|||
|
|||
return text; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,20 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
namespace Squidex.Domain.Apps.Entities.Apps.Invitiation |
|||
{ |
|||
public sealed class InvitationEmailTextOptions |
|||
{ |
|||
public string NewUserSubject { get; set; } |
|||
|
|||
public string NewUserBody { get; set; } |
|||
|
|||
public string ExistingUserSubject { get; set; } |
|||
|
|||
public string ExistingUserBody { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,14 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
namespace Squidex.Domain.Apps.Entities |
|||
{ |
|||
public interface IEmailUrlGenerator |
|||
{ |
|||
string GenerateUIUrl(); |
|||
} |
|||
} |
|||
@ -0,0 +1,16 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System.Threading.Tasks; |
|||
|
|||
namespace Squidex.Infrastructure.Email |
|||
{ |
|||
public interface IEmailSender |
|||
{ |
|||
Task SendAsync(string recipient, string subject, string text); |
|||
} |
|||
} |
|||
@ -0,0 +1,33 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
namespace Squidex.Infrastructure.Email |
|||
{ |
|||
public sealed class SmptOptions |
|||
{ |
|||
public string Server { get; set; } |
|||
|
|||
public string Sender { get; set; } |
|||
|
|||
public string Username { get; set; } |
|||
|
|||
public string Password { get; set; } |
|||
|
|||
public bool EnableSsl { get; set; } |
|||
|
|||
public int Port { get; set; } = 587; |
|||
|
|||
public bool IsConfigured() |
|||
{ |
|||
return |
|||
!string.IsNullOrWhiteSpace(Server) && |
|||
!string.IsNullOrWhiteSpace(Sender) && |
|||
!string.IsNullOrWhiteSpace(Username) && |
|||
!string.IsNullOrWhiteSpace(Password); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,42 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System.Net; |
|||
using System.Net.Mail; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.Extensions.Options; |
|||
|
|||
namespace Squidex.Infrastructure.Email |
|||
{ |
|||
public sealed class SmtpEmailSender : IEmailSender |
|||
{ |
|||
private readonly SmtpClient smtpClient; |
|||
private readonly string sender; |
|||
|
|||
public SmtpEmailSender(IOptions<SmptOptions> options) |
|||
{ |
|||
Guard.NotNull(options, nameof(options)); |
|||
|
|||
var config = options.Value; |
|||
|
|||
smtpClient = new SmtpClient(config.Server, config.Port) |
|||
{ |
|||
Credentials = new NetworkCredential( |
|||
config.Username, |
|||
config.Password), |
|||
EnableSsl = config.EnableSsl |
|||
}; |
|||
|
|||
sender = config.Sender; |
|||
} |
|||
|
|||
public Task SendAsync(string recipient, string subject, string body) |
|||
{ |
|||
return smtpClient.SendMailAsync(sender, recipient, subject, body); |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue