update: handle user update and quota sync logic
This commit is contained in:
@@ -9,6 +9,7 @@ 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/update-user.dto";
|
||||
import { AddressInfo } from "../../mail-server/interfaces/addresses-response.interface";
|
||||
import { MailServerService } from "../../mail-server/services/mail-server.service";
|
||||
import { QuotaSyncService } from "../../quota-sync/services/quota-sync.service";
|
||||
import { UserSettingsService } from "../../settings/services/user-settings.service";
|
||||
@@ -260,12 +261,7 @@ export class UsersService {
|
||||
if (aliases && aliases.length > 0) {
|
||||
for (const alias of aliases) {
|
||||
try {
|
||||
await firstValueFrom(
|
||||
this.mailServerService.addresses.createUserAddress(mailServerUser.id, {
|
||||
address: alias,
|
||||
main: false,
|
||||
}),
|
||||
);
|
||||
await firstValueFrom(this.mailServerService.addresses.createUserAddress(mailServerUser.id, { address: alias, main: false }));
|
||||
} catch (error) {
|
||||
this.logger.warn(`Failed to create alias ${alias} for user ${emailAddress}:`, error);
|
||||
}
|
||||
@@ -325,9 +321,7 @@ export class UsersService {
|
||||
|
||||
await this.em.persistAndFlush(user);
|
||||
|
||||
//sync quota
|
||||
await this.quotaSyncService.scheduleUserQuotaSync(user.id, businessId, user.wildduckUserId);
|
||||
//sync business quota
|
||||
await this.quotaSyncService.scheduleBusinessQuotaSync(businessId, user.business.name);
|
||||
|
||||
this.logger.log(`Email user deleted: ${user.emailAddress} for business ${businessId}`);
|
||||
@@ -342,165 +336,112 @@ export class UsersService {
|
||||
/*******************************/
|
||||
|
||||
async updateEmailUser(userId: string, businessId: string, updateEmailUserDto: UpdateEmailUserDto) {
|
||||
const em = this.em.fork();
|
||||
|
||||
const { password, displayName, quota, aliases, title, templateId, forwarders } = updateEmailUserDto;
|
||||
|
||||
const user = await this.userRepository.findOne(
|
||||
{
|
||||
id: userId,
|
||||
business: { id: businessId },
|
||||
emailEnabled: true,
|
||||
},
|
||||
{ populate: ["business"] },
|
||||
);
|
||||
|
||||
if (!user) throw new BadRequestException(UserMessage.EMAIL_USER_NOT_FOUND);
|
||||
|
||||
if (templateId) {
|
||||
const template = await this.em.findOne(Template, { id: templateId, business: { id: businessId } });
|
||||
if (!template) throw new BadRequestException(TemplateMessage.TEMPLATE_NOT_FOUND);
|
||||
user.template = template;
|
||||
}
|
||||
|
||||
// Validate business quota if quota is being updated
|
||||
if (quota) {
|
||||
const currentQuota = user.emailQuota || 0;
|
||||
this.validateBusinessQuotaAvailability(user.business, currentQuota, quota);
|
||||
}
|
||||
|
||||
try {
|
||||
em.begin();
|
||||
|
||||
const user = await em.findOne(User, { id: userId, business: { id: businessId }, emailEnabled: true }, { populate: ["business"] });
|
||||
|
||||
if (!user) throw new BadRequestException(UserMessage.EMAIL_USER_NOT_FOUND);
|
||||
|
||||
if (templateId) {
|
||||
const template = await em.findOne(Template, { id: templateId, business: { id: businessId } });
|
||||
if (!template) throw new BadRequestException(TemplateMessage.TEMPLATE_NOT_FOUND);
|
||||
user.template = template;
|
||||
}
|
||||
|
||||
if (quota) {
|
||||
const currentQuota = user.emailQuota || 0;
|
||||
this.validateBusinessQuotaAvailability(user.business, currentQuota, quota);
|
||||
}
|
||||
|
||||
const updateData: UpdateUserDto = {};
|
||||
|
||||
if (password) {
|
||||
updateData.password = password;
|
||||
// updateData.password = password;
|
||||
const hashedPassword = await this.passwordService.hashPassword(password);
|
||||
user.password = hashedPassword;
|
||||
updateData.password = hashedPassword;
|
||||
}
|
||||
|
||||
if (displayName) updateData.name = displayName;
|
||||
|
||||
if (quota) updateData.quota = quota;
|
||||
|
||||
if (forwarders !== undefined) {
|
||||
if (forwarders !== undefined && forwarders.length > 0) {
|
||||
updateData.targets = forwarders;
|
||||
user.forwarders = forwarders;
|
||||
}
|
||||
|
||||
this.logger.warn(`Updating email user ${user.emailAddress} with data: ${JSON.stringify(updateData)}`);
|
||||
|
||||
if (Object.keys(updateData).length > 0) {
|
||||
await firstValueFrom(this.mailServerService.users.updateUser(user.wildduckUserId!, updateData));
|
||||
await firstValueFrom(this.mailServerService.users.updateUser(user.wildduckUserId, { ...updateData, hashedPassword: true }));
|
||||
}
|
||||
|
||||
if (aliases && aliases.length > 0) {
|
||||
const currentAliases = await firstValueFrom(this.mailServerService.addresses.listUserAddresses(user.wildduckUserId!));
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
await this.deleteUserAliases(user, currentAliases.results);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
await this.createUserAliases(user, aliases);
|
||||
}
|
||||
|
||||
if (displayName) user.displayName = displayName;
|
||||
if (title) user.title = title;
|
||||
|
||||
if (quota) {
|
||||
const currentQuota = user.emailQuota || 0;
|
||||
user.emailQuota = quota;
|
||||
|
||||
// Update business quota tracking
|
||||
this.updateBusinessQuotaTracking(user.business, currentQuota, quota);
|
||||
}
|
||||
|
||||
if (title) user.title = title;
|
||||
await em.flush();
|
||||
|
||||
await this.em.flush();
|
||||
await this.quotaSyncService.scheduleUserQuotaSync(user.id, businessId, user.wildduckUserId);
|
||||
|
||||
//sync quota
|
||||
if (user.wildduckUserId) {
|
||||
await this.quotaSyncService.scheduleUserQuotaSync(user.id, businessId, user.wildduckUserId);
|
||||
}
|
||||
|
||||
//sync business quota
|
||||
await this.quotaSyncService.scheduleBusinessQuotaSync(businessId, user.business.name);
|
||||
|
||||
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,
|
||||
forwarders: user.forwarders || [],
|
||||
isActive: user.isActive,
|
||||
updatedAt: user.updatedAt,
|
||||
},
|
||||
};
|
||||
await em.commit();
|
||||
|
||||
return { message: UserMessage.EMAIL_USER_UPDATED_SUCCESSFULLY, user: { id: user.id, emailAddress: user.emailAddress } };
|
||||
} catch (error) {
|
||||
this.logger.error(`Failed to update email user ${user.emailAddress}:`, error);
|
||||
await em.rollback();
|
||||
this.logger.error(`Failed to update email user ${userId}:`, error);
|
||||
throw new BadRequestException("Failed to update email user");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update email user quota
|
||||
*/
|
||||
/*******************************/
|
||||
async updateEmailUserQuota(userId: string, businessId: string, newQuota: number) {
|
||||
const user = await this.userRepository.findOne(
|
||||
{
|
||||
id: userId,
|
||||
business: { id: businessId },
|
||||
emailEnabled: true,
|
||||
},
|
||||
{ populate: ["business"] },
|
||||
);
|
||||
const user = await this.userRepository.findOne({ id: userId, business: { id: businessId }, emailEnabled: true }, { populate: ["business"] });
|
||||
|
||||
if (!user) throw new BadRequestException(UserMessage.EMAIL_USER_NOT_FOUND);
|
||||
|
||||
// Validate business quota before updating
|
||||
const currentQuota = user.emailQuota || 0;
|
||||
this.validateBusinessQuotaAvailability(user.business, currentQuota, newQuota);
|
||||
|
||||
try {
|
||||
if (user.wildduckUserId) {
|
||||
await firstValueFrom(
|
||||
this.mailServerService.users.updateUser(user.wildduckUserId, {
|
||||
quota: newQuota,
|
||||
}),
|
||||
);
|
||||
await firstValueFrom(this.mailServerService.users.updateUser(user.wildduckUserId, { quota: newQuota }));
|
||||
}
|
||||
|
||||
user.emailQuota = newQuota;
|
||||
|
||||
// Update business quota tracking
|
||||
this.updateBusinessQuotaTracking(user.business, currentQuota, newQuota);
|
||||
|
||||
await this.em.persistAndFlush([user, user.business]);
|
||||
|
||||
//sync quota
|
||||
if (user.wildduckUserId) {
|
||||
await this.quotaSyncService.scheduleUserQuotaSync(user.id, businessId, user.wildduckUserId);
|
||||
}
|
||||
|
||||
//sync business quota
|
||||
await this.quotaSyncService.scheduleBusinessQuotaSync(businessId, user.business.name);
|
||||
|
||||
this.logger.log(`Email user quota updated: ${user.emailAddress} to ${newQuota} bytes`);
|
||||
@@ -545,7 +486,7 @@ export class UsersService {
|
||||
}
|
||||
}
|
||||
|
||||
//=====================================================
|
||||
//#####################################################
|
||||
|
||||
private updateBusinessQuotaTracking(business: Business, currentUserQuota: number, newUserQuota: number): void {
|
||||
const quotaDifference = newUserQuota - currentUserQuota;
|
||||
@@ -555,4 +496,30 @@ export class UsersService {
|
||||
|
||||
this.logger.log(`Updated business quota for ${business.name}: used=${business.usedQuota}, remaining=${business.remainingQuota}`);
|
||||
}
|
||||
|
||||
//#####################################################
|
||||
|
||||
private async createUserAliases(user: User, aliases: string[]) {
|
||||
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} for user ${user.emailAddress}:`, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//#####################################################
|
||||
|
||||
private async deleteUserAliases(user: User, aliases: AddressInfo[]) {
|
||||
for (const alias of aliases) {
|
||||
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} for user ${user.emailAddress}:`, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user