chore: add update users
This commit is contained in:
@@ -99,6 +99,7 @@ export const enum UserMessage {
|
||||
TITLE_OPTIONAL = "عنوان اختیاری است",
|
||||
TITLE_NOT_EMPTY = "عنوان نمیتواند خالی باشد",
|
||||
EMAIL_USER_CREATED_SUCCESSFULLY = "ایمیل با موفقیت ایجاد شد",
|
||||
EMAIL_USER_UPDATED_SUCCESSFULLY = "ایمیل کاربر با موفقیت بهروزرسانی شد",
|
||||
STATUS_UPDATED = "وضعیت کاربر با موفقیت تغییر کرد",
|
||||
}
|
||||
|
||||
|
||||
@@ -213,7 +213,7 @@ export class EmailController {
|
||||
@Post("messages/bulk-action")
|
||||
@ApiOperation({
|
||||
summary: "Perform bulk actions on multiple messages",
|
||||
description: "Apply actions (seen, delete, archive, favorite, junk, trash) to multiple messages at once",
|
||||
description: "Apply actions (seen, delete, archive, unarchive, favorite, unfavorite, junk, trash, restore) to multiple messages at once",
|
||||
})
|
||||
@ApiResponse({ status: 200, description: "Bulk action completed successfully" })
|
||||
@ApiResponse({ status: 206, description: "Bulk action partially completed" })
|
||||
|
||||
@@ -2,18 +2,12 @@ import { ApiPropertyOptional } from "@nestjs/swagger";
|
||||
import { IsArray, IsBoolean, IsNumber, IsOptional, IsString, IsUrl } from "class-validator";
|
||||
|
||||
export class UpdateUserDto {
|
||||
@ApiPropertyOptional({
|
||||
description: "Updated display name for the user",
|
||||
example: "Updated User Name",
|
||||
})
|
||||
@ApiPropertyOptional({ description: "Updated display name for the user", example: "Updated User Name" })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
name?: string;
|
||||
|
||||
@ApiPropertyOptional({
|
||||
description: "Updated password for the user",
|
||||
example: "newsecretpassword123",
|
||||
})
|
||||
@ApiPropertyOptional({ description: "Updated password for the user", example: "newsecretpassword123" })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
password?: string;
|
||||
@@ -27,102 +21,68 @@ export class UpdateUserDto {
|
||||
@IsString({ each: true })
|
||||
forward?: string[];
|
||||
|
||||
@ApiPropertyOptional({
|
||||
description: "URL to upload all messages to",
|
||||
example: "https://example.com/webhook",
|
||||
})
|
||||
@ApiPropertyOptional({ description: "URL to upload all messages to", example: "https://example.com/webhook" })
|
||||
@IsOptional()
|
||||
@IsUrl()
|
||||
targetUrl?: string;
|
||||
|
||||
@ApiPropertyOptional({
|
||||
description: "Maximum storage in bytes allowed for this user",
|
||||
example: 2147483648,
|
||||
})
|
||||
@ApiPropertyOptional({ description: "Maximum storage in bytes allowed for this user", example: 2147483648 })
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
quota?: number;
|
||||
|
||||
@ApiPropertyOptional({
|
||||
description: "Default retention time in milliseconds for mailboxes",
|
||||
example: 2592000000,
|
||||
})
|
||||
@ApiPropertyOptional({ description: "Default retention time in milliseconds for mailboxes", example: 2592000000 })
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
retention?: number;
|
||||
|
||||
@ApiPropertyOptional({
|
||||
description: "Language code for the user",
|
||||
example: "en",
|
||||
})
|
||||
@ApiPropertyOptional({ description: "Language code for the user", example: "en" })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
language?: string;
|
||||
|
||||
@ApiPropertyOptional({
|
||||
description: "Maximum number of recipients allowed to send mail to in 24h",
|
||||
example: 3000,
|
||||
})
|
||||
@ApiPropertyOptional({ description: "Maximum number of recipients allowed to send mail to in 24h", example: 3000 })
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
recipients?: number;
|
||||
|
||||
@ApiPropertyOptional({
|
||||
description: "Maximum number of forwarded emails in 24h",
|
||||
example: 3000,
|
||||
})
|
||||
@ApiPropertyOptional({ description: "Maximum number of forwarded emails in 24h", example: 3000 })
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
forwards?: number;
|
||||
|
||||
@ApiPropertyOptional({
|
||||
description: "Array of tags to associate with the user",
|
||||
example: ["premium", "customer"],
|
||||
})
|
||||
@ApiPropertyOptional({ description: "Array of tags to associate with the user", example: ["premium", "customer"] })
|
||||
@IsOptional()
|
||||
@IsArray()
|
||||
@IsString({ each: true })
|
||||
tags?: string[];
|
||||
|
||||
@ApiPropertyOptional({
|
||||
description: "PGP public key for encryption",
|
||||
})
|
||||
@ApiPropertyOptional({ description: "PGP public key for encryption" })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
pubKey?: string;
|
||||
|
||||
@ApiPropertyOptional({
|
||||
description: "Whether to encrypt stored messages",
|
||||
})
|
||||
@ApiPropertyOptional({ description: "Whether to encrypt stored messages" })
|
||||
@IsOptional()
|
||||
@IsBoolean()
|
||||
encryptMessages?: boolean;
|
||||
|
||||
@ApiPropertyOptional({
|
||||
description: "Whether to encrypt forwarded messages",
|
||||
})
|
||||
@ApiPropertyOptional({ description: "Whether to encrypt forwarded messages" })
|
||||
@IsOptional()
|
||||
@IsBoolean()
|
||||
encryptForwarded?: boolean;
|
||||
|
||||
@ApiPropertyOptional({
|
||||
description: "Current password for verification",
|
||||
})
|
||||
@ApiPropertyOptional({ description: "Current password for verification" })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
existingPassword?: string;
|
||||
|
||||
@ApiPropertyOptional({
|
||||
description: "Session identifier for logging",
|
||||
})
|
||||
@ApiPropertyOptional({ description: "Session identifier for logging" })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
sess?: string;
|
||||
|
||||
@ApiPropertyOptional({
|
||||
description: "IP address the request was made from",
|
||||
example: "192.168.1.1",
|
||||
})
|
||||
@ApiPropertyOptional({ description: "IP address the request was made from", example: "192.168.1.1" })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
ip?: string;
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
import { ApiPropertyOptional } from "@nestjs/swagger";
|
||||
import { Transform } from "class-transformer";
|
||||
import { IsArray, IsNotEmpty, IsNumber, IsOptional, IsString, Matches, MinLength } from "class-validator";
|
||||
|
||||
import { UserMessage } from "../../../common/enums/message.enum";
|
||||
|
||||
export class UpdateEmailUserDto {
|
||||
@IsOptional()
|
||||
@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 })
|
||||
@ApiPropertyOptional({ description: "New password for email account", example: "NewSecurePassword123!", minLength: 8 })
|
||||
password?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString({ message: UserMessage.DISPLAY_NAME_STRING })
|
||||
@Transform(({ value }) => value?.trim())
|
||||
@ApiPropertyOptional({ description: "Display name for the user", example: "John Doe" })
|
||||
displayName?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsNotEmpty({ message: UserMessage.QUOTA_NOT_EMPTY })
|
||||
@IsNumber({ allowNaN: false, allowInfinity: false }, { message: UserMessage.QUOTA_NUMBER })
|
||||
@ApiPropertyOptional({ description: "Email quota in bytes", example: 2147483648 })
|
||||
quota?: number;
|
||||
|
||||
@IsOptional()
|
||||
@IsArray({ message: UserMessage.ALIASES_ARRAY })
|
||||
@IsString({ each: true, message: UserMessage.ALIASES_STRING })
|
||||
@ApiPropertyOptional({
|
||||
description: "Email aliases for this user (will replace existing aliases)",
|
||||
example: ["johndoe@example.com", "j.doe@example.com"],
|
||||
})
|
||||
aliases?: string[];
|
||||
|
||||
@IsOptional()
|
||||
@IsString({ message: UserMessage.TITLE_STRING })
|
||||
@Transform(({ value }) => value?.trim())
|
||||
@ApiPropertyOptional({ description: "User role/title", example: "Senior Marketing Manager" })
|
||||
title?: string;
|
||||
}
|
||||
@@ -16,9 +16,11 @@ import { Business } from "../../businesses/entities/business.entity";
|
||||
import { DomainStatus } from "../../domains/enums/domain-status.enum";
|
||||
import { DomainAutomationService } from "../../domains/services/domain-automation.service";
|
||||
import { DomainsService } from "../../domains/services/domains.service";
|
||||
import { UpdateUserDto } from "../../mail-server/DTO";
|
||||
import { MailServerService } from "../../mail-server/services/mail-server.service";
|
||||
import { PasswordService } from "../../utils/services/password.service";
|
||||
import { CreateEmailUserDto } from "../DTO/create-email-user.dto";
|
||||
import { UpdateEmailUserDto } from "../DTO/update-email-user.dto";
|
||||
import { UserListQueryDto } from "../DTO/user-list-query.dto";
|
||||
import { Role } from "../entities/role.entity";
|
||||
import { User } from "../entities/user.entity";
|
||||
@@ -238,6 +240,92 @@ export class UsersService {
|
||||
}
|
||||
}
|
||||
|
||||
/*******************************/
|
||||
|
||||
async updateEmailUser(userId: string, businessId: string, updateEmailUserDto: UpdateEmailUserDto) {
|
||||
const { password, displayName, quota, aliases, title } = updateEmailUserDto;
|
||||
|
||||
const user = await this.userRepository.findOne({
|
||||
id: userId,
|
||||
business: { id: businessId },
|
||||
emailEnabled: true,
|
||||
});
|
||||
|
||||
if (!user) throw new NotFoundException(UserMessage.EMAIL_USER_NOT_FOUND);
|
||||
|
||||
try {
|
||||
const updateData: UpdateUserDto = {};
|
||||
|
||||
if (password) {
|
||||
updateData.password = password;
|
||||
const hashedPassword = await this.passwordService.hashPassword(password);
|
||||
user.password = hashedPassword;
|
||||
}
|
||||
|
||||
if (displayName) updateData.name = displayName;
|
||||
|
||||
if (quota) updateData.quota = quota;
|
||||
|
||||
if (Object.keys(updateData).length > 0) {
|
||||
await firstValueFrom(this.mailServerService.users.updateUser(user.wildduckUserId!, updateData));
|
||||
}
|
||||
|
||||
if (aliases && aliases.length > 0) {
|
||||
const currentAliases = await firstValueFrom(this.mailServerService.addresses.listUserAddresses(user.wildduckUserId!));
|
||||
|
||||
for (const alias of currentAliases.results) {
|
||||
if (!alias.main && alias.address !== user.emailAddress) {
|
||||
try {
|
||||
await firstValueFrom(this.mailServerService.addresses.deleteUserAddress(user.wildduckUserId!, alias.id));
|
||||
} catch (error) {
|
||||
this.logger.warn(`Failed to delete alias ${alias.address}:`, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const alias of aliases) {
|
||||
try {
|
||||
await firstValueFrom(
|
||||
this.mailServerService.addresses.createUserAddress(user.wildduckUserId!, {
|
||||
address: alias,
|
||||
main: false,
|
||||
}),
|
||||
);
|
||||
} catch (error) {
|
||||
this.logger.warn(`Failed to create alias ${alias}:`, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (displayName) user.displayName = displayName;
|
||||
|
||||
if (quota) user.emailQuota = quota;
|
||||
|
||||
if (title) user.title = title;
|
||||
|
||||
await this.em.flush();
|
||||
|
||||
this.logger.log(`Email user updated: ${user.emailAddress} for business ${businessId}`);
|
||||
|
||||
return {
|
||||
message: UserMessage.EMAIL_USER_UPDATED_SUCCESSFULLY,
|
||||
user: {
|
||||
id: user.id,
|
||||
emailAddress: user.emailAddress!,
|
||||
displayName: user.displayName!,
|
||||
title: user.title,
|
||||
emailQuota: user.emailQuota!,
|
||||
emailEnabled: user.emailEnabled,
|
||||
isActive: user.isActive,
|
||||
updatedAt: user.updatedAt,
|
||||
},
|
||||
};
|
||||
} catch (error) {
|
||||
this.logger.error(`Failed to update email user ${user.emailAddress}:`, error);
|
||||
throw new BadRequestException("Failed to update email user");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update email user quota
|
||||
*/
|
||||
|
||||
@@ -2,6 +2,7 @@ import { Body, Controller, Delete, Get, Param, Patch, Post, Query, UseIntercepto
|
||||
import { ApiHeader, ApiOperation, ApiResponse } from "@nestjs/swagger";
|
||||
|
||||
import { CreateEmailUserDto } from "./DTO/create-email-user.dto";
|
||||
import { UpdateEmailUserDto } from "./DTO/update-email-user.dto";
|
||||
import { UserListQueryDto } from "./DTO/user-list-query.dto";
|
||||
import { UsersService } from "./services/users.service";
|
||||
import { AuthGuards } from "../../common/decorators/auth-guard.decorator";
|
||||
@@ -45,6 +46,13 @@ export class UsersController {
|
||||
return this.usersService.deleteEmailUser(paramDto.id, businessId);
|
||||
}
|
||||
|
||||
@Patch(":id")
|
||||
@ApiOperation({ summary: "Update email user" })
|
||||
@ApiResponse({ status: 200, description: "Email user updated successfully" })
|
||||
updateEmailUser(@Param() paramDto: ParamDto, @BusinessDec("id") businessId: string, @Body() body: UpdateEmailUserDto) {
|
||||
return this.usersService.updateEmailUser(paramDto.id, businessId, body);
|
||||
}
|
||||
|
||||
@Patch(":id/quota")
|
||||
@ApiOperation({ summary: "Update email user quota" })
|
||||
@ApiResponse({ status: 200, description: "Email user quota updated successfully" })
|
||||
|
||||
Reference in New Issue
Block a user