diff --git a/src/cluster.ts b/src/cluster.ts index aae93595..1e134347 100644 --- a/src/cluster.ts +++ b/src/cluster.ts @@ -4,8 +4,10 @@ import os from 'os' export class Cluster { static register(workers: Number, callback: Function): void { if (cluster.isPrimary) { - consola.info(`Primary server started on ${process.pid}`) + const cpus = os.cpus().length + consola.info(`Primary server started on ${process.pid}`) + consola.info('CPU:' + cpus) //ensure workers exit cleanly process.on('SIGINT', function () { consola.info('Cluster shutting down...') @@ -16,12 +18,20 @@ export class Cluster { process.exit(0) }) - const cpus = os.cpus().length if (workers > cpus) workers = cpus for (let i = 0; i < workers; i++) { cluster.fork() } + + cluster.on('fork', function (worker) { + worker.on('message', function (msg) { + Object.keys(cluster.workers).forEach(function (id) { + cluster.workers[id].send(msg) + }) + }) + }) + cluster.on('online', function (worker) { consola.info('Worker %s is online', worker.process.pid) }) diff --git a/src/common/filters/any-exception.filter.ts b/src/common/filters/any-exception.filter.ts index 4b0ebcd0..06cbdc13 100644 --- a/src/common/filters/any-exception.filter.ts +++ b/src/common/filters/any-exception.filter.ts @@ -15,7 +15,7 @@ import { resolve } from 'path' import { HTTP_REQUEST_TIME } from '~/constants/meta.constant' import { LOGGER_DIR } from '~/constants/path.constant' import { REFLECTOR } from '~/constants/system.constant' -import { isDev } from '~/utils/index.util' +import { isDev } from '~/utils' import { getIp } from '../../utils/ip.util' import { LoggingInterceptor } from '../interceptors/logging.interceptor' type myError = { diff --git a/src/common/guard/auth.guard.ts b/src/common/guard/auth.guard.ts index 8e524258..626dada8 100644 --- a/src/common/guard/auth.guard.ts +++ b/src/common/guard/auth.guard.ts @@ -1,7 +1,7 @@ import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common' import { AuthGuard as _AuthGuard } from '@nestjs/passport' import { mockUser1 } from '~/mock/user.mock' -import { isTest } from '~/utils/index.util' +import { isTest } from '~/utils' import { getNestExecutionContextRequest } from '~/utils/nest.util' /** diff --git a/src/common/guard/spider.guard.ts b/src/common/guard/spider.guard.ts index ce89d800..ca706043 100644 --- a/src/common/guard/spider.guard.ts +++ b/src/common/guard/spider.guard.ts @@ -10,7 +10,7 @@ import { Injectable, } from '@nestjs/common' import { Observable } from 'rxjs' -import { isDev } from '~/utils/index.util' +import { isDev } from '~/utils' import { getNestExecutionContextRequest } from '~/utils/nest.util' @Injectable() diff --git a/src/constants/path.constant.ts b/src/constants/path.constant.ts index bf76fe9c..de42505e 100644 --- a/src/constants/path.constant.ts +++ b/src/constants/path.constant.ts @@ -1,6 +1,6 @@ import { homedir } from 'os' import { join } from 'path' -import { isDev } from '~/utils/index.util' +import { isDev } from '~/utils' export const HOME = homedir() diff --git a/src/modules/comment/comment.interceptor.ts b/src/modules/comment/comment.interceptor.ts index 9a1944b2..82f03773 100644 --- a/src/modules/comment/comment.interceptor.ts +++ b/src/modules/comment/comment.interceptor.ts @@ -7,7 +7,7 @@ import { import { isDefined } from 'class-validator' import { cloneDeep, isArrayLike, isObjectLike } from 'lodash' import { map } from 'rxjs' -import { getAvatar } from '~/utils/index.util' +import { getAvatar } from '~/utils' import { getNestExecutionContextRequest } from '~/utils/nest.util' @Injectable() export class CommentFilterEmailInterceptor implements NestInterceptor { diff --git a/src/modules/comment/comment.model.ts b/src/modules/comment/comment.model.ts index cd8f6c38..ec0a77a1 100644 --- a/src/modules/comment/comment.model.ts +++ b/src/modules/comment/comment.model.ts @@ -8,7 +8,7 @@ import { import { BeAnObject } from '@typegoose/typegoose/lib/types' import { Query, Types } from 'mongoose' import { BaseModel } from '~/shared/model/base.model' -import { getAvatar } from '~/utils/index.util' +import { getAvatar } from '~/utils' import { NoteModel } from '../note/note.model' import { PageModel } from '../page/page.model' import { PostModel } from '../post/post.model' diff --git a/src/modules/comment/comment.service.ts b/src/modules/comment/comment.service.ts index cf8f0e0d..cd3fe471 100644 --- a/src/modules/comment/comment.service.ts +++ b/src/modules/comment/comment.service.ts @@ -11,7 +11,7 @@ import { ReplyMailType, } from '~/processors/helper/helper.email.service' import { WriteBaseModel } from '~/shared/model/base.model' -import { hasChinese } from '~/utils/index.util' +import { hasChinese } from '~/utils' import { ConfigsService } from '../configs/configs.service' import { UserService } from '../user/user.service' import BlockedKeywords from './block-keywords.json' diff --git a/src/modules/configs/configs.service.ts b/src/modules/configs/configs.service.ts index 5d4acd9ad..4801c427 100644 --- a/src/modules/configs/configs.service.ts +++ b/src/modules/configs/configs.service.ts @@ -10,6 +10,7 @@ import { BeAnObject } from '@typegoose/typegoose/lib/types' import camelcaseKeys from 'camelcase-keys' import { ClassConstructor, plainToClass } from 'class-transformer' import { validateSync, ValidatorOptions } from 'class-validator' +import cluster from 'cluster' import { cloneDeep, mergeWith } from 'lodash' import { LeanDocument } from 'mongoose' import { InjectModel } from 'nestjs-typegoose' @@ -17,7 +18,7 @@ import { API_VERSION } from '~/app.config' import { RedisKeys } from '~/constants/cache.constant' import { EventBusEvents } from '~/constants/event.constant' import { CacheService } from '~/processors/cache/cache.service' -import { sleep } from '~/utils/index.util' +import { sleep, workerEmit } from '~/utils' import { getRedisKey } from '~/utils/redis.util' import * as optionDtos from '../configs/configs.dto' import { UserModel } from '../user/user.model' @@ -201,7 +202,11 @@ export class ConfigsService { this.validWithDto(MailOptionsDto, value), ) if (option.enable) { - this.eventEmitter.emit(EventBusEvents.EmailInit) + if (cluster.isPrimary) { + this.eventEmitter.emit(EventBusEvents.EmailInit) + } else { + workerEmit(EventBusEvents.EmailInit) + } } return option diff --git a/src/modules/link/link.service.ts b/src/modules/link/link.service.ts index 464dadf5..659e1446 100644 --- a/src/modules/link/link.service.ts +++ b/src/modules/link/link.service.ts @@ -6,7 +6,7 @@ import { EmailService, LinkApplyEmailType, } from '~/processors/helper/helper.email.service' -import { isDev } from '~/utils/index.util' +import { isDev } from '~/utils' import { ConfigsService } from '../configs/configs.service' import { LinkModel, LinkState, LinkType } from './link.model' diff --git a/src/modules/note/note.service.ts b/src/modules/note/note.service.ts index fb0b6de1..ee565366 100644 --- a/src/modules/note/note.service.ts +++ b/src/modules/note/note.service.ts @@ -8,7 +8,7 @@ import { CacheService } from '~/processors/cache/cache.service' import { EventTypes } from '~/processors/gateway/events.types' import { WebEventsGateway } from '~/processors/gateway/web/events.gateway' import { ImageService } from '~/processors/helper/helper.image.service' -import { deleteKeys } from '~/utils/index.util' +import { deleteKeys } from '~/utils' import { NoteModel } from './note.model' @Injectable() diff --git a/src/modules/user/user.controller.ts b/src/modules/user/user.controller.ts index 711b8522..1135d7d2 100644 --- a/src/modules/user/user.controller.ts +++ b/src/modules/user/user.controller.ts @@ -15,7 +15,7 @@ import { CurrentUser } from '~/common/decorator/current-user.decorator' import { IpLocation, IpRecord } from '~/common/decorator/ip.decorator' import { ApiName } from '~/common/decorator/openapi.decorator' import { IsMaster } from '~/common/decorator/role.decorator' -import { getAvatar } from '~/utils/index.util' +import { getAvatar } from '~/utils' import { AuthService } from '../auth/auth.service' import { LoginDto, UserDto, UserPatchDto } from './user.dto' import { UserDocument, UserModel } from './user.model' diff --git a/src/modules/user/user.service.ts b/src/modules/user/user.service.ts index 0f678aac..49861d9e 100644 --- a/src/modules/user/user.service.ts +++ b/src/modules/user/user.service.ts @@ -12,7 +12,7 @@ import { nanoid } from 'nanoid' import { InjectModel } from 'nestjs-typegoose' import { RedisKeys } from '~/constants/cache.constant' import { CacheService } from '~/processors/cache/cache.service' -import { getAvatar, sleep } from '~/utils/index.util' +import { getAvatar, sleep } from '~/utils' import { getRedisKey } from '~/utils/redis.util' import { AuthService } from '../auth/auth.service' import { UserDocument, UserModel } from './user.model' diff --git a/src/processors/helper/helper.email.service.ts b/src/processors/helper/helper.email.service.ts index 46f5ee86..d763f943 100644 --- a/src/processors/helper/helper.email.service.ts +++ b/src/processors/helper/helper.email.service.ts @@ -1,5 +1,6 @@ import { Injectable, Logger } from '@nestjs/common' import { OnEvent } from '@nestjs/event-emitter' +import cluster from 'cluster' import { render } from 'ejs' import { createTransport } from 'nodemailer' import { EventBusEvents } from '~/constants/event.constant' @@ -27,6 +28,22 @@ export class EmailService { ) { this.init() this.logger = new Logger(EmailService.name) + + if (cluster.isWorker) { + // listen email option change in node cluster + process.on('message', (message: any) => { + const { event } = message + + switch (event) { + case EventBusEvents.EmailInit: + this.init() + break + + default: + break + } + }) + } } async readTemplate(type: ReplyMailType) { diff --git a/src/utils/cluster.util.ts b/src/utils/cluster.util.ts new file mode 100644 index 00000000..05328b4e --- /dev/null +++ b/src/utils/cluster.util.ts @@ -0,0 +1,7 @@ +import cluster from 'cluster' +import { EventBusEvents } from '~/constants/event.constant' +export const workerEmit = (event: EventBusEvents, data?: any) => { + if (cluster.isWorker) { + process.send({ event, data, workerId: cluster.worker.id }) + } +} diff --git a/src/utils/consola.util.ts b/src/utils/consola.util.ts index 9dd45dd7..e19a3a08 100644 --- a/src/utils/consola.util.ts +++ b/src/utils/consola.util.ts @@ -1,6 +1,7 @@ import consola_, { FancyReporter, LogLevel } from 'consola' import { argv } from 'zx' -import { isDev } from './index.util' +import { isDev } from './tool.util' + class DateTimeReporter extends FancyReporter { formatDate(date: Date) { return date.toLocaleString(undefined, { diff --git a/src/utils/global.util.ts b/src/utils/global.util.ts index ef2df4d1..7224229d 100644 --- a/src/utils/global.util.ts +++ b/src/utils/global.util.ts @@ -1,6 +1,6 @@ +import { isDev } from '.' import { consola } from './consola.util' import './dayjs.util' -import { isDev } from './index.util' console.debug = (...rest) => { if (isDev) { diff --git a/src/utils/index.ts b/src/utils/index.ts new file mode 100644 index 00000000..8c3483c1 --- /dev/null +++ b/src/utils/index.ts @@ -0,0 +1,13 @@ +export * from './cluster.util' +export * from './consola.util' +export * from './crud.util' +export * from './dayjs.util' +export * from './ip.util' +export * from './nest.util' +export * from './pic.util' +export * from './query.util' +export * from './redis.util' +export * from './system.util' +export * from './time.util' +export * from './tool.util' +export * from './transfrom.util' diff --git a/src/utils/index.util.ts b/src/utils/tool.util.ts similarity index 97% rename from src/utils/index.util.ts rename to src/utils/tool.util.ts index 639c7eac..056681ee 100644 --- a/src/utils/index.util.ts +++ b/src/utils/tool.util.ts @@ -1,6 +1,5 @@ import { isObject } from 'lodash' -export * from './ip.util' export const isDev = process.env.NODE_ENV == 'development' export const isTest = !!process.env.TEST diff --git a/test/src/utils/pic.util.spec.ts b/test/src/utils/pic.util.spec.ts index 46982979..5665a08b 100644 --- a/test/src/utils/pic.util.spec.ts +++ b/test/src/utils/pic.util.spec.ts @@ -1,4 +1,4 @@ -import { sleep } from '~/utils/index.util' +import { sleep } from '~/utils' import { pickImagesFromMarkdown } from '~/utils/pic.util' describe('src/utils/pic.util', () => {