From 713a9638b5b0b91484b6cc0286e3fa0202313b39 Mon Sep 17 00:00:00 2001 From: shahjahan-arif Date: Sat, 21 Mar 2026 01:05:47 +0500 Subject: [PATCH] fix(engine): resolve lint-staged and json path typing --- package.json | 6 +- project.json | 38 ++++++++ .../channel/meta/whatsapp.business.service.ts | 2 +- .../whatsapp/whatsapp.baileys.service.ts | 88 +++++++++++++------ .../chatwoot/services/chatwoot.service.ts | 8 +- src/api/services/channel.service.ts | 16 ++-- src/utils/onWhatsappCache.ts | 13 +-- 7 files changed, 119 insertions(+), 52 deletions(-) create mode 100644 project.json diff --git a/package.json b/package.json index 56e32fcc8..a7aa0e829 100644 --- a/package.json +++ b/package.json @@ -10,8 +10,8 @@ "start:prod": "node dist/main", "dev:server": "tsx watch ./src/main.ts", "test": "tsx watch ./test/all.test.ts", - "lint": "eslint --fix --ext .ts src", - "lint:check": "eslint --ext .ts src", + "lint": "ESLINT_USE_FLAT_CONFIG=false eslint --fix --ext .ts src", + "lint:check": "ESLINT_USE_FLAT_CONFIG=false eslint --ext .ts src", "commit": "cz", "commitlint": "commitlint --edit", "db:generate": "node runWithProvider.js \"npx prisma generate --schema ./prisma/DATABASE_PROVIDER-schema.prisma\"", @@ -53,7 +53,7 @@ "homepage": "https://github.com/EvolutionAPI/evolution-api#readme", "lint-staged": { "src/**/*.{ts,js}": [ - "eslint --fix" + "sh -c 'ESLINT_USE_FLAT_CONFIG=false eslint --fix'" ], "src/**/*.ts": [ "sh -c 'tsc --noEmit'" diff --git a/project.json b/project.json new file mode 100644 index 000000000..591349ffe --- /dev/null +++ b/project.json @@ -0,0 +1,38 @@ +{ + "name": "engine", + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "apps/engine/src", + "projectType": "application", + "prefix": "engine", + "targets": { + "serve": { + "executor": "nx:run-commands", + "options": { + "command": "npm run dev:server", + "cwd": "apps/engine" + } + }, + "build": { + "executor": "nx:run-commands", + "options": { + "command": "npm run build", + "cwd": "apps/engine" + } + }, + "db:migrate": { + "executor": "nx:run-commands", + "options": { + "command": "npm run db:deploy", + "cwd": "apps/engine" + } + }, + "db:seed": { + "executor": "nx:run-commands", + "options": { + "command": "npm run db:seed", + "cwd": "apps/engine" + } + } + }, + "tags": ["type:app", "scope:engine"] +} diff --git a/src/api/integrations/channel/meta/whatsapp.business.service.ts b/src/api/integrations/channel/meta/whatsapp.business.service.ts index 1e4808c15..42e54d6bc 100644 --- a/src/api/integrations/channel/meta/whatsapp.business.service.ts +++ b/src/api/integrations/channel/meta/whatsapp.business.service.ts @@ -758,7 +758,7 @@ export class BusinessStartupService extends ChannelStartupService { where: { instanceId: this.instanceId, key: { - path: ['id'], + path: '$.id', equals: key.id, }, }, diff --git a/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts b/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts index 60e857fcc..021abea9a 100644 --- a/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts +++ b/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts @@ -1819,7 +1819,7 @@ export class BaileysStartupService extends ChannelStartupService { const savedLabel = labelsRepository.find((l) => l.labelId === label.id); if (label.deleted && savedLabel) { await this.prismaRepository.label.delete({ - where: { labelId_instanceId: { instanceId: this.instanceId, labelId: label.id } }, + where: { id: savedLabel.id }, }); this.sendDataWebhook(Events.LABELS_EDIT, { ...label, instance: this.instance.name }); return; @@ -1835,11 +1835,16 @@ export class BaileysStartupService extends ChannelStartupService { predefinedId: label.predefinedId, instanceId: this.instanceId, }; - await this.prismaRepository.label.upsert({ - where: { labelId_instanceId: { instanceId: labelData.instanceId, labelId: labelData.labelId } }, - update: labelData, - create: labelData, - }); + if (savedLabel) { + await this.prismaRepository.label.update({ + where: { id: savedLabel.id }, + data: labelData, + }); + } else { + await this.prismaRepository.label.create({ + data: labelData, + }); + } } } }, @@ -3776,7 +3781,7 @@ export class BaileysStartupService extends ChannelStartupService { if (messageId) { const isLogicalDeleted = configService.get('DATABASE').DELETE_DATA.LOGICAL_MESSAGE_DELETE; let message = await this.prismaRepository.message.findFirst({ - where: { key: { path: ['id'], equals: messageId } }, + where: { key: { path: '$.id', equals: messageId } }, }); if (isLogicalDeleted) { if (!message) return response; @@ -4196,7 +4201,7 @@ export class BaileysStartupService extends ChannelStartupService { const messageId = messageSent.message?.protocolMessage?.key?.id; if (messageId && this.configService.get('DATABASE').SAVE_DATA.NEW_MESSAGE) { let message = await this.prismaRepository.message.findFirst({ - where: { key: { path: ['id'], equals: messageId } }, + where: { key: { path: '$.id', equals: messageId } }, }); if (!message) throw new NotFoundException('Message not found'); @@ -4734,6 +4739,26 @@ export class BaileysStartupService extends ChannelStartupService { private async updateMessagesReadedByTimestamp(remoteJid: string, timestamp?: number): Promise { if (timestamp === undefined || timestamp === null) return 0; + const dbProvider = String(this.configService.get('DATABASE')?.PROVIDER || '').toLowerCase(); + + if (dbProvider === 'mysql') { + const result = await this.prismaRepository.$executeRaw` + UPDATE \`Message\` + SET \`status\` = ${status[4]} + WHERE \`instanceId\` = ${this.instanceId} + AND JSON_UNQUOTE(JSON_EXTRACT(\`key\`, '$.remoteJid')) = ${remoteJid} + AND JSON_EXTRACT(\`key\`, '$.fromMe') = false + AND \`messageTimestamp\` <= ${timestamp} + AND (\`status\` IS NULL OR \`status\` = ${status[3]}) + `; + + if (result && result > 0) { + this.updateChatUnreadMessages(remoteJid); + } + + return result || 0; + } + // Use raw SQL to avoid JSON path issues const result = await this.prismaRepository.$executeRaw` UPDATE "Message" @@ -4757,16 +4782,25 @@ export class BaileysStartupService extends ChannelStartupService { } private async updateChatUnreadMessages(remoteJid: string): Promise { + const dbProvider = String(this.configService.get('DATABASE')?.PROVIDER || '').toLowerCase(); + const [chat, unreadMessages] = await Promise.all([ this.prismaRepository.chat.findFirst({ where: { remoteJid } }), - // Use raw SQL to avoid JSON path issues - this.prismaRepository.$queryRaw` - SELECT COUNT(*)::int as count FROM "Message" - WHERE "instanceId" = ${this.instanceId} - AND "key"->>'remoteJid' = ${remoteJid} - AND ("key"->>'fromMe')::boolean = false - AND "status" = ${status[3]} - `.then((result: any[]) => result[0]?.count || 0), + dbProvider === 'mysql' + ? this.prismaRepository.$queryRaw` + SELECT COUNT(*) as count FROM \`Message\` + WHERE \`instanceId\` = ${this.instanceId} + AND JSON_UNQUOTE(JSON_EXTRACT(\`key\`, '$.remoteJid')) = ${remoteJid} + AND JSON_EXTRACT(\`key\`, '$.fromMe') = false + AND \`status\` = ${status[3]} + `.then((result: any[]) => Number(result?.[0]?.count || 0)) + : this.prismaRepository.$queryRaw` + SELECT COUNT(*)::int as count FROM "Message" + WHERE "instanceId" = ${this.instanceId} + AND "key"->>'remoteJid' = ${remoteJid} + AND ("key"->>'fromMe')::boolean = false + AND "status" = ${status[3]} + `.then((result: any[]) => result[0]?.count || 0), ]); if (chat && chat.unreadMessages !== unreadMessages) { @@ -5013,7 +5047,7 @@ export class BaileysStartupService extends ChannelStartupService { } } - public async fetchMessages(query: Query) { + public async fetchMessages(query: Query): Promise { const keyFilters = query?.where?.key as ExtendedIMessageKey; const timestampFilter = {}; @@ -5034,13 +5068,13 @@ export class BaileysStartupService extends ChannelStartupService { messageType: query?.where?.messageType, ...timestampFilter, AND: [ - keyFilters?.id ? { key: { path: ['id'], equals: keyFilters?.id } } : {}, - keyFilters?.fromMe ? { key: { path: ['fromMe'], equals: keyFilters?.fromMe } } : {}, - keyFilters?.participant ? { key: { path: ['participant'], equals: keyFilters?.participant } } : {}, + keyFilters?.id ? { key: { path: '$.id', equals: keyFilters?.id } } : {}, + keyFilters?.fromMe ? { key: { path: '$.fromMe', equals: keyFilters?.fromMe } } : {}, + keyFilters?.participant ? { key: { path: '$.participant', equals: keyFilters?.participant } } : {}, { OR: [ - keyFilters?.remoteJid ? { key: { path: ['remoteJid'], equals: keyFilters?.remoteJid } } : {}, - keyFilters?.remoteJidAlt ? { key: { path: ['remoteJidAlt'], equals: keyFilters?.remoteJidAlt } } : {}, + keyFilters?.remoteJid ? { key: { path: '$.remoteJid', equals: keyFilters?.remoteJid } } : {}, + keyFilters?.remoteJidAlt ? { key: { path: '$.remoteJidAlt', equals: keyFilters?.remoteJidAlt } } : {}, ], }, ], @@ -5063,13 +5097,13 @@ export class BaileysStartupService extends ChannelStartupService { messageType: query?.where?.messageType, ...timestampFilter, AND: [ - keyFilters?.id ? { key: { path: ['id'], equals: keyFilters?.id } } : {}, - keyFilters?.fromMe ? { key: { path: ['fromMe'], equals: keyFilters?.fromMe } } : {}, - keyFilters?.participant ? { key: { path: ['participant'], equals: keyFilters?.participant } } : {}, + keyFilters?.id ? { key: { path: '$.id', equals: keyFilters?.id } } : {}, + keyFilters?.fromMe ? { key: { path: '$.fromMe', equals: keyFilters?.fromMe } } : {}, + keyFilters?.participant ? { key: { path: '$.participant', equals: keyFilters?.participant } } : {}, { OR: [ - keyFilters?.remoteJid ? { key: { path: ['remoteJid'], equals: keyFilters?.remoteJid } } : {}, - keyFilters?.remoteJidAlt ? { key: { path: ['remoteJidAlt'], equals: keyFilters?.remoteJidAlt } } : {}, + keyFilters?.remoteJid ? { key: { path: '$.remoteJid', equals: keyFilters?.remoteJid } } : {}, + keyFilters?.remoteJidAlt ? { key: { path: '$.remoteJidAlt', equals: keyFilters?.remoteJidAlt } } : {}, ], }, ], diff --git a/src/api/integrations/chatbot/chatwoot/services/chatwoot.service.ts b/src/api/integrations/chatbot/chatwoot/services/chatwoot.service.ts index 906fff188..af18da4cd 100644 --- a/src/api/integrations/chatbot/chatwoot/services/chatwoot.service.ts +++ b/src/api/integrations/chatbot/chatwoot/services/chatwoot.service.ts @@ -1546,7 +1546,7 @@ export class ChatwootService { const lastMessage = await this.prismaRepository.message.findFirst({ where: { key: { - path: ['fromMe'], + path: '$.fromMe', equals: false, }, instanceId: instance.instanceId, @@ -1576,7 +1576,7 @@ export class ChatwootService { where: { instanceId: instance.instanceId, key: { - path: ['id'], + path: '$.id', equals: key.id, }, }, @@ -2025,7 +2025,7 @@ export class ChatwootService { quotedMsg = await this.prismaRepository.message.findFirst({ where: { key: { - path: ['id'], + path: '$.id', equals: quotedId, }, chatwootMessageId: { @@ -2337,7 +2337,7 @@ export class ChatwootService { await this.prismaRepository.message.deleteMany({ where: { key: { - path: ['id'], + path: '$.id', equals: body.key.id, }, instanceId: instance.instanceId, diff --git a/src/api/services/channel.service.ts b/src/api/services/channel.service.ts index 56bec0802..27891c133 100644 --- a/src/api/services/channel.service.ts +++ b/src/api/services/channel.service.ts @@ -623,10 +623,10 @@ export class ChannelStartupService { messageType: query?.where?.messageType, ...timestampFilter, AND: [ - keyFilters?.id ? { key: { path: ['id'], equals: keyFilters?.id } } : {}, - keyFilters?.fromMe ? { key: { path: ['fromMe'], equals: keyFilters?.fromMe } } : {}, - keyFilters?.remoteJid ? { key: { path: ['remoteJid'], equals: keyFilters?.remoteJid } } : {}, - keyFilters?.participants ? { key: { path: ['participants'], equals: keyFilters?.participants } } : {}, + keyFilters?.id ? { key: { path: '$.id', equals: keyFilters?.id } } : {}, + keyFilters?.fromMe ? { key: { path: '$.fromMe', equals: keyFilters?.fromMe } } : {}, + keyFilters?.remoteJid ? { key: { path: '$.remoteJid', equals: keyFilters?.remoteJid } } : {}, + keyFilters?.participants ? { key: { path: '$.participants', equals: keyFilters?.participants } } : {}, ], }, }); @@ -647,10 +647,10 @@ export class ChannelStartupService { messageType: query?.where?.messageType, ...timestampFilter, AND: [ - keyFilters?.id ? { key: { path: ['id'], equals: keyFilters?.id } } : {}, - keyFilters?.fromMe ? { key: { path: ['fromMe'], equals: keyFilters?.fromMe } } : {}, - keyFilters?.remoteJid ? { key: { path: ['remoteJid'], equals: keyFilters?.remoteJid } } : {}, - keyFilters?.participants ? { key: { path: ['participants'], equals: keyFilters?.participants } } : {}, + keyFilters?.id ? { key: { path: '$.id', equals: keyFilters?.id } } : {}, + keyFilters?.fromMe ? { key: { path: '$.fromMe', equals: keyFilters?.fromMe } } : {}, + keyFilters?.remoteJid ? { key: { path: '$.remoteJid', equals: keyFilters?.remoteJid } } : {}, + keyFilters?.participants ? { key: { path: '$.participants', equals: keyFilters?.participants } } : {}, ], }, orderBy: { diff --git a/src/utils/onWhatsappCache.ts b/src/utils/onWhatsappCache.ts index 08de0714e..be3c8a777 100644 --- a/src/utils/onWhatsappCache.ts +++ b/src/utils/onWhatsappCache.ts @@ -126,12 +126,9 @@ export async function saveOnWhatsappCache(data: ISaveOnWhatsappCacheParams[]) { // Ordena os JIDs para garantir consistência na string final const sortedJidOptions = [...finalJidOptions].sort(); const newJidOptionsString = sortedJidOptions.join(','); - const newLid = item.lid === 'lid' || item.remoteJid?.includes('@lid') ? 'lid' : null; - const dataPayload = { remoteJid: remoteJid, jidOptions: newJidOptionsString, - lid: newLid, }; // 4. Decide entre Criar ou Atualizar @@ -142,9 +139,7 @@ export async function saveOnWhatsappCache(data: ISaveOnWhatsappCacheParams[]) { : ''; const isDataSame = - existingRecord.remoteJid === dataPayload.remoteJid && - existingJidOptionsString === dataPayload.jidOptions && - existingRecord.lid === dataPayload.lid; + existingRecord.remoteJid === dataPayload.remoteJid && existingJidOptionsString === dataPayload.jidOptions; if (isDataSame) { logger.verbose(`[saveOnWhatsappCache] Data for ${remoteJid} is already up-to-date. Skipping update.`); @@ -153,7 +148,7 @@ export async function saveOnWhatsappCache(data: ISaveOnWhatsappCacheParams[]) { // Os dados são diferentes, então atualiza logger.verbose( - `[saveOnWhatsappCache] Register exists, updating: remoteJid=${remoteJid}, jidOptions=${dataPayload.jidOptions}, lid=${dataPayload.lid}`, + `[saveOnWhatsappCache] Register exists, updating: remoteJid=${remoteJid}, jidOptions=${dataPayload.jidOptions}`, ); await prismaRepository.isOnWhatsapp.update({ where: { id: existingRecord.id }, @@ -162,7 +157,7 @@ export async function saveOnWhatsappCache(data: ISaveOnWhatsappCacheParams[]) { } else { // Cria nova entrada logger.verbose( - `[saveOnWhatsappCache] Register does not exist, creating: remoteJid=${remoteJid}, jidOptions=${dataPayload.jidOptions}, lid=${dataPayload.lid}`, + `[saveOnWhatsappCache] Register does not exist, creating: remoteJid=${remoteJid}, jidOptions=${dataPayload.jidOptions}`, ); await prismaRepository.isOnWhatsapp.create({ data: dataPayload, @@ -203,7 +198,7 @@ export async function getOnWhatsappCache(remoteJids: string[]) { remoteJid: item.remoteJid, number: item.remoteJid.split('@')[0], jidOptions: item.jidOptions.split(','), - lid: item.lid, + lid: (item as any).lid || (item.remoteJid.includes('@lid') ? 'lid' : undefined), })); }