From ebe9b00a9fdb09fdccfe4f2177b96cdb8d1419cc Mon Sep 17 00:00:00 2001 From: w35l3y Date: Sun, 1 Jun 2025 17:25:20 -0300 Subject: [PATCH 1/4] Querying json data now works in mysql Added prisma extension to convert "path" from array format (postgresql) to string format (mysql) --- .../prismaExtensionPgpathToMysql.js | 61 +++++++++++++++++++ src/api/server.module.ts | 12 +++- src/utils/extendsWithProxy.ts | 27 ++++++++ tsconfig.json | 1 + 4 files changed, 99 insertions(+), 2 deletions(-) create mode 100644 src/api/extensions/prismaExtensionPgpathToMysql.js create mode 100644 src/utils/extendsWithProxy.ts diff --git a/src/api/extensions/prismaExtensionPgpathToMysql.js b/src/api/extensions/prismaExtensionPgpathToMysql.js new file mode 100644 index 000000000..dc76bcf0b --- /dev/null +++ b/src/api/extensions/prismaExtensionPgpathToMysql.js @@ -0,0 +1,61 @@ +import { Prisma } from '@prisma/client' +import { Logger } from '@config/logger.config'; + +const logger = new Logger('PGPATH2MYSQL'); + +function convertPgPathToMysql (path) { + if (!Array.isArray(path)) return path + let result = '$' + for (const item of path) { + if (/^\d+$/.test(item)) { + result += `[${item}]` + } else { + result += `.${item}` + } + } + return result +} + +function processWhere (obj) { + if (obj && typeof obj === 'object') { + for (const key in obj) { + if (Object.prototype.hasOwnProperty.call(obj, key)) { + if (key === 'path') { + obj[key] = convertPgPathToMysql(obj[key]); + } else { + processWhere(obj[key]); + } + } + } + } +} + +// https://www.prisma.io/docs/orm/prisma-client/client-extensions/query#modify-all-operations-in-all-models-of-your-schema +// https://www.prisma.io/docs/orm/prisma-client/client-extensions/query#modify-a-specific-operation-in-a-specific-model + +const overriddenOperation = async ({ model, operation, args, query }) => { + if (args?.where) { + processWhere(args.where) + } + const result = await query(args) + logger.debug({ model, operation, args: JSON.stringify(args), result }) + return result +} + +export default Prisma.defineExtension({ + name: 'prisma-extension-pgpath-to-mysql', + query: { + $allModels: { + findFirst: overriddenOperation, + findMany: overriddenOperation, + updateMany: overriddenOperation, + count: overriddenOperation, + deleteMany: overriddenOperation, + + delete: overriddenOperation, + findUnique: overriddenOperation, + update: overriddenOperation, + upsert: overriddenOperation, + } + } +}) diff --git a/src/api/server.module.ts b/src/api/server.module.ts index 49fc56952..e29ddaf1c 100644 --- a/src/api/server.module.ts +++ b/src/api/server.module.ts @@ -1,5 +1,5 @@ import { CacheEngine } from '@cache/cacheengine'; -import { Chatwoot, configService, ProviderSession } from '@config/env.config'; +import { Chatwoot, configService, ProviderSession, Database } from '@config/env.config'; import { eventEmitter } from '@config/event.config'; import { Logger } from '@config/logger.config'; @@ -40,6 +40,9 @@ import { ProxyService } from './services/proxy.service'; import { SettingsService } from './services/settings.service'; import { TemplateService } from './services/template.service'; +import pgPathToMysql from './extensions/prismaExtensionPgpathToMysql'; +import { extendsWithProxy } from '@utils/extendsWithProxy'; + const logger = new Logger('WA MODULE'); let chatwootCache: CacheService = null; @@ -55,7 +58,12 @@ if (configService.get('PROVIDER').ENABLED) { providerFiles = new ProviderFiles(configService); } -export const prismaRepository = new PrismaRepository(configService); +const provider = configService.get('DATABASE').PROVIDER; +let extendablePrismaRepository: PrismaRepository = new PrismaRepository(configService) +if (provider === "mysql") { + extendablePrismaRepository = extendsWithProxy(extendablePrismaRepository, pgPathToMysql); +} +export const prismaRepository = extendablePrismaRepository; export const waMonitor = new WAMonitoringService( eventEmitter, diff --git a/src/utils/extendsWithProxy.ts b/src/utils/extendsWithProxy.ts new file mode 100644 index 000000000..f21e6c46c --- /dev/null +++ b/src/utils/extendsWithProxy.ts @@ -0,0 +1,27 @@ +import { PrismaClient } from '@prisma/client'; + +type ExtensionArgs = Parameters[0]; + +export function extendsWithProxy( + instanciaBase: T, + extensao: ExtensionArgs +): T { + const instanciaEstendida = instanciaBase.$extends(extensao); + + const proxy = new Proxy(instanciaBase as unknown as object, { + get(target, prop, receiver) { + if (prop === 'toString') { + return () => '[Proxy toString]'; + } + if (prop === Symbol.toStringTag) { + return undefined; + } + return prop in instanciaEstendida ? Reflect.get(instanciaEstendida as any, prop, receiver) : Reflect.get(target, prop, receiver); + }, + has(target, prop) { + return prop in target || prop in (instanciaEstendida as any); + }, + }); + + return proxy as unknown as T; +} diff --git a/tsconfig.json b/tsconfig.json index af814134a..28a3630a5 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -17,6 +17,7 @@ "strictNullChecks": false, "incremental": true, "noImplicitAny": false, + "allowJs": true, "baseUrl": ".", "paths": { "@api/*": ["./src/api/*"], From bbbe667cfa5ad25592e48e59672dc4b805a39aa3 Mon Sep 17 00:00:00 2001 From: w35l3y Date: Sun, 1 Jun 2025 17:30:03 -0300 Subject: [PATCH 2/4] Copied the last status of original messages --- .../integrations/channel/whatsapp/whatsapp.baileys.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts b/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts index 10feb7ce1..4bf34bdc0 100644 --- a/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts +++ b/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts @@ -1509,7 +1509,7 @@ export class BaileysStartupService extends ChannelStartupService { remoteJid: key.remoteJid, fromMe: key.fromMe, participant: key?.remoteJid, - status: status[update.status], + status: status[update.status] || findMessage.status, pollUpdates, instanceId: this.instanceId, }; From 460c893c3a3e3fc88f8f577d7148fa1e2dc9f06e Mon Sep 17 00:00:00 2001 From: w35l3y Date: Sun, 1 Jun 2025 18:26:53 -0300 Subject: [PATCH 3/4] Added suggestions by @sourcery-ai --- src/api/extensions/prismaExtensionPgpathToMysql.js | 4 +++- .../integrations/channel/whatsapp/whatsapp.baileys.service.ts | 2 +- src/api/server.module.ts | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/api/extensions/prismaExtensionPgpathToMysql.js b/src/api/extensions/prismaExtensionPgpathToMysql.js index dc76bcf0b..1cc3e2b0f 100644 --- a/src/api/extensions/prismaExtensionPgpathToMysql.js +++ b/src/api/extensions/prismaExtensionPgpathToMysql.js @@ -4,7 +4,9 @@ import { Logger } from '@config/logger.config'; const logger = new Logger('PGPATH2MYSQL'); function convertPgPathToMysql (path) { - if (!Array.isArray(path)) return path + if (!Array.isArray(path)) { + return path + } let result = '$' for (const item of path) { if (/^\d+$/.test(item)) { diff --git a/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts b/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts index 4bf34bdc0..2e29b7cdc 100644 --- a/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts +++ b/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts @@ -1509,7 +1509,7 @@ export class BaileysStartupService extends ChannelStartupService { remoteJid: key.remoteJid, fromMe: key.fromMe, participant: key?.remoteJid, - status: status[update.status] || findMessage.status, + status: status[update.status] ?? findMessage.status, pollUpdates, instanceId: this.instanceId, }; diff --git a/src/api/server.module.ts b/src/api/server.module.ts index e29ddaf1c..62a8193fd 100644 --- a/src/api/server.module.ts +++ b/src/api/server.module.ts @@ -60,7 +60,7 @@ if (configService.get('PROVIDER').ENABLED) { const provider = configService.get('DATABASE').PROVIDER; let extendablePrismaRepository: PrismaRepository = new PrismaRepository(configService) -if (provider === "mysql") { +if (typeof provider === 'string' && provider?.toLowerCase() === 'mysql') { extendablePrismaRepository = extendsWithProxy(extendablePrismaRepository, pgPathToMysql); } export const prismaRepository = extendablePrismaRepository; From e6a393870f6410dd78931624eaa8f8022da24269 Mon Sep 17 00:00:00 2001 From: w35l3y Date: Tue, 3 Jun 2025 17:01:43 +0000 Subject: [PATCH 4/4] Run lint --- src/api/server.module.ts | 9 ++++----- src/utils/extendsWithProxy.ts | 9 ++++----- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/api/server.module.ts b/src/api/server.module.ts index 62a8193fd..c72a0a0cb 100644 --- a/src/api/server.module.ts +++ b/src/api/server.module.ts @@ -1,7 +1,8 @@ import { CacheEngine } from '@cache/cacheengine'; -import { Chatwoot, configService, ProviderSession, Database } from '@config/env.config'; +import { Chatwoot, configService, Database, ProviderSession } from '@config/env.config'; import { eventEmitter } from '@config/event.config'; import { Logger } from '@config/logger.config'; +import { extendsWithProxy } from '@utils/extendsWithProxy'; import { CallController } from './controllers/call.controller'; import { ChatController } from './controllers/chat.controller'; @@ -12,6 +13,7 @@ import { ProxyController } from './controllers/proxy.controller'; import { SendMessageController } from './controllers/sendMessage.controller'; import { SettingsController } from './controllers/settings.controller'; import { TemplateController } from './controllers/template.controller'; +import pgPathToMysql from './extensions/prismaExtensionPgpathToMysql'; import { ChannelController } from './integrations/channel/channel.controller'; import { EvolutionController } from './integrations/channel/evolution/evolution.controller'; import { MetaController } from './integrations/channel/meta/meta.controller'; @@ -40,9 +42,6 @@ import { ProxyService } from './services/proxy.service'; import { SettingsService } from './services/settings.service'; import { TemplateService } from './services/template.service'; -import pgPathToMysql from './extensions/prismaExtensionPgpathToMysql'; -import { extendsWithProxy } from '@utils/extendsWithProxy'; - const logger = new Logger('WA MODULE'); let chatwootCache: CacheService = null; @@ -59,7 +58,7 @@ if (configService.get('PROVIDER').ENABLED) { } const provider = configService.get('DATABASE').PROVIDER; -let extendablePrismaRepository: PrismaRepository = new PrismaRepository(configService) +let extendablePrismaRepository: PrismaRepository = new PrismaRepository(configService); if (typeof provider === 'string' && provider?.toLowerCase() === 'mysql') { extendablePrismaRepository = extendsWithProxy(extendablePrismaRepository, pgPathToMysql); } diff --git a/src/utils/extendsWithProxy.ts b/src/utils/extendsWithProxy.ts index f21e6c46c..90c92238f 100644 --- a/src/utils/extendsWithProxy.ts +++ b/src/utils/extendsWithProxy.ts @@ -2,10 +2,7 @@ import { PrismaClient } from '@prisma/client'; type ExtensionArgs = Parameters[0]; -export function extendsWithProxy( - instanciaBase: T, - extensao: ExtensionArgs -): T { +export function extendsWithProxy(instanciaBase: T, extensao: ExtensionArgs): T { const instanciaEstendida = instanciaBase.$extends(extensao); const proxy = new Proxy(instanciaBase as unknown as object, { @@ -16,7 +13,9 @@ export function extendsWithProxy( if (prop === Symbol.toStringTag) { return undefined; } - return prop in instanciaEstendida ? Reflect.get(instanciaEstendida as any, prop, receiver) : Reflect.get(target, prop, receiver); + return prop in instanciaEstendida + ? Reflect.get(instanciaEstendida as any, prop, receiver) + : Reflect.get(target, prop, receiver); }, has(target, prop) { return prop in target || prop in (instanciaEstendida as any);