Initial commit - SaveTheMoment Atlas Basis-Setup
This commit is contained in:
295
lib/email-service.ts
Normal file
295
lib/email-service.ts
Normal file
@@ -0,0 +1,295 @@
|
||||
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,
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user