import nodemailer from 'nodemailer'; import { getCredentialsFromSession, decryptCredentials } from '~/server/utils/encryption'; import { uploadFile } from '~/server/utils/minio'; import { updateInterest } from '~/server/utils/nocodb'; export default defineEventHandler(async (event) => { const xTagHeader = getRequestHeader(event, "x-tag"); if (!xTagHeader || (xTagHeader !== "094ut234" && xTagHeader !== "pjnvü1230")) { throw createError({ statusCode: 401, statusMessage: "unauthenticated" }); } try { const body = await readBody(event); const { to, subject, body: emailBody, interestId, sessionId, includeSignature = true, signatureConfig } = body; if (!to || !subject || !emailBody || !sessionId) { throw createError({ statusCode: 400, statusMessage: "To, subject, body, and sessionId are required" }); } // Get encrypted credentials from session const encryptedCredentials = getCredentialsFromSession(sessionId); if (!encryptedCredentials) { throw createError({ statusCode: 401, statusMessage: "Email credentials not found. Please reconnect." }); } // Decrypt credentials const { email, password } = decryptCredentials(encryptedCredentials); // Get user info for signature const defaultName = email.split('@')[0].replace('.', ' ').replace(/\b\w/g, l => l.toUpperCase()); // Build email signature with customizable fields const sig = signatureConfig || {}; const contactLines = sig.contactInfo ? sig.contactInfo.split('\n').filter((line: string) => line.trim()).join('
') : ''; const signature = includeSignature ? `

Port Nimara
${sig.name || defaultName}
${sig.title || 'Sales & Marketing Director'}
${sig.company || 'Port Nimara'}
${contactLines ? contactLines + '
' : ''} ${sig.email || email}

The information in this message is confidential and may be privileged.
It is intended for the addressee alone.
If you are not the intended recipient it is prohibited to disclose, use or copy this information.
Please contact the Sender immediately should this message have been transmitted incorrectly.
` : ''; // Convert plain text body to HTML with line breaks const htmlBody = emailBody.replace(/\n/g, '
') + signature; // Configure SMTP transport const transporter = nodemailer.createTransport({ host: process.env.NUXT_EMAIL_SMTP_HOST || 'mail.portnimara.com', port: parseInt(process.env.NUXT_EMAIL_SMTP_PORT || '587'), secure: false, // false for STARTTLS auth: { user: email, pass: password }, tls: { rejectUnauthorized: false // Allow self-signed certificates } }); // Send email const fromName = sig.name || defaultName; const info = await transporter.sendMail({ from: `"${fromName}" <${email}>`, to: to, subject: subject, text: emailBody, // Plain text version html: htmlBody // HTML version with signature }); // Store email in MinIO for thread history and update EOI Time Sent if (interestId) { try { const emailData = { id: info.messageId, from: email, to: to, subject: subject, body: emailBody, html: htmlBody, timestamp: new Date().toISOString(), direction: 'sent', interestId: interestId }; const objectName = `client-emails/interest-${interestId}/${Date.now()}-sent.json`; const buffer = Buffer.from(JSON.stringify(emailData, null, 2)); await uploadFile( objectName, buffer, 'application/json' ); // Update EOI Time Sent if the email contains an EOI link if (emailBody.includes('signatures.portnimara.dev/sign/')) { try { await updateInterest(interestId, { 'EOI Time Sent': new Date().toISOString() }); } catch (updateError) { console.error('Failed to update EOI Time Sent:', updateError); // Continue even if update fails } } } catch (storageError) { console.error('Failed to store email in MinIO:', storageError); // Continue even if storage fails } } return { success: true, message: "Email sent successfully", messageId: info.messageId }; } catch (error) { console.error('Failed to send email:', error); if (error instanceof Error) { throw createError({ statusCode: 500, statusMessage: `Failed to send email: ${error.message}` }); } else { throw createError({ statusCode: 500, statusMessage: "An unexpected error occurred", }); } } });