chore: update user fix

This commit is contained in:
mahyargdz
2025-07-13 13:16:40 +03:30
parent ed0e14c89a
commit 00dd77377d
14 changed files with 205 additions and 209 deletions
+70 -35
View File
@@ -2,14 +2,6 @@ import { EntityManager } from "@mikro-orm/postgresql";
import { BadRequestException, Injectable, Logger, NotFoundException } from "@nestjs/common";
import { firstValueFrom } from "rxjs";
import {
DANAK_IMAPS_ENCRYPTION,
DANAK_IMAPS_PORT,
DANAK_IMAPS_SERVER,
DANAK_SMTPS_ENCRYPTION,
DANAK_SMTPS_PORT,
DANAK_SMTPS_SERVER,
} from "../../../common/constants";
import { BusinessMessage, DomainMessage, MailServerMessage, RoleMessage, UserMessage } from "../../../common/enums/message.enum";
import { QUOTA_CONSTANTS } from "../../businesses/constant";
import { Business } from "../../businesses/entities/business.entity";
@@ -106,6 +98,7 @@ export class UsersService {
if (domain.status !== DomainStatus.VERIFIED) throw new BadRequestException(DomainMessage.NOT_VERIFIED);
const emailAddress = `${username}@${domain.name}`;
const finalUserName = `${username}-${domain.name}`;
const existingUser = await this.userRepository.findOne({ emailAddress });
if (existingUser) throw new BadRequestException(UserMessage.EMAIL_ADDRESS_ALREADY_EXISTS);
@@ -120,7 +113,7 @@ export class UsersService {
const mailServerUser = await firstValueFrom(
this.mailServerService.users.createUser({
username: `${username}-${domain.name}`,
username: finalUserName,
password,
address: emailAddress,
name: displayName || username,
@@ -132,7 +125,7 @@ export class UsersService {
const user = this.userRepository.create({
title: title || displayName || username,
userName: username,
userName: finalUserName,
password: hashedPassword,
emailAddress,
emailEnabled: true,
@@ -182,19 +175,6 @@ export class UsersService {
createdAt: user.createdAt,
isActive: user.isActive,
},
setupInfo: {
emailAddress: user.emailAddress!,
imapSettings: {
server: DANAK_IMAPS_SERVER,
port: DANAK_IMAPS_PORT,
encryption: DANAK_IMAPS_ENCRYPTION,
},
smtpSettings: {
server: DANAK_SMTPS_SERVER,
port: DANAK_SMTPS_PORT,
encryption: DANAK_SMTPS_ENCRYPTION,
},
},
message: UserMessage.EMAIL_USER_CREATED_SUCCESSFULLY,
};
} catch (error) {
@@ -245,14 +225,23 @@ 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,
});
const user = await this.userRepository.findOne(
{
id: userId,
business: { id: businessId },
emailEnabled: true,
},
{ populate: ["business"] },
);
if (!user) throw new NotFoundException(UserMessage.EMAIL_USER_NOT_FOUND);
// Validate business quota if quota is being updated
if (quota) {
const currentQuota = user.emailQuota || 0;
this.validateBusinessQuotaAvailability(user.business, currentQuota, quota);
}
try {
const updateData: UpdateUserDto = {};
@@ -299,7 +288,13 @@ export class UsersService {
if (displayName) user.displayName = displayName;
if (quota) user.emailQuota = quota;
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;
@@ -330,14 +325,21 @@ export class UsersService {
* 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,
});
const user = await this.userRepository.findOne(
{
id: userId,
business: { id: businessId },
emailEnabled: true,
},
{ populate: ["business"] },
);
if (!user) throw new NotFoundException(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(
@@ -348,7 +350,11 @@ export class UsersService {
}
user.emailQuota = newQuota;
await this.em.persistAndFlush(user);
// Update business quota tracking
this.updateBusinessQuotaTracking(user.business, currentQuota, newQuota);
await this.em.persistAndFlush([user, user.business]);
this.logger.log(`Email user quota updated: ${user.emailAddress} to ${newQuota} bytes`);
@@ -373,4 +379,33 @@ export class UsersService {
status: user.isActive,
};
}
/*******************************/
private validateBusinessQuotaAvailability(business: Business, currentUserQuota: number, newUserQuota: number): void {
// Prevent quota downgrade - users can only upgrade their quota
if (newUserQuota < currentUserQuota) {
throw new BadRequestException(BusinessMessage.QUOTA_DOWNGRADE_NOT_ALLOWED);
}
const quotaDifference = newUserQuota - currentUserQuota;
if (quotaDifference > 0) {
const remainingQuota = Number(business.remainingQuota) || 0;
if (quotaDifference > remainingQuota) {
throw new BadRequestException(BusinessMessage.INSUFFICIENT_QUOTA);
}
}
}
//=====================================================
private updateBusinessQuotaTracking(business: Business, currentUserQuota: number, newUserQuota: number): void {
const quotaDifference = newUserQuota - currentUserQuota;
business.usedQuota = Number(business.usedQuota) + quotaDifference;
business.remainingQuota = Number(business.quota) - Number(business.usedQuota);
this.logger.log(`Updated business quota for ${business.name}: used=${business.usedQuota}, remaining=${business.remainingQuota}`);
}
}