chore: send email and recive
This commit is contained in:
+7
-2
@@ -2,7 +2,7 @@ import { MikroOrmModule } from "@mikro-orm/nestjs";
|
|||||||
import { HttpModule } from "@nestjs/axios";
|
import { HttpModule } from "@nestjs/axios";
|
||||||
import { BullModule } from "@nestjs/bullmq";
|
import { BullModule } from "@nestjs/bullmq";
|
||||||
import { CacheModule } from "@nestjs/cache-manager";
|
import { CacheModule } from "@nestjs/cache-manager";
|
||||||
import { Module } from "@nestjs/common";
|
import { MiddlewareConsumer, Module, NestModule } from "@nestjs/common";
|
||||||
import { ConfigModule } from "@nestjs/config";
|
import { ConfigModule } from "@nestjs/config";
|
||||||
import { ScheduleModule } from "@nestjs/schedule";
|
import { ScheduleModule } from "@nestjs/schedule";
|
||||||
import { ThrottlerModule } from "@nestjs/throttler";
|
import { ThrottlerModule } from "@nestjs/throttler";
|
||||||
@@ -12,6 +12,7 @@ import { cacheConfig } from "./configs/cache.config";
|
|||||||
import { httpConfig } from "./configs/http.config";
|
import { httpConfig } from "./configs/http.config";
|
||||||
import { databaseConfig } from "./configs/mikro-orm.config";
|
import { databaseConfig } from "./configs/mikro-orm.config";
|
||||||
import { rateLimitConfig } from "./configs/rateLimit.config";
|
import { rateLimitConfig } from "./configs/rateLimit.config";
|
||||||
|
import { HTTPLogger } from "./core/middlewares/logger.middleware";
|
||||||
import { AuthModule } from "./modules/auth/auth.module";
|
import { AuthModule } from "./modules/auth/auth.module";
|
||||||
import { DomainsModule } from "./modules/domains/domains.module";
|
import { DomainsModule } from "./modules/domains/domains.module";
|
||||||
import { EmailModule } from "./modules/email/email.module";
|
import { EmailModule } from "./modules/email/email.module";
|
||||||
@@ -40,4 +41,8 @@ import { UtilsModule } from "./modules/utils/utils.module";
|
|||||||
QuotaSyncModule,
|
QuotaSyncModule,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class AppModule {}
|
export class AppModule implements NestModule {
|
||||||
|
configure(consumer: MiddlewareConsumer) {
|
||||||
|
consumer.apply(HTTPLogger).forRoutes("*");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -145,6 +145,47 @@ export const enum EmailMessage {
|
|||||||
QUEUE_ID_MUST_BE_STRING = "شناسه پیشنویس ایمیل باید یک رشته باشد",
|
QUEUE_ID_MUST_BE_STRING = "شناسه پیشنویس ایمیل باید یک رشته باشد",
|
||||||
MESSAGE_ID_MUST_BE_NUMBER = "شناسه ایمیل باید یک عدد باشد",
|
MESSAGE_ID_MUST_BE_NUMBER = "شناسه ایمیل باید یک عدد باشد",
|
||||||
FROM_ADDRESS_REQUIRED = "آدرس از مورد نیاز است",
|
FROM_ADDRESS_REQUIRED = "آدرس از مورد نیاز است",
|
||||||
|
|
||||||
|
// Validation Messages for Email DTOs
|
||||||
|
RECIPIENT_NAME_MUST_BE_STRING = "نام گیرنده باید یک رشته باشد",
|
||||||
|
RECIPIENT_ADDRESS_REQUIRED = "آدرس گیرنده مورد نیاز است",
|
||||||
|
RECIPIENT_ADDRESS_MUST_BE_EMAIL = "آدرس گیرنده باید یک ایمیل معتبر باشد",
|
||||||
|
HEADER_KEY_REQUIRED = "کلید هدر مورد نیاز است",
|
||||||
|
HEADER_KEY_MUST_BE_STRING = "کلید هدر باید یک رشته باشد",
|
||||||
|
HEADER_VALUE_REQUIRED = "مقدار هدر مورد نیاز است",
|
||||||
|
HEADER_VALUE_MUST_BE_STRING = "مقدار هدر باید یک رشته باشد",
|
||||||
|
ATTACHMENT_FILENAME_MUST_BE_STRING = "نام فایل پیوست باید یک رشته باشد",
|
||||||
|
ATTACHMENT_CONTENT_TYPE_MUST_BE_STRING = "نوع محتوای پیوست باید یک رشته باشد",
|
||||||
|
ATTACHMENT_ENCODING_MUST_BE_STRING = "کدگذاری پیوست باید یک رشته باشد",
|
||||||
|
ATTACHMENT_CONTENT_TRANSFER_ENCODING_MUST_BE_STRING = "کدگذاری انتقال محتوای پیوست باید یک رشته باشد",
|
||||||
|
ATTACHMENT_CONTENT_DISPOSITION_INVALID = "نحوه نمایش پیوست باید inline یا attachment باشد",
|
||||||
|
ATTACHMENT_CONTENT_REQUIRED = "محتوای پیوست مورد نیاز است",
|
||||||
|
ATTACHMENT_CONTENT_MUST_BE_STRING = "محتوای پیوست باید یک رشته باشد",
|
||||||
|
ATTACHMENT_CID_MUST_BE_STRING = "شناسه محتوای پیوست باید یک رشته باشد",
|
||||||
|
DRAFT_MAILBOX_REQUIRED = "شناسه صندوق پیشنویس مورد نیاز است",
|
||||||
|
DRAFT_MAILBOX_MUST_BE_STRING = "شناسه صندوق پیشنویس باید یک رشته باشد",
|
||||||
|
DRAFT_ID_REQUIRED = "شناسه پیشنویس مورد نیاز است",
|
||||||
|
DRAFT_ID_MUST_BE_NUMBER = "شناسه پیشنویس باید یک عدد باشد",
|
||||||
|
ENVELOPE_FROM_REQUIRED = "آدرس فرستنده در envelope مورد نیاز است",
|
||||||
|
ENVELOPE_TO_REQUIRED = "آدرس گیرنده در envelope مورد نیاز است",
|
||||||
|
ENVELOPE_TO_MUST_BE_ARRAY = "آدرس گیرنده در envelope باید یک آرایه باشد",
|
||||||
|
MAILBOX_MUST_BE_STRING = "شناسه صندوق باید یک رشته باشد",
|
||||||
|
REPLY_TO_MUST_BE_VALID = "آدرس پاسخ باید معتبر باشد",
|
||||||
|
TO_ADDRESSES_REQUIRED = "آدرسهای گیرنده مورد نیاز است",
|
||||||
|
TO_ADDRESSES_MUST_BE_ARRAY = "آدرسهای گیرنده باید یک آرایه باشد",
|
||||||
|
CC_ADDRESSES_MUST_BE_ARRAY = "آدرسهای کپی کربن باید یک آرایه باشد",
|
||||||
|
BCC_ADDRESSES_MUST_BE_ARRAY = "آدرسهای کپی مخفی باید یک آرایه باشد",
|
||||||
|
HEADERS_MUST_BE_ARRAY = "هدرهای سفارشی باید یک آرایه باشد",
|
||||||
|
SUBJECT_MUST_BE_STRING = "موضوع ایمیل باید یک رشته باشد",
|
||||||
|
TEXT_CONTENT_MUST_BE_STRING = "محتوای متنی باید یک رشته باشد",
|
||||||
|
HTML_CONTENT_MUST_BE_STRING = "محتوای HTML باید یک رشته باشد",
|
||||||
|
ATTACHMENTS_MUST_BE_ARRAY = "پیوستها باید یک آرایه باشد",
|
||||||
|
SESSION_ID_MUST_BE_STRING = "شناسه جلسه باید یک رشته باشد",
|
||||||
|
IP_ADDRESS_MUST_BE_STRING = "آدرس IP باید یک رشته باشد",
|
||||||
|
IS_DRAFT_MUST_BE_BOOLEAN = "وضعیت پیشنویس باید بولین باشد",
|
||||||
|
SEND_TIME_MUST_BE_DATE = "زمان ارسال باید یک تاریخ معتبر باشد",
|
||||||
|
UPLOAD_ONLY_MUST_BE_BOOLEAN = "حالت فقط آپلود باید بولین باشد",
|
||||||
|
ENVELOPE_MUST_BE_VALID = "envelope باید معتبر باشد",
|
||||||
}
|
}
|
||||||
|
|
||||||
export const enum SmsMessage {
|
export const enum SmsMessage {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { PassportModule } from "@nestjs/passport";
|
|||||||
|
|
||||||
import { AuthController } from "./auth.controller";
|
import { AuthController } from "./auth.controller";
|
||||||
import { jwtConfig } from "../../configs/jwt.config";
|
import { jwtConfig } from "../../configs/jwt.config";
|
||||||
|
import { MailServerModule } from "../mail-server/mail-server.module";
|
||||||
import { UtilsModule } from "../utils/utils.module";
|
import { UtilsModule } from "../utils/utils.module";
|
||||||
import { AuthService } from "./services/auth.service";
|
import { AuthService } from "./services/auth.service";
|
||||||
import { TokensService } from "./services/tokens.service";
|
import { TokensService } from "./services/tokens.service";
|
||||||
@@ -15,7 +16,15 @@ import { BusinessesModule } from "../businesses/businesses.module";
|
|||||||
import { User } from "../users/entities/user.entity";
|
import { User } from "../users/entities/user.entity";
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [MikroOrmModule.forFeature([User]), UtilsModule, UsersModule, PassportModule, JwtModule.registerAsync(jwtConfig()), BusinessesModule],
|
imports: [
|
||||||
|
MikroOrmModule.forFeature([User]),
|
||||||
|
UtilsModule,
|
||||||
|
UsersModule,
|
||||||
|
PassportModule,
|
||||||
|
JwtModule.registerAsync(jwtConfig()),
|
||||||
|
BusinessesModule,
|
||||||
|
MailServerModule,
|
||||||
|
],
|
||||||
controllers: [AuthController],
|
controllers: [AuthController],
|
||||||
providers: [AuthService, TokensService, LocalJwtStrategy, ConsoleJwtStrategy],
|
providers: [AuthService, TokensService, LocalJwtStrategy, ConsoleJwtStrategy],
|
||||||
exports: [AuthService],
|
exports: [AuthService],
|
||||||
|
|||||||
@@ -3,6 +3,12 @@ import { RoleEnum } from "../../users/enums/role.enum";
|
|||||||
export interface ILocalTokenPayload {
|
export interface ILocalTokenPayload {
|
||||||
id: string;
|
id: string;
|
||||||
role: RoleEnum;
|
role: RoleEnum;
|
||||||
|
inboxId: string;
|
||||||
|
draftsId: string;
|
||||||
|
junkId: string;
|
||||||
|
sentId: string;
|
||||||
|
trashId: string;
|
||||||
|
wildduckUserId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IConsoleTokenPayload {
|
export interface IConsoleTokenPayload {
|
||||||
|
|||||||
@@ -1,20 +1,25 @@
|
|||||||
import { EntityRepository } from "@mikro-orm/core";
|
import { EntityRepository } from "@mikro-orm/core";
|
||||||
import { InjectRepository } from "@mikro-orm/nestjs";
|
import { InjectRepository } from "@mikro-orm/nestjs";
|
||||||
import { BadRequestException, Injectable } from "@nestjs/common";
|
import { BadRequestException, Injectable, Logger } from "@nestjs/common";
|
||||||
|
import { firstValueFrom } from "rxjs";
|
||||||
|
|
||||||
import { TokensService } from "./tokens.service";
|
import { TokensService } from "./tokens.service";
|
||||||
import { AuthMessage } from "../../../common/enums/message.enum";
|
import { AuthMessage } from "../../../common/enums/message.enum";
|
||||||
|
import { MailServerService } from "../../mail-server/services/mail-server.service";
|
||||||
import { User } from "../../users/entities/user.entity";
|
import { User } from "../../users/entities/user.entity";
|
||||||
import { PasswordService } from "../../utils/services/password.service";
|
import { PasswordService } from "../../utils/services/password.service";
|
||||||
import { LoginDto } from "../DTO/login.dto";
|
import { LoginDto } from "../DTO/login.dto";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class AuthService {
|
export class AuthService {
|
||||||
|
private readonly logger = new Logger(AuthService.name);
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@InjectRepository(User)
|
@InjectRepository(User)
|
||||||
private readonly userRepository: EntityRepository<User>,
|
private readonly userRepository: EntityRepository<User>,
|
||||||
private readonly tokensService: TokensService,
|
private readonly tokensService: TokensService,
|
||||||
private readonly passwordService: PasswordService,
|
private readonly passwordService: PasswordService,
|
||||||
|
private readonly mailServerService: MailServerService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
//==============================================
|
//==============================================
|
||||||
@@ -24,7 +29,25 @@ export class AuthService {
|
|||||||
|
|
||||||
const user = await this.checkUserLoginCredentialWithEmail(email, password);
|
const user = await this.checkUserLoginCredentialWithEmail(email, password);
|
||||||
|
|
||||||
const tokens = await this.tokensService.generateTokens(user);
|
// Fetch user's mailboxes from WildDuck
|
||||||
|
let mailboxes: string[] = [];
|
||||||
|
if (user.wildduckUserId) {
|
||||||
|
try {
|
||||||
|
this.logger.log(`Fetching mailboxes for user ${user.id} (WildDuck ID: ${user.wildduckUserId})`);
|
||||||
|
const mailboxesResponse = await firstValueFrom(this.mailServerService.mailboxes.listMailboxes(user.wildduckUserId));
|
||||||
|
|
||||||
|
if (mailboxesResponse.success && mailboxesResponse.results) {
|
||||||
|
mailboxes = mailboxesResponse.results.map((mailbox) => mailbox.id);
|
||||||
|
|
||||||
|
this.logger.log(`Successfully fetched ${mailboxes.length} mailboxes for user ${user.id}`);
|
||||||
|
}
|
||||||
|
} catch (error: any) {
|
||||||
|
this.logger.error(`Failed to fetch mailboxes for user ${user.id}: ${error.message}`);
|
||||||
|
// Continue with login even if mailbox fetch fails
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const tokens = await this.tokensService.generateTokens(user, mailboxes);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
message: AuthMessage.PASSWORD_LOGIN_SUCCESS,
|
message: AuthMessage.PASSWORD_LOGIN_SUCCESS,
|
||||||
|
|||||||
@@ -20,11 +20,17 @@ export class TokensService {
|
|||||||
private readonly em: EntityManager,
|
private readonly em: EntityManager,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async generateTokens(user: User, em?: EntityManager) {
|
async generateTokens(user: User, mailboxes?: string[], em?: EntityManager) {
|
||||||
return this.generateAccessAndRefreshToken(
|
return this.generateAccessAndRefreshToken(
|
||||||
{
|
{
|
||||||
id: user.id,
|
id: user.id,
|
||||||
role: user.role.name,
|
role: user.role.name,
|
||||||
|
inboxId: mailboxes?.[0] || "",
|
||||||
|
sentId: mailboxes?.[1] || "",
|
||||||
|
draftsId: mailboxes?.[2] || "",
|
||||||
|
trashId: mailboxes?.[3] || "",
|
||||||
|
junkId: mailboxes?.[4] || "",
|
||||||
|
wildduckUserId: user.wildduckUserId,
|
||||||
},
|
},
|
||||||
em,
|
em,
|
||||||
);
|
);
|
||||||
@@ -101,7 +107,7 @@ export class TokensService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await entityManager.removeAndFlush(token);
|
await entityManager.removeAndFlush(token);
|
||||||
const tokens = await this.generateTokens(token.user, entityManager);
|
const tokens = await this.generateTokens(token.user, undefined, entityManager);
|
||||||
|
|
||||||
await entityManager.commit();
|
await entityManager.commit();
|
||||||
return tokens;
|
return tokens;
|
||||||
|
|||||||
@@ -32,14 +32,14 @@ export class QueueIdParamDto {
|
|||||||
queueId: string;
|
queueId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class UserMailboxParamDto extends UserIdParamDto {
|
export class UserMailboxParamDto {
|
||||||
@ApiProperty({ description: "Mailbox ID", example: "507f1f77bcf86cd799439012" })
|
@ApiProperty({ description: "Mailbox ID", example: "507f1f77bcf86cd799439012" })
|
||||||
@IsString({ message: EmailMessage.MAILBOX_ID_MUST_BE_STRING })
|
@IsString({ message: EmailMessage.MAILBOX_ID_MUST_BE_STRING })
|
||||||
@IsNotEmpty({ message: EmailMessage.MAILBOX_ID_REQUIRED })
|
@IsNotEmpty({ message: EmailMessage.MAILBOX_ID_REQUIRED })
|
||||||
mailboxId: string;
|
mailboxId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class UserMailboxMessageParamDto extends UserMailboxParamDto {
|
export class UserMailboxMessageParamDto {
|
||||||
@ApiProperty({ description: "Message ID", example: "12345" })
|
@ApiProperty({ description: "Message ID", example: "12345" })
|
||||||
@Transform(({ value }) => parseInt(value))
|
@Transform(({ value }) => parseInt(value))
|
||||||
@IsNumberString({ no_symbols: true }, { message: EmailMessage.MESSAGE_ID_MUST_BE_NUMBER })
|
@IsNumberString({ no_symbols: true }, { message: EmailMessage.MESSAGE_ID_MUST_BE_NUMBER })
|
||||||
|
|||||||
@@ -6,94 +6,103 @@ import { EmailMessage } from "../../../common/enums/message.enum";
|
|||||||
|
|
||||||
//########################################################
|
//########################################################
|
||||||
export class EmailRecipientDto {
|
export class EmailRecipientDto {
|
||||||
@ApiPropertyOptional({ description: "Display name", example: "John Doe" })
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsString()
|
@IsString({ message: EmailMessage.RECIPIENT_NAME_MUST_BE_STRING })
|
||||||
|
@ApiPropertyOptional({ description: "Display nam (optional) ", example: "John Doe" })
|
||||||
name?: string;
|
name?: string;
|
||||||
|
|
||||||
|
@IsNotEmpty({ message: EmailMessage.RECIPIENT_ADDRESS_REQUIRED })
|
||||||
|
@IsEmail({}, { message: EmailMessage.RECIPIENT_ADDRESS_MUST_BE_EMAIL })
|
||||||
@ApiProperty({ description: "Email address", example: "john@example.com" })
|
@ApiProperty({ description: "Email address", example: "john@example.com" })
|
||||||
@IsEmail()
|
|
||||||
address: string;
|
address: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
//########################################################
|
//########################################################
|
||||||
export class EmailHeaderDto {
|
export class EmailHeaderDto {
|
||||||
@ApiProperty({ description: "Header key", example: "X-Mailer" })
|
@ApiProperty({ description: "Header key", example: "X-Mailer" })
|
||||||
@IsString()
|
@IsNotEmpty({ message: EmailMessage.HEADER_KEY_REQUIRED })
|
||||||
|
@IsString({ message: EmailMessage.HEADER_KEY_MUST_BE_STRING })
|
||||||
key: string;
|
key: string;
|
||||||
|
|
||||||
@ApiProperty({ description: "Header value", example: "My Awesome Mailing Service" })
|
@ApiProperty({ description: "Header value", example: "My Awesome Mailing Service" })
|
||||||
@IsString()
|
@IsNotEmpty({ message: EmailMessage.HEADER_VALUE_REQUIRED })
|
||||||
|
@IsString({ message: EmailMessage.HEADER_VALUE_MUST_BE_STRING })
|
||||||
value: string;
|
value: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
//########################################################
|
//########################################################
|
||||||
export class EmailAttachmentDto {
|
export class EmailAttachmentDto {
|
||||||
@ApiPropertyOptional({ description: "Filename", example: "document.pdf" })
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsString()
|
@IsString({ message: EmailMessage.ATTACHMENT_FILENAME_MUST_BE_STRING })
|
||||||
|
@ApiPropertyOptional({ description: "Filename (optional)", example: "document.pdf" })
|
||||||
filename?: string;
|
filename?: string;
|
||||||
|
|
||||||
@ApiPropertyOptional({ description: "MIME type for the attachment file", example: "application/pdf" })
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsString()
|
@IsString({ message: EmailMessage.ATTACHMENT_CONTENT_TYPE_MUST_BE_STRING })
|
||||||
|
@ApiPropertyOptional({ description: "MIME type for the attachment file (optional)", example: "application/pdf" })
|
||||||
contentType?: string;
|
contentType?: string;
|
||||||
|
|
||||||
@ApiPropertyOptional({ description: "Encoding to use to store the attachments", example: "base64", default: "base64" })
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsString()
|
@IsString({ message: EmailMessage.ATTACHMENT_ENCODING_MUST_BE_STRING })
|
||||||
|
@ApiPropertyOptional({ description: "Encoding to use to store the attachments (optional)", example: "base64", default: "base64" })
|
||||||
encoding?: string;
|
encoding?: string;
|
||||||
|
|
||||||
@ApiPropertyOptional({ description: "Transfer encoding" })
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsString()
|
@IsString({ message: EmailMessage.ATTACHMENT_CONTENT_TRANSFER_ENCODING_MUST_BE_STRING })
|
||||||
|
@ApiPropertyOptional({ description: "Transfer encoding (optional)" })
|
||||||
contentTransferEncoding?: string;
|
contentTransferEncoding?: string;
|
||||||
|
|
||||||
@ApiPropertyOptional({ description: "Content Disposition", enum: ["inline", "attachment"] })
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsEnum(["inline", "attachment"])
|
@IsEnum(["inline", "attachment"], { message: EmailMessage.ATTACHMENT_CONTENT_DISPOSITION_INVALID })
|
||||||
|
@ApiPropertyOptional({ description: "Content Disposition (optional)", enum: ["inline", "attachment"] })
|
||||||
contentDisposition?: "inline" | "attachment";
|
contentDisposition?: "inline" | "attachment";
|
||||||
|
|
||||||
|
@IsNotEmpty({ message: EmailMessage.ATTACHMENT_CONTENT_REQUIRED })
|
||||||
|
@IsString({ message: EmailMessage.ATTACHMENT_CONTENT_MUST_BE_STRING })
|
||||||
@ApiProperty({ description: "Base64 encoded attachment content" })
|
@ApiProperty({ description: "Base64 encoded attachment content" })
|
||||||
@IsString()
|
|
||||||
content: string;
|
content: string;
|
||||||
|
|
||||||
@ApiPropertyOptional({ description: "Content-ID value if you want to reference to this attachment from HTML formatted message" })
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsString()
|
@IsString({ message: EmailMessage.ATTACHMENT_CID_MUST_BE_STRING })
|
||||||
|
@ApiPropertyOptional({ description: "Content-ID value if you want to reference to this attachment from HTML formatted message (optional)" })
|
||||||
cid?: string;
|
cid?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
//########################################################
|
//########################################################
|
||||||
export class DraftReferenceDto {
|
export class DraftReferenceDto {
|
||||||
|
@IsNotEmpty({ message: EmailMessage.DRAFT_MAILBOX_REQUIRED })
|
||||||
|
@IsString({ message: EmailMessage.DRAFT_MAILBOX_MUST_BE_STRING })
|
||||||
@ApiProperty({ description: "ID of the Mailbox", example: "507f1f77bcf86cd799439012" })
|
@ApiProperty({ description: "ID of the Mailbox", example: "507f1f77bcf86cd799439012" })
|
||||||
@IsString()
|
|
||||||
mailbox: string;
|
mailbox: string;
|
||||||
|
|
||||||
|
@IsNotEmpty({ message: EmailMessage.DRAFT_ID_REQUIRED })
|
||||||
|
@IsNumber({}, { message: EmailMessage.DRAFT_ID_MUST_BE_NUMBER })
|
||||||
@ApiProperty({ description: "Message ID", example: 12345 })
|
@ApiProperty({ description: "Message ID", example: 12345 })
|
||||||
@IsNumber()
|
|
||||||
id: number;
|
id: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
//########################################################
|
//########################################################
|
||||||
export class EmailEnvelopeDto {
|
export class EmailEnvelopeDto {
|
||||||
@ApiProperty({ description: "From address for envelope" })
|
@IsNotEmpty({ message: EmailMessage.ENVELOPE_FROM_REQUIRED })
|
||||||
@ValidateNested()
|
@ValidateNested()
|
||||||
@Type(() => EmailRecipientDto)
|
@Type(() => EmailRecipientDto)
|
||||||
|
@ApiProperty({ description: "From address for envelope" })
|
||||||
from: EmailRecipientDto;
|
from: EmailRecipientDto;
|
||||||
|
|
||||||
@ApiProperty({ description: "To addresses for envelope", type: [EmailRecipientDto] })
|
@IsNotEmpty({ message: EmailMessage.ENVELOPE_TO_REQUIRED })
|
||||||
@IsArray()
|
@IsArray({ message: EmailMessage.ENVELOPE_TO_MUST_BE_ARRAY })
|
||||||
@ValidateNested({ each: true })
|
@ValidateNested({ each: true })
|
||||||
@Type(() => EmailRecipientDto)
|
@Type(() => EmailRecipientDto)
|
||||||
|
@ApiProperty({ description: "To addresses for envelope", type: [EmailRecipientDto] })
|
||||||
to: EmailRecipientDto[];
|
to: EmailRecipientDto[];
|
||||||
}
|
}
|
||||||
|
|
||||||
//########################################################
|
//########################################################
|
||||||
export class SendEmailDto {
|
export class SendEmailDto {
|
||||||
@ApiPropertyOptional({ description: "ID of the Mailbox", example: "507f1f77bcf86cd799439012" })
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsString()
|
@IsNotEmpty()
|
||||||
|
@IsString({ message: EmailMessage.MAILBOX_MUST_BE_STRING })
|
||||||
|
@ApiPropertyOptional({ description: "ID of the Mailbox (optional)", example: "507f1f77bcf86cd799439012" })
|
||||||
mailbox?: string;
|
mailbox?: string;
|
||||||
|
|
||||||
@IsNotEmpty({ message: EmailMessage.FROM_ADDRESS_REQUIRED })
|
@IsNotEmpty({ message: EmailMessage.FROM_ADDRESS_REQUIRED })
|
||||||
@@ -102,106 +111,107 @@ export class SendEmailDto {
|
|||||||
@ApiProperty({ description: "Address for the From: header" })
|
@ApiProperty({ description: "Address for the From: header" })
|
||||||
from: EmailRecipientDto;
|
from: EmailRecipientDto;
|
||||||
|
|
||||||
@ApiPropertyOptional({ description: "Address for the Reply-To: header" })
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@ValidateNested()
|
@ValidateNested({ message: EmailMessage.REPLY_TO_MUST_BE_VALID })
|
||||||
@Type(() => EmailRecipientDto)
|
@Type(() => EmailRecipientDto)
|
||||||
|
@ApiPropertyOptional({ description: "Address for the Reply-To: header (optional)" })
|
||||||
replyTo?: EmailRecipientDto;
|
replyTo?: EmailRecipientDto;
|
||||||
|
|
||||||
@ApiProperty({ description: "Addresses for the To: header", type: [EmailRecipientDto] })
|
@IsNotEmpty({ message: EmailMessage.TO_ADDRESSES_REQUIRED })
|
||||||
@IsArray()
|
@IsArray({ message: EmailMessage.TO_ADDRESSES_MUST_BE_ARRAY })
|
||||||
@ValidateNested({ each: true })
|
@ValidateNested({ each: true })
|
||||||
@Type(() => EmailRecipientDto)
|
@Type(() => EmailRecipientDto)
|
||||||
|
@ApiProperty({ description: "Addresses for the To: header", type: [EmailRecipientDto] })
|
||||||
to: EmailRecipientDto[];
|
to: EmailRecipientDto[];
|
||||||
|
|
||||||
@ApiPropertyOptional({ description: "Addresses for the Cc: header", type: [EmailRecipientDto] })
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsArray()
|
@IsArray({ message: EmailMessage.CC_ADDRESSES_MUST_BE_ARRAY })
|
||||||
@ValidateNested({ each: true })
|
@ValidateNested({ each: true })
|
||||||
@Type(() => EmailRecipientDto)
|
@Type(() => EmailRecipientDto)
|
||||||
|
@ApiPropertyOptional({ description: "Addresses for the Cc: header (optional)", type: [EmailRecipientDto] })
|
||||||
cc?: EmailRecipientDto[];
|
cc?: EmailRecipientDto[];
|
||||||
|
|
||||||
@ApiPropertyOptional({ description: "Addresses for the Bcc: header", type: [EmailRecipientDto] })
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsArray()
|
@IsArray({ message: EmailMessage.BCC_ADDRESSES_MUST_BE_ARRAY })
|
||||||
@ValidateNested({ each: true })
|
@ValidateNested({ each: true })
|
||||||
@Type(() => EmailRecipientDto)
|
@Type(() => EmailRecipientDto)
|
||||||
|
@ApiPropertyOptional({ description: "Addresses for the Bcc: header (optional)", type: [EmailRecipientDto] })
|
||||||
bcc?: EmailRecipientDto[];
|
bcc?: EmailRecipientDto[];
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@IsArray({ message: EmailMessage.HEADERS_MUST_BE_ARRAY })
|
||||||
|
@ValidateNested({ each: true })
|
||||||
|
@Type(() => EmailHeaderDto)
|
||||||
@ApiPropertyOptional({
|
@ApiPropertyOptional({
|
||||||
description: "Custom headers for the message. If reference message is set then In-Reply-To and References headers are set automatically",
|
description: "Custom headers for the message. If reference message is set then In-Reply-To and References headers are set automatically",
|
||||||
type: [EmailHeaderDto],
|
type: [EmailHeaderDto],
|
||||||
})
|
})
|
||||||
@IsOptional()
|
|
||||||
@IsArray()
|
|
||||||
@ValidateNested({ each: true })
|
|
||||||
@Type(() => EmailHeaderDto)
|
|
||||||
headers?: EmailHeaderDto[];
|
headers?: EmailHeaderDto[];
|
||||||
|
|
||||||
@ApiPropertyOptional({ description: "Message subject. If not then resolved from Reference message", example: "Test email" })
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsString()
|
@IsString({ message: EmailMessage.SUBJECT_MUST_BE_STRING })
|
||||||
|
@ApiPropertyOptional({ description: "Message subject. If not then resolved from Reference message (optional)", example: "Test email" })
|
||||||
subject?: string;
|
subject?: string;
|
||||||
|
|
||||||
@ApiPropertyOptional({ description: "Plaintext message" })
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsString()
|
@IsString({ message: EmailMessage.TEXT_CONTENT_MUST_BE_STRING })
|
||||||
|
@ApiPropertyOptional({ description: "Plaintext message (optional)" })
|
||||||
text?: string;
|
text?: string;
|
||||||
|
|
||||||
@ApiPropertyOptional({ description: "HTML formatted message" })
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsString()
|
@IsString({ message: EmailMessage.HTML_CONTENT_MUST_BE_STRING })
|
||||||
|
@ApiPropertyOptional({ description: "HTML formatted message (optional)" })
|
||||||
html?: string;
|
html?: string;
|
||||||
|
|
||||||
@ApiPropertyOptional({ description: "Attachments for the message", type: [EmailAttachmentDto] })
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsArray()
|
@IsArray({ message: EmailMessage.ATTACHMENTS_MUST_BE_ARRAY })
|
||||||
@ValidateNested({ each: true })
|
@ValidateNested({ each: true })
|
||||||
@Type(() => EmailAttachmentDto)
|
@Type(() => EmailAttachmentDto)
|
||||||
|
@ApiPropertyOptional({ description: "Attachments for the message (optional)", type: [EmailAttachmentDto] })
|
||||||
attachments?: EmailAttachmentDto[];
|
attachments?: EmailAttachmentDto[];
|
||||||
|
|
||||||
@ApiPropertyOptional({ description: "Optional metadata, must be an object or JSON formatted string" })
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
|
@ApiPropertyOptional({ description: "Optional metadata, must be an object or JSON formatted string (optional)" })
|
||||||
meta?: any;
|
meta?: any;
|
||||||
|
|
||||||
@ApiPropertyOptional({ description: "Session identifier for the logs" })
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsString()
|
@IsString({ message: EmailMessage.SESSION_ID_MUST_BE_STRING })
|
||||||
|
@ApiPropertyOptional({ description: "Session identifier for the logs (optional)" })
|
||||||
sess?: string;
|
sess?: string;
|
||||||
|
|
||||||
@ApiPropertyOptional({ description: "IP address for the logs" })
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsString()
|
@IsString({ message: EmailMessage.IP_ADDRESS_MUST_BE_STRING })
|
||||||
|
@ApiPropertyOptional({ description: "IP address for the logs (optional)" })
|
||||||
ip?: string;
|
ip?: string;
|
||||||
|
|
||||||
@ApiPropertyOptional({ description: "Reference message data" })
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
|
@ApiPropertyOptional({ description: "Reference message data (optional)" })
|
||||||
reference?: any;
|
reference?: any;
|
||||||
|
|
||||||
@ApiPropertyOptional({ description: "Is the message a draft or not", default: false })
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsBoolean()
|
@IsBoolean({ message: EmailMessage.IS_DRAFT_MUST_BE_BOOLEAN })
|
||||||
|
@ApiPropertyOptional({ description: "Is the message a draft or not (optional)", default: false })
|
||||||
isDraft?: boolean;
|
isDraft?: boolean;
|
||||||
|
|
||||||
@ApiPropertyOptional({ description: "Draft message to base this one on" })
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@ValidateNested()
|
@ValidateNested()
|
||||||
@Type(() => DraftReferenceDto)
|
@Type(() => DraftReferenceDto)
|
||||||
|
@ApiPropertyOptional({ description: "Draft message to base this one on (optional)" })
|
||||||
draft?: DraftReferenceDto;
|
draft?: DraftReferenceDto;
|
||||||
|
|
||||||
@ApiPropertyOptional({ description: "Send time", example: "2023-12-01T10:00:00Z" })
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsDateString()
|
@IsDateString({}, { message: EmailMessage.SEND_TIME_MUST_BE_DATE })
|
||||||
|
@ApiPropertyOptional({ description: "Send time (optional)", example: "2023-12-01T10:00:00Z" })
|
||||||
sendTime?: string;
|
sendTime?: string;
|
||||||
|
|
||||||
@ApiPropertyOptional({ description: "If true only uploads the message but does not send it", default: false })
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsBoolean()
|
@IsBoolean({ message: EmailMessage.UPLOAD_ONLY_MUST_BE_BOOLEAN })
|
||||||
|
@ApiPropertyOptional({ description: "If true only uploads the message but does not send it", default: false })
|
||||||
uploadOnly?: boolean;
|
uploadOnly?: boolean;
|
||||||
|
|
||||||
@ApiPropertyOptional({ description: "Optional envelope" })
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@ValidateNested()
|
@ValidateNested({ message: EmailMessage.ENVELOPE_MUST_BE_VALID })
|
||||||
@Type(() => EmailEnvelopeDto)
|
@Type(() => EmailEnvelopeDto)
|
||||||
|
@ApiPropertyOptional({ description: "Optional envelope (optional)" })
|
||||||
envelope?: EmailEnvelopeDto;
|
envelope?: EmailEnvelopeDto;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,25 +1,26 @@
|
|||||||
import { Body, Controller, Delete, Get, Param, Post, Query } from "@nestjs/common";
|
import { Body, Controller, Delete, Get, Param, Post, Query } from "@nestjs/common";
|
||||||
import { ApiOperation, ApiResponse } from "@nestjs/swagger";
|
import { ApiOperation, ApiResponse } from "@nestjs/swagger";
|
||||||
|
|
||||||
import { QueueIdParamDto, UserIdParamDto, UserMailboxMessageParamDto, UserMailboxParamDto } from "./DTO/email-params.dto";
|
import { QueueIdParamDto, UserMailboxMessageParamDto } from "./DTO/email-params.dto";
|
||||||
import { MessageListQueryDto } from "./DTO/email-query.dto";
|
import { MessageListQueryDto } from "./DTO/email-query.dto";
|
||||||
import { SearchMessagesQueryDto } from "./DTO/email-query.dto";
|
import { SearchMessagesQueryDto } from "./DTO/email-query.dto";
|
||||||
import { SendEmailDto } from "./DTO/send-email.dto";
|
import { SendEmailDto } from "./DTO/send-email.dto";
|
||||||
import { EmailService } from "./services/email.service";
|
import { EmailService } from "./services/email.service";
|
||||||
import { AuthGuards } from "../../common/decorators/auth-guard.decorator";
|
import { AuthGuards } from "../../common/decorators/auth-guard.decorator";
|
||||||
|
import { UserDec } from "../../common/decorators/user.decorator";
|
||||||
|
|
||||||
@Controller("email")
|
@Controller("email")
|
||||||
@AuthGuards()
|
@AuthGuards()
|
||||||
export class EmailController {
|
export class EmailController {
|
||||||
constructor(private readonly emailService: EmailService) {}
|
constructor(private readonly emailService: EmailService) {}
|
||||||
|
|
||||||
@Post(":userId/send")
|
@Post("send")
|
||||||
@ApiOperation({ summary: "Send an email" })
|
@ApiOperation({ summary: "Send an email" })
|
||||||
@ApiResponse({ status: 201, description: "Email sent successfully" })
|
@ApiResponse({ status: 201, description: "Email sent successfully" })
|
||||||
@ApiResponse({ status: 400, description: "Invalid email data" })
|
@ApiResponse({ status: 400, description: "Invalid email data" })
|
||||||
@ApiResponse({ status: 403, description: "Not authorized to send email" })
|
@ApiResponse({ status: 403, description: "Not authorized to send email" })
|
||||||
sendEmail(@Param() params: UserIdParamDto, @Body() sendEmailDto: SendEmailDto) {
|
sendEmail(@UserDec("id") userId: string, @Body() sendEmailDto: SendEmailDto) {
|
||||||
return this.emailService.sendEmail(params.userId, sendEmailDto);
|
return this.emailService.sendEmail(userId, sendEmailDto);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get("status/:queueId")
|
@Get("status/:queueId")
|
||||||
@@ -30,34 +31,34 @@ export class EmailController {
|
|||||||
return this.emailService.getEmailStatus(params.queueId);
|
return this.emailService.getEmailStatus(params.queueId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get(":userId/messages/:mailboxId")
|
@Get("messages/inbox")
|
||||||
@ApiOperation({ summary: "List messages in a mailbox" })
|
@ApiOperation({ summary: "List messages in a inbox" })
|
||||||
@ApiResponse({ status: 200, description: "List of messages" })
|
@ApiResponse({ status: 200, description: "List of messages" })
|
||||||
@ApiResponse({ status: 404, description: "Mailbox not found" })
|
@ApiResponse({ status: 404, description: "Mailbox not found" })
|
||||||
listMessages(@Param() params: UserMailboxParamDto, @Query() query: MessageListQueryDto) {
|
listMessages(@UserDec("id") userId: string, @UserDec("inboxId") inboxId: string, @Query() query: MessageListQueryDto) {
|
||||||
return this.emailService.listMessages(params.userId, params.mailboxId, query);
|
return this.emailService.listMessages(userId, inboxId, query);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get(":userId/messages/:mailboxId/:messageId")
|
@Get("messages/inbox/:messageId")
|
||||||
@ApiOperation({ summary: "Get a specific message" })
|
@ApiOperation({ summary: "Get a specific message" })
|
||||||
@ApiResponse({ status: 200, description: "Message details" })
|
@ApiResponse({ status: 200, description: "Message details" })
|
||||||
@ApiResponse({ status: 404, description: "Message not found" })
|
@ApiResponse({ status: 404, description: "Message not found" })
|
||||||
getMessage(@Param() params: UserMailboxMessageParamDto) {
|
getMessage(@UserDec("id") userId: string, @UserDec("inboxId") inboxId: string, @Param() params: UserMailboxMessageParamDto) {
|
||||||
return this.emailService.getMessage(params.userId, params.mailboxId, params.messageId);
|
return this.emailService.getMessage(userId, inboxId, params.messageId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Delete(":userId/messages/:mailboxId/:messageId")
|
@Delete("messages/inbox/:messageId")
|
||||||
@ApiOperation({ summary: "Delete a message" })
|
@ApiOperation({ summary: "Delete a message" })
|
||||||
@ApiResponse({ status: 200, description: "Message deleted successfully" })
|
@ApiResponse({ status: 200, description: "Message deleted successfully" })
|
||||||
@ApiResponse({ status: 404, description: "Message not found" })
|
@ApiResponse({ status: 404, description: "Message not found" })
|
||||||
deleteMessage(@Param() params: UserMailboxMessageParamDto) {
|
deleteMessage(@UserDec("id") userId: string, @UserDec("inboxId") inboxId: string, @Param() params: UserMailboxMessageParamDto) {
|
||||||
return this.emailService.deleteMessage(params.userId, params.mailboxId, params.messageId);
|
return this.emailService.deleteMessage(userId, inboxId, params.messageId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get(":userId/search")
|
@Get("search")
|
||||||
@ApiOperation({ summary: "Search messages across all mailboxes" })
|
@ApiOperation({ summary: "Search messages across all mailboxes" })
|
||||||
@ApiResponse({ status: 200, description: "Search results" })
|
@ApiResponse({ status: 200, description: "Search results" })
|
||||||
searchMessages(@Param() params: UserIdParamDto, @Query() query: SearchMessagesQueryDto) {
|
searchMessages(@UserDec("id") userId: string, @Query() query: SearchMessagesQueryDto) {
|
||||||
return this.emailService.searchMessages(params.userId, query);
|
return this.emailService.searchMessages(userId, query);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,9 +3,10 @@ import { Module } from "@nestjs/common";
|
|||||||
import { EmailController } from "./email.controller";
|
import { EmailController } from "./email.controller";
|
||||||
import { EmailService } from "./services/email.service";
|
import { EmailService } from "./services/email.service";
|
||||||
import { MailServerModule } from "../mail-server/mail-server.module";
|
import { MailServerModule } from "../mail-server/mail-server.module";
|
||||||
|
import { UsersModule } from "../users/users.module";
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [MailServerModule],
|
imports: [MailServerModule, UsersModule],
|
||||||
controllers: [EmailController],
|
controllers: [EmailController],
|
||||||
providers: [EmailService],
|
providers: [EmailService],
|
||||||
exports: [EmailService],
|
exports: [EmailService],
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { firstValueFrom } from "rxjs";
|
|||||||
|
|
||||||
import { EmailMessage } from "../../../common/enums/message.enum";
|
import { EmailMessage } from "../../../common/enums/message.enum";
|
||||||
import { MailServerService } from "../../mail-server/services/mail-server.service";
|
import { MailServerService } from "../../mail-server/services/mail-server.service";
|
||||||
|
import { UsersService } from "../../users/services/users.service";
|
||||||
import { MessageListQueryDto, SearchMessagesQueryDto } from "../DTO/email-query.dto";
|
import { MessageListQueryDto, SearchMessagesQueryDto } from "../DTO/email-query.dto";
|
||||||
import { SendEmailDto } from "../DTO/send-email.dto";
|
import { SendEmailDto } from "../DTO/send-email.dto";
|
||||||
|
|
||||||
@@ -10,14 +11,19 @@ import { SendEmailDto } from "../DTO/send-email.dto";
|
|||||||
export class EmailService {
|
export class EmailService {
|
||||||
private readonly logger = new Logger(EmailService.name);
|
private readonly logger = new Logger(EmailService.name);
|
||||||
|
|
||||||
constructor(private readonly mailServerService: MailServerService) {}
|
constructor(
|
||||||
|
private readonly mailServerService: MailServerService,
|
||||||
|
private readonly userService: UsersService,
|
||||||
|
) {}
|
||||||
|
|
||||||
//########################################################
|
//########################################################
|
||||||
async sendEmail(userId: string, sendEmailDto: SendEmailDto) {
|
async sendEmail(userId: string, sendEmailDto: SendEmailDto) {
|
||||||
this.logger.log(`Sending email from user ${userId} to ${sendEmailDto.to.map((t) => t.address).join(", ")}`);
|
const userEmailId = await this.userService.getUserEmailIdFromId(userId);
|
||||||
|
|
||||||
|
this.logger.log(`Sending email from user ${userEmailId} to ${sendEmailDto.to.map((t) => t.address).join(", ")}`);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await firstValueFrom(this.mailServerService.submission.submitMessage(userId, sendEmailDto));
|
const response = await firstValueFrom(this.mailServerService.submission.submitMessage(userEmailId, sendEmailDto));
|
||||||
|
|
||||||
this.logger.log(`Email sent successfully: ${response.id} (Queue: ${response.queueId})`);
|
this.logger.log(`Email sent successfully: ${response.id} (Queue: ${response.queueId})`);
|
||||||
|
|
||||||
@@ -31,7 +37,7 @@ export class EmailService {
|
|||||||
created: response.created,
|
created: response.created,
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.logger.error(`Failed to send email for user ${userId}: ${error instanceof Error ? error.message : "Unknown error"}`);
|
this.logger.error(`Failed to send email for user ${userEmailId}: ${error instanceof Error ? error.message : "Unknown error"}`);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -77,13 +83,13 @@ export class EmailService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//########################################################
|
//########################################################
|
||||||
async listMessages(userId: string, mailboxId: string, query: MessageListQueryDto) {
|
async listMessages(userId: string, inboxId: string, query: MessageListQueryDto) {
|
||||||
this.logger.log(`Listing messages in mailbox ${mailboxId} for user: ${userId}`);
|
this.logger.log(`Listing messages in mailbox ${inboxId} for user: ${userId}`);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await firstValueFrom(this.mailServerService.messages.listMessages(userId, mailboxId, query));
|
const response = await firstValueFrom(this.mailServerService.messages.listMessages(userId, inboxId, query));
|
||||||
|
|
||||||
this.logger.log(`Found ${response.results.length} messages in mailbox ${mailboxId} for user: ${userId}`);
|
this.logger.log(`Found ${response.results.length} messages in mailbox ${inboxId} for user: ${userId}`);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
message: EmailMessage.MESSAGES_LISTED_SUCCESSFULLY,
|
message: EmailMessage.MESSAGES_LISTED_SUCCESSFULLY,
|
||||||
@@ -91,7 +97,7 @@ export class EmailService {
|
|||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.logger.error(
|
this.logger.error(
|
||||||
`Failed to list messages for mailbox ${mailboxId} and user ${userId}: ${error instanceof Error ? error.message : "Unknown error"}`,
|
`Failed to list messages for mailbox ${inboxId} and user ${userId}: ${error instanceof Error ? error.message : "Unknown error"}`,
|
||||||
);
|
);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,8 +32,8 @@ export class User extends BaseEntity {
|
|||||||
emailQuotaUsed?: number; // Email quota used in bytes
|
emailQuotaUsed?: number; // Email quota used in bytes
|
||||||
|
|
||||||
// Email account integration fields
|
// Email account integration fields
|
||||||
@Property({ type: "varchar", length: 255, nullable: true })
|
@Property({ type: "varchar", length: 255, nullable: false })
|
||||||
wildduckUserId?: string; // WildDuck user ID
|
wildduckUserId: string; // WildDuck user ID
|
||||||
|
|
||||||
@Property({ type: "varchar", length: 255, nullable: true })
|
@Property({ type: "varchar", length: 255, nullable: true })
|
||||||
displayName?: string; // Display name for email
|
displayName?: string; // Display name for email
|
||||||
|
|||||||
@@ -35,6 +35,12 @@ export class UsersService {
|
|||||||
private readonly domainsService: DomainsService,
|
private readonly domainsService: DomainsService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
|
async getUserEmailIdFromId(userId: string) {
|
||||||
|
const user = await this.userRepository.findOne({ id: userId }, { populate: ["role"] });
|
||||||
|
if (!user) throw new BadRequestException(UserMessage.USER_NOT_FOUND);
|
||||||
|
return user.wildduckUserId;
|
||||||
|
}
|
||||||
|
|
||||||
async findOneWithEmail(email: string, businessId: string) {
|
async findOneWithEmail(email: string, businessId: string) {
|
||||||
const user = await this.userRepository.findOne({ emailAddress: email, business: { id: businessId } }, { populate: ["role"] });
|
const user = await this.userRepository.findOne({ emailAddress: email, business: { id: businessId } }, { populate: ["role"] });
|
||||||
return user;
|
return user;
|
||||||
|
|||||||
Reference in New Issue
Block a user