* docs: prepare Vendoo 1.41.2 git-ignore boundary hotfix * fix: track native source modules with root-anchored runtime ignores
215 lines
8.0 KiB
JavaScript
215 lines
8.0 KiB
JavaScript
import nodemailer from 'nodemailer';
|
|
|
|
let transporter = null;
|
|
|
|
export function initMailer() {
|
|
const host = process.env.SMTP_HOST;
|
|
const port = parseInt(process.env.SMTP_PORT || '587');
|
|
const user = process.env.SMTP_USER;
|
|
const pass = process.env.SMTP_PASS;
|
|
const from = process.env.SMTP_FROM || user;
|
|
|
|
if (!host || !user || !pass) {
|
|
console.warn('SMTP nicht konfiguriert — Magic Links werden in der Konsole ausgegeben');
|
|
return false;
|
|
}
|
|
|
|
transporter = nodemailer.createTransport({
|
|
host,
|
|
port,
|
|
secure: port === 465,
|
|
auth: { user, pass },
|
|
tls: { rejectUnauthorized: !/^(0|false|no)$/i.test(String(process.env.SMTP_TLS_REJECT_UNAUTHORIZED || 'true')) },
|
|
});
|
|
|
|
return true;
|
|
}
|
|
|
|
function brandedHtml(title, content, buttonText, buttonUrl) {
|
|
return `<!DOCTYPE html>
|
|
<html>
|
|
<head><meta charset="utf-8"><meta name="viewport" content="width=device-width"></head>
|
|
<body style="margin:0;padding:0;background:#f5f5f0;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif">
|
|
<table width="100%" cellpadding="0" cellspacing="0" style="background:#f5f5f0;padding:40px 20px">
|
|
<tr><td align="center">
|
|
<table width="520" cellpadding="0" cellspacing="0" style="background:white;border-radius:12px;box-shadow:0 2px 12px rgba(0,0,0,0.08);overflow:hidden">
|
|
<tr><td style="background:#4a7c59;padding:28px 32px">
|
|
<h1 style="margin:0;color:white;font-size:22px;font-weight:700;letter-spacing:-0.5px">Vendoo</h1>
|
|
</td></tr>
|
|
<tr><td style="padding:32px">
|
|
<h2 style="margin:0 0 16px;color:#1a1a1a;font-size:18px">${title}</h2>
|
|
<div style="color:#4a4a4a;font-size:14px;line-height:1.7">${content}</div>
|
|
${buttonUrl ? `
|
|
<div style="margin:28px 0;text-align:center">
|
|
<a href="${buttonUrl}" style="display:inline-block;background:#4a7c59;color:white;text-decoration:none;padding:14px 36px;border-radius:8px;font-weight:600;font-size:14px;letter-spacing:0.3px">${buttonText}</a>
|
|
</div>
|
|
<p style="color:#999;font-size:11px;margin-top:20px">Oder kopiere diesen Link:<br>
|
|
<a href="${buttonUrl}" style="color:#4a7c59;word-break:break-all;font-size:11px">${buttonUrl}</a></p>` : ''}
|
|
</td></tr>
|
|
<tr><td style="padding:20px 32px;background:#fafaf8;border-top:1px solid #eee">
|
|
<p style="margin:0;color:#999;font-size:11px;text-align:center">Vendoo — Marketplace Listing Generator</p>
|
|
</td></tr>
|
|
</table>
|
|
</td></tr>
|
|
</table>
|
|
</body></html>`;
|
|
}
|
|
|
|
export async function sendMagicLink(email, token, baseUrl, type = 'login') {
|
|
const url = `${baseUrl}/auth/magic?token=${token}`;
|
|
|
|
const isInvite = type === 'invite';
|
|
const title = isInvite ? 'Du wurdest eingeladen!' : 'Dein Login-Link';
|
|
const content = isInvite
|
|
? `<p>Du wurdest zum Vendoo eingeladen. Klicke auf den Button, um dein Konto zu aktivieren und loszulegen.</p><p>Der Link ist <strong>48 Stunden</strong> gültig.</p>`
|
|
: `<p>Klicke auf den Button, um dich einzuloggen. Kein Passwort nötig.</p><p>Der Link ist <strong>15 Minuten</strong> gültig und kann nur einmal verwendet werden.</p>`;
|
|
const buttonText = isInvite ? 'Einladung annehmen' : 'Jetzt einloggen';
|
|
|
|
const html = brandedHtml(title, content, buttonText, url);
|
|
const subject = isInvite ? 'Vendoo — Du wurdest eingeladen' : 'Vendoo — Dein Login-Link';
|
|
|
|
if (!transporter) {
|
|
console.log(`\n${'='.repeat(60)}`);
|
|
console.log(`MAGIC LINK (${type}) für ${email}:`);
|
|
console.log(url);
|
|
console.log(`${'='.repeat(60)}\n`);
|
|
return { sent: false, consoleFallback: true, url };
|
|
}
|
|
|
|
try {
|
|
await transporter.sendMail({
|
|
from: process.env.SMTP_FROM || process.env.SMTP_USER,
|
|
to: email,
|
|
subject,
|
|
html,
|
|
});
|
|
return { sent: true };
|
|
} catch (err) {
|
|
console.error('Email senden fehlgeschlagen:', err.message);
|
|
console.log(`Fallback — Magic Link für ${email}: ${url}`);
|
|
return { sent: false, error: err.message, url };
|
|
}
|
|
}
|
|
|
|
export async function sendCredentials(email, password, name, baseUrl) {
|
|
const title = 'Dein Account wurde erstellt';
|
|
const content = `<p>Hallo${name ? ' ' + name : ''},</p>
|
|
<p>Ein Admin hat ein Konto für dich erstellt. Hier sind deine Zugangsdaten:</p>
|
|
<table style="margin:16px 0;border-collapse:collapse">
|
|
<tr><td style="padding:6px 14px;font-weight:600;color:#666;border-bottom:1px solid #eee">E-Mail</td><td style="padding:6px 14px;border-bottom:1px solid #eee">${email}</td></tr>
|
|
<tr><td style="padding:6px 14px;font-weight:600;color:#666;border-bottom:1px solid #eee">Passwort</td><td style="padding:6px 14px;border-bottom:1px solid #eee;font-family:monospace;font-size:15px;letter-spacing:1px">${password}</td></tr>
|
|
</table>
|
|
<p>Bitte ändere dein Passwort nach dem ersten Login.</p>`;
|
|
const buttonText = 'Jetzt einloggen';
|
|
const buttonUrl = baseUrl + '/login.html';
|
|
|
|
const html = brandedHtml(title, content, buttonText, buttonUrl);
|
|
const subject2 = 'Vendoo — Dein Account wurde erstellt';
|
|
|
|
if (!transporter) {
|
|
console.log(`\n${'='.repeat(60)}`);
|
|
console.log(`CREDENTIALS für ${email}:`);
|
|
console.log(`Passwort: ${password}`);
|
|
console.log(`Login: ${baseUrl}/login.html`);
|
|
console.log(`${'='.repeat(60)}\n`);
|
|
return { sent: false, consoleFallback: true };
|
|
}
|
|
|
|
try {
|
|
await transporter.sendMail({
|
|
from: process.env.SMTP_FROM || process.env.SMTP_USER,
|
|
to: email,
|
|
subject: subject2,
|
|
html,
|
|
});
|
|
return { sent: true };
|
|
} catch (err) {
|
|
console.error('Credentials email failed:', err.message);
|
|
console.log(`Fallback — Credentials für ${email}: Passwort=${password}`);
|
|
return { sent: false, error: err.message };
|
|
}
|
|
}
|
|
|
|
export async function sendNotificationEmail(email, subject, title, content) {
|
|
const html = brandedHtml(title, content, null, null);
|
|
|
|
if (!transporter) {
|
|
console.log(`Notification for ${email}: ${subject}`);
|
|
return { sent: false };
|
|
}
|
|
|
|
try {
|
|
await transporter.sendMail({
|
|
from: process.env.SMTP_FROM || process.env.SMTP_USER,
|
|
to: email,
|
|
subject: `Vendoo — ${subject}`,
|
|
html,
|
|
});
|
|
return { sent: true };
|
|
} catch (err) {
|
|
console.error('Notification email failed:', err.message);
|
|
return { sent: false, error: err.message };
|
|
}
|
|
}
|
|
|
|
export function isMailerConfigured() {
|
|
return transporter !== null;
|
|
}
|
|
|
|
export function reinitMailer(config) {
|
|
if (!config || !config.host || !config.user || !config.pass) {
|
|
transporter = null;
|
|
return false;
|
|
}
|
|
const port = parseInt(config.port || '587');
|
|
transporter = nodemailer.createTransport({
|
|
host: config.host,
|
|
port,
|
|
secure: port === 465,
|
|
auth: { user: config.user, pass: config.pass },
|
|
tls: { rejectUnauthorized: !/^(0|false|no)$/i.test(String(process.env.SMTP_TLS_REJECT_UNAUTHORIZED || 'true')) },
|
|
});
|
|
return true;
|
|
}
|
|
|
|
export function getMailerConfig() {
|
|
if (!transporter || !transporter.options) {
|
|
return {
|
|
host: process.env.SMTP_HOST || '',
|
|
port: process.env.SMTP_PORT || '587',
|
|
user: process.env.SMTP_USER || '',
|
|
pass: process.env.SMTP_PASS ? '••••••••' : '',
|
|
from: process.env.SMTP_FROM || process.env.SMTP_USER || '',
|
|
configured: transporter !== null,
|
|
};
|
|
}
|
|
const opts = transporter.options;
|
|
return {
|
|
host: opts.host || '',
|
|
port: String(opts.port || '587'),
|
|
user: opts.auth?.user || '',
|
|
pass: opts.auth?.pass ? '••••••••' : '',
|
|
from: process.env.SMTP_FROM || opts.auth?.user || '',
|
|
configured: true,
|
|
};
|
|
}
|
|
|
|
export async function testMailer(toEmail) {
|
|
if (!transporter) throw new Error('SMTP nicht konfiguriert');
|
|
const html = `<!DOCTYPE html>
|
|
<html><head><meta charset="utf-8"></head>
|
|
<body style="font-family:system-ui,sans-serif;padding:20px">
|
|
<h2 style="color:#4a7c59">Vendoo — SMTP Test</h2>
|
|
<p>Diese E-Mail bestätigt, dass deine SMTP-Konfiguration korrekt funktioniert.</p>
|
|
<p style="color:#999;font-size:12px">Gesendet: ${new Date().toLocaleString('de-DE')}</p>
|
|
</body></html>`;
|
|
|
|
await transporter.sendMail({
|
|
from: process.env.SMTP_FROM || transporter.options?.auth?.user,
|
|
to: toEmail,
|
|
subject: 'Vendoo — SMTP Testmail',
|
|
html,
|
|
});
|
|
return { sent: true };
|
|
}
|