181 lines
7.2 KiB
JavaScript
181 lines
7.2 KiB
JavaScript
|
const { SESClient, SendEmailCommand } = require('@aws-sdk/client-ses');
|
||
|
const { S3Client, GetObjectCommand, PutObjectCommand } = require('@aws-sdk/client-s3');
|
||
|
const { getSignedUrl } = require('@aws-sdk/s3-request-presigner');
|
||
|
const { simpleParser } = require('mailparser');
|
||
|
|
||
|
// Initialize clients
|
||
|
const sesClient = new SESClient({ region: process.env.AWS_REGION || 'us-west-2' });
|
||
|
const s3Client = new S3Client({ region: process.env.AWS_REGION || 'us-west-2' });
|
||
|
|
||
|
exports.handler = async (event) => {
|
||
|
try {
|
||
|
// Extract S3 event information
|
||
|
if (!event.Records || !event.Records[0] || !event.Records[0].s3) {
|
||
|
throw new Error('Invalid S3 event structure');
|
||
|
}
|
||
|
|
||
|
const s3Record = event.Records[0].s3;
|
||
|
const bucketName = s3Record.bucket.name;
|
||
|
const objectKey = s3Record.object.key;
|
||
|
|
||
|
// Filter out attachment files to prevent infinite loops
|
||
|
if (objectKey.startsWith('attachments/')) {
|
||
|
console.log(`Skipping attachment file: ${objectKey}`);
|
||
|
return {
|
||
|
statusCode: 200,
|
||
|
body: JSON.stringify({
|
||
|
message: 'Skipped attachment file',
|
||
|
reason: 'Object is in attachments/ prefix',
|
||
|
objectKey: objectKey
|
||
|
})
|
||
|
};
|
||
|
}
|
||
|
|
||
|
console.log(`Processing email: ${objectKey}`);
|
||
|
|
||
|
// Get the email content from S3
|
||
|
const getObjectParams = {
|
||
|
Bucket: bucketName,
|
||
|
Key: objectKey
|
||
|
};
|
||
|
|
||
|
const s3Response = await s3Client.send(new GetObjectCommand(getObjectParams));
|
||
|
const emailContent = await s3Response.Body.transformToString();
|
||
|
|
||
|
// Extract recipient from the object key path
|
||
|
const pathParts = objectKey.split('/');
|
||
|
const recipient = pathParts[0]; // e.g., "calculator"
|
||
|
|
||
|
const forwardEmail = process.env.FORWARD_EMAIL;
|
||
|
const domainName = process.env.DOMAIN_NAME || '127local.net';
|
||
|
const environment = process.env.ENVIRONMENT || 'production';
|
||
|
|
||
|
// Parse the email using mailparser
|
||
|
const parsedEmail = await simpleParser(emailContent);
|
||
|
|
||
|
// Extract email details
|
||
|
const subject = parsedEmail.subject || 'No Subject';
|
||
|
const from = parsedEmail.from?.text || 'unknown@example.com';
|
||
|
const emailBody = parsedEmail.text || parsedEmail.html || 'No body content';
|
||
|
|
||
|
// Create a clean, properly formatted forwarded email
|
||
|
const forwardSubject = `[${environment.toUpperCase()}] FWD: ${subject}`;
|
||
|
|
||
|
// Build the email body with proper formatting
|
||
|
let forwardBody = `Email forwarded from ${from} to ${recipient}@${domainName} (${environment.toUpperCase()})\n`;
|
||
|
forwardBody += `Forwarded at: ${new Date().toISOString()}\n`;
|
||
|
forwardBody += `\n--- Original Email ---\n\n`;
|
||
|
forwardBody += `Subject: ${subject}\n`;
|
||
|
forwardBody += `From: ${from}\n`;
|
||
|
forwardBody += `\n${emailBody}`;
|
||
|
|
||
|
// Process attachments and generate download links
|
||
|
let attachmentLinks = [];
|
||
|
if (parsedEmail.attachments && parsedEmail.attachments.length > 0) {
|
||
|
console.log(`Processing ${parsedEmail.attachments.length} attachments...`);
|
||
|
|
||
|
for (const attachment of parsedEmail.attachments) {
|
||
|
try {
|
||
|
// Create unique filename for S3
|
||
|
const timestamp = Date.now();
|
||
|
const safeFilename = attachment.filename.replace(/[^a-zA-Z0-9.-]/g, '_');
|
||
|
const s3Key = `attachments/${recipient}/${timestamp}-${safeFilename}`;
|
||
|
|
||
|
// Upload attachment to S3
|
||
|
const uploadParams = {
|
||
|
Bucket: bucketName,
|
||
|
Key: s3Key,
|
||
|
Body: attachment.content,
|
||
|
ContentType: attachment.contentType,
|
||
|
Metadata: {
|
||
|
originalEmail: objectKey,
|
||
|
recipient: recipient,
|
||
|
sender: from,
|
||
|
originalFilename: attachment.filename
|
||
|
}
|
||
|
};
|
||
|
|
||
|
await s3Client.send(new PutObjectCommand(uploadParams));
|
||
|
console.log(`Uploaded: ${attachment.filename}`);
|
||
|
|
||
|
// Generate pre-signed download URL (expires in 7 days)
|
||
|
const downloadUrl = await getSignedUrl(
|
||
|
s3Client,
|
||
|
new GetObjectCommand({
|
||
|
Bucket: bucketName,
|
||
|
Key: s3Key
|
||
|
}),
|
||
|
{ expiresIn: 604800 } // 7 days
|
||
|
);
|
||
|
|
||
|
attachmentLinks.push({
|
||
|
filename: attachment.filename,
|
||
|
type: attachment.contentType,
|
||
|
size: attachment.size,
|
||
|
downloadUrl: downloadUrl
|
||
|
});
|
||
|
|
||
|
} catch (error) {
|
||
|
console.error(`Failed to process attachment ${attachment.filename}:`, error);
|
||
|
// Continue with other attachments
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Add attachment information with download links
|
||
|
if (attachmentLinks.length > 0) {
|
||
|
forwardBody += `\n\n--- Attachments (${attachmentLinks.length}) ---\n`;
|
||
|
attachmentLinks.forEach((attachment) => {
|
||
|
forwardBody += `• ${attachment.filename} (${attachment.type}, ${attachment.size} bytes)\n`;
|
||
|
forwardBody += ` Download: ${attachment.downloadUrl}\n`;
|
||
|
forwardBody += ` Note: Download link expires in 7 days\n\n`;
|
||
|
});
|
||
|
}
|
||
|
|
||
|
// Create the forwarded email using SendEmail
|
||
|
const forwardParams = {
|
||
|
Source: `noreply@${domainName}`,
|
||
|
Destination: {
|
||
|
ToAddresses: [forwardEmail]
|
||
|
},
|
||
|
Message: {
|
||
|
Subject: {
|
||
|
Data: forwardSubject
|
||
|
},
|
||
|
Body: {
|
||
|
Text: {
|
||
|
Data: forwardBody
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
|
||
|
// Send the properly formatted email
|
||
|
const result = await sesClient.send(new SendEmailCommand(forwardParams));
|
||
|
console.log(`Email forwarded successfully: ${result.MessageId}`);
|
||
|
|
||
|
return {
|
||
|
statusCode: 200,
|
||
|
body: JSON.stringify({
|
||
|
message: 'Email forwarded successfully',
|
||
|
messageId: result.MessageId,
|
||
|
originalRecipient: recipient,
|
||
|
forwardedTo: forwardEmail,
|
||
|
s3Location: `${bucketName}/${objectKey}`
|
||
|
})
|
||
|
};
|
||
|
|
||
|
} catch (error) {
|
||
|
console.error('Error processing email:', error);
|
||
|
|
||
|
return {
|
||
|
statusCode: 500,
|
||
|
body: JSON.stringify({
|
||
|
error: 'Failed to process email',
|
||
|
message: error.message,
|
||
|
stack: error.stack
|
||
|
})
|
||
|
};
|
||
|
}
|
||
|
};
|