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

347
routes/integrations.js Normal file
View File

@@ -0,0 +1,347 @@
const express = require('express');
const router = express.Router();
const OperaPMSService = require('../services/OperaPMSService');
const BookingComService = require('../services/BookingComService');
const TripComService = require('../services/TripComService');
const ExpediaService = require('../services/ExpediaService');
const adminAuth = require('../middleware/adminAuth');
const logger = require('../utils/logger');
// Initialize services
const operaPMS = new OperaPMSService();
const bookingCom = new BookingComService();
const tripCom = new TripComService();
const expedia = new ExpediaService();
// @route GET /api/integrations/health
// @desc Check health of all integrations
// @access Private/Admin
router.get('/health', adminAuth, async (req, res) => {
try {
const healthChecks = await Promise.allSettled([
operaPMS.healthCheck(),
bookingCom.healthCheck(),
tripCom.healthCheck(),
expedia.healthCheck()
]);
const results = {
operaPMS: healthChecks[0].status === 'fulfilled' ? healthChecks[0].value : { status: 'error', error: healthChecks[0].reason.message },
bookingCom: healthChecks[1].status === 'fulfilled' ? healthChecks[1].value : { status: 'error', error: healthChecks[1].reason.message },
tripCom: healthChecks[2].status === 'fulfilled' ? healthChecks[2].value : { status: 'error', error: healthChecks[2].reason.message },
expedia: healthChecks[3].status === 'fulfilled' ? healthChecks[3].value : { status: 'error', error: healthChecks[3].reason.message }
};
const overallStatus = Object.values(results).every(result => result.status === 'connected') ? 'healthy' : 'partial';
res.json({
success: true,
data: {
overallStatus,
services: results,
timestamp: new Date().toISOString()
}
});
} catch (error) {
logger.error('Integration health check failed:', error);
res.status(500).json({
success: false,
message: 'Failed to check integration health'
});
}
});
// @route POST /api/integrations/opera/sync-rooms
// @desc Sync room inventory with Opera PMS
// @access Private/Admin
router.post('/opera/sync-rooms', adminAuth, async (req, res) => {
try {
const result = await operaPMS.syncRoomInventory();
logger.integrationLog('Room inventory sync completed', {
admin: req.admin.email,
result
});
res.json({
success: true,
message: 'Room inventory synchronized successfully',
data: result
});
} catch (error) {
logger.error('Room inventory sync failed:', error);
res.status(500).json({
success: false,
message: 'Failed to sync room inventory'
});
}
});
// @route POST /api/integrations/opera/create-reservation
// @desc Create reservation in Opera PMS
// @access Private (called internally)
router.post('/opera/create-reservation', async (req, res) => {
try {
const { booking } = req.body;
const result = await operaPMS.createReservation(booking);
res.json({
success: true,
data: result
});
} catch (error) {
logger.error('Opera PMS reservation creation failed:', error);
res.status(500).json({
success: false,
message: 'Failed to create reservation in Opera PMS'
});
}
});
// @route POST /api/integrations/sync-rates
// @desc Sync rates across all platforms
// @access Private/Admin
router.post('/sync-rates', adminAuth, async (req, res) => {
try {
const { roomType, rates, dates } = req.body;
// Sync rates to all platforms
const syncPromises = [
bookingCom.updateRates(roomType, rates, dates),
tripCom.updateRates(roomType, rates, dates),
expedia.updateRates(roomType, rates, dates)
];
const results = await Promise.allSettled(syncPromises);
const syncResults = {
bookingCom: results[0].status === 'fulfilled' ? results[0].value : { error: results[0].reason.message },
tripCom: results[1].status === 'fulfilled' ? results[1].value : { error: results[1].reason.message },
expedia: results[2].status === 'fulfilled' ? results[2].value : { error: results[2].reason.message }
};
logger.integrationLog('Rate sync completed', {
admin: req.admin.email,
roomType,
dates,
results: syncResults
});
res.json({
success: true,
message: 'Rates synchronized across platforms',
data: syncResults
});
} catch (error) {
logger.error('Rate sync failed:', error);
res.status(500).json({
success: false,
message: 'Failed to sync rates'
});
}
});
// @route POST /api/integrations/sync-availability
// @desc Sync availability across all platforms
// @access Private/Admin
router.post('/sync-availability', adminAuth, async (req, res) => {
try {
const { roomType, availability, dates } = req.body;
// Sync availability to all platforms
const syncPromises = [
bookingCom.updateAvailability(roomType, availability, dates),
tripCom.updateAvailability(roomType, availability, dates),
expedia.updateAvailability(roomType, availability, dates)
];
const results = await Promise.allSettled(syncPromises);
const syncResults = {
bookingCom: results[0].status === 'fulfilled' ? results[0].value : { error: results[0].reason.message },
tripCom: results[1].status === 'fulfilled' ? results[1].value : { error: results[1].reason.message },
expedia: results[2].status === 'fulfilled' ? results[2].value : { error: results[2].reason.message }
};
logger.integrationLog('Availability sync completed', {
admin: req.admin.email,
roomType,
dates,
results: syncResults
});
res.json({
success: true,
message: 'Availability synchronized across platforms',
data: syncResults
});
} catch (error) {
logger.error('Availability sync failed:', error);
res.status(500).json({
success: false,
message: 'Failed to sync availability'
});
}
});
// @route GET /api/integrations/bookings/external
// @desc Get bookings from external platforms
// @access Private/Admin
router.get('/bookings/external', adminAuth, async (req, res) => {
try {
const { startDate, endDate } = req.query;
const start = new Date(startDate || Date.now() - 7 * 24 * 60 * 60 * 1000);
const end = new Date(endDate || Date.now() + 30 * 24 * 60 * 60 * 1000);
// Fetch bookings from all platforms
const bookingPromises = [
bookingCom.getBookings(start, end),
tripCom.getBookings(start, end),
expedia.getBookings(start, end)
];
const results = await Promise.allSettled(bookingPromises);
const externalBookings = {
bookingCom: results[0].status === 'fulfilled' ? results[0].value : [],
tripCom: results[1].status === 'fulfilled' ? results[1].value : [],
expedia: results[2].status === 'fulfilled' ? results[2].value : []
};
// Combine all bookings
const allBookings = [
...externalBookings.bookingCom.map(b => ({ ...b, source: 'Booking.com' })),
...externalBookings.tripCom.map(b => ({ ...b, source: 'Trip.com' })),
...externalBookings.expedia.map(b => ({ ...b, source: 'Expedia' }))
];
res.json({
success: true,
data: {
bookings: allBookings,
summary: {
total: allBookings.length,
byPlatform: {
bookingCom: externalBookings.bookingCom.length,
tripCom: externalBookings.tripCom.length,
expedia: externalBookings.expedia.length
}
}
}
});
} catch (error) {
logger.error('External bookings fetch failed:', error);
res.status(500).json({
success: false,
message: 'Failed to fetch external bookings'
});
}
});
// @route POST /api/integrations/webhook/booking-com
// @desc Handle Booking.com webhooks
// @access Public (webhook)
router.post('/webhook/booking-com', async (req, res) => {
try {
const webhookData = req.body;
logger.integrationLog('Booking.com webhook received', {
type: webhookData.event_type,
bookingId: webhookData.booking_id
});
// Process webhook based on event type
switch (webhookData.event_type) {
case 'booking_created':
await bookingCom.processNewBooking(webhookData);
break;
case 'booking_modified':
await bookingCom.processBookingModification(webhookData);
break;
case 'booking_cancelled':
await bookingCom.processBookingCancellation(webhookData);
break;
default:
logger.integrationLog('Unknown Booking.com webhook event', {
type: webhookData.event_type
});
}
res.json({ success: true, message: 'Webhook processed successfully' });
} catch (error) {
logger.error('Booking.com webhook processing failed:', error);
res.status(500).json({
success: false,
message: 'Webhook processing failed'
});
}
});
// @route POST /api/integrations/webhook/expedia
// @desc Handle Expedia webhooks
// @access Public (webhook)
router.post('/webhook/expedia', async (req, res) => {
try {
const webhookData = req.body;
logger.integrationLog('Expedia webhook received', {
type: webhookData.event_type,
bookingId: webhookData.booking_id
});
// Process Expedia webhook
await expedia.processWebhook(webhookData);
res.json({ success: true, message: 'Webhook processed successfully' });
} catch (error) {
logger.error('Expedia webhook processing failed:', error);
res.status(500).json({
success: false,
message: 'Webhook processing failed'
});
}
});
// @route GET /api/integrations/analytics/platform-performance
// @desc Get performance analytics for all platforms
// @access Private/Admin
router.get('/analytics/platform-performance', adminAuth, async (req, res) => {
try {
const { startDate, endDate } = req.query;
const start = new Date(startDate || Date.now() - 30 * 24 * 60 * 60 * 1000);
const end = new Date(endDate || Date.now());
// Get performance data from all platforms
const performancePromises = [
bookingCom.getPerformanceData(start, end),
tripCom.getPerformanceData(start, end),
expedia.getPerformanceData(start, end)
];
const results = await Promise.allSettled(performancePromises);
const performanceData = {
bookingCom: results[0].status === 'fulfilled' ? results[0].value : null,
tripCom: results[1].status === 'fulfilled' ? results[1].value : null,
expedia: results[2].status === 'fulfilled' ? results[2].value : null
};
res.json({
success: true,
data: {
dateRange: { start, end },
platforms: performanceData
}
});
} catch (error) {
logger.error('Platform performance analytics failed:', error);
res.status(500).json({
success: false,
message: 'Failed to fetch platform performance data'
});
}
});
module.exports = router;