Refactor solution structure and add RabbitMQ config
Reorganized the solution structure to align with a layered architecture: - Replaced `src` folder with `core`, `infrastructure`, and `presentation`. - Moved projects to their respective folders. - Added `DigitalData.MessagingService.Publisher.Abstraction` project. - Removed `DigitalData.MessagingService.Client` project. Updated project configurations and nesting in the solution file. Added `appsettings.Secrets.json` with RabbitMQ and email account settings: - RabbitMQ configuration includes hostname, port, credentials, and queue/exchange details. - Email configuration includes SMTP server details and credentials.
This commit is contained in:
@@ -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; }
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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<EmailAccountDto> 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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using AutoMapper;
|
||||
using DigitalData.MessagingService.Application.EmailSending.Commands;
|
||||
using DigitalData.MessagingService.Publisher.Abstraction;
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user