Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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\"",
Expand Down Expand Up @@ -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'"
Expand Down
38 changes: 38 additions & 0 deletions project.json
Original file line number Diff line number Diff line change
@@ -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"]
}
Original file line number Diff line number Diff line change
Expand Up @@ -758,7 +758,7 @@ export class BusinessStartupService extends ChannelStartupService {
where: {
instanceId: this.instanceId,
key: {
path: ['id'],
path: '$.id',
equals: key.id,
},
},
Expand Down
88 changes: 61 additions & 27 deletions src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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,
});
}
}
}
},
Expand Down Expand Up @@ -3776,7 +3781,7 @@ export class BaileysStartupService extends ChannelStartupService {
if (messageId) {
const isLogicalDeleted = configService.get<Database>('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;
Expand Down Expand Up @@ -4196,7 +4201,7 @@ export class BaileysStartupService extends ChannelStartupService {
const messageId = messageSent.message?.protocolMessage?.key?.id;
if (messageId && this.configService.get<Database>('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');

Expand Down Expand Up @@ -4734,6 +4739,26 @@ export class BaileysStartupService extends ChannelStartupService {
private async updateMessagesReadedByTimestamp(remoteJid: string, timestamp?: number): Promise<number> {
if (timestamp === undefined || timestamp === null) return 0;

const dbProvider = String(this.configService.get<Database>('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"
Expand All @@ -4757,16 +4782,25 @@ export class BaileysStartupService extends ChannelStartupService {
}

private async updateChatUnreadMessages(remoteJid: string): Promise<number> {
const dbProvider = String(this.configService.get<Database>('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) {
Expand Down Expand Up @@ -5013,7 +5047,7 @@ export class BaileysStartupService extends ChannelStartupService {
}
}

public async fetchMessages(query: Query<Message>) {
public async fetchMessages(query: Query<Message>): Promise<any> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: The fetchMessages return type Promise<any> loses useful typing information.

The method used to infer its return type from prismaRepository.message.findMany/findFirst, but explicitly returning Promise<any> hides the data shape and removes type safety and editor/compiler assistance for callers.

Please narrow the return type to the concrete message type (e.g. Promise<Message[]> or an appropriate Prisma type such as Prisma.MessageGetPayload<...>), matching what the method actually returns, so the service API remains type-safe without changing runtime behavior.

Suggested change
public async fetchMessages(query: Query<Message>): Promise<any> {
public async fetchMessages(query: Query<Message>) {

const keyFilters = query?.where?.key as ExtendedIMessageKey;

const timestampFilter = {};
Expand All @@ -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 } } : {},
],
},
],
Expand All @@ -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 } } : {},
],
},
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -1576,7 +1576,7 @@ export class ChatwootService {
where: {
instanceId: instance.instanceId,
key: {
path: ['id'],
path: '$.id',
equals: key.id,
},
},
Expand Down Expand Up @@ -2025,7 +2025,7 @@ export class ChatwootService {
quotedMsg = await this.prismaRepository.message.findFirst({
where: {
key: {
path: ['id'],
path: '$.id',
equals: quotedId,
},
chatwootMessageId: {
Expand Down Expand Up @@ -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,
Expand Down
16 changes: 8 additions & 8 deletions src/api/services/channel.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 } } : {},
],
},
});
Expand All @@ -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: {
Expand Down
13 changes: 4 additions & 9 deletions src/utils/onWhatsappCache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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.`);
Expand All @@ -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 },
Expand All @@ -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,
Expand Down Expand Up @@ -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),
}));
}

Expand Down