Files
sgse-app/packages/backend/convex/actions/email.ts
deyvisonwanderley 5d2df8077b feat: enhance email handling with improved error reporting and statistics
- 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.
2025-11-04 02:14:07 -03:00

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 };
}
},
});