chore: add forwarder address for users

This commit is contained in:
mahyargdz
2025-07-15 16:31:58 +03:30
parent 63ddd394c9
commit 81a3f583a7
5 changed files with 43 additions and 6 deletions
+6 -2
View File
@@ -62,7 +62,7 @@ export const enum AuthMessage {
TOKEN_EXPIRED_OR_INVALID = "توکن منقضی شده یا نامعتبر است", TOKEN_EXPIRED_OR_INVALID = "توکن منقضی شده یا نامعتبر است",
ACCESS_DENIED = "دسترسی شما محدود شده است", ACCESS_DENIED = "دسترسی شما محدود شده است",
USER_ACCOUNT_INACTIVE = "حساب کاربری شما غیر فعال است", USER_ACCOUNT_INACTIVE = "حساب کاربری شما غیر فعال است",
TOKEN_MISSED_OR_EXPIRED = "TOKEN_MISSED_OR_EXPIRED", TOKEN_MISSED_OR_EXPIRED = "توکن مفقود یا منقضی شده است",
} }
export const enum UserMessage { export const enum UserMessage {
@@ -102,6 +102,10 @@ export const enum UserMessage {
STATUS_UPDATED = "وضعیت کاربر با موفقیت تغییر کرد", STATUS_UPDATED = "وضعیت کاربر با موفقیت تغییر کرد",
TEMPLATE_UUID = "شناسه قالب باید یک یو یو آی دی باشد", TEMPLATE_UUID = "شناسه قالب باید یک یو یو آی دی باشد",
TEMPLATE_NOT_EMPTY = "شناسه قالب نمیتواند خالی باشد", TEMPLATE_NOT_EMPTY = "شناسه قالب نمیتواند خالی باشد",
ALIASES_EMAIL = "آدرس ایمیل الیاس باید معتبر باشد",
FORWARDERS_EMAIL_VALID = "آدرس ایمیل فوروارد باید معتبر باشد",
FORWARDERS_ARRAY = "آدرس های فوروارد باید یک آرایه باشد",
FORWARDERS_STRING = "آدرس های فوروارد باید یک رشته باشد",
} }
export const enum CommonMessage { export const enum CommonMessage {
@@ -425,7 +429,7 @@ export const enum DomainMessage {
export const enum MailServerMessage { export const enum MailServerMessage {
FAILED_TO_CREATE_ACCOUNT = "خطا در ایجاد ایمیل", FAILED_TO_CREATE_ACCOUNT = "خطا در ایجاد ایمیل",
FAILED_TO_CREATE_DKIM_KEY = "FAILED_TO_CREATE_DKIM_KEY", FAILED_TO_CREATE_DKIM_KEY = "خطا در ایجاد کلید DKIM",
} }
export const enum DnsRecordMessage { export const enum DnsRecordMessage {
+11 -1
View File
@@ -1,6 +1,6 @@
import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger"; import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger";
import { Transform } from "class-transformer"; import { Transform } from "class-transformer";
import { IsArray, IsNotEmpty, IsNumber, IsOptional, IsString, IsUUID, Matches, MinLength } from "class-validator"; import { IsArray, IsEmail, IsNotEmpty, IsNumber, IsOptional, IsString, IsUUID, Matches, MinLength } from "class-validator";
import { UserMessage } from "../../../common/enums/message.enum"; import { UserMessage } from "../../../common/enums/message.enum";
@@ -53,4 +53,14 @@ export class CreateEmailUserDto {
@IsUUID("7", { message: UserMessage.TEMPLATE_UUID }) @IsUUID("7", { message: UserMessage.TEMPLATE_UUID })
@ApiPropertyOptional({ description: "Template ID", example: "713e5000-0000-0000-0000-000000000000" }) @ApiPropertyOptional({ description: "Template ID", example: "713e5000-0000-0000-0000-000000000000" })
templateId?: string; templateId?: string;
@IsOptional()
@IsEmail({}, { each: true, message: UserMessage.FORWARDERS_EMAIL_VALID })
@IsArray({ message: UserMessage.FORWARDERS_ARRAY })
@IsString({ each: true, message: UserMessage.FORWARDERS_STRING })
@ApiPropertyOptional({
description: "Array of email addresses to forward all messages to",
example: ["forward1@example.com", "forward2@example.com"],
})
forwarders?: string[];
} }
+11 -1
View File
@@ -1,6 +1,6 @@
import { ApiPropertyOptional } from "@nestjs/swagger"; import { ApiPropertyOptional } from "@nestjs/swagger";
import { Transform } from "class-transformer"; import { Transform } from "class-transformer";
import { IsArray, IsNotEmpty, IsNumber, IsOptional, IsString, IsUUID, Matches, MinLength } from "class-validator"; import { IsArray, IsEmail, IsNotEmpty, IsNumber, IsOptional, IsString, IsUUID, Matches, MinLength } from "class-validator";
import { UserMessage } from "../../../common/enums/message.enum"; import { UserMessage } from "../../../common/enums/message.enum";
@@ -44,4 +44,14 @@ export class UpdateEmailUserDto {
@IsUUID("7", { message: UserMessage.TEMPLATE_UUID }) @IsUUID("7", { message: UserMessage.TEMPLATE_UUID })
@ApiPropertyOptional({ description: "Template ID", example: "713e5000-0000-0000-0000-000000000000" }) @ApiPropertyOptional({ description: "Template ID", example: "713e5000-0000-0000-0000-000000000000" })
templateId?: string; templateId?: string;
@IsOptional()
@IsEmail({}, { each: true, message: UserMessage.FORWARDERS_EMAIL_VALID })
@IsArray({ message: UserMessage.FORWARDERS_ARRAY })
@IsString({ each: true, message: UserMessage.FORWARDERS_STRING })
@ApiPropertyOptional({
description: "Array of email addresses to forward all messages to",
example: ["forward1@example.com", "forward2@example.com"],
})
forwarders?: string[];
} }
@@ -42,6 +42,9 @@ export class User extends BaseEntity {
@Property({ type: "timestamptz", nullable: true }) @Property({ type: "timestamptz", nullable: true })
lastQuotaSync?: Date; lastQuotaSync?: Date;
@Property({ type: "json", nullable: true })
forwarders?: string[];
@Property({ default: true, nullable: false }) @Property({ default: true, nullable: false })
isActive: boolean & Opt; isActive: boolean & Opt;
+12 -2
View File
@@ -71,6 +71,7 @@ export class UsersService {
remainingInGB: Math.round(Number(user.emailQuota - user.emailQuotaUsed) / (1024 * 1024 * 1024)), remainingInGB: Math.round(Number(user.emailQuota - user.emailQuotaUsed) / (1024 * 1024 * 1024)),
usagePercentage: Math.round((Number(user.emailQuotaUsed) / Number(user.emailQuota)) * 100), usagePercentage: Math.round((Number(user.emailQuotaUsed) / Number(user.emailQuota)) * 100),
}, },
forwarders: user.forwarders || [],
}; };
} }
/*******************************/ /*******************************/
@@ -83,7 +84,7 @@ export class UsersService {
/*******************************/ /*******************************/
async createEmailUser(createEmailUserDto: CreateEmailUserDto, businessId: string) { async createEmailUser(createEmailUserDto: CreateEmailUserDto, businessId: string) {
const { username, password, domainId, displayName, quota, aliases, title, templateId } = createEmailUserDto; const { username, password, domainId, displayName, quota, aliases, title, templateId, forwarders } = createEmailUserDto;
const business = await this.em.findOne(Business, { id: businessId }); const business = await this.em.findOne(Business, { id: businessId });
if (!business) throw new BadRequestException(BusinessMessage.NOT_FOUND); if (!business) throw new BadRequestException(BusinessMessage.NOT_FOUND);
@@ -122,6 +123,7 @@ export class UsersService {
address: emailAddress, address: emailAddress,
name: displayName || username, name: displayName || username,
quota: userQuota, quota: userQuota,
targets: forwarders || [],
}), }),
); );
@@ -140,6 +142,7 @@ export class UsersService {
business, business,
domain, domain,
role: userRole, role: userRole,
forwarders: forwarders || [],
}); });
// Update business quota after successful user creation // Update business quota after successful user creation
@@ -189,6 +192,7 @@ export class UsersService {
emailQuota: user.emailQuota!, emailQuota: user.emailQuota!,
emailEnabled: user.emailEnabled, emailEnabled: user.emailEnabled,
domain: domain.name, domain: domain.name,
forwarders: user.forwarders || [],
createdAt: user.createdAt, createdAt: user.createdAt,
isActive: user.isActive, isActive: user.isActive,
}, },
@@ -245,7 +249,7 @@ export class UsersService {
/*******************************/ /*******************************/
async updateEmailUser(userId: string, businessId: string, updateEmailUserDto: UpdateEmailUserDto) { async updateEmailUser(userId: string, businessId: string, updateEmailUserDto: UpdateEmailUserDto) {
const { password, displayName, quota, aliases, title, templateId } = updateEmailUserDto; const { password, displayName, quota, aliases, title, templateId, forwarders } = updateEmailUserDto;
const user = await this.userRepository.findOne( const user = await this.userRepository.findOne(
{ {
@@ -283,6 +287,11 @@ export class UsersService {
if (quota) updateData.quota = quota; if (quota) updateData.quota = quota;
if (forwarders !== undefined) {
updateData.forward = forwarders;
user.forwarders = forwarders;
}
if (Object.keys(updateData).length > 0) { if (Object.keys(updateData).length > 0) {
await firstValueFrom(this.mailServerService.users.updateUser(user.wildduckUserId!, updateData)); await firstValueFrom(this.mailServerService.users.updateUser(user.wildduckUserId!, updateData));
} }
@@ -347,6 +356,7 @@ export class UsersService {
title: user.title, title: user.title,
emailQuota: user.emailQuota!, emailQuota: user.emailQuota!,
emailEnabled: user.emailEnabled, emailEnabled: user.emailEnabled,
forwarders: user.forwarders || [],
isActive: user.isActive, isActive: user.isActive,
updatedAt: user.updatedAt, updatedAt: user.updatedAt,
}, },