Skip to content
Open
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
162 changes: 89 additions & 73 deletions src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1576,52 +1576,59 @@ export class BaileysStartupService extends ChannelStartupService {
if (isMedia) {
if (this.configService.get<S3>('S3').ENABLE) {
try {
// Skip only the S3 upload here — NOT the whole handler. The
// previous `return` statements exited messages.upsert before
// sendDataWebhook(Events.MESSAGES_UPSERT) below, silently dropping
// every media message whose S3 upload was skipped or failed —
// notably `fromMe` media sent from another device, where
// getBase64FromMediaMessage cannot fetch the file (its mediaKey
// belongs to that device). Webhook delivery must not depend on the
// outcome of the storage step.
if (isVideo && !this.configService.get<S3>('S3').SAVE_VIDEO) {
this.logger.warn('Video upload is disabled. Skipping video upload.');
// Skip video upload by returning early from this block
return;
}

const message: any = received;

// Verificação adicional para garantir que há conteúdo de mídia real
const hasRealMedia = this.hasValidMediaContent(message);

if (!hasRealMedia) {
this.logger.warn('Message detected as media but contains no valid media content');
} else {
const media = await this.getBase64FromMediaMessage({ message }, true);

if (!media) {
this.logger.verbose('No valid media to upload (messageContextInfo only), skipping MinIO');
return;
const message: any = received;

// Verificação adicional para garantir que há conteúdo de mídia real
const hasRealMedia = this.hasValidMediaContent(message);

if (!hasRealMedia) {
this.logger.warn('Message detected as media but contains no valid media content');
} else {
const media = await this.getBase64FromMediaMessage({ message }, true);

if (!media) {
this.logger.verbose('No valid media to upload (messageContextInfo only), skipping MinIO');
} else {
const { buffer, mediaType, fileName, size } = media;
const mimetype = mimeTypes.lookup(fileName).toString();
Comment on lines +1603 to +1604

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

issue (bug_risk): Guard against mimeTypes.lookup returning a falsy value before calling toString.

mimeTypes.lookup(fileName) can return false/null for unknown types, so .toString() may throw and break the upload flow. Please handle the falsy case (e.g. with a default like 'application/octet-stream' or an explicit check) before converting to string.

const fullName = join(
`${this.instance.id}`,
received.key.remoteJid,
mediaType,
`${Date.now()}_${fileName}`,
);
await s3Service.uploadFile(fullName, buffer, size.fileLength?.low, {
'Content-Type': mimetype,
});

await this.prismaRepository.media.create({
data: {
messageId: msg.id,
instanceId: this.instanceId,
type: mediaType,
fileName: fullName,
mimetype,
},
});

const mediaUrl = await s3Service.getObjectUrl(fullName);

(messageRaw.message as any).mediaUrl = mediaUrl;

await this.prismaRepository.message.update({ where: { id: msg.id }, data: messageRaw });
}
}

const { buffer, mediaType, fileName, size } = media;
const mimetype = mimeTypes.lookup(fileName).toString();
const fullName = join(
`${this.instance.id}`,
received.key.remoteJid,
mediaType,
`${Date.now()}_${fileName}`,
);
await s3Service.uploadFile(fullName, buffer, size.fileLength?.low, { 'Content-Type': mimetype });

await this.prismaRepository.media.create({
data: {
messageId: msg.id,
instanceId: this.instanceId,
type: mediaType,
fileName: fullName,
mimetype,
},
});

const mediaUrl = await s3Service.getObjectUrl(fullName);

(messageRaw.message as any).mediaUrl = mediaUrl;

await this.prismaRepository.message.update({ where: { id: msg.id }, data: messageRaw });
}
} catch (error) {
this.logger.error(['Error on upload file to minio', error?.message, error?.stack]);
Expand Down Expand Up @@ -2770,48 +2777,57 @@ export class BaileysStartupService extends ChannelStartupService {

if (isMedia && this.configService.get<S3>('S3').ENABLE) {
try {
// Skip only the S3 upload here — NOT the whole method. The previous `return`
// exited sendMessageWithTyping before sendDataWebhook(Events.SEND_MESSAGE) and
// `return messageRaw` below, making POST /message/sendMedia respond empty.
// Webhook delivery and the API response must not depend on the storage step.
if (isVideo && !this.configService.get<S3>('S3').SAVE_VIDEO) {
throw new Error('Video upload is disabled.');
}

const message: any = messageRaw;

// Verificação adicional para garantir que há conteúdo de mídia real
const hasRealMedia = this.hasValidMediaContent(message);

if (!hasRealMedia) {
this.logger.warn('Message detected as media but contains no valid media content');
this.logger.warn('Video upload is disabled. Skipping video upload.');
} else {
const media = await this.getBase64FromMediaMessage({ message }, true);
const message: any = messageRaw;

if (!media) {
this.logger.verbose('No valid media to upload (messageContextInfo only), skipping MinIO');
return;
}
// Verificação adicional para garantir que há conteúdo de mídia real
const hasRealMedia = this.hasValidMediaContent(message);

const { buffer, mediaType, fileName, size } = media;
if (!hasRealMedia) {
this.logger.warn('Message detected as media but contains no valid media content');
} else {
const media = await this.getBase64FromMediaMessage({ message }, true);

const mimetype = mimeTypes.lookup(fileName).toString();
if (!media) {
this.logger.verbose('No valid media to upload (messageContextInfo only), skipping MinIO');
} else {
const { buffer, mediaType, fileName, size } = media;

const fullName = join(
`${this.instance.id}`,
messageRaw.key.remoteJid,
`${messageRaw.key.id}`,
mediaType,
fileName,
);
const mimetype = mimeTypes.lookup(fileName).toString();

await s3Service.uploadFile(fullName, buffer, size.fileLength?.low, { 'Content-Type': mimetype });
const fullName = join(
`${this.instance.id}`,
messageRaw.key.remoteJid,
`${messageRaw.key.id}`,
mediaType,
fileName,
);

await this.prismaRepository.media.create({
data: { messageId: msg.id, instanceId: this.instanceId, type: mediaType, fileName: fullName, mimetype },
});
await s3Service.uploadFile(fullName, buffer, size.fileLength?.low, { 'Content-Type': mimetype });

await this.prismaRepository.media.create({
data: {
messageId: msg.id,
instanceId: this.instanceId,
type: mediaType,
fileName: fullName,
mimetype,
},
});

const mediaUrl = await s3Service.getObjectUrl(fullName);
const mediaUrl = await s3Service.getObjectUrl(fullName);

messageRaw.message.mediaUrl = mediaUrl;
messageRaw.message.mediaUrl = mediaUrl;

await this.prismaRepository.message.update({ where: { id: msg.id }, data: messageRaw });
await this.prismaRepository.message.update({ where: { id: msg.id }, data: messageRaw });
}
}
}
} catch (error) {
this.logger.error(['Error on upload file to minio', error?.message, error?.stack]);
Expand Down
Loading