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