feat(api): add IMAP send, POP3 receive, OAuth2 send/receive endpoints to EmailController

This commit is contained in:
2026-08-17 10:16:04 +02:00
parent 7782e86db3
commit f7d5abb132

View File

@@ -112,4 +112,130 @@ public class EmailController(IMediator mediator) : ControllerBase
return Ok(res);
}
#endregion Receive
#region IMAP Send
/// <summary>
/// Send an email using IMAP account credentials (via RabbitMQ queue).
/// After the message is sent, it is appended to the IMAP Sent Items folder.
/// </summary>
/// <param name="command">Email fields as form values</param>
/// <param name="attachments">Optional uploaded files</param>
/// <param name="cancellationToken">Cancellation token</param>
/// <returns>HTTP 202 Accepted with the queued event ID</returns>
[HttpPost("imap/send")]
[Consumes("multipart/form-data")]
[ProducesResponseType(StatusCodes.Status202Accepted)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<IActionResult> 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
/// <summary>
/// Fetch emails from a POP3 mailbox.
/// Triggers an on-demand POP3 sync before returning results.
/// </summary>
/// <param name="query">Query parameters for filtering and fetching emails from the POP3 mailbox.</param>
/// <param name="only">Optional filter to return only specific fields.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>HTTP 200 with list of received emails.</returns>
[HttpGet("pop3")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<IActionResult> 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
/// <summary>
/// Send an email via SMTP using OAuth2 authentication (via RabbitMQ queue).
/// The account must have <c>UseOAuth2 = true</c> and valid OAuth2 credentials configured.
/// </summary>
/// <param name="command">Email fields as form values</param>
/// <param name="attachments">Optional uploaded files</param>
/// <param name="cancellationToken">Cancellation token</param>
/// <returns>HTTP 202 Accepted with the queued event ID</returns>
[HttpPost("oauth2/send")]
[Consumes("multipart/form-data")]
[ProducesResponseType(StatusCodes.Status202Accepted)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<IActionResult> 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
/// <summary>
/// Fetch emails from an IMAP mailbox using OAuth2 authentication.
/// The account must have <c>UseOAuth2 = true</c> and valid OAuth2 credentials configured.
/// </summary>
/// <param name="query">Query parameters for filtering and fetching emails.</param>
/// <param name="only">Optional filter to return only specific fields.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>HTTP 200 with list of received emails.</returns>
[HttpGet("oauth2/imap")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<IActionResult> 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
}