Initial commit: CMS backend for Old Vine Hotel

- Complete Express.js API server
- MongoDB integration with Mongoose
- Admin authentication and authorization
- Room management (CRUD operations)
- Booking management system
- Guest management
- Payment processing (Stripe integration)
- Content management (pages, blog, gallery)
- Media upload and management
- Integration services (Booking.com, Expedia, Opera PMS, Trip.com)
- Email notifications
- Comprehensive logging and error handling
This commit is contained in:
Talal Sharabi
2026-01-06 12:21:56 +04:00
commit a3308a26e2
48 changed files with 15294 additions and 0 deletions

284
utils/sendEmail.js Normal file
View File

@@ -0,0 +1,284 @@
const nodemailer = require('nodemailer');
const logger = require('./logger');
// Create transporter
const createTransporter = () => {
return nodemailer.createTransporter({
host: process.env.EMAIL_HOST,
port: process.env.EMAIL_PORT,
secure: false, // true for 465, false for other ports
auth: {
user: process.env.EMAIL_USER,
pass: process.env.EMAIL_PASS,
},
tls: {
rejectUnauthorized: false
}
});
};
// Email templates
const generateBookingConfirmationHTML = (context) => {
const { guest, booking, room } = context;
return `
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
body { font-family: Arial, sans-serif; line-height: 1.6; color: #333; }
.header { background: #8B4513; color: white; padding: 20px; text-align: center; }
.content { padding: 20px; }
.booking-details { background: #f9f9f9; padding: 15px; border-radius: 5px; margin: 20px 0; }
.footer { background: #f4f4f4; padding: 20px; text-align: center; font-size: 12px; }
.btn { background: #D4AF37; color: white; padding: 10px 20px; text-decoration: none; border-radius: 5px; }
</style>
</head>
<body>
<div class="header">
<h1>The Old Vine Hotel</h1>
<h2>Booking Confirmation</h2>
</div>
<div class="content">
<p>Dear ${guest.firstName} ${guest.lastName},</p>
<p>Thank you for choosing The Old Vine Hotel! We're delighted to confirm your reservation.</p>
<div class="booking-details">
<h3>Booking Details</h3>
<p><strong>Booking Number:</strong> ${booking.bookingNumber}</p>
<p><strong>Confirmation Code:</strong> ${booking.confirmationCode}</p>
<p><strong>Room:</strong> ${room.name} (${room.type})</p>
<p><strong>Check-in:</strong> ${booking.checkInDate.toLocaleDateString()} (3:00 PM)</p>
<p><strong>Check-out:</strong> ${booking.checkOutDate.toLocaleDateString()} (11:00 AM)</p>
<p><strong>Guests:</strong> ${booking.numberOfGuests.adults} Adult(s)${booking.numberOfGuests.children ? `, ${booking.numberOfGuests.children} Child(ren)` : ''}</p>
<p><strong>Nights:</strong> ${booking.numberOfNights}</p>
<p><strong>Total Amount:</strong> $${booking.totalAmount.toFixed(2)}</p>
</div>
${booking.specialRequests ? `
<div class="booking-details">
<h3>Special Requests</h3>
<p>${booking.specialRequests}</p>
</div>
` : ''}
<h3>What to Expect</h3>
<ul>
<li>Luxury accommodations with premium amenities</li>
<li>24/7 concierge service</li>
<li>Complimentary WiFi throughout the hotel</li>
<li>Fine dining restaurant and bar</li>
<li>Spa and fitness center access</li>
</ul>
<h3>Hotel Information</h3>
<p>
<strong>Address:</strong> 123 Luxury Avenue, Downtown District, City, State 12345<br>
<strong>Phone:</strong> +1 (555) 123-4567<br>
<strong>Email:</strong> info@oldvinehotel.com
</p>
<p>If you need to modify or cancel your reservation, please contact us at least 24 hours in advance.</p>
<p>We look forward to welcoming you to The Old Vine Hotel!</p>
<p>Warm regards,<br>
The Old Vine Hotel Team</p>
</div>
<div class="footer">
<p>&copy; 2025 The Old Vine Hotel. All rights reserved.</p>
<p>This is an automated message. Please do not reply to this email.</p>
</div>
</body>
</html>
`;
};
const generateBookingCancellationHTML = (context) => {
const { guest, booking, room, cancellationFee, refundAmount } = context;
return `
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
body { font-family: Arial, sans-serif; line-height: 1.6; color: #333; }
.header { background: #8B4513; color: white; padding: 20px; text-align: center; }
.content { padding: 20px; }
.booking-details { background: #f9f9f9; padding: 15px; border-radius: 5px; margin: 20px 0; }
.footer { background: #f4f4f4; padding: 20px; text-align: center; font-size: 12px; }
</style>
</head>
<body>
<div class="header">
<h1>The Old Vine Hotel</h1>
<h2>Booking Cancellation</h2>
</div>
<div class="content">
<p>Dear ${guest.firstName} ${guest.lastName},</p>
<p>We have processed your cancellation request for the following booking:</p>
<div class="booking-details">
<h3>Cancelled Booking Details</h3>
<p><strong>Booking Number:</strong> ${booking.bookingNumber}</p>
<p><strong>Room:</strong> ${room.name}</p>
<p><strong>Check-in Date:</strong> ${booking.checkInDate.toLocaleDateString()}</p>
<p><strong>Check-out Date:</strong> ${booking.checkOutDate.toLocaleDateString()}</p>
<p><strong>Original Amount:</strong> $${booking.totalAmount.toFixed(2)}</p>
${cancellationFee > 0 ? `<p><strong>Cancellation Fee:</strong> $${cancellationFee.toFixed(2)}</p>` : ''}
<p><strong>Refund Amount:</strong> $${refundAmount.toFixed(2)}</p>
</div>
${refundAmount > 0 ? `
<p>Your refund of $${refundAmount.toFixed(2)} will be processed within 5-7 business days and will appear on your original payment method.</p>
` : ''}
<p>We're sorry to see you cancel your stay with us. We hope to welcome you to The Old Vine Hotel in the future.</p>
<p>If you have any questions about this cancellation, please don't hesitate to contact us.</p>
<p>Best regards,<br>
The Old Vine Hotel Team</p>
</div>
<div class="footer">
<p>&copy; 2025 The Old Vine Hotel. All rights reserved.</p>
</div>
</body>
</html>
`;
};
const generateContactFormHTML = (context) => {
const { name, email, phone, message } = context;
return `
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
body { font-family: Arial, sans-serif; line-height: 1.6; color: #333; }
.header { background: #8B4513; color: white; padding: 20px; text-align: center; }
.content { padding: 20px; }
.details { background: #f9f9f9; padding: 15px; border-radius: 5px; margin: 20px 0; }
</style>
</head>
<body>
<div class="header">
<h1>The Old Vine Hotel</h1>
<h2>New Contact Form Submission</h2>
</div>
<div class="content">
<h3>Contact Details</h3>
<div class="details">
<p><strong>Name:</strong> ${name}</p>
<p><strong>Email:</strong> ${email}</p>
<p><strong>Phone:</strong> ${phone || 'Not provided'}</p>
</div>
<h3>Message</h3>
<div class="details">
<p>${message}</p>
</div>
<p><em>This message was sent from the hotel website contact form.</em></p>
</div>
</body>
</html>
`;
};
// Main send email function
const sendEmail = async ({ to, subject, template, context, html, text }) => {
try {
const transporter = createTransporter();
let emailHTML = html;
// Generate HTML based on template
if (template && context) {
switch (template) {
case 'bookingConfirmation':
emailHTML = generateBookingConfirmationHTML(context);
break;
case 'bookingCancellation':
emailHTML = generateBookingCancellationHTML(context);
break;
case 'contactForm':
emailHTML = generateContactFormHTML(context);
break;
default:
throw new Error(`Unknown email template: ${template}`);
}
}
const mailOptions = {
from: `"The Old Vine Hotel" <${process.env.EMAIL_FROM}>`,
to,
subject,
html: emailHTML,
text: text || '', // Plain text version
};
const result = await transporter.sendMail(mailOptions);
logger.info(`Email sent successfully to ${to}`, {
messageId: result.messageId,
subject
});
return result;
} catch (error) {
logger.error('Email sending error:', {
error: error.message,
to,
subject,
template
});
throw new Error(`Failed to send email: ${error.message}`);
}
};
// Send bulk emails
const sendBulkEmails = async (emails) => {
const results = [];
for (const emailData of emails) {
try {
const result = await sendEmail(emailData);
results.push({ success: true, to: emailData.to, messageId: result.messageId });
} catch (error) {
results.push({ success: false, to: emailData.to, error: error.message });
}
}
return results;
};
// Send newsletter
const sendNewsletter = async (subscribers, subject, content) => {
const emails = subscribers.map(subscriber => ({
to: subscriber.email,
subject,
html: content,
template: null
}));
return sendBulkEmails(emails);
};
module.exports = {
sendEmail,
sendBulkEmails,
sendNewsletter
};