From f7d5abb1326bad0bf8f42c8412bbce6b02c89a94 Mon Sep 17 00:00:00 2001 From: TekH Date: Mon, 17 Aug 2026 10:16:04 +0200 Subject: [PATCH] feat(api): add IMAP send, POP3 receive, OAuth2 send/receive endpoints to EmailController --- .../Controllers/EmailController.cs | 126 ++++++++++++++++++ 1 file changed, 126 insertions(+) diff --git a/src/presentation/DigitalData.MessagingService.API/Controllers/EmailController.cs b/src/presentation/DigitalData.MessagingService.API/Controllers/EmailController.cs index 64548a1..71181df 100644 --- a/src/presentation/DigitalData.MessagingService.API/Controllers/EmailController.cs +++ b/src/presentation/DigitalData.MessagingService.API/Controllers/EmailController.cs @@ -112,4 +112,130 @@ public class EmailController(IMediator mediator) : ControllerBase return Ok(res); } #endregion Receive + + #region IMAP Send + /// + /// Send an email using IMAP account credentials (via RabbitMQ queue). + /// After the message is sent, it is appended to the IMAP Sent Items folder. + /// + /// Email fields as form values + /// Optional uploaded files + /// Cancellation token + /// HTTP 202 Accepted with the queued event ID + [HttpPost("imap/send")] + [Consumes("multipart/form-data")] + [ProducesResponseType(StatusCodes.Status202Accepted)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + public async Task SendEmailViaImap( + [FromForm] PublishEmailViaImapCommand 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 }); + } + #endregion IMAP Send + + #region POP3 Receive + /// + /// Fetch emails from a POP3 mailbox. + /// Triggers an on-demand POP3 sync before returning results. + /// + /// Query parameters for filtering and fetching emails from the POP3 mailbox. + /// Optional filter to return only specific fields. + /// Cancellation token. + /// HTTP 200 with list of received emails. + [HttpGet("pop3")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public async Task FetchEmailsViaPop3( + [FromQuery] ReadEmailViaPop3Query query, + [FromQuery] OnlyFilter? only = null, + CancellationToken cancellationToken = default) + { + var res = await mediator.Send(query, cancellationToken); + + if (!res.Emails.Any()) + return NotFound("No emails found matching the specified criteria."); + + if (only == OnlyFilter.HtmlBody) + { + if (res.Emails.FirstOrDefault()?.HtmlBody is string htmlBody) + return Content(htmlBody, "text/html"); + else + return NotFound(); + } + else if (only == OnlyFilter.Uid) + return Ok(res.Emails.Select(e => e.Uid).ToList()); + else + return Ok(res); + } + #endregion POP3 Receive + + #region OAuth2 Send + /// + /// Send an email via SMTP using OAuth2 authentication (via RabbitMQ queue). + /// The account must have UseOAuth2 = true and valid OAuth2 credentials configured. + /// + /// Email fields as form values + /// Optional uploaded files + /// Cancellation token + /// HTTP 202 Accepted with the queued event ID + [HttpPost("oauth2/send")] + [Consumes("multipart/form-data")] + [ProducesResponseType(StatusCodes.Status202Accepted)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + public async Task SendEmailViaOAuth2( + [FromForm] PublishEmailViaOAuth2Command 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 }); + } + #endregion OAuth2 Send + + #region OAuth2 Receive + /// + /// Fetch emails from an IMAP mailbox using OAuth2 authentication. + /// The account must have UseOAuth2 = true and valid OAuth2 credentials configured. + /// + /// Query parameters for filtering and fetching emails. + /// Optional filter to return only specific fields. + /// Cancellation token. + /// HTTP 200 with list of received emails. + [HttpGet("oauth2/imap")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public async Task FetchEmailsViaOAuth2( + [FromQuery] ReadEmailViaOAuth2Query query, + [FromQuery] OnlyFilter? only = null, + CancellationToken cancellationToken = default) + { + var res = await mediator.Send(query, cancellationToken); + + if (!res.Emails.Any()) + return NotFound("No emails found matching the specified criteria."); + + if (only == OnlyFilter.HtmlBody) + { + if (res.Emails.FirstOrDefault()?.HtmlBody is string htmlBody) + return Content(htmlBody, "text/html"); + else + return NotFound(); + } + else if (only == OnlyFilter.Uid) + return Ok(res.Emails.Select(e => e.Uid).ToList()); + else + return Ok(res); + } + #endregion OAuth2 Receive }