Adds transactional outbox and email verification
Implements outbox pattern for reliable message delivery Adds email verification flow with Postmark integration Enhances account registration with secure token generation Introduces background processing for asynchronous email sending Implements database-level notification mechanism for message processing
This commit is contained in:
parent
88812177a9
commit
54b057886c
35 changed files with 1174 additions and 358 deletions
71
PlanTempus.Components/Outbox/ProcessOutboxHandler.cs
Normal file
71
PlanTempus.Components/Outbox/ProcessOutboxHandler.cs
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
using System.Text.Json;
|
||||
using Microsoft.ApplicationInsights;
|
||||
using PlanTempus.Core.CommandQueries;
|
||||
using PlanTempus.Core.Email;
|
||||
using PlanTempus.Core.Outbox;
|
||||
|
||||
namespace PlanTempus.Components.Outbox;
|
||||
|
||||
public class ProcessOutboxHandler(
|
||||
IOutboxService outboxService,
|
||||
IEmailService emailService,
|
||||
TelemetryClient telemetryClient) : ICommandHandler<ProcessOutboxCommand>
|
||||
{
|
||||
public async Task<CommandResponse> Handle(ProcessOutboxCommand command)
|
||||
{
|
||||
telemetryClient.TrackTrace($"ProcessOutboxHandler started");
|
||||
|
||||
var messages = await outboxService.GetPendingAsync(command.BatchSize);
|
||||
|
||||
telemetryClient.TrackTrace($"ProcessOutboxHandler found {messages.Count} pending messages");
|
||||
|
||||
foreach (var message in messages)
|
||||
{
|
||||
try
|
||||
{
|
||||
telemetryClient.TrackTrace($"Processing message {message.Id} of type {message.Type}");
|
||||
await ProcessMessageAsync(message);
|
||||
await outboxService.MarkAsSentAsync(message.Id);
|
||||
telemetryClient.TrackTrace($"Message {message.Id} marked as sent");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
telemetryClient.TrackTrace($"Message {message.Id} failed: {ex.Message}");
|
||||
await outboxService.MarkAsFailedAsync(message.Id, ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
return new CommandResponse(command.CorrelationId, nameof(ProcessOutboxCommand), command.TransactionId);
|
||||
}
|
||||
|
||||
private async Task ProcessMessageAsync(OutboxMessage message)
|
||||
{
|
||||
switch (message.Type)
|
||||
{
|
||||
case OutboxMessageTypes.VerificationEmail:
|
||||
await ProcessVerificationEmailAsync(message);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new InvalidOperationException($"Unknown outbox message type: {message.Type}");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ProcessVerificationEmailAsync(OutboxMessage message)
|
||||
{
|
||||
var payload = ((JsonElement)message.Payload).Deserialize<VerificationEmailPayload>()
|
||||
?? throw new InvalidOperationException("Invalid verification email payload");
|
||||
|
||||
var verifyUrl = $"https://plantempus.dk/confirm-email?token={payload.Token}";
|
||||
|
||||
var result = await emailService.SendVerificationEmailAsync(
|
||||
payload.Email,
|
||||
payload.UserName,
|
||||
verifyUrl);
|
||||
|
||||
if (!result.Success)
|
||||
{
|
||||
throw new InvalidOperationException($"Failed to send email: {result.ErrorMessage}");
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue