using DigitalData.MessagingService.Application.EmailSending.Commands; using DigitalData.MessagingService.Application.EmailReceiving.Queries; using DigitalData.MessagingService.Application.EmailReceiving.Commands; using DigitalData.MessagingService.Application.EmailAccount.Queries; using DigitalData.MessagingService.Abstraction; using MediatR; using Microsoft.AspNetCore.Mvc; namespace DigitalData.MessagingService.API.Controllers; /// /// Email sending API controller. /// Enqueues outgoing emails to RabbitMQ for async processing. /// [ApiController] [Route("api/[controller]")] public class EmailController(IMediator mediator) : ControllerBase { /// /// /// public enum OnlyFilter { /// /// /// HtmlBody, /// /// /// Uid, } #region Send /// /// Send an email, optionally with file attachments. /// Omit the attachments field for a plain send. /// /// Email fields as form values /// Optional uploaded files /// Cancellation token /// HTTP 202 Accepted with the queued event ID [HttpPost] [Consumes("multipart/form-data")] [ProducesResponseType(StatusCodes.Status202Accepted)] [ProducesResponseType(StatusCodes.Status400BadRequest)] public async Task SendEmail( [FromForm] PublishEmailCommand command, IFormFileCollection? attachments, CancellationToken cancellationToken) { var commandWithAttachments = command.WithAttachments( await BuildAttachmentsAsync(attachments, cancellationToken)); var eventId = await mediator.Send(commandWithAttachments, cancellationToken); return Accepted(new { Id = eventId }); } private static async Task> BuildAttachmentsAsync( IFormFileCollection? files, CancellationToken cancellationToken) { if (files is null || files.Count == 0) return []; var result = new List(files.Count); foreach (var file in files) { using var ms = new MemoryStream(); await file.CopyToAsync(ms, cancellationToken); result.Add(new EmailAttachmentContext { FileName = file.FileName, Content = ms.ToArray(), ContentType = file.ContentType }); } return result; } #endregion Send #region Receive /// /// Fetch emails from an IMAP mailbox. /// /// Query parameters for filtering and fetching emails from the IMAP mailbox. /// /// Cancellation token. /// HTTP 200 with list of received emails. [HttpGet] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task FetchEmails([FromQuery] FetchEmailsQuery query, [FromQuery] OnlyFilter? only = null, CancellationToken cancellationToken = default) { var emails = await mediator.Send(query, cancellationToken); if(!emails.Any()) return NotFound("No emails found matching the specified criteria."); if (only == OnlyFilter.HtmlBody) { if (emails.FirstOrDefault()?.HtmlBody is string htmlBody) return Content(htmlBody, "text/html"); else return NotFound(); } else if (only == OnlyFilter.Uid) return Ok(emails.Select(e => e.Uid).ToList()); else return Ok(emails); } #endregion Receive }