fix : test email

This commit is contained in:
morteza-mortezai
2025-12-07 20:11:03 +03:30
parent de72d62b69
commit 8caaef9f3a
6 changed files with 23206 additions and 4 deletions
+27 -1
View File
@@ -7,7 +7,9 @@ import { AttachmentIdQueryDto, MailboxIdQueryDto, MessageParamDto, QueueIdParamD
import { MessageListQueryDto } from "./DTO/email-query.dto";
import { EmailSuggestionsQueryDto, SearchMessagesQueryDto } from "./DTO/email-query.dto";
import { SendEmailDto, UpdateDraftDto } from "./DTO/send-email.dto";
import { EmailSpamService } from "./services/email-spam.service";
import { EmailService } from "./services/email.service";
import { AdminRoute } from "../../common/decorators/admin.decorator";
import { AuthGuards } from "../../common/decorators/auth-guard.decorator";
import { UserDec } from "../../common/decorators/user.decorator";
import { User } from "../users/entities/user.entity";
@@ -15,7 +17,10 @@ import { User } from "../users/entities/user.entity";
@AuthGuards()
@Controller("email")
export class EmailController {
constructor(private readonly emailService: EmailService) {}
constructor(
private readonly emailService: EmailService,
private readonly emailSpamService: EmailSpamService,
) {}
@Post("send")
@ApiOperation({
@@ -313,4 +318,25 @@ export class EmailController {
performBulkAction(@UserDec() user: User, @Body() bulkActionDto: BulkActionDto, @Query() query: MailboxIdQueryDto) {
return this.emailService.performBulkAction(user.wildduckUserId, user.id, bulkActionDto, query.mailbox);
}
@Get("diagnose-blocking")
@AdminRoute()
@ApiOperation({
summary: "Diagnose email blocking issues (Admin only)",
description: "Check if a sender domain is blocked for a recipient and optionally unblock it",
})
@ApiResponse({ status: 200, description: "Diagnostic information" })
@ApiResponse({ status: 400, description: "Invalid parameters" })
@ApiResponse({ status: 403, description: "Not authorized (Admin only)" })
async diagnoseEmailBlocking(
@Query("recipient") recipientEmail: string,
@Query("senderDomain") senderDomain: string,
@Query("unblock") unblock?: string,
) {
if (!recipientEmail || !senderDomain) {
throw new Error("Both recipient and senderDomain query parameters are required");
}
const shouldUnblock = unblock === "true" || unblock === "1";
return this.emailSpamService.diagnoseEmailBlocking(recipientEmail, senderDomain, shouldUnblock);
}
}
@@ -25,8 +25,10 @@ export interface IGenerateTransactionalHeadersOptions extends Pick<IGenerateAnti
category?: string;
}
export interface IGenerateMarketingHeadersOptions
extends Pick<IGenerateAntiThreadingHeadersOptions, "businessDomain" | "businessName" | "listId" | "listUnsubscribe"> {
export interface IGenerateMarketingHeadersOptions extends Pick<
IGenerateAntiThreadingHeadersOptions,
"businessDomain" | "businessName" | "listId" | "listUnsubscribe"
> {
campaignId?: string;
unsubscribeUrl: string;
}
@@ -1,16 +1,24 @@
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 { ListDomainAccessQueryDto } from "../../mail-server/DTO/domain-access.dto";
import { MailServerService } from "../../mail-server/services/mail-server.service";
import { User } from "../../users/entities/user.entity";
import { UserRepository } from "../../users/repositories/user.repository";
import { MessageSenderInfo, SpamActionOptions } from "../interfaces/email-spam.interface";
@Injectable()
export class EmailSpamService {
private readonly logger = new Logger(EmailSpamService.name);
constructor(private readonly mailServerService: MailServerService) {}
constructor(
private readonly mailServerService: MailServerService,
@InjectRepository(User)
private readonly userRepository: UserRepository,
) {}
async getMessageSenderInfo(userId: string, messageId: number): Promise<MessageSenderInfo | null> {
try {
@@ -207,4 +215,84 @@ export class EmailSpamService {
throw error;
}
}
//==============================================
/**
* Diagnostic method to check if a domain is blocked for a user
* and optionally unblock it
*/
async diagnoseEmailBlocking(recipientEmail: string, senderDomain: string, unblock: boolean = false) {
this.logger.log(`Diagnosing email blocking for recipient: ${recipientEmail}, sender domain: ${senderDomain}`);
// Find user by email
const user = await this.userRepository.findOne({ emailAddress: recipientEmail, deletedAt: null }, { populate: ["business"] });
if (!user) {
throw new Error(`User with email ${recipientEmail} not found`);
}
const domainTag = user.business?.id || "default";
const wildduckUserId = user.wildduckUserId;
// Check blocked domains
const blockedDomainsResponse = await this.listBlockedDomains(domainTag);
const blockedDomains = blockedDomainsResponse?.results || [];
const isBlocked = blockedDomains.some((entry: any) => entry.domain?.toLowerCase() === senderDomain.toLowerCase());
// Check filters
let blockingFilters: any[] = [];
try {
const filtersResponse = await firstValueFrom(this.mailServerService.filters.listFilters(wildduckUserId));
const filters = filtersResponse?.results || [];
blockingFilters = filters.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;
});
} catch (error) {
this.logger.warn(`Could not check filters: ${error instanceof Error ? error.message : "Unknown error"}`);
}
const result: any = {
recipientEmail,
senderDomain,
user: {
id: user.id,
email: user.emailAddress,
wildduckUserId,
businessId: user.business?.id || null,
},
domainTag,
blocked: isBlocked,
blockedDomains: blockedDomains.map((entry: any) => ({
domain: entry.domain,
description: entry.description,
created: entry.created,
})),
blockingFilters: blockingFilters.map((filter: any) => ({
id: filter.id,
name: filter.name,
queryFrom: filter.query_from,
actions: filter.action || [],
})),
};
// Unblock if requested and domain is blocked
if (isBlocked && unblock) {
try {
await this.removeDomainFromBlocklist(senderDomain, domainTag);
result.unblocked = true;
result.message = `Domain ${senderDomain} has been unblocked successfully`;
this.logger.log(`Unblocked domain ${senderDomain} for user ${recipientEmail}`);
} catch (error) {
result.unblockError = error instanceof Error ? error.message : "Unknown error";
this.logger.error(`Failed to unblock domain: ${result.unblockError}`);
}
} else if (isBlocked && !unblock) {
result.message = `Domain ${senderDomain} is blocked. Use unblock=true to unblock it.`;
} else {
result.message = `Domain ${senderDomain} is not blocked.`;
}
return result;
}
}