296 lines
8.0 KiB
TypeScript
296 lines
8.0 KiB
TypeScript
import nodemailer from 'nodemailer';
|
|
import path from 'path';
|
|
import { readFile } from 'fs/promises';
|
|
|
|
let transporter: nodemailer.Transporter | null = null;
|
|
|
|
function getTransporter() {
|
|
if (transporter) return transporter;
|
|
|
|
const smtpHost = process.env.SMTP_HOST;
|
|
const smtpPort = parseInt(process.env.SMTP_PORT || '587');
|
|
const smtpUser = process.env.SMTP_USER;
|
|
const smtpPass = process.env.SMTP_PASS;
|
|
const smtpFrom = process.env.SMTP_FROM || 'noreply@savethemoment.photos';
|
|
|
|
if (!smtpHost || !smtpUser || !smtpPass) {
|
|
console.warn('⚠️ SMTP credentials not configured. Email sending disabled.');
|
|
throw new Error('SMTP not configured');
|
|
}
|
|
|
|
transporter = nodemailer.createTransport({
|
|
host: smtpHost,
|
|
port: smtpPort,
|
|
secure: smtpPort === 465,
|
|
auth: {
|
|
user: smtpUser,
|
|
pass: smtpPass,
|
|
},
|
|
});
|
|
|
|
console.log('✅ SMTP transporter initialized');
|
|
return transporter;
|
|
}
|
|
|
|
interface SendEmailOptions {
|
|
to: string;
|
|
subject: string;
|
|
text: string;
|
|
html: string;
|
|
attachments?: {
|
|
filename: string;
|
|
content?: Buffer;
|
|
path?: string;
|
|
}[];
|
|
}
|
|
|
|
export async function sendEmail(options: SendEmailOptions) {
|
|
try {
|
|
const transport = getTransporter();
|
|
const from = process.env.SMTP_FROM || 'SaveTheMoment <noreply@savethemoment.photos>';
|
|
|
|
const info = await transport.sendMail({
|
|
from,
|
|
to: options.to,
|
|
subject: options.subject,
|
|
text: options.text,
|
|
html: options.html,
|
|
attachments: options.attachments,
|
|
});
|
|
|
|
console.log('✅ Email sent:', info.messageId);
|
|
return { success: true, messageId: info.messageId };
|
|
} catch (error: any) {
|
|
console.error('❌ Email send error:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
export async function sendContractEmail(
|
|
booking: any,
|
|
contractPdfPath: string
|
|
) {
|
|
const signToken = Buffer.from(`${booking.id}-${Date.now()}`).toString('base64url');
|
|
const signUrl = `${process.env.NEXTAUTH_URL}/contract/sign/${signToken}`;
|
|
|
|
const subject = `Ihr Mietvertrag für ${booking.eventLocation || 'Ihr Event'}`;
|
|
|
|
const html = `
|
|
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<style>
|
|
body {
|
|
font-family: Arial, sans-serif;
|
|
line-height: 1.6;
|
|
color: #333;
|
|
max-width: 600px;
|
|
margin: 0 auto;
|
|
padding: 20px;
|
|
}
|
|
.header {
|
|
background: linear-gradient(135deg, #DC2626 0%, #EC4899 100%);
|
|
color: white;
|
|
padding: 30px;
|
|
text-align: center;
|
|
border-radius: 10px 10px 0 0;
|
|
}
|
|
.content {
|
|
background: #f9fafb;
|
|
padding: 30px;
|
|
border-radius: 0 0 10px 10px;
|
|
}
|
|
.button {
|
|
display: inline-block;
|
|
background: linear-gradient(135deg, #DC2626 0%, #EC4899 100%);
|
|
color: white;
|
|
padding: 15px 30px;
|
|
text-decoration: none;
|
|
border-radius: 8px;
|
|
margin: 20px 0;
|
|
font-weight: bold;
|
|
}
|
|
.details {
|
|
background: white;
|
|
padding: 20px;
|
|
border-radius: 8px;
|
|
margin: 20px 0;
|
|
border-left: 4px solid #DC2626;
|
|
}
|
|
.footer {
|
|
text-align: center;
|
|
color: #666;
|
|
font-size: 12px;
|
|
margin-top: 30px;
|
|
padding-top: 20px;
|
|
border-top: 1px solid #ddd;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="header">
|
|
<h1>🎉 SaveTheMoment</h1>
|
|
<p>Ihr Mietvertrag ist bereit!</p>
|
|
</div>
|
|
|
|
<div class="content">
|
|
<p>Hallo ${booking.customerName},</p>
|
|
|
|
<p>vielen Dank für Ihre Buchung bei SaveTheMoment! Wir freuen uns sehr, Teil Ihres besonderen Anlasses zu sein.</p>
|
|
|
|
<div class="details">
|
|
<h3>📋 Buchungsdetails</h3>
|
|
<p><strong>Buchungsnummer:</strong> ${booking.bookingNumber}</p>
|
|
<p><strong>Event-Datum:</strong> ${new Date(booking.eventDate).toLocaleDateString('de-DE', {
|
|
day: '2-digit',
|
|
month: 'long',
|
|
year: 'numeric'
|
|
})}</p>
|
|
<p><strong>Location:</strong> ${booking.eventLocation || booking.eventAddress}</p>
|
|
<p><strong>Fotobox:</strong> ${booking.photobox?.model || 'N/A'}</p>
|
|
</div>
|
|
|
|
<p>Im Anhang finden Sie Ihren Mietvertrag als PDF-Datei.</p>
|
|
|
|
<p><strong>Nächste Schritte:</strong></p>
|
|
<ol>
|
|
<li>Bitte lesen Sie den Vertrag sorgfältig durch</li>
|
|
<li>Signieren Sie den Vertrag online oder drucken Sie ihn aus und senden Sie ihn zurück</li>
|
|
<li>Nach Erhalt der Unterschrift ist Ihre Buchung verbindlich bestätigt</li>
|
|
</ol>
|
|
|
|
<center>
|
|
<a href="${signUrl}" class="button">
|
|
✍️ Vertrag online signieren
|
|
</a>
|
|
</center>
|
|
|
|
<p>Alternativ können Sie den Vertrag auch ausdrucken, unterschreiben und uns per E-Mail oder Post zurücksenden.</p>
|
|
|
|
<p>Bei Fragen stehen wir Ihnen jederzeit gerne zur Verfügung!</p>
|
|
|
|
<p>Mit freundlichen Grüßen<br>
|
|
Ihr SaveTheMoment Team</p>
|
|
</div>
|
|
|
|
<div class="footer">
|
|
<p>SaveTheMoment Fotoboxen<br>
|
|
E-Mail: info@savethemoment.photos<br>
|
|
Web: www.savethemoment.photos</p>
|
|
<p style="color: #999; font-size: 11px;">
|
|
Diese E-Mail wurde automatisch generiert. Bitte antworten Sie nicht direkt auf diese E-Mail.
|
|
</p>
|
|
</div>
|
|
</body>
|
|
</html>
|
|
`.trim();
|
|
|
|
const text = `
|
|
Hallo ${booking.customerName},
|
|
|
|
vielen Dank für Ihre Buchung bei SaveTheMoment!
|
|
|
|
Buchungsdetails:
|
|
- Buchungsnummer: ${booking.bookingNumber}
|
|
- Event-Datum: ${new Date(booking.eventDate).toLocaleDateString('de-DE')}
|
|
- Location: ${booking.eventLocation || booking.eventAddress}
|
|
|
|
Im Anhang finden Sie Ihren Mietvertrag als PDF-Datei.
|
|
|
|
Sie können den Vertrag online signieren unter:
|
|
${signUrl}
|
|
|
|
Oder drucken Sie ihn aus und senden Sie ihn uns zurück.
|
|
|
|
Bei Fragen stehen wir Ihnen jederzeit zur Verfügung!
|
|
|
|
Mit freundlichen Grüßen
|
|
Ihr SaveTheMoment Team
|
|
|
|
---
|
|
SaveTheMoment Fotoboxen
|
|
E-Mail: info@savethemoment.photos
|
|
Web: www.savethemoment.photos
|
|
`.trim();
|
|
|
|
let pdfBuffer: Buffer;
|
|
try {
|
|
pdfBuffer = await readFile(path.join(process.cwd(), 'public', contractPdfPath));
|
|
} catch (error) {
|
|
console.error('Failed to read contract PDF:', error);
|
|
throw new Error('Contract PDF not found');
|
|
}
|
|
|
|
return sendEmail({
|
|
to: booking.customerEmail,
|
|
subject,
|
|
text,
|
|
html,
|
|
attachments: [
|
|
{
|
|
filename: `Mietvertrag_${booking.bookingNumber}.pdf`,
|
|
content: pdfBuffer,
|
|
},
|
|
],
|
|
});
|
|
}
|
|
|
|
export async function sendBookingConfirmationEmail(booking: any) {
|
|
const subject = `Buchungsbestätigung - ${booking.bookingNumber}`;
|
|
|
|
const html = `
|
|
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<style>
|
|
body { font-family: Arial, sans-serif; line-height: 1.6; color: #333; max-width: 600px; margin: 0 auto; padding: 20px; }
|
|
.header { background: linear-gradient(135deg, #DC2626 0%, #EC4899 100%); color: white; padding: 30px; text-align: center; border-radius: 10px 10px 0 0; }
|
|
.content { background: #f9fafb; padding: 30px; border-radius: 0 0 10px 10px; }
|
|
.details { background: white; padding: 20px; border-radius: 8px; margin: 20px 0; border-left: 4px solid #DC2626; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="header">
|
|
<h1>✅ Buchung bestätigt!</h1>
|
|
</div>
|
|
<div class="content">
|
|
<p>Hallo ${booking.customerName},</p>
|
|
<p>Ihre Buchung wurde erfolgreich bestätigt!</p>
|
|
<div class="details">
|
|
<h3>Buchungsdetails</h3>
|
|
<p><strong>Buchungsnummer:</strong> ${booking.bookingNumber}</p>
|
|
<p><strong>Event-Datum:</strong> ${new Date(booking.eventDate).toLocaleDateString('de-DE')}</p>
|
|
<p><strong>Location:</strong> ${booking.eventLocation || booking.eventAddress}</p>
|
|
</div>
|
|
<p>Wir freuen uns auf Ihr Event!</p>
|
|
<p>Mit freundlichen Grüßen<br>Ihr SaveTheMoment Team</p>
|
|
</div>
|
|
</body>
|
|
</html>
|
|
`;
|
|
|
|
const text = `
|
|
Hallo ${booking.customerName},
|
|
|
|
Ihre Buchung wurde erfolgreich bestätigt!
|
|
|
|
Buchungsnummer: ${booking.bookingNumber}
|
|
Event-Datum: ${new Date(booking.eventDate).toLocaleDateString('de-DE')}
|
|
Location: ${booking.eventLocation || booking.eventAddress}
|
|
|
|
Wir freuen uns auf Ihr Event!
|
|
|
|
Mit freundlichen Grüßen
|
|
Ihr SaveTheMoment Team
|
|
`;
|
|
|
|
return sendEmail({
|
|
to: booking.customerEmail,
|
|
subject,
|
|
text,
|
|
html,
|
|
});
|
|
}
|