diff --git a/src/core/DigitalData.MessagingService.Application/Common/Interfaces/IImapEmailService.cs b/src/core/DigitalData.MessagingService.Application/Common/Interfaces/IImapEmailService.cs
index f73b4c4..fdf7cc8 100644
--- a/src/core/DigitalData.MessagingService.Application/Common/Interfaces/IImapEmailService.cs
+++ b/src/core/DigitalData.MessagingService.Application/Common/Interfaces/IImapEmailService.cs
@@ -14,15 +14,16 @@ public interface IImapEmailService
/// Fetches emails from the specified mailbox folder.
///
/// Account whose IMAP settings will be used.
- /// Filter to apply when fetching emails.
+ ///
/// When (default), fetched messages are marked as \Seen on the server.
/// Set to for a non-destructive read (uses BODY.PEEK internally).
///
/// Cancellation token.
- Task> FetchEmailsAsync(
+ public Task SyncEmailsAsync(
EmailAccount account,
- MailSearchFilter filter,
- CancellationToken cancellationToken = default);
+ DateFilter date,
+ string folder = "INBOX",
+ CancellationToken cancel = default);
///
/// Marks a message as seen (read) on the server.
diff --git a/src/infrastructure/DigitalData.MessagingService.Infrastructure/Services/Background/EmailSyncWorker.cs b/src/infrastructure/DigitalData.MessagingService.Infrastructure/Services/Background/EmailSyncWorker.cs
index 7222811..706381f 100644
--- a/src/infrastructure/DigitalData.MessagingService.Infrastructure/Services/Background/EmailSyncWorker.cs
+++ b/src/infrastructure/DigitalData.MessagingService.Infrastructure/Services/Background/EmailSyncWorker.cs
@@ -47,7 +47,7 @@ public class EmailSyncWorker(IImapEmailService imapService, IOptions
-public class LimilabsImapEmailService(ILogger Logger) : IImapEmailService
+public class LimilabsImapEmailService(ILogger Logger, IRepository Repository) : IImapEmailService
{
static LimilabsImapEmailService()
{
@@ -23,42 +25,40 @@ public class LimilabsImapEmailService(ILogger Logger)
}
// Public API
- public async Task> FetchEmailsAsync(
+ public async Task SyncEmailsAsync(
EmailAccount account,
- MailSearchFilter filter,
+ DateFilter date,
+ string folder = "INBOX",
CancellationToken cancel = default)
{
- using var imap = await OpenAsync(account, filter.Folder, cancel);
+ using var imap = await OpenAsync(account, folder, cancel);
try
{
#region Find UIDs
// Server-side: only date range; all other filters are applied in-process after cache retrieval
List criterions = [];
- if (filter.Date is DateFilter dateF)
- {
- if (dateF.After is DateTime after)
- criterions.Add(Expression.SentSince(after.Date));
+ if (date.After is DateTime after)
+ criterions.Add(Expression.SentSince(after.Date));
- // IMAP BEFORE is exclusive, so add one day to make the bound inclusive
- if (dateF.Before is DateTime before)
- criterions.Add(Expression.SentBefore(before.Date.AddDays(1)));
- }
+ // IMAP BEFORE is exclusive, so add one day to make the bound inclusive
+ if (date.Before is DateTime before)
+ criterions.Add(Expression.SentBefore(before.Date.AddDays(1)));
var searchExpression = criterions.Count > 0 ? Expression.And([.. criterions]) : Expression.All();
List uids = [.. await imap.SearchAsync(searchExpression, cancel)];
-
- if (filter.SortOrder == MailSortOrder.NewestFirst)
- uids.Reverse();
#endregion
if (uids.Count == 0)
- return [];
+ return;
- var results = new List(uids.Count);
+ var emails = new List(uids.Count);
foreach (var uid in uids)
{
+ if (await Repository.AnyAsync(x => x.Uid == uid, cancel))
+ continue;
+
cancel.ThrowIfCancellationRequested();
try
@@ -109,71 +109,24 @@ public class LimilabsImapEmailService(ILogger Logger)
};
#endregion Read email
- if (filter.UnseenOnly && email.IsSeen)
- continue;
-
- if (filter.SubjectContains is string subject &&
- !email.Subject.Contains(subject, StringComparison.OrdinalIgnoreCase))
- continue;
-
- if (filter.SenderContains is string sender &&
- !email.From.Contains(sender, StringComparison.OrdinalIgnoreCase))
- continue;
-
- if (filter.RecipientContains is string recipient &&
- !email.To.Any(t => t.Contains(recipient, StringComparison.OrdinalIgnoreCase)) &&
- !email.Cc.Any(c => c.Contains(recipient, StringComparison.OrdinalIgnoreCase)))
- continue;
-
- if (filter.BodyContains is string body &&
- !email.TextBody.Contains(body, StringComparison.OrdinalIgnoreCase) &&
- !email.HtmlBody.Contains(body, StringComparison.OrdinalIgnoreCase))
- continue;
-
- if (filter.Uid is UidFilter uidF)
- {
- if (uidF.Absolute is long exactUid && email.Uid != exactUid)
- continue;
-
- if (uidF.Min is long min && email.Uid < min)
- continue;
-
- if (uidF.Max is long max && email.Uid > max)
- continue;
- }
-
- if (filter.WithAttachments)
- results.Add(email);
- else
- results.Add(email with { Attachments = [] });
-
+ emails.Add(email);
}
catch (Exception ex)
{
Logger.LogWarning(ex,
"Failed to fetch IMAP message UID={Uid} from folder {Folder}. Skipping.",
- uid, filter.Folder);
+ uid, folder);
}
}
await imap.CloseAsync(cancel);
- if (filter.MaxCount is int maxCount && maxCount > 0 && results.Count > maxCount)
- return results.Take(maxCount);
-
- return results;
+ await Repository.CreateAsync(emails, cancel);
}
- catch (Limilabs.Client.ServerException ex)
+ catch
{
await imap.CloseSafelyAsync();
- throw new AuthenticationFailedException(
- $"IMAP authentication failed for account '{account.Username}'.", ex);
- }
- catch (Exception ex) when (ex is not OperationCanceledException)
- {
- await imap.CloseSafelyAsync();
- throw new InvalidOperationException(
- $"Failed to fetch emails from IMAP server '{account.ImapServer}'.", ex);
+ throw;
}
}
@@ -189,21 +142,14 @@ public class LimilabsImapEmailService(ILogger Logger)
await imap.MarkMessageSeenByUIDAsync(uid, cancel);
await imap.CloseAsync(cancel);
}
- catch (Limilabs.Client.ServerException ex)
+ catch
{
await imap.CloseSafelyAsync();
- throw new AuthenticationFailedException(
- $"IMAP authentication failed for account '{account.Username}'.", ex);
- }
- catch (Exception ex) when (ex is not OperationCanceledException)
- {
- await imap.CloseSafelyAsync();
- throw new InvalidOperationException(
- $"Failed to mark message UID={uid} as seen on '{account.ImapServer}'.", ex);
+ throw;
}
}
- private static async Task OpenAsync(EmailAccount account, string folder, CancellationToken cancel)
+ private static async Task OpenAsync(EmailAccount account, string folder = "INBOX", CancellationToken cancel = default)
{
var imap = new Imap();