121 lines
5.2 KiB
TypeScript
121 lines
5.2 KiB
TypeScript
|
|
import nodemailer from 'nodemailer';
|
||
|
|
|
||
|
|
// Import just the template helper without hitting DB
|
||
|
|
// We'll construct the email manually since the DB connection fails
|
||
|
|
|
||
|
|
const BRAND = {
|
||
|
|
red: '#de0f1e',
|
||
|
|
darkBlue: '#053d57',
|
||
|
|
white: '#fefefe',
|
||
|
|
teal: '#557f8c',
|
||
|
|
};
|
||
|
|
|
||
|
|
const token = '6f974b1da9fae95f74bbcd2419df589730979ac945aeaa5413021c00311b5165';
|
||
|
|
const url = 'http://localhost:3000/accept-invite?token=' + token;
|
||
|
|
|
||
|
|
// Replicate the styled email template from email.ts
|
||
|
|
function getStyledHtml(name: string, inviteUrl: string) {
|
||
|
|
return `<!DOCTYPE html>
|
||
|
|
<html lang="en">
|
||
|
|
<head>
|
||
|
|
<meta charset="utf-8">
|
||
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
|
|
<title>You're invited to join the MOPC Portal</title>
|
||
|
|
</head>
|
||
|
|
<body style="margin: 0; padding: 0; background-color: #f8fafc; font-family: 'Montserrat', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;">
|
||
|
|
<table role="presentation" width="100%" cellspacing="0" cellpadding="0" border="0" style="background-color: #f8fafc;">
|
||
|
|
<tr>
|
||
|
|
<td align="center" style="padding: 40px 20px;">
|
||
|
|
<table role="presentation" width="600" cellspacing="0" cellpadding="0" border="0" style="max-width: 600px; width: 100%;">
|
||
|
|
<!-- Header -->
|
||
|
|
<tr>
|
||
|
|
<td style="background: linear-gradient(135deg, ${BRAND.darkBlue} 0%, ${BRAND.teal} 100%); border-radius: 16px 16px 0 0; padding: 32px 40px; text-align: center;">
|
||
|
|
<h1 style="color: ${BRAND.white}; font-size: 22px; font-weight: 700; margin: 0; letter-spacing: -0.02em;">
|
||
|
|
Monaco Ocean Protection Challenge
|
||
|
|
</h1>
|
||
|
|
<p style="color: rgba(255,255,255,0.8); font-size: 13px; font-weight: 300; margin: 8px 0 0 0; letter-spacing: 0.05em; text-transform: uppercase;">
|
||
|
|
Together for a healthier ocean
|
||
|
|
</p>
|
||
|
|
</td>
|
||
|
|
</tr>
|
||
|
|
<!-- Body -->
|
||
|
|
<tr>
|
||
|
|
<td style="background-color: #ffffff; padding: 40px; border-radius: 0 0 16px 16px; box-shadow: 0 4px 6px -1px rgba(0,0,0,0.1);">
|
||
|
|
<h2 style="color: ${BRAND.darkBlue}; font-size: 20px; font-weight: 600; margin: 0 0 24px 0;">
|
||
|
|
Hello ${name},
|
||
|
|
</h2>
|
||
|
|
<p style="color: #475569; font-size: 15px; line-height: 1.7; margin: 0 0 16px 0; font-weight: 400;">
|
||
|
|
You've been invited to join the Monaco Ocean Protection Challenge platform as an <strong>applicant</strong>.
|
||
|
|
</p>
|
||
|
|
<p style="color: #475569; font-size: 15px; line-height: 1.7; margin: 0 0 24px 0; font-weight: 400;">
|
||
|
|
Click the button below to set up your account and get started.
|
||
|
|
</p>
|
||
|
|
<!-- CTA Button -->
|
||
|
|
<table role="presentation" width="100%" cellspacing="0" cellpadding="0" border="0" style="margin: 28px 0;">
|
||
|
|
<tr>
|
||
|
|
<td align="center">
|
||
|
|
<a href="${inviteUrl}" style="display: inline-block; background: linear-gradient(135deg, ${BRAND.red} 0%, #c40d19 100%); color: #ffffff; text-decoration: none; padding: 14px 36px; border-radius: 10px; font-size: 15px; font-weight: 600; letter-spacing: 0.02em; box-shadow: 0 4px 14px rgba(222, 15, 30, 0.3);">
|
||
|
|
Accept Invitation
|
||
|
|
</a>
|
||
|
|
</td>
|
||
|
|
</tr>
|
||
|
|
</table>
|
||
|
|
<!-- Info Box -->
|
||
|
|
<table role="presentation" width="100%" cellspacing="0" cellpadding="0" border="0" style="margin: 20px 0;">
|
||
|
|
<tr>
|
||
|
|
<td style="background-color: #eff6ff; border-left: 4px solid ${BRAND.darkBlue}; border-radius: 0 8px 8px 0; padding: 16px 20px;">
|
||
|
|
<p style="color: #1e40af; margin: 0; font-size: 13px; line-height: 1.6;">
|
||
|
|
This link will expire in 3 days.
|
||
|
|
</p>
|
||
|
|
</td>
|
||
|
|
</tr>
|
||
|
|
</table>
|
||
|
|
</td>
|
||
|
|
</tr>
|
||
|
|
<!-- Footer -->
|
||
|
|
<tr>
|
||
|
|
<td style="padding: 24px 40px; text-align: center;">
|
||
|
|
<p style="color: #94a3b8; font-size: 12px; line-height: 1.6; margin: 0;">
|
||
|
|
Monaco Ocean Protection Challenge<br>
|
||
|
|
<span style="color: #cbd5e1;">Together for a healthier ocean.</span>
|
||
|
|
</p>
|
||
|
|
</td>
|
||
|
|
</tr>
|
||
|
|
</table>
|
||
|
|
</td>
|
||
|
|
</tr>
|
||
|
|
</table>
|
||
|
|
</body>
|
||
|
|
</html>`;
|
||
|
|
}
|
||
|
|
|
||
|
|
async function main() {
|
||
|
|
console.log('Creating transporter...');
|
||
|
|
const transporter = nodemailer.createTransport({
|
||
|
|
host: 'mail.monaco-opc.com',
|
||
|
|
port: 587,
|
||
|
|
secure: false,
|
||
|
|
auth: {
|
||
|
|
user: 'noreply@monaco-opc.com',
|
||
|
|
pass: '9EythPDcz1Fya4M88iigkB1wojNf8QEVPuRRnD9dJMBpT3pk2',
|
||
|
|
},
|
||
|
|
});
|
||
|
|
|
||
|
|
console.log('Sending styled invitation email...');
|
||
|
|
const info = await transporter.sendMail({
|
||
|
|
from: 'MOPC Portal <noreply@monaco-opc.com>',
|
||
|
|
to: 'matt.ciaccio@gmail.com',
|
||
|
|
subject: "You're invited to join the MOPC Portal",
|
||
|
|
text: `Hello Matt Ciaccio,\n\nYou've been invited to join the Monaco Ocean Protection Challenge platform as an applicant.\n\nClick the link below to set up your account:\n\n${url}\n\nThis link will expire in 3 days.\n\n---\nMonaco Ocean Protection Challenge\nTogether for a healthier ocean.`,
|
||
|
|
html: getStyledHtml('Matt Ciaccio', url),
|
||
|
|
});
|
||
|
|
|
||
|
|
console.log('SUCCESS! Message ID:', info.messageId);
|
||
|
|
process.exit(0);
|
||
|
|
}
|
||
|
|
|
||
|
|
main().catch(err => {
|
||
|
|
console.error('FAILED:', err);
|
||
|
|
process.exit(1);
|
||
|
|
});
|