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:
347
routes/integrations.js
Normal file
347
routes/integrations.js
Normal 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;
|
||||
Reference in New Issue
Block a user