refactor: Rename EmailPorifler to MessagingService

This commit is contained in:
2026-07-24 13:59:43 +02:00
parent 77d3b52d16
commit 5e587da957
58 changed files with 202 additions and 202 deletions

View File

@@ -0,0 +1,21 @@
namespace DigitalData.MessagingService.Application.Common.Dtos;
/// <summary>
/// DTO for EmailAccount query results.
/// </summary>
public class EmailAccountDto
{
public string Username { get; set; } = null!;
public string Password { get; set; } = null!;
public bool PasswordEncrypted { get; set; } = false;
public string SmtpServer { get; set; } = null!;
public int SmtpPort { get; set; }
public bool SmtpUseSsl { get; set; }
public bool UseOAuth2 { get; set; }
}

View File

@@ -0,0 +1,28 @@
namespace DigitalData.MessagingService.Application.Common.Events;
public class OutgoingEmailEvent
{
public required Guid Id { get; init; }
/// <summary>
/// Recipient email address
/// </summary>
public required string Recipient { get; init; }
/// <summary>
/// Email subject
/// </summary>
public required string Subject { get; init; }
/// <summary>
/// Email body (HTML or plain text)
/// </summary>
public required string Body { get; init; }
/// <summary>
/// Is HTML email (default: true)
/// </summary>
public bool IsHtml { get; init; } = true;
public DateTime QueuedAt { get; init; }
}

View File

@@ -0,0 +1,19 @@
using MediatR;
namespace DigitalData.MessagingService.Application.Common.Interfaces;
/// <summary>
/// Interface for publishing commands to a message broker (e.g., RabbitMQ)
/// </summary>
public interface ICommandPublisher
{
/// <summary>
/// Publishes a command to the message broker for asynchronous processing
/// </summary>
/// <typeparam name="TCommand">The command type (must implement IBaseRequest - covers both IRequest and IRequest<T>)</typeparam>
/// <param name="command">The command to publish</param>
/// <param name="cancellationToken">Cancellation token</param>
/// <returns>Task representing the publish operation</returns>
Task PublishAsync<TCommand>(TCommand command, CancellationToken cancellationToken = default)
where TCommand : IBaseRequest;
}

View File

@@ -0,0 +1,16 @@
namespace DigitalData.MessagingService.Application.Common.Interfaces;
/// <summary>
/// Email service interface for SMTP operations.
/// Implementation uses Limilabs Mail.dll for production email sending.
/// SMTP configuration is injected via IOptions&lt;EmailAccountDto&gt; in appsettings.json.
/// Throws AuthenticationFailedException when SMTP authentication fails.
/// </summary>
public interface IEmailService
{
/// <summary>
/// Sends an email using the configured SMTP account.
/// SMTP credentials are configured in appsettings.json (EmailAccount section).
/// </summary>
Task SendEmailAsync(string to, string subject, string body, bool isHtml = true, CancellationToken cancellationToken = default);
}

View File

@@ -0,0 +1,10 @@
namespace DigitalData.MessagingService.Application.Common.Interfaces;
/// <summary>
/// Encryption service interface for password encryption.
/// </summary>
public interface IEncryptionService
{
string Encrypt(string plainText);
string Decrypt(string cipherText);
}

View File

@@ -0,0 +1,14 @@
using DigitalData.MessagingService.Application.Common.Events;
using DigitalData.MessagingService.Application.EmailSending.Commands;
namespace DigitalData.MessagingService.Application.Common.Interfaces;
/// <summary>
/// Email queue interface for outgoing emails.
/// </summary>
public interface IOutgoingEmailQueue
{
Task EnqueueAsync(OutgoingEmailEvent outgoingEmailEvent, CancellationToken cancellationToken = default);
Task<int> GetQueueDepthAsync(CancellationToken cancellationToken = default);
}

View File

@@ -0,0 +1,26 @@
namespace DigitalData.MessagingService.Application.Common.Interfaces;
/// <summary>
/// PDF processing service interface.
/// Operates on streams instead of file paths for flexibility.
/// </summary>
public interface IPdfProcessingService
{
/// <summary>
/// Validates if the provided stream contains a valid PDF document.
/// Throws InvalidPdfException if the stream is not a valid PDF.
/// </summary>
Task<bool> ValidatePdfAsync(Stream pdfStream, CancellationToken cancellationToken = default);
/// <summary>
/// Extracts embedded files from PDF stream to the specified output directory.
/// Returns a list of paths to extracted files.
/// </summary>
Task<IEnumerable<string>> ExtractEmbeddedFilesAsync(Stream pdfStream, string outputDirectory, CancellationToken cancellationToken = default);
/// <summary>
/// Gets the page count of the PDF document.
/// Throws InvalidPdfException if the stream is not a valid PDF.
/// </summary>
Task<int> GetPageCountAsync(Stream pdfStream, CancellationToken cancellationToken = default);
}

View File

@@ -0,0 +1,30 @@
using System.Linq.Expressions;
namespace DigitalData.MessagingService.Application.Common.Interfaces;
/// <summary>
/// Generic repository interface for CRUD operations.
/// All operations auto-save changes - NO explicit SaveChangesAsync needed!
/// </summary>
public interface IRepository<TEntity> where TEntity : class
{
// CREATE
Task<TEntity> CreateAsync<TDto>(TDto dto, CancellationToken cancellationToken = default);
// READ
Task<TEntity?> GetByIdAsync(int id, CancellationToken cancellationToken = default);
Task<IEnumerable<TEntity>> GetAllAsync(CancellationToken cancellationToken = default);
Task<IEnumerable<TEntity>> FindAsync(Expression<Func<TEntity, bool>> predicate, int? skip = null, int? take = null, CancellationToken cancellationToken = default);
Task<TEntity?> FindFirstAsync(Expression<Func<TEntity, bool>> predicate, CancellationToken cancellationToken = default);
Task<TEntity?> FindSingleAsync(Expression<Func<TEntity, bool>> predicate, CancellationToken cancellationToken = default);
Task<int> CountAsync(Expression<Func<TEntity, bool>>? predicate = null, CancellationToken cancellationToken = default);
Task<bool> AnyAsync(Expression<Func<TEntity, bool>> predicate, CancellationToken cancellationToken = default);
// UPDATE
Task UpdateSingleAsync<TDto>(Expression<Func<TEntity, bool>> predicate, TDto dto, CancellationToken cancellationToken = default);
Task<int> UpdateAsync<TDto>(Expression<Func<TEntity, bool>> predicate, TDto dto, CancellationToken cancellationToken = default);
// DELETE
Task DeleteSingleAsync(Expression<Func<TEntity, bool>> predicate, CancellationToken cancellationToken = default);
Task<int> DeleteAsync(Expression<Func<TEntity, bool>> predicate, CancellationToken cancellationToken = default);
}

View File

@@ -0,0 +1,19 @@
using AutoMapper;
using DigitalData.MessagingService.Application.Common.Events;
using DigitalData.MessagingService.Application.EmailSending.Commands;
namespace DigitalData.MessagingService.Application.Common.Mappings;
/// <summary>
/// AutoMapper profile for Emails
/// </summary>
public class EmailMappingProfile : Profile
{
public EmailMappingProfile()
{
// SendEmailCommand -> OutgoingEmailEvent
CreateMap<SendEmailCommand, OutgoingEmailEvent>()
.ForMember(dest => dest.Id, opt => opt.MapFrom(_ => Guid.NewGuid()))
.ForMember(dest => dest.QueuedAt, opt => opt.MapFrom(_ => DateTime.Now));
}
}