- Updated the `reenviarEmail` mutation to return detailed error messages for better user feedback. - Added a new query to obtain email queue statistics, providing insights into email statuses. - Enhanced the `processarFilaEmails` mutation to track processing failures and successes more effectively. - Implemented a manual email processing mutation for immediate testing and control over email sending. - Improved email validation and error handling in the email sending action, ensuring robust delivery processes.
119 lines
3.5 KiB
TypeScript
119 lines
3.5 KiB
TypeScript
"use node";
|
|
|
|
import { action } from "../_generated/server";
|
|
import { v } from "convex/values";
|
|
import { internal } from "../_generated/api";
|
|
|
|
export const enviar = action({
|
|
args: {
|
|
emailId: v.id("notificacoesEmail"),
|
|
},
|
|
returns: v.object({ sucesso: v.boolean(), erro: v.optional(v.string()) }),
|
|
handler: async (ctx, args) => {
|
|
"use node";
|
|
const nodemailer = await import("nodemailer");
|
|
|
|
try {
|
|
// Buscar email da fila
|
|
const email = await ctx.runQuery(internal.email.getEmailById, {
|
|
emailId: args.emailId,
|
|
});
|
|
|
|
if (!email) {
|
|
return { sucesso: false, erro: "Email não encontrado" };
|
|
}
|
|
|
|
// Buscar configuração SMTP ativa com senha descriptografada
|
|
const config = await ctx.runQuery(internal.email.getActiveEmailConfigWithPassword, {});
|
|
|
|
if (!config) {
|
|
return {
|
|
sucesso: false,
|
|
erro: "Configuração de email não encontrada ou inativa",
|
|
};
|
|
}
|
|
|
|
if (!config.testadoEm) {
|
|
return {
|
|
sucesso: false,
|
|
erro: "Configuração SMTP não foi testada. Teste a conexão primeiro!",
|
|
};
|
|
}
|
|
|
|
// Marcar como enviando
|
|
await ctx.runMutation(internal.email.markEmailEnviando, {
|
|
emailId: args.emailId,
|
|
});
|
|
|
|
// Criar transporter do nodemailer
|
|
const transporter = nodemailer.createTransport({
|
|
host: config.servidor,
|
|
port: config.porta,
|
|
secure: config.usarSSL,
|
|
requireTLS: config.usarTLS,
|
|
auth: {
|
|
user: config.usuario,
|
|
pass: config.senha, // Senha já descriptografada
|
|
},
|
|
tls: {
|
|
// Permitir certificados autoassinados apenas se necessário
|
|
rejectUnauthorized: false,
|
|
ciphers: "SSLv3",
|
|
},
|
|
connectionTimeout: 10000, // 10 segundos
|
|
greetingTimeout: 10000,
|
|
socketTimeout: 10000,
|
|
});
|
|
|
|
// Validar email destinatário antes de enviar
|
|
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
if (!emailRegex.test(email.destinatario)) {
|
|
throw new Error(`Email destinatário inválido: ${email.destinatario}`);
|
|
}
|
|
|
|
// Enviar email
|
|
const info = await transporter.sendMail({
|
|
from: `"${config.nomeRemetente}" <${config.emailRemetente}>`,
|
|
to: email.destinatario,
|
|
subject: email.assunto,
|
|
html: email.corpo,
|
|
text: email.corpo.replace(/<[^>]*>/g, ""), // Versão texto para clientes que não suportam HTML
|
|
});
|
|
|
|
interface MessageInfo {
|
|
messageId?: string;
|
|
response?: string;
|
|
}
|
|
|
|
const messageInfo = info as MessageInfo;
|
|
|
|
console.log("✅ Email enviado com sucesso!", {
|
|
para: email.destinatario,
|
|
assunto: email.assunto,
|
|
messageId: messageInfo.messageId,
|
|
response: messageInfo.response,
|
|
});
|
|
|
|
// Marcar como enviado
|
|
await ctx.runMutation(internal.email.markEmailEnviado, {
|
|
emailId: args.emailId,
|
|
});
|
|
|
|
return { sucesso: true };
|
|
} catch (error) {
|
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
console.error("❌ Erro ao enviar email:", errorMessage);
|
|
|
|
// Marcar como falha
|
|
await ctx.runMutation(internal.email.markEmailFalha, {
|
|
emailId: args.emailId,
|
|
erro: errorMessage,
|
|
});
|
|
|
|
return { sucesso: false, erro: errorMessage };
|
|
}
|
|
},
|
|
});
|
|
|
|
|