From c7c18ebd4c8c69d8d089701766e0502e4157fa3a Mon Sep 17 00:00:00 2001 From: mahyargdz Date: Tue, 15 Jul 2025 15:52:01 +0330 Subject: [PATCH] chore: change the quota sync to the queue --- package.json | 1 - pnpm-lock.yaml | 29 ----- src/app.module.ts | 2 - src/modules/quota-sync/constants/index.ts | 11 ++ .../interfaces/quota-sync-job.interface.ts | 16 +++ .../quota-sync/queue/quota-sync.processor.ts | 109 ++++++++++++++++ src/modules/quota-sync/quota-sync.module.ts | 19 ++- .../quota-sync/services/quota-sync.service.ts | 120 +++++++++++++++++- src/modules/templates/DTO/index.ts | 4 - .../templates/DTO/update-template.dto.ts | 45 +------ src/modules/users/services/users.service.ts | 28 ++++ src/modules/users/users.module.ts | 3 +- 12 files changed, 300 insertions(+), 87 deletions(-) create mode 100644 src/modules/quota-sync/constants/index.ts create mode 100644 src/modules/quota-sync/interfaces/quota-sync-job.interface.ts create mode 100644 src/modules/quota-sync/queue/quota-sync.processor.ts delete mode 100644 src/modules/templates/DTO/index.ts diff --git a/package.json b/package.json index d957a7e..321eaab 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,6 @@ "@nestjs/passport": "^11.0.5", "@nestjs/platform-fastify": "^11.1.3", "@nestjs/platform-socket.io": "^11.1.3", - "@nestjs/schedule": "^6.0.0", "@nestjs/swagger": "^11.2.0", "@nestjs/throttler": "^6.4.0", "@nestjs/websockets": "^11.1.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 917730f..e67699d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -71,9 +71,6 @@ importers: '@nestjs/platform-socket.io': specifier: ^11.1.3 version: 11.1.3(@nestjs/common@11.1.3(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@11.1.3)(rxjs@7.8.2) - '@nestjs/schedule': - specifier: ^6.0.0 - version: 6.0.0(@nestjs/common@11.1.3(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.3) '@nestjs/swagger': specifier: ^11.2.0 version: 11.2.0(@fastify/static@8.2.0)(@nestjs/common@11.1.3(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.3)(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2) @@ -1282,12 +1279,6 @@ packages: '@nestjs/websockets': ^11.0.0 rxjs: ^7.1.0 - '@nestjs/schedule@6.0.0': - resolution: {integrity: sha512-aQySMw6tw2nhitELXd3EiRacQRgzUKD9mFcUZVOJ7jPLqIBvXOyvRWLsK9SdurGA+jjziAlMef7iB5ZEFFoQpw==} - peerDependencies: - '@nestjs/common': ^10.0.0 || ^11.0.0 - '@nestjs/core': ^10.0.0 || ^11.0.0 - '@nestjs/schematics@11.0.5': resolution: {integrity: sha512-T50SCNyqCZ/fDssaOD7meBKLZ87ebRLaJqZTJPvJKjlib1VYhMOCwXYsr7bjMPmuPgiQHOwvppz77xN/m6GM7A==} peerDependencies: @@ -1606,9 +1597,6 @@ packages: '@types/jsonwebtoken@9.0.7': resolution: {integrity: sha512-ugo316mmTYBl2g81zDFnZ7cfxlut3o+/EQdaP7J8QN2kY6lJ22hmQYCK5EHcJHbrW+dkCGSCPgbG8JtYj6qSrg==} - '@types/luxon@3.6.2': - resolution: {integrity: sha512-R/BdP7OxEMc44l2Ex5lSXHoIXTB2JLNa3y2QISIbr58U/YcsffyQrYW//hZSdrfxrjRZj3GcUoxMPGdO8gSYuw==} - '@types/methods@1.1.4': resolution: {integrity: sha512-ymXWVrDiCxTBE3+RIrrP533E70eA+9qu7zdWoHuOmGujkYtzf4HQF96b8nwHLqhuf4ykX61IGRIB38CC6/sImQ==} @@ -2524,10 +2512,6 @@ packages: resolution: {integrity: sha512-p0SaNjrHOnQeR8/VnfGbmg9te2kfyYSQ7Sc/j/6DtPL3JQvKxmjO9TSjNFpujqV3vEYYBvNNvXSxzyksBWAx1Q==} engines: {node: '>=12.0.0'} - cron@4.3.0: - resolution: {integrity: sha512-ciiYNLfSlF9MrDqnbMdRWFiA6oizSF7kA1osPP9lRzNu0Uu+AWog1UKy7SkckiDY2irrNjeO6qLyKnXC8oxmrw==} - engines: {node: '>=18.x'} - cross-spawn@6.0.6: resolution: {integrity: sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==} engines: {node: '>=4.8'} @@ -7191,12 +7175,6 @@ snapshots: - supports-color - utf-8-validate - '@nestjs/schedule@6.0.0(@nestjs/common@11.1.3(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.3)': - dependencies: - '@nestjs/common': 11.1.3(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2) - '@nestjs/core': 11.1.3(@nestjs/common@11.1.3(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@11.1.3)(reflect-metadata@0.2.2)(rxjs@7.8.2) - cron: 4.3.0 - '@nestjs/schematics@11.0.5(chokidar@4.0.3)(typescript@5.8.3)': dependencies: '@angular-devkit/core': 19.2.6(chokidar@4.0.3) @@ -7514,8 +7492,6 @@ snapshots: dependencies: '@types/node': 22.16.0 - '@types/luxon@3.6.2': {} - '@types/methods@1.1.4': {} '@types/mime@1.3.5': {} @@ -8569,11 +8545,6 @@ snapshots: dependencies: luxon: 3.6.1 - cron@4.3.0: - dependencies: - '@types/luxon': 3.6.2 - luxon: 3.6.1 - cross-spawn@6.0.6: dependencies: nice-try: 1.0.5 diff --git a/src/app.module.ts b/src/app.module.ts index e3e7ba6..1970bef 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -5,7 +5,6 @@ import { CacheModule } from "@nestjs/cache-manager"; import { MiddlewareConsumer, Module, NestModule } from "@nestjs/common"; import { ConfigModule } from "@nestjs/config"; import { JwtModule } from "@nestjs/jwt"; -import { ScheduleModule } from "@nestjs/schedule"; import { ThrottlerModule } from "@nestjs/throttler"; import { bullMqConfig } from "./configs/bullmq.config"; @@ -30,7 +29,6 @@ import { UtilsModule } from "./modules/utils/utils.module"; imports: [ ConfigModule.forRoot({ isGlobal: true, cache: true }), JwtModule.registerAsync(jwtConfig()), - ScheduleModule.forRoot(), BullModule.forRootAsync(bullMqConfig()), CacheModule.registerAsync(cacheConfig()), HttpModule.registerAsync(httpConfig()), diff --git a/src/modules/quota-sync/constants/index.ts b/src/modules/quota-sync/constants/index.ts new file mode 100644 index 0000000..51ebbb1 --- /dev/null +++ b/src/modules/quota-sync/constants/index.ts @@ -0,0 +1,11 @@ +export const QUOTA_SYNC_QUEUE = Object.freeze({ + QUEUE_NAME: "quota-sync", + QUEUE_PREFIX: "quota", + + // Job names + SYNC_USER_QUOTA_JOB: "sync.user.quota", + SYNC_BUSINESS_QUOTA_JOB: "sync.business.quota", + SYNC_ALL_USERS_QUOTA_JOB: "sync.all.users.quota", + SYNC_ALL_BUSINESSES_QUOTA_JOB: "sync.all.businesses.quota", + SYNC_COMPREHENSIVE_QUOTA_JOB: "sync.comprehensive.quota", +}); diff --git a/src/modules/quota-sync/interfaces/quota-sync-job.interface.ts b/src/modules/quota-sync/interfaces/quota-sync-job.interface.ts new file mode 100644 index 0000000..34f891b --- /dev/null +++ b/src/modules/quota-sync/interfaces/quota-sync-job.interface.ts @@ -0,0 +1,16 @@ +export interface ISyncUserQuotaJob { + userId: string; + businessId: string; + wildduckUserId: string; +} + +export interface ISyncBusinessQuotaJob { + businessId: string; + businessName: string; +} + +export type ISyncAllUsersQuotaJob = Record; + +export type ISyncAllBusinessesQuotaJob = Record; + +export type ISyncComprehensiveQuotaJob = Record; diff --git a/src/modules/quota-sync/queue/quota-sync.processor.ts b/src/modules/quota-sync/queue/quota-sync.processor.ts new file mode 100644 index 0000000..5e4e3e3 --- /dev/null +++ b/src/modules/quota-sync/queue/quota-sync.processor.ts @@ -0,0 +1,109 @@ +// import { EntityManager } from "@mikro-orm/core"; +import { Processor } from "@nestjs/bullmq"; +import { Logger } from "@nestjs/common"; +import { Job } from "bullmq"; + +import { WorkerProcessor } from "../../../common/queues/worker.processor"; +import { QUOTA_SYNC_QUEUE } from "../constants"; +import { + ISyncAllBusinessesQuotaJob, + ISyncAllUsersQuotaJob, + ISyncBusinessQuotaJob, + ISyncComprehensiveQuotaJob, + ISyncUserQuotaJob, +} from "../interfaces/quota-sync-job.interface"; +import { QuotaSyncService } from "../services/quota-sync.service"; + +@Processor(QUOTA_SYNC_QUEUE.QUEUE_NAME) +export class QuotaSyncProcessor extends WorkerProcessor { + protected readonly logger = new Logger(QuotaSyncProcessor.name); + + constructor( + private readonly quotaSyncService: QuotaSyncService, + // private readonly em: EntityManager, + ) { + super(); + } + + async process(job: Job): Promise { + const startTime = Date.now(); + + try { + await this.handleJob(job); + this.logJobSuccess(job, startTime); + } catch (error) { + this.logJobFailure(job, error, startTime); + throw error; + } + } + + private async handleJob(job: Job): Promise { + switch (job.name) { + case QUOTA_SYNC_QUEUE.SYNC_USER_QUOTA_JOB: + await this.handleSyncUserQuota(job); + break; + case QUOTA_SYNC_QUEUE.SYNC_BUSINESS_QUOTA_JOB: + await this.handleSyncBusinessQuota(job); + break; + case QUOTA_SYNC_QUEUE.SYNC_ALL_USERS_QUOTA_JOB: + await this.handleSyncAllUsersQuota(job); + break; + case QUOTA_SYNC_QUEUE.SYNC_ALL_BUSINESSES_QUOTA_JOB: + await this.handleSyncAllBusinessesQuota(job); + break; + case QUOTA_SYNC_QUEUE.SYNC_COMPREHENSIVE_QUOTA_JOB: + await this.handleSyncComprehensiveQuota(job); + break; + default: + throw new Error(`Unsupported job type: ${job.name}`); + } + } + + private async handleSyncUserQuota(job: Job): Promise { + const { userId } = job.data; + this.logger.log(`Processing user quota sync for user: ${userId}`); + + await this.quotaSyncService.syncUserQuotaById(userId); + this.logger.log(`Successfully synced quota for user: ${userId}`); + } + + private async handleSyncBusinessQuota(job: Job): Promise { + const { businessId, businessName } = job.data; + this.logger.log(`Processing business quota sync for business: ${businessName} (${businessId})`); + + await this.quotaSyncService.syncBusinessQuotaById(businessId); + this.logger.log(`Successfully synced quota for business: ${businessName} (${businessId})`); + } + + private async handleSyncAllUsersQuota(_job: Job): Promise { + this.logger.log("Processing quota sync for all users"); + + await this.quotaSyncService.syncAllUsersQuota(); + this.logger.log("Successfully synced quota for all users"); + } + + private async handleSyncAllBusinessesQuota(_job: Job): Promise { + this.logger.log("Processing quota sync for all businesses"); + + await this.quotaSyncService.syncAllBusinessQuotas(); + this.logger.log("Successfully synced quota for all businesses"); + } + + private async handleSyncComprehensiveQuota(_job: Job): Promise { + this.logger.log("Processing comprehensive quota sync"); + + await this.quotaSyncService.syncAllQuotas(); + this.logger.log("Successfully completed comprehensive quota sync"); + } + + private logJobSuccess(job: Job, startTime: number): void { + const duration = Date.now() - startTime; + this.logger.log(`Job ${job.id} (${job.name}) completed successfully in ${duration}ms`); + } + + private logJobFailure(job: Job, error: unknown, startTime: number): void { + const duration = Date.now() - startTime; + const errorMessage = error instanceof Error ? error.message : "Unknown error"; + this.logger.error(`Job ${job.id} (${job.name}) failed after ${duration}ms: ${errorMessage}`); + } +} diff --git a/src/modules/quota-sync/quota-sync.module.ts b/src/modules/quota-sync/quota-sync.module.ts index 2fb0d47..1b8ede2 100644 --- a/src/modules/quota-sync/quota-sync.module.ts +++ b/src/modules/quota-sync/quota-sync.module.ts @@ -1,11 +1,24 @@ +import { BullModule } from "@nestjs/bullmq"; import { Module } from "@nestjs/common"; -import { MailServerModule } from "../mail-server/mail-server.module"; +import { QUOTA_SYNC_QUEUE } from "./constants"; +import { QuotaSyncProcessor } from "./queue/quota-sync.processor"; import { QuotaSyncService } from "./services/quota-sync.service"; +import { MailServerModule } from "../mail-server/mail-server.module"; @Module({ - imports: [MailServerModule], - providers: [QuotaSyncService], + imports: [ + MailServerModule, + BullModule.registerQueue({ + name: QUOTA_SYNC_QUEUE.QUEUE_NAME, + prefix: QUOTA_SYNC_QUEUE.QUEUE_PREFIX, + defaultJobOptions: { + removeOnComplete: true, + removeOnFail: true, + }, + }), + ], + providers: [QuotaSyncService, QuotaSyncProcessor], exports: [QuotaSyncService], }) export class QuotaSyncModule {} diff --git a/src/modules/quota-sync/services/quota-sync.service.ts b/src/modules/quota-sync/services/quota-sync.service.ts index 563e69d..2477160 100644 --- a/src/modules/quota-sync/services/quota-sync.service.ts +++ b/src/modules/quota-sync/services/quota-sync.service.ts @@ -1,11 +1,13 @@ import { EntityManager } from "@mikro-orm/core"; +import { InjectQueue } from "@nestjs/bullmq"; import { Injectable, Logger } from "@nestjs/common"; -import { Cron, CronExpression } from "@nestjs/schedule"; +import { Queue } from "bullmq"; import { firstValueFrom } from "rxjs"; import { Business } from "../../businesses/entities/business.entity"; import { WildDuckUsersService } from "../../mail-server/services/wildduck-users.service"; import { User } from "../../users/entities/user.entity"; +import { QUOTA_SYNC_QUEUE } from "../constants"; import { BusinessQuotaInfoDto, BusinessQuotaStatisticsDto, @@ -14,20 +16,127 @@ import { QuotaSyncResultDto, UserQuotaStatisticsDto, } from "../DTO/quota-statistics.dto"; +import { ISyncBusinessQuotaJob, ISyncUserQuotaJob } from "../interfaces/quota-sync-job.interface"; @Injectable() export class QuotaSyncService { private readonly logger = new Logger(QuotaSyncService.name); constructor( + @InjectQueue(QUOTA_SYNC_QUEUE.QUEUE_NAME) private readonly quotaSyncQueue: Queue, private readonly wildDuckUsersService: WildDuckUsersService, private readonly em: EntityManager, ) {} /** - * Sync quota usage every 2 hours + * Add a job to sync quota usage for all users + */ + async scheduleAllUsersQuotaSync(): Promise { + await this.quotaSyncQueue.add( + QUOTA_SYNC_QUEUE.SYNC_ALL_USERS_QUOTA_JOB, + {}, + { + removeOnComplete: true, + removeOnFail: false, + attempts: 3, + backoff: { + type: "exponential", + delay: 2000, + }, + }, + ); + this.logger.log("Scheduled all users quota sync job"); + } + + /** + * Add a job to sync business quota usage for all businesses + */ + async scheduleAllBusinessQuotasSync(): Promise { + await this.quotaSyncQueue.add( + QUOTA_SYNC_QUEUE.SYNC_ALL_BUSINESSES_QUOTA_JOB, + {}, + { + removeOnComplete: true, + removeOnFail: false, + attempts: 3, + backoff: { + type: "exponential", + delay: 2000, + }, + }, + ); + this.logger.log("Scheduled all businesses quota sync job"); + } + + /** + * Add a job to sync quota for a specific user + */ + async scheduleUserQuotaSync(userId: string, businessId: string, wildduckUserId: string): Promise { + const jobData: ISyncUserQuotaJob = { + userId, + businessId, + wildduckUserId, + }; + + await this.quotaSyncQueue.add(QUOTA_SYNC_QUEUE.SYNC_USER_QUOTA_JOB, jobData, { + removeOnComplete: true, + removeOnFail: false, + attempts: 3, + backoff: { + type: "exponential", + delay: 2000, + }, + }); + this.logger.log(`Scheduled user quota sync job for user: ${userId}`); + } + + /** + * Add a job to sync quota for a specific business + */ + async scheduleBusinessQuotaSync(businessId: string, businessName: string): Promise { + const jobData: ISyncBusinessQuotaJob = { + businessId, + businessName, + }; + + await this.quotaSyncQueue.add(QUOTA_SYNC_QUEUE.SYNC_BUSINESS_QUOTA_JOB, jobData, { + removeOnComplete: true, + removeOnFail: false, + attempts: 3, + backoff: { + type: "exponential", + delay: 2000, + }, + }); + this.logger.log(`Scheduled business quota sync job for business: ${businessName}`); + } + + /** + * Add a job for comprehensive quota sync + */ + async scheduleComprehensiveQuotaSync(): Promise { + await this.quotaSyncQueue.add( + QUOTA_SYNC_QUEUE.SYNC_COMPREHENSIVE_QUOTA_JOB, + {}, + { + removeOnComplete: true, + removeOnFail: false, + attempts: 3, + backoff: { + type: "exponential", + delay: 2000, + }, + }, + ); + this.logger.log("Scheduled comprehensive quota sync job"); + } + + // The following methods remain the same but without cron decorators + // They are now called by the queue processor + + /** + * Sync quota usage for all users (called by processor) */ - @Cron(CronExpression.EVERY_2_HOURS) async syncAllUsersQuota(): Promise { const em = this.em.fork(); this.logger.log("Starting quota synchronization for all users"); @@ -60,13 +169,13 @@ export class QuotaSyncService { this.logger.log(`Quota sync completed: ${successCount} success, ${errorCount} errors`); } catch (error) { this.logger.error("Failed to sync quota for users", `${error instanceof Error ? error.message : "Unknown error"}`); + throw error; } } /** - * Sync business quota usage every 4 hours + * Sync business quota usage for all businesses (called by processor) */ - @Cron(CronExpression.EVERY_4_HOURS) async syncAllBusinessQuotas(): Promise { const em = this.em.fork(); this.logger.log("Starting business quota synchronization"); @@ -97,6 +206,7 @@ export class QuotaSyncService { this.logger.log(`Business quota sync completed: ${successCount} success, ${errorCount} errors`); } catch (error) { this.logger.error("Failed to sync quota for businesses", `${error instanceof Error ? error.message : "Unknown error"}`); + throw error; } } diff --git a/src/modules/templates/DTO/index.ts b/src/modules/templates/DTO/index.ts deleted file mode 100644 index a2c0ddb..0000000 --- a/src/modules/templates/DTO/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export { CreateTemplateDto } from "./create-template.dto"; -export { UpdateTemplateDto } from "./update-template.dto"; -export { TemplateQueryDto } from "./template-query.dto"; -export { TemplateParamDto } from "./template-param.dto"; diff --git a/src/modules/templates/DTO/update-template.dto.ts b/src/modules/templates/DTO/update-template.dto.ts index 9705181..20895ec 100644 --- a/src/modules/templates/DTO/update-template.dto.ts +++ b/src/modules/templates/DTO/update-template.dto.ts @@ -1,44 +1,5 @@ -import { ApiPropertyOptional } from "@nestjs/swagger"; -import { Type } from "class-transformer"; -import { IsOptional, IsString, MaxLength, ValidateNested } from "class-validator"; +import { PartialType } from "@nestjs/swagger"; -import { TemplateMessage } from "../../../common/enums/message.enum"; -import { PersonalityDataType } from "../interfaces/structure.interface"; +import { CreateTemplateDto } from "./create-template.dto"; -export class UpdateTemplateDto { - @IsOptional() - @IsString({ message: TemplateMessage.TEMPLATE_NAME_STRING }) - @MaxLength(255, { message: TemplateMessage.TEMPLATE_NAME_MAX_LENGTH }) - @ApiPropertyOptional({ description: "Template name", example: "Updated Corporate Email Template", maxLength: 255 }) - name?: string; - - @IsOptional() - @ValidateNested({ message: TemplateMessage.TEMPLATE_STRUCTURE_VALID }) - @Type(() => Object) - @ApiPropertyOptional({ - description: "Template structure containing header, content, and footer sections", - example: { - header: { - columnsCount: 1, - items: [], - }, - content: { - columnsCount: 2, - items: [], - }, - footer: { - columnsCount: 1, - items: [], - }, - }, - }) - structure?: PersonalityDataType; - - @IsOptional() - @IsString({ message: TemplateMessage.TEMPLATE_RAW_HTML_STRING }) - @ApiPropertyOptional({ - description: "Raw HTML content of the template", - example: "

Updated Template Content

", - }) - rawHtml?: string; -} +export class UpdateTemplateDto extends PartialType(CreateTemplateDto) {} diff --git a/src/modules/users/services/users.service.ts b/src/modules/users/services/users.service.ts index d67fbaf..7756ecc 100644 --- a/src/modules/users/services/users.service.ts +++ b/src/modules/users/services/users.service.ts @@ -10,6 +10,7 @@ import { DomainAutomationService } from "../../domains/services/domain-automatio import { DomainsService } from "../../domains/services/domains.service"; import { UpdateUserDto } from "../../mail-server/DTO"; import { MailServerService } from "../../mail-server/services/mail-server.service"; +import { QuotaSyncService } from "../../quota-sync/services/quota-sync.service"; import { Template } from "../../templates/entities/template.entity"; import { PasswordService } from "../../utils/services/password.service"; import { CreateEmailUserDto } from "../DTO/create-email-user.dto"; @@ -31,6 +32,7 @@ export class UsersService { private readonly mailServerService: MailServerService, private readonly domainsService: DomainsService, private readonly domainAutomationService: DomainAutomationService, + private readonly quotaSyncService: QuotaSyncService, ) {} async getUserEmailIdFromId(userId: string) { @@ -169,6 +171,11 @@ export class UsersService { await this.em.flush(); + //sync quota + await this.quotaSyncService.scheduleUserQuotaSync(user.id, businessId, mailServerUser.id); + //sync business quota + await this.quotaSyncService.scheduleBusinessQuotaSync(businessId, business.name); + this.logger.log(`Email user created: ${emailAddress} for business ${businessId}`); await this.domainAutomationService.createDefaultMailboxes(mailServerUser.id); @@ -221,6 +228,11 @@ 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}`); return { message: UserMessage.EMAIL_USER_DELETED_SUCCESSFULLY }; @@ -316,6 +328,14 @@ export class UsersService { await this.em.flush(); + //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 { @@ -372,6 +392,14 @@ export class UsersService { 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`); return { message: UserMessage.EMAIL_USER_QUOTA_UPDATED_SUCCESSFULLY, user }; diff --git a/src/modules/users/users.module.ts b/src/modules/users/users.module.ts index 7784a57..26f64a9 100644 --- a/src/modules/users/users.module.ts +++ b/src/modules/users/users.module.ts @@ -9,10 +9,11 @@ import { UsersController } from "./users.controller"; import { BusinessesModule } from "../businesses/businesses.module"; import { DomainsModule } from "../domains/domains.module"; import { MailServerModule } from "../mail-server/mail-server.module"; +import { QuotaSyncModule } from "../quota-sync/quota-sync.module"; import { UtilsModule } from "../utils/utils.module"; @Module({ - imports: [MikroOrmModule.forFeature([User, RefreshToken, Role]), UtilsModule, MailServerModule, DomainsModule, BusinessesModule], + imports: [MikroOrmModule.forFeature([User, RefreshToken, Role]), UtilsModule, MailServerModule, DomainsModule, BusinessesModule, QuotaSyncModule], controllers: [UsersController], providers: [UsersService], exports: [UsersService],