chore: change the email creation dto
This commit is contained in:
@@ -1,73 +1,50 @@
|
||||
import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger";
|
||||
import { Transform } from "class-transformer";
|
||||
import { IsArray, IsNotEmpty, IsNumber, IsOptional, IsString, Matches, MinLength } from "class-validator";
|
||||
import { IsArray, IsNotEmpty, IsNumber, IsOptional, IsString, IsUUID, Matches, MinLength } from "class-validator";
|
||||
|
||||
import { UserMessage } from "../../../common/enums/message.enum";
|
||||
|
||||
export class CreateEmailUserDto {
|
||||
@ApiProperty({
|
||||
description: "Username part of the email address (before @domain.com)",
|
||||
example: "john.doe",
|
||||
})
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
@MinLength(2)
|
||||
@Matches(/^[a-zA-Z0-9._-]+$/, {
|
||||
message: "Username can only contain letters, numbers, dots, underscores, and hyphens",
|
||||
})
|
||||
@IsNotEmpty({ message: UserMessage.USERNAME_REQUIRED })
|
||||
@IsString({ message: UserMessage.USERNAME_STRING })
|
||||
@MinLength(2, { message: UserMessage.USERNAME_MIN_LENGTH })
|
||||
@Matches(/^[a-zA-Z0-9._-]+$/, { message: UserMessage.USERNAME_MATCHES })
|
||||
@ApiProperty({ description: "Username part of the email address (before @domain.com)", example: "john.doe" })
|
||||
username: string;
|
||||
|
||||
@ApiProperty({
|
||||
description: "User password for email account",
|
||||
example: "SecurePassword123!",
|
||||
minLength: 8,
|
||||
})
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
@MinLength(8)
|
||||
@Matches(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]/, {
|
||||
message: "Password must contain at least one uppercase letter, one lowercase letter, one number, and one special character",
|
||||
})
|
||||
@IsNotEmpty({ message: UserMessage.PASSWORD_REQUIRED })
|
||||
@IsString({ message: UserMessage.PASSWORD_STRING })
|
||||
@MinLength(8, { message: UserMessage.PASSWORD_MIN_LENGTH })
|
||||
@Matches(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]/, { message: UserMessage.PASSWORD_MATCHES })
|
||||
@ApiProperty({ description: "User password for email account", example: "SecurePassword123!", minLength: 8 })
|
||||
password: string;
|
||||
|
||||
@ApiProperty({
|
||||
description: "Domain ID for the email address",
|
||||
example: "12345678-1234-1234-1234-123456789012",
|
||||
})
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
@IsNotEmpty({ message: UserMessage.DOMAIN_ID_REQUIRED })
|
||||
@IsUUID("7", { message: UserMessage.DOMAIN_ID_UUID })
|
||||
@ApiProperty({ description: "Domain ID for the email address", example: "12345678-1234-1234-1234-123456789012" })
|
||||
domainId: string;
|
||||
|
||||
@ApiPropertyOptional({
|
||||
description: "Display name for the user",
|
||||
example: "John Doe",
|
||||
})
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
@ApiPropertyOptional({ description: "Display name for the user", example: "John Doe" })
|
||||
@IsString({ message: UserMessage.DISPLAY_NAME_STRING })
|
||||
@IsOptional({ message: UserMessage.DISPLAY_NAME_OPTIONAL })
|
||||
@Transform(({ value }) => value?.trim())
|
||||
displayName?: string;
|
||||
|
||||
@ApiPropertyOptional({
|
||||
description: "Email quota in bytes (default: 1GB)",
|
||||
example: 1073741824,
|
||||
})
|
||||
@IsNumber()
|
||||
@IsOptional()
|
||||
@IsOptional({ message: UserMessage.QUOTA_OPTIONAL })
|
||||
@IsNotEmpty({ message: UserMessage.QUOTA_NOT_EMPTY })
|
||||
@IsNumber({ allowNaN: false, allowInfinity: false }, { message: UserMessage.QUOTA_NUMBER })
|
||||
@ApiPropertyOptional({ description: "Email quota in bytes (default: 1GB)", example: 1073741824 })
|
||||
quota?: number;
|
||||
|
||||
@ApiPropertyOptional({
|
||||
description: "Additional email aliases for this user",
|
||||
example: ["johndoe@example.com", "j.doe@example.com"],
|
||||
})
|
||||
@IsArray()
|
||||
@IsString({ each: true })
|
||||
@IsOptional()
|
||||
@IsArray({ message: UserMessage.ALIASES_ARRAY })
|
||||
@IsString({ each: true, message: UserMessage.ALIASES_STRING })
|
||||
@IsOptional({ message: UserMessage.ALIASES_OPTIONAL })
|
||||
@ApiPropertyOptional({ description: "Additional email aliases for this user", example: ["johndoe@example.com", "j.doe@example.com"] })
|
||||
aliases?: string[];
|
||||
|
||||
@ApiPropertyOptional({
|
||||
description: "User role/title",
|
||||
example: "Marketing Manager",
|
||||
})
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
@IsNotEmpty({ message: UserMessage.TITLE_NOT_EMPTY })
|
||||
@IsString({ message: UserMessage.TITLE_STRING })
|
||||
@Transform(({ value }) => value?.trim())
|
||||
title?: string;
|
||||
@ApiProperty({ description: "User role/title", example: "Marketing Manager" })
|
||||
title: string;
|
||||
}
|
||||
|
||||
@@ -48,8 +48,8 @@ export class User extends BaseEntity {
|
||||
@ManyToOne(() => Business, { deleteRule: "restrict" })
|
||||
business!: Business;
|
||||
|
||||
@ManyToOne(() => Domain, { nullable: true })
|
||||
domain?: Domain; // The domain this email user belongs to
|
||||
@ManyToOne(() => Domain, { deleteRule: "restrict" })
|
||||
domain!: Domain; // The domain this email user belongs to
|
||||
|
||||
[EntityRepositoryType]?: UserRepository;
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
DANAK_SMTPS_PORT,
|
||||
DANAK_SMTPS_SERVER,
|
||||
} from "../../../common/constants";
|
||||
import { BusinessMessage, DomainMessage, MailServerMessage, UserMessage } from "../../../common/enums/message.enum";
|
||||
import { BusinessMessage, DomainMessage, MailServerMessage, RoleMessage, UserMessage } from "../../../common/enums/message.enum";
|
||||
import { Business } from "../../businesses/entities/business.entity";
|
||||
import { DomainStatus } from "../../domains/enums/domain-status.enum";
|
||||
import { DomainsService } from "../../domains/services/domains.service";
|
||||
@@ -66,29 +66,21 @@ export class UsersService {
|
||||
const business = await this.em.findOne(Business, { id: businessId });
|
||||
if (!business) throw new BadRequestException(BusinessMessage.NOT_FOUND);
|
||||
|
||||
const domain = await this.domainsService.getDomainById(domainId);
|
||||
const domain = await this.domainsService.getDomainById(domainId, businessId);
|
||||
|
||||
if (domain.business.id !== businessId) {
|
||||
throw new BadRequestException(DomainMessage.NOT_BELONG_TO_BUSINESS);
|
||||
}
|
||||
if (domain.business.id !== businessId) throw new BadRequestException(DomainMessage.NOT_BELONG_TO_BUSINESS);
|
||||
|
||||
if (domain.status !== DomainStatus.VERIFIED) {
|
||||
throw new BadRequestException(DomainMessage.NOT_VERIFIED);
|
||||
}
|
||||
if (domain.status !== DomainStatus.VERIFIED) throw new BadRequestException(DomainMessage.NOT_VERIFIED);
|
||||
|
||||
const emailAddress = `${username}@${domain.name}`;
|
||||
|
||||
// Check if email address already exists
|
||||
const existingUser = await this.userRepository.findOne({ emailAddress });
|
||||
if (existingUser) throw new BadRequestException(UserMessage.EMAIL_ADDRESS_ALREADY_EXISTS);
|
||||
|
||||
const hashedPassword = await this.passwordService.hashPassword(password);
|
||||
|
||||
let userRole = await this.em.findOne(Role, { name: RoleEnum.USER });
|
||||
if (!userRole) {
|
||||
userRole = this.em.create(Role, { name: RoleEnum.USER });
|
||||
await this.em.persistAndFlush(userRole);
|
||||
}
|
||||
const userRole = await this.em.findOne(Role, { name: RoleEnum.USER });
|
||||
if (!userRole) throw new BadRequestException(RoleMessage.NOT_FOUND);
|
||||
|
||||
try {
|
||||
const mailServerUser = await firstValueFrom(
|
||||
@@ -97,8 +89,8 @@ export class UsersService {
|
||||
password,
|
||||
address: emailAddress,
|
||||
name: displayName || username,
|
||||
quota: quota || 1073741824, // 1GB default
|
||||
tags: ["dmail-user"],
|
||||
quota: quota || 1073741824,
|
||||
language: "fa",
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -121,7 +113,6 @@ export class UsersService {
|
||||
|
||||
await this.em.persistAndFlush(user);
|
||||
|
||||
// Create aliases if provided
|
||||
if (aliases && aliases.length > 0) {
|
||||
for (const alias of aliases) {
|
||||
try {
|
||||
@@ -164,7 +155,7 @@ export class UsersService {
|
||||
encryption: DANAK_SMTPS_ENCRYPTION,
|
||||
},
|
||||
},
|
||||
message: "Email user created successfully",
|
||||
message: UserMessage.EMAIL_USER_CREATED_SUCCESSFULLY,
|
||||
};
|
||||
} catch (error) {
|
||||
this.logger.error(`Failed to create email user ${emailAddress}:`, error);
|
||||
@@ -252,4 +243,19 @@ export class UsersService {
|
||||
throw new BadRequestException(UserMessage.FAILED_TO_UPDATE_EMAIL_USER_QUOTA);
|
||||
}
|
||||
}
|
||||
|
||||
//=====================================================
|
||||
|
||||
async updateEmailUserStatus(userId: string, businessId: string) {
|
||||
const user = await this.userRepository.findOne({ id: userId, business: { id: businessId }, emailEnabled: true });
|
||||
if (!user) throw new NotFoundException(UserMessage.EMAIL_USER_NOT_FOUND);
|
||||
|
||||
user.isActive = !user.isActive;
|
||||
await this.em.flush();
|
||||
|
||||
return {
|
||||
message: UserMessage.STATUS_UPDATED,
|
||||
status: user.isActive,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Body, Controller, Delete, Get, Param, Patch, Post, Query } from "@nestjs/common";
|
||||
import { ApiHeader, ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger";
|
||||
import { ApiHeader, ApiOperation, ApiResponse } from "@nestjs/swagger";
|
||||
|
||||
import { CreateEmailUserDto } from "./DTO/create-email-user.dto";
|
||||
import { UsersService } from "./services/users.service";
|
||||
@@ -7,8 +7,7 @@ import { AuthGuards } from "../../common/decorators/auth-guard.decorator";
|
||||
import { BusinessDec } from "../../common/decorators/business.decorator";
|
||||
import { ParamDto } from "../../common/DTO/param.dto";
|
||||
|
||||
@ApiTags("email-users")
|
||||
@Controller("email-users")
|
||||
@Controller("users")
|
||||
@ApiHeader({ name: "x-business-id", description: "Business ID" })
|
||||
@AuthGuards()
|
||||
export class UsersController {
|
||||
@@ -41,4 +40,11 @@ export class UsersController {
|
||||
updateEmailUserQuota(@Param() paramDto: ParamDto, @BusinessDec("id") businessId: string, @Body() body: { quota: number }) {
|
||||
return this.usersService.updateEmailUserQuota(paramDto.id, businessId, body.quota);
|
||||
}
|
||||
|
||||
@Patch(":id/toggle-status")
|
||||
@ApiOperation({ summary: "Update email user status" })
|
||||
@ApiResponse({ status: 200, description: "Email user status updated successfully" })
|
||||
updateEmailUserStatus(@Param() paramDto: ParamDto, @BusinessDec("id") businessId: string) {
|
||||
return this.usersService.updateEmailUserStatus(paramDto.id, businessId);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user