Files
Atlas/lib/email-service.ts
2025-11-12 20:21:32 +01:00

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,
});
}