port-nimara-client-portal/server/api/email/send.ts

145 lines
4.7 KiB
TypeScript

import nodemailer from 'nodemailer';
import { getCredentialsFromSession, decryptCredentials } from '~/server/utils/encryption';
import { uploadFile } from '~/server/utils/minio';
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('<br>') : '';
const signature = includeSignature ? `
<div style="margin-top: 20px; font-family: Arial, sans-serif;">
<div style="font-weight: bold;">${sig.name || defaultName}</div>
<div style="color: #666;">${sig.title || 'Sales & Marketing Director'}</div>
<br>
<div style="font-weight: bold;">${sig.company || 'Port Nimara'}</div>
<br>
${contactLines ? contactLines + '<br>' : ''}
<a href="mailto:${sig.email || email}" style="color: #0066cc;">${sig.email || email}</a>
<br><br>
<img src="${process.env.NUXT_EMAIL_LOGO_URL || 'https://portnimara.com/logo.png'}" alt="Port Nimara" style="height: 80px;">
<br>
<div style="color: #666; font-size: 12px; margin-top: 10px;">
The information in this message is confidential and may be privileged.<br>
It is intended for the addressee alone.<br>
If you are not the intended recipient it is prohibited to disclose, use or copy this information.<br>
Please contact the Sender immediately should this message have been transmitted incorrectly.
</div>
</div>` : '';
// Convert plain text body to HTML with line breaks
const htmlBody = emailBody.replace(/\n/g, '<br>') + 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
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'
);
} 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",
});
}
}
});