diff --git a/src/modules/email/services/email-spam.service.ts b/src/modules/email/services/email-spam.service.ts index 61ddc0e..56d2225 100644 --- a/src/modules/email/services/email-spam.service.ts +++ b/src/modules/email/services/email-spam.service.ts @@ -1,9 +1,9 @@ import { InjectRepository } from "@mikro-orm/nestjs"; import { Injectable, Logger } from "@nestjs/common"; -// import { EntityRepository } from "@mikro-orm/postgresql"; import { firstValueFrom } from "rxjs"; import { EmailMessage } from "../../../common/enums/message.enum"; +import { MailboxResolverService } from "../../email-utils/services/mailbox-resolver.service"; import { ListDomainAccessQueryDto } from "../../mail-server/DTO/domain-access.dto"; import { MailServerService } from "../../mail-server/services/mail-server.service"; import { User } from "../../users/entities/user.entity"; @@ -18,6 +18,7 @@ export class EmailSpamService { private readonly mailServerService: MailServerService, @InjectRepository(User) private readonly userRepository: UserRepository, + private readonly mailboxResolverService: MailboxResolverService, ) {} async getMessageSenderInfo(userId: string, messageId: number): Promise { @@ -241,10 +242,11 @@ export class EmailSpamService { // Check filters let blockingFilters: any[] = []; + let allFilters: any[] = []; try { const filtersResponse = await firstValueFrom(this.mailServerService.filters.listFilters(wildduckUserId)); - const filters = filtersResponse?.results || []; - blockingFilters = filters.filter((filter: any) => { + allFilters = filtersResponse?.results || []; + blockingFilters = allFilters.filter((filter: any) => { const queryFrom = filter.query_from || filter.query?.find((q: any[]) => q[0] === "from")?.[1]; return (queryFrom && queryFrom.toLowerCase().includes(senderDomain.toLowerCase())) || filter.action_delete || filter.action_spam; }); @@ -252,6 +254,102 @@ export class EmailSpamService { this.logger.warn(`Could not check filters: ${error instanceof Error ? error.message : "Unknown error"}`); } + // Check for emails from this sender in various mailboxes + let emailsFromSender: any = { + inbox: [], + junk: [], + trash: [], + archive: [], + total: 0, + }; + + try { + const mailboxIds = await this.mailboxResolverService.getUserMailboxIds(wildduckUserId); + + // Search for messages from this sender domain + const searchQuery = { from: `@${senderDomain}`, limit: 50 }; + const searchResults = await firstValueFrom(this.mailServerService.messages.searchMessages(wildduckUserId, searchQuery)); + + if (searchResults?.results && searchResults.results.length > 0) { + emailsFromSender.total = searchResults.total || searchResults.results.length; + + // Check which mailbox each message is in + for (const message of searchResults.results) { + try { + // Get message details to find mailbox + const messageDetails = await firstValueFrom( + this.mailServerService.messages.getMessage(wildduckUserId, message.mailbox, message.id) + ); + + const mailboxName = await this.mailboxResolverService.getMailboxName(wildduckUserId, message.mailbox); + + if (mailboxName === "INBOX") { + emailsFromSender.inbox.push({ + id: message.id, + subject: message.subject, + from: message.from, + date: message.date, + }); + } else if (mailboxName === "Junk" || mailboxName === "Spam") { + emailsFromSender.junk.push({ + id: message.id, + subject: message.subject, + from: message.from, + date: message.date, + }); + } else if (mailboxName === "Trash") { + emailsFromSender.trash.push({ + id: message.id, + subject: message.subject, + from: message.from, + date: message.date, + }); + } else if (mailboxName === "Archive") { + emailsFromSender.archive.push({ + id: message.id, + subject: message.subject, + from: message.from, + date: message.date, + }); + } + } catch (error) { + // Skip if we can't get message details + continue; + } + } + } + } catch (error) { + this.logger.warn(`Could not search for emails from sender: ${error instanceof Error ? error.message : "Unknown error"}`); + } + + // Check WildDuck user settings + let userSettings: any = null; + try { + const userDetails = await firstValueFrom(this.mailServerService.users.getUser(wildduckUserId)); + userSettings = { + enabled: userDetails.retention !== undefined ? userDetails.retention > 0 : null, + retention: userDetails.retention, + disabled: userDetails.disabled, + }; + } catch (error) { + this.logger.warn(`Could not get user settings: ${error instanceof Error ? error.message : "Unknown error"}`); + } + + // Check address settings + let addressSettings: any = null; + try { + const addresses = await firstValueFrom(this.mailServerService.addresses.listUserAddresses(wildduckUserId)); + addressSettings = { + addresses: addresses.results?.map((addr: any) => ({ + address: addr.address, + main: addr.main, + verified: addr.verified, + })) || [], + }; + } catch (error) { + this.logger.warn(`Could not get address settings: ${error instanceof Error ? error.message : "Unknown error"}`); + } + const result: any = { recipientEmail, senderDomain, @@ -268,12 +366,24 @@ export class EmailSpamService { description: entry.description, created: entry.created, })), + allFilters: allFilters.map((filter: any) => ({ + id: filter.id, + name: filter.name, + queryFrom: filter.query_from, + queryTo: filter.query_to, + querySubject: filter.query_subject, + actions: filter.action || [], + disabled: filter.disabled, + })), blockingFilters: blockingFilters.map((filter: any) => ({ id: filter.id, name: filter.name, queryFrom: filter.query_from, actions: filter.action || [], })), + emailsFromSender, + userSettings, + addressSettings, }; // Unblock if requested and domain is blocked @@ -290,7 +400,18 @@ export class EmailSpamService { } else if (isBlocked && !unblock) { result.message = `Domain ${senderDomain} is blocked. Use unblock=true to unblock it.`; } else { - result.message = `Domain ${senderDomain} is not blocked.`; + // Provide more detailed message based on findings + if (emailsFromSender.total > 0) { + const locations = []; + if (emailsFromSender.inbox.length > 0) locations.push(`${emailsFromSender.inbox.length} in inbox`); + if (emailsFromSender.junk.length > 0) locations.push(`${emailsFromSender.junk.length} in junk/spam`); + if (emailsFromSender.trash.length > 0) locations.push(`${emailsFromSender.trash.length} in trash`); + if (emailsFromSender.archive.length > 0) locations.push(`${emailsFromSender.archive.length} in archive`); + + result.message = `Domain ${senderDomain} is not blocked. Found ${emailsFromSender.total} email(s) from this domain: ${locations.join(", ")}.`; + } else { + result.message = `Domain ${senderDomain} is not blocked, but no emails from this domain were found. This suggests emails may be rejected at SMTP level or not reaching the server.`; + } } return result;