diff --git a/api/nest-cli.json b/api/nest-cli.json index 17ba0936..245d2952 100644 --- a/api/nest-cli.json +++ b/api/nest-cli.json @@ -4,6 +4,9 @@ "sourceRoot": "src", "compilerOptions": { "deleteOutDir": true, - "assets": [{ "include": "config/i18n/**/*", "watchAssets": true }] + "assets": [ + { "include": "config/i18n/**/*", "watchAssets": true }, + { "include": "templates/**/*.mjml", "watchAssets": true } + ] } } diff --git a/api/package-lock.json b/api/package-lock.json index 27c54bbf..c9982881 100644 --- a/api/package-lock.json +++ b/api/package-lock.json @@ -1,12 +1,12 @@ { "name": "hexabot", - "version": "2.2.2", + "version": "2.2.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "hexabot", - "version": "2.2.2", + "version": "2.2.3", "hasInstallScript": true, "license": "AGPL-3.0-only", "dependencies": { diff --git a/api/package.json b/api/package.json index 89f7fd84..cb1d9a61 100644 --- a/api/package.json +++ b/api/package.json @@ -1,6 +1,6 @@ { "name": "hexabot", - "version": "2.2.2", + "version": "2.2.3", "description": "Hexabot is a solution for creating and managing chatbots across multiple channels, leveraging AI for advanced conversational capabilities. It provides a user-friendly interface for building, training, and deploying chatbots with integrated support for various messaging platforms.", "author": "Hexastack", "license": "AGPL-3.0-only", diff --git a/api/patches/@nestjs-modules+mailer+1.11.2.patch b/api/patches/@nestjs-modules+mailer+1.11.2.patch index b346b9f8..f100ec72 100644 --- a/api/patches/@nestjs-modules+mailer+1.11.2.patch +++ b/api/patches/@nestjs-modules+mailer+1.11.2.patch @@ -1,3 +1,30 @@ +diff --git a/node_modules/@nestjs-modules/mailer/dist/adapters/mjml.adapter.js b/node_modules/@nestjs-modules/mailer/dist/adapters/mjml.adapter.js +index 5eb7a4a..afaaaa9 100644 +--- a/node_modules/@nestjs-modules/mailer/dist/adapters/mjml.adapter.js ++++ b/node_modules/@nestjs-modules/mailer/dist/adapters/mjml.adapter.js +@@ -26,8 +26,20 @@ class MjmlAdapter { + compile(mail, callback, mailerOptions) { + var _a; + (_a = this === null || this === void 0 ? void 0 : this.engine) === null || _a === void 0 ? void 0 : _a.compile(mail, () => { +- mail.data.html = mjml2html(mail.data.html).html; +- callback(); ++ Promise.resolve(mail.data.html).then((html) => { ++ Promise.resolve(mjml2html(html)).then((result) => { ++ mail.data.html = result.html; ++ callback(); ++ }).catch((err) => { ++ console.error('@nestjs-modules/mailer: Unable to convert mjml to html', err) ++ mail.data.html = ''; ++ callback(); ++ }) ++ }).catch((err) => { ++ console.error('@nestjs-modules/mailer: Unable to compiling mjml', err) ++ mail.data.html = ''; ++ callback(); ++ }) + }, mailerOptions); + } + } diff --git a/node_modules/@nestjs-modules/mailer/dist/mailer.service.js b/node_modules/@nestjs-modules/mailer/dist/mailer.service.js index 016055b..d534240 100644 --- a/node_modules/@nestjs-modules/mailer/dist/mailer.service.js diff --git a/api/src/analytics/services/bot-stats.service.ts b/api/src/analytics/services/bot-stats.service.ts index 7c47b457..2b319a66 100644 --- a/api/src/analytics/services/bot-stats.service.ts +++ b/api/src/analytics/services/bot-stats.service.ts @@ -1,5 +1,5 @@ /* - * Copyright © 2024 Hexastack. All rights reserved. + * Copyright © 2025 Hexastack. All rights reserved. * * Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms: * 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission. @@ -127,10 +127,10 @@ export class BotStatsService extends BaseService { try { await this.updateOne(insight.id, { value: insight.value + 1 }); } catch (err) { - this.logger.error('Stats hook : Unable to update insight', err); + this.logger.error('Unable to update insight', err); } } catch (err) { - this.logger.error('Stats hook : Unable to find or create insight', err); + this.logger.error('Unable to find or create insight', err); } } } diff --git a/api/src/app.module.ts b/api/src/app.module.ts index 4712d01d..3c4692a1 100644 --- a/api/src/app.module.ts +++ b/api/src/app.module.ts @@ -72,7 +72,7 @@ const i18nOptions: I18nOptions = { }), template: { adapter: new MjmlAdapter('ejs', { inlineCssEnabled: false }), - dir: './src/templates', + dir: path.join(process.cwd(), 'dist', 'templates'), options: { context: { appName: config.parameters.appName, diff --git a/api/src/attachment/controllers/attachment.controller.spec.ts b/api/src/attachment/controllers/attachment.controller.spec.ts index 124bfc13..eb13cdb1 100644 --- a/api/src/attachment/controllers/attachment.controller.spec.ts +++ b/api/src/attachment/controllers/attachment.controller.spec.ts @@ -108,7 +108,7 @@ describe('AttachmentController', () => { helperService = module.get(HelperService); settingService = module.get(SettingService); - loggerService = module.get(LoggerService); + loggerService = await module.resolve(LoggerService); helperService.register( new LocalStorageHelper(settingService, helperService, loggerService), diff --git a/api/src/chat/controllers/message.controller.ts b/api/src/chat/controllers/message.controller.ts index ca34cd55..dda5430d 100644 --- a/api/src/chat/controllers/message.controller.ts +++ b/api/src/chat/controllers/message.controller.ts @@ -1,5 +1,5 @@ /* - * Copyright © 2024 Hexastack. All rights reserved. + * Copyright © 2025 Hexastack. All rights reserved. * * Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms: * 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission. @@ -173,7 +173,7 @@ export class MessageController extends BaseController< success: true, }; } catch (err) { - this.logger.debug('MessageController send : Unable to send message', err); + this.logger.debug('Unable to send message', err); throw new BadRequestException( 'MessageController send : unable to send message', ); diff --git a/api/src/chat/services/block.service.ts b/api/src/chat/services/block.service.ts index 2b6f6346..c7fedabf 100644 --- a/api/src/chat/services/block.service.ts +++ b/api/src/chat/services/block.service.ts @@ -286,7 +286,7 @@ export class BlockService extends BaseService< return e.entity === ev.entity; }); } else { - this.logger.warn('Block Service : Unknown NLP match type', ev); + this.logger.warn('Unknown NLP match type', ev); return false; } }); diff --git a/api/src/chat/services/bot.service.ts b/api/src/chat/services/bot.service.ts index 8a9ab5ed..96e87519 100644 --- a/api/src/chat/services/bot.service.ts +++ b/api/src/chat/services/bot.service.ts @@ -1,5 +1,5 @@ /* - * Copyright © 2024 Hexastack. All rights reserved. + * Copyright © 2025 Hexastack. All rights reserved. * * Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms: * 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission. @@ -62,10 +62,7 @@ export class BotService { context = context || getDefaultConversationContext(); fallback = typeof fallback !== 'undefined' ? fallback : false; const options = block.options; - this.logger.debug( - 'Bot service : Sending message ... ', - event.getSenderForeignId(), - ); + this.logger.debug('Sending message ... ', event.getSenderForeignId()); // Process message : Replace tokens with context data and then send the message const recipient = event.getSender(); const envelope = await this.blockService.processMessage( @@ -116,7 +113,7 @@ export class BotService { assignTo, ); - this.logger.debug('Bot service : Assigned labels ', blockLabels); + this.logger.debug('Assigned labels ', blockLabels); return response; } @@ -375,7 +372,7 @@ export class BotService { ); this.logger.debug( - 'Bot service : Started a new conversation with ', + 'Started a new conversation with ', subscriber.id, block.name, ); @@ -386,14 +383,11 @@ export class BotService { false, ); } catch (err) { - this.logger.error('Bot service : Unable to store context data!', err); + this.logger.error('Unable to store context data!', err); this.eventEmitter.emit('hook:conversation:end', convo, true); } } catch (err) { - this.logger.error( - 'Botservice : Unable to start a new conversation with ', - err, - ); + this.logger.error('Unable to start a new conversation with ', err); } } diff --git a/api/src/chat/services/message.service.ts b/api/src/chat/services/message.service.ts index 6ee4463c..75031541 100644 --- a/api/src/chat/services/message.service.ts +++ b/api/src/chat/services/message.service.ts @@ -1,5 +1,5 @@ /* - * Copyright © 2024 Hexastack. All rights reserved. + * Copyright © 2025 Hexastack. All rights reserved. * * Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms: * 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission. @@ -65,10 +65,7 @@ export class MessageService extends BaseService< success: true, }); } catch (e) { - this.logger.error( - 'MessageController subscribe : Websocket subscription', - e, - ); + this.logger.error('Websocket subscription', e); throw new InternalServerErrorException(e); } } diff --git a/api/src/chat/services/subscriber.service.ts b/api/src/chat/services/subscriber.service.ts index 0f6e4e89..dcc7bd73 100644 --- a/api/src/chat/services/subscriber.service.ts +++ b/api/src/chat/services/subscriber.service.ts @@ -72,9 +72,7 @@ export class SubscriberService extends BaseService< subscribe: Room.SUBSCRIBER, }); } catch (e) { - this.logger.error( - 'SubscriberController subscribe : Websocket subscription', - ); + this.logger.error('Websocket subscription'); throw new InternalServerErrorException(e); } } diff --git a/api/src/extensions/channels/web/base-web-channel.ts b/api/src/extensions/channels/web/base-web-channel.ts index 81b4ea4e..80024107 100644 --- a/api/src/extensions/channels/web/base-web-channel.ts +++ b/api/src/extensions/channels/web/base-web-channel.ts @@ -89,7 +89,7 @@ export default abstract class BaseWebChannelHandler< * @returns - */ init(): void { - this.logger.debug('Web Channel Handler : initialization ...'); + this.logger.debug('initialization ...'); } /** @@ -108,22 +108,17 @@ export default abstract class BaseWebChannelHandler< return; } - this.logger.debug( - 'Web Channel Handler : WS connected .. sending settings', - ); + this.logger.debug('WS connected .. sending settings'); try { const menu = await this.menuService.getTree(); return client.emit('settings', { menu, ...settings }); } catch (err) { - this.logger.warn('Web Channel Handler : Unable to retrieve menu ', err); + this.logger.warn('Unable to retrieve menu ', err); return client.emit('settings', settings); } } catch (err) { - this.logger.error( - 'Web Channel Handler : Unable to initiate websocket connection', - err, - ); + this.logger.error('Unable to initiate websocket connection', err); client.disconnect(); } } @@ -315,7 +310,7 @@ export default abstract class BaseWebChannelHandler< // Check if we have an origin header... if (!req.headers?.origin) { - this.logger.debug('Web Channel Handler : No origin ', req.headers); + this.logger.debug('No origin ', req.headers); throw new Error('CORS - No origin provided!'); } @@ -332,10 +327,7 @@ export default abstract class BaseWebChannelHandler< try { return new URL(origin.trim()).origin; } catch (error) { - this.logger.error( - `Web Channel Handler : Invalid URL in allowed domains: ${origin}`, - error, - ); + this.logger.error(`Invalid URL in allowed domains: ${origin}`, error); return null; } }) @@ -353,10 +345,7 @@ export default abstract class BaseWebChannelHandler< // For HTTP requests, set the Access-Control-Allow-Origin header to '', which the browser will // interpret as, 'no way Jose.' res.set('Access-Control-Allow-Origin', ''); - this.logger.debug( - 'Web Channel Handler : No origin found ', - req.headers.origin, - ); + this.logger.debug('No origin found ', req.headers.origin); throw new Error('CORS - Domain not allowed!'); } else { res.set('Access-Control-Allow-Origin', originUrl.origin); @@ -384,10 +373,7 @@ export default abstract class BaseWebChannelHandler< next: (profile: Subscriber) => void, ) { if (!req.session?.web?.profile?.id) { - this.logger.warn( - 'Web Channel Handler : No session ID to be found!', - req.session, - ); + this.logger.warn('No session ID to be found!', req.session); return res .status(403) .json({ err: 'Web Channel Handler : Unauthorized!' }); @@ -397,7 +383,7 @@ export default abstract class BaseWebChannelHandler< !Array.isArray(req.session.web.messageQueue) ) { this.logger.warn( - 'Web Channel Handler : Mixed channel request or invalid session data!', + 'Mixed channel request or invalid session data!', req.session, ); return res @@ -420,10 +406,7 @@ export default abstract class BaseWebChannelHandler< try { await this.validateCors(req, res); } catch (err) { - this.logger.warn( - 'Web Channel Handler : Attempt to access from an unauthorized origin', - err, - ); + this.logger.warn('Attempt to access from an unauthorized origin', err); throw new Error('Unauthorized, invalid origin !'); } } @@ -498,18 +481,14 @@ export default abstract class BaseWebChannelHandler< private getMessageQueue(req: Request, res: Response) { // Polling not authorized when using websockets if (this.isSocketRequest(req)) { - this.logger.warn( - 'Web Channel Handler : Polling not authorized when using websockets', - ); + this.logger.warn('Polling not authorized when using websockets'); return res .status(403) .json({ err: 'Polling not authorized when using websockets' }); } // Session must be active if (!(req.session && req.session.web && req.session.web.profile.id)) { - this.logger.warn( - 'Web Channel Handler : Must be connected to poll messages', - ); + this.logger.warn('Must be connected to poll messages'); return res .status(403) .json({ err: 'Polling not authorized : Must be connected' }); @@ -517,9 +496,7 @@ export default abstract class BaseWebChannelHandler< // Can only request polling once at a time if (req.session && req.session.web && req.session.web.polling) { - this.logger.warn( - 'Web Channel Handler : Poll rejected ... already requested', - ); + this.logger.warn('Poll rejected ... already requested'); return res .status(403) .json({ err: 'Poll rejected ... already requested' }); @@ -543,16 +520,14 @@ export default abstract class BaseWebChannelHandler< req.session.web.polling = false; return res.status(200).json(messages.map((msg) => ['message', msg])); } else { - this.logger.error( - 'Web Channel Handler : Polling failed .. no session data', - ); + this.logger.error('Polling failed .. no session data'); return res.status(500).json({ err: 'No session data' }); } } catch (err) { if (req.session.web) { req.session.web.polling = false; } - this.logger.error('Web Channel Handler : Polling failed', err); + this.logger.error('Polling failed', err); return res.status(500).json({ err: 'Polling failed' }); } }; @@ -569,11 +544,7 @@ export default abstract class BaseWebChannelHandler< req: Request | SocketRequest, res: Response | SocketResponse, ) { - this.logger.debug( - 'Web Channel Handler : subscribe (isSocket=' + - this.isSocketRequest(req) + - ')', - ); + this.logger.debug('subscribe (isSocket=' + this.isSocketRequest(req) + ')'); try { const profile = await this.getOrCreateSession(req); // Join socket room when using websocket @@ -581,10 +552,7 @@ export default abstract class BaseWebChannelHandler< try { await req.socket.join(profile.foreign_id); } catch (err) { - this.logger.error( - 'Web Channel Handler : Unable to subscribe via websocket', - err, - ); + this.logger.error('Unable to subscribe via websocket', err); } } // Fetch message history @@ -595,7 +563,7 @@ export default abstract class BaseWebChannelHandler< const messages = await this.fetchHistory(req, criteria); return res.status(200).json({ profile, messages }); } catch (err) { - this.logger.warn('Web Channel Handler : Unable to subscribe ', err); + this.logger.warn('Unable to subscribe ', err); return res.status(500).json({ err: 'Unable to subscribe' }); } } @@ -610,13 +578,13 @@ export default abstract class BaseWebChannelHandler< const { type, data } = req.body as Web.IncomingMessage; if (!req.session?.web?.profile?.id) { - this.logger.debug('Web Channel Handler : No session'); + this.logger.debug('No session'); return null; } // Check if any file is provided if (type !== 'file' || !('file' in data) || !data.file) { - this.logger.debug('Web Channel Handler : No files provided'); + this.logger.debug('No files provided'); return null; } @@ -636,10 +604,7 @@ export default abstract class BaseWebChannelHandler< createdBy: req.session?.web?.profile?.id, }); } catch (err) { - this.logger.error( - 'Web Channel Handler : Unable to store uploaded file', - err, - ); + this.logger.error('Unable to store uploaded file', err); throw new Error('Unable to upload file!'); } } @@ -671,10 +636,7 @@ export default abstract class BaseWebChannelHandler< (resolve, reject) => { upload(req as Request, res as Response, async (err?: any) => { if (err) { - this.logger.error( - 'Web Channel Handler : Unable to store uploaded file', - err, - ); + this.logger.error('Unable to store uploaded file', err); reject(new Error('Unable to upload file!')); } @@ -689,7 +651,7 @@ export default abstract class BaseWebChannelHandler< // Check if any file is provided if (!file) { - this.logger.debug('Web Channel Handler : No files provided'); + this.logger.debug('No files provided'); return null; } @@ -703,10 +665,7 @@ export default abstract class BaseWebChannelHandler< createdBy: req.session.web.profile?.id, }); } catch (err) { - this.logger.error( - 'Web Channel Handler : Unable to store uploaded file', - err, - ); + this.logger.error('Unable to store uploaded file', err); throw err; } } @@ -724,7 +683,7 @@ export default abstract class BaseWebChannelHandler< ): Promise { // Check if any file is provided if (!req.session.web) { - this.logger.debug('Web Channel Handler : No session provided'); + this.logger.debug('No session provided'); return null; } @@ -784,7 +743,7 @@ export default abstract class BaseWebChannelHandler< ): void { // @TODO: perform payload validation if (!req.body) { - this.logger.debug('Web Channel Handler : Empty body'); + this.logger.debug('Empty body'); res.status(400).json({ err: 'Web Channel Handler : Bad Request!' }); return; } else { @@ -814,10 +773,7 @@ export default abstract class BaseWebChannelHandler< }; } } catch (err) { - this.logger.warn( - 'Web Channel Handler : Unable to upload file ', - err, - ); + this.logger.warn('Unable to upload file ', err); return res .status(403) .json({ err: 'Web Channel Handler : File upload failed!' }); @@ -849,10 +805,7 @@ export default abstract class BaseWebChannelHandler< if (type) { this.eventEmitter.emit(`hook:chatbot:${type}`, event); } else { - this.logger.error( - 'Web Channel Handler : Webhook received unknown event ', - event, - ); + this.logger.error('Webhook received unknown event ', event); } res.status(200).json(event._adapter.raw); }); @@ -883,9 +836,7 @@ export default abstract class BaseWebChannelHandler< if (!this.isSocketRequest(req) && req.query._get) { switch (req.query._get) { case 'settings': - this.logger.debug( - 'Web Channel Handler : connected .. sending settings', - ); + this.logger.debug('connected .. sending settings'); try { const menu = await this.menuService.getTree(); return res.status(200).json({ @@ -894,19 +845,14 @@ export default abstract class BaseWebChannelHandler< ...settings, }); } catch (err) { - this.logger.warn( - 'Web Channel Handler : Unable to retrieve menu ', - err, - ); + this.logger.warn('Unable to retrieve menu ', err); return res.status(500).json({ err: 'Unable to retrieve menu' }); } case 'polling': // Handle polling when user is not connected via websocket return this.getMessageQueue(req, res as Response); default: - this.logger.error( - 'Web Channel Handler : Webhook received unknown command', - ); + this.logger.error('Webhook received unknown command'); return res .status(500) .json({ err: 'Webhook received unknown command' }); @@ -923,7 +869,7 @@ export default abstract class BaseWebChannelHandler< return this._handleEvent(req, res); } } catch (err) { - this.logger.warn('Web Channel Handler : Request check failed', err); + this.logger.warn('Request check failed', err); return res .status(403) .json({ err: 'Web Channel Handler : Unauthorized!' }); @@ -1131,9 +1077,7 @@ export default abstract class BaseWebChannelHandler< // Items count min check if (!data.length) { - this.logger.error( - 'Web Channel Handler : Unsufficient content count (must be >= 0 for list)', - ); + this.logger.error('Unsufficient content count (must be >= 0 for list)'); throw new Error('Unsufficient content count (list >= 0)'); } @@ -1181,7 +1125,7 @@ export default abstract class BaseWebChannelHandler< // Items count min check if (data.length === 0) { this.logger.error( - 'Web Channel Handler : Unsufficient content count (must be > 0 for carousel)', + 'Unsufficient content count (must be > 0 for carousel)', ); throw new Error('Unsufficient content count (carousel > 0)'); } @@ -1293,10 +1237,7 @@ export default abstract class BaseWebChannelHandler< await this.sendTypingIndicator(subscriber, timeout); return next(); } catch (err) { - this.logger.error( - 'Web Channel Handler : Failed in sending typing indicator ', - err, - ); + this.logger.error('Failed in sending typing indicator ', err); } } diff --git a/api/src/helper/lib/base-helper.ts b/api/src/helper/lib/base-helper.ts index 1c46f0ac..e7dffd1f 100644 --- a/api/src/helper/lib/base-helper.ts +++ b/api/src/helper/lib/base-helper.ts @@ -8,8 +8,9 @@ import path from 'path'; -import { LoggerService, OnModuleInit } from '@nestjs/common'; +import { OnModuleInit } from '@nestjs/common'; +import { LoggerService } from '@/logger/logger.service'; import { SettingService } from '@/setting/services/setting.service'; import { Extension } from '@/utils/generics/extension'; import { HyphenToUnderscore } from '@/utils/types/extension'; diff --git a/api/src/logger/logger.service.ts b/api/src/logger/logger.service.ts index be15d874..c14ebbf1 100644 --- a/api/src/logger/logger.service.ts +++ b/api/src/logger/logger.service.ts @@ -1,11 +1,58 @@ /* - * Copyright © 2024 Hexastack. All rights reserved. + * Copyright © 2025 Hexastack. All rights reserved. * * Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms: * 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission. * 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file). */ -import { ConsoleLogger } from '@nestjs/common'; +import { + ConsoleLogger, + Inject, + Injectable, + LogLevel, + Scope, +} from '@nestjs/common'; +import { INQUIRER } from '@nestjs/core'; -export class LoggerService extends ConsoleLogger {} +@Injectable({ scope: Scope.TRANSIENT }) +export class LoggerService extends ConsoleLogger { + constructor(@Inject(INQUIRER) private parentClass: object) { + super(parentClass.constructor.name, { + logLevels: process.env.NODE_ENV?.includes('dev') + ? ['log', 'debug', 'error', 'verbose', 'fatal', 'warn'] + : ['log', 'warn', 'error'], + }); + } + + log(message: string, ...args: any[]) { + this.logArguments('log', message, args); + } + + error(message: string, ...args: any[]) { + this.logArguments('error', message, args); + } + + warn(message: string, ...args: any[]) { + this.logArguments('warn', message, args); + } + + debug(message: string, ...args: any[]) { + this.logArguments('debug', message, args); + } + + verbose(message: string, ...args: any[]) { + this.logArguments('verbose', message, args); + } + + fatal(message: string, ...args: any[]) { + this.logArguments('fatal', message, args); + } + + private logArguments(type: LogLevel, message: string, args: any[]) { + super[type](message); + args.forEach((arg) => { + super[type](arg); + }); + } +} diff --git a/api/src/main.ts b/api/src/main.ts index 507c3361..1ba080a4 100644 --- a/api/src/main.ts +++ b/api/src/main.ts @@ -101,10 +101,11 @@ async function bootstrap() { app.useWebSocketAdapter(redisIoAdapter); } - process.on('uncaughtException', (error) => { - if (error.stack?.toLowerCase().includes('smtp')) - app.get(LoggerService).error('SMTP error', error.stack); - else throw error; + process.on('uncaughtException', async (error) => { + if (error.stack?.toLowerCase().includes('smtp')) { + const logger = await app.resolve(LoggerService); + logger.error('SMTP error', error.stack); + } else throw error; }); if (!isProduction) { diff --git a/api/src/migration/migration.service.spec.ts b/api/src/migration/migration.service.spec.ts index 724bd798..1abd7464 100644 --- a/api/src/migration/migration.service.spec.ts +++ b/api/src/migration/migration.service.spec.ts @@ -77,7 +77,7 @@ describe('MigrationService', () => { }).compile(); service = module.get(MigrationService); - loggerService = module.get(LoggerService); + loggerService = await module.resolve(LoggerService); metadataService = module.get(MetadataService); }); diff --git a/api/src/seeder.ts b/api/src/seeder.ts index 1811ae3f..c3abfaac 100644 --- a/api/src/seeder.ts +++ b/api/src/seeder.ts @@ -37,7 +37,7 @@ import { UserSeeder } from './user/seeds/user.seed'; import { userModels } from './user/seeds/user.seed-model'; export async function seedDatabase(app: INestApplicationContext) { - const logger = app.get(LoggerService); + const logger = await app.resolve(LoggerService); const modelSeeder = app.get(ModelSeeder); const categorySeeder = app.get(CategorySeeder); const contextVarSeeder = app.get(ContextVarSeeder); diff --git a/api/src/templates/account_confirmation.mjml b/api/src/templates/account_confirmation.mjml index cf10ff87..07230f9d 100644 --- a/api/src/templates/account_confirmation.mjml +++ b/api/src/templates/account_confirmation.mjml @@ -2,8 +2,8 @@ - - + + <%= t('hi') %> <%= first_name %>, @@ -17,7 +17,7 @@ <%= t('confirm') %> diff --git a/api/src/templates/invitation.mjml b/api/src/templates/invitation.mjml index e608bc0d..515a4899 100644 --- a/api/src/templates/invitation.mjml +++ b/api/src/templates/invitation.mjml @@ -2,8 +2,8 @@ - - + + <%= t('welcome') %>, @@ -19,7 +19,7 @@ <%= t('join') %> diff --git a/api/src/templates/password_reset.mjml b/api/src/templates/password_reset.mjml index 34975029..015425ae 100644 --- a/api/src/templates/password_reset.mjml +++ b/api/src/templates/password_reset.mjml @@ -2,8 +2,8 @@ - - + + <%= t('hi') %> <%= first_name %>, @@ -16,7 +16,7 @@ <%= t('reset') %> diff --git a/frontend/package.json b/frontend/package.json index 8449cd56..0e2bf816 100755 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,7 +1,7 @@ { "name": "hexabot-ui", "private": true, - "version": "2.2.2", + "version": "2.2.3", "description": "Hexabot is a solution for creating and managing chatbots across multiple channels, leveraging AI for advanced conversational capabilities. It provides a user-friendly interface for building, training, and deploying chatbots with integrated support for various messaging platforms.", "author": "Hexastack", "license": "AGPL-3.0-only", diff --git a/package-lock.json b/package-lock.json index 22017a7c..2372b00e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "hexabot", - "version": "2.2.1", + "version": "2.2.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "hexabot", - "version": "2.2.1", + "version": "2.2.2", "license": "AGPL-3.0-only", "workspaces": [ "frontend", @@ -45,7 +45,7 @@ }, "frontend": { "name": "hexabot-ui", - "version": "2.2.2", + "version": "2.2.3", "license": "AGPL-3.0-only", "dependencies": { "@chatscope/chat-ui-kit-react": "^2.0.3", @@ -10304,7 +10304,7 @@ }, "widget": { "name": "hexabot-chat-widget", - "version": "2.2.2", + "version": "2.2.3", "license": "AGPL-3.0-only", "dependencies": { "@types/emoji-js": "^3.5.2", diff --git a/package.json b/package.json index efe1b1f8..b2d718f2 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "frontend", "widget" ], - "version": "2.2.2", + "version": "2.2.3", "description": "Hexabot is a solution for creating and managing chatbots across multiple channels, leveraging AI for advanced conversational capabilities. It provides a user-friendly interface for building, training, and deploying chatbots with integrated support for various messaging platforms.", "author": "Hexastack", "license": "AGPL-3.0-only", diff --git a/widget/package.json b/widget/package.json index 73cea4b8..87cc17fb 100644 --- a/widget/package.json +++ b/widget/package.json @@ -1,6 +1,6 @@ { "name": "hexabot-chat-widget", - "version": "2.2.2", + "version": "2.2.3", "description": "Hexabot is a solution for creating and managing chatbots across multiple channels, leveraging AI for advanced conversational capabilities. It provides a user-friendly interface for building, training, and deploying chatbots with integrated support for various messaging platforms.", "author": "Hexastack", "license": "AGPL-3.0-only",