Initial commit: Static hotel website (CMS/backend excluded)

This commit is contained in:
Talal Sharabi
2025-12-17 13:34:35 +04:00
commit 2b0547b484
386 changed files with 29377 additions and 0 deletions

820
docs/DEPLOYMENT.md Normal file
View File

@@ -0,0 +1,820 @@
# Deployment Guide - The Old Vine Hotel
## 🚀 Production Deployment
This guide covers deploying The Old Vine Hotel website to production environments.
## 🏗️ Infrastructure Requirements
### Minimum Server Specifications
**Backend Server:**
- CPU: 2 vCPUs
- RAM: 4GB
- Storage: 50GB SSD
- OS: Ubuntu 20.04 LTS or CentOS 8
**Database Server:**
- CPU: 2 vCPUs
- RAM: 4GB
- Storage: 100GB SSD
- MongoDB 5.0+
**Recommended for High Traffic:**
- Load Balancer (nginx)
- Redis for caching
- CDN for static assets
- Multiple application instances
## 🐳 Docker Deployment (Recommended)
### 1. Build Production Images
```bash
# Clone repository
git clone <your-repo-url>
cd old-vine-hotel-website
# Build frontend
cd client
npm install
npm run build
# Build backend
cd ../server
npm install
```
### 2. Docker Configuration
**Frontend Dockerfile:**
```dockerfile
# client/Dockerfile
FROM node:16-alpine as builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM nginx:alpine
COPY --from=builder /app/build /usr/share/nginx/html
COPY nginx.conf /etc/nginx/nginx.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
```
**Backend Dockerfile:**
```dockerfile
# server/Dockerfile
FROM node:16-alpine
WORKDIR /app
# Install dependencies
COPY package*.json ./
RUN npm ci --only=production
# Copy source code
COPY . .
# Create logs directory
RUN mkdir -p logs
# Expose port
EXPOSE 5000
# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD curl -f http://localhost:5000/health || exit 1
CMD ["npm", "start"]
```
### 3. Production Docker Compose
```yaml
# docker-compose.prod.yml
version: '3.8'
services:
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
- ./nginx/ssl:/etc/nginx/ssl:ro
depends_on:
- frontend
- backend
restart: unless-stopped
frontend:
build:
context: ./client
dockerfile: Dockerfile
restart: unless-stopped
backend:
build:
context: ./server
dockerfile: Dockerfile
environment:
- NODE_ENV=production
- MONGODB_URI=${MONGODB_URI}
- JWT_SECRET=${JWT_SECRET}
- STRIPE_SECRET_KEY=${STRIPE_SECRET_KEY}
env_file:
- .env.production
volumes:
- ./logs:/app/logs
depends_on:
- mongodb
- redis
restart: unless-stopped
mongodb:
image: mongo:5.0
environment:
- MONGO_INITDB_ROOT_USERNAME=${MONGO_ROOT_USERNAME}
- MONGO_INITDB_ROOT_PASSWORD=${MONGO_ROOT_PASSWORD}
- MONGO_INITDB_DATABASE=oldvinehotel
volumes:
- mongodb_data:/data/db
- ./mongodb/mongod.conf:/etc/mongod.conf:ro
restart: unless-stopped
redis:
image: redis:7-alpine
command: redis-server --appendonly yes
volumes:
- redis_data:/data
restart: unless-stopped
volumes:
mongodb_data:
redis_data:
```
### 4. Nginx Configuration
```nginx
# nginx/nginx.conf
events {
worker_connections 1024;
}
http {
upstream backend {
server backend:5000;
}
upstream frontend {
server frontend:80;
}
# Rate limiting
limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
limit_req_zone $binary_remote_addr zone=login:10m rate=5r/m;
# SSL Configuration
server {
listen 80;
server_name oldvinehotel.com www.oldvinehotel.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name oldvinehotel.com www.oldvinehotel.com;
ssl_certificate /etc/nginx/ssl/fullchain.pem;
ssl_certificate_key /etc/nginx/ssl/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512;
# Security headers
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains";
# API routes
location /api/ {
limit_req zone=api burst=20 nodelay;
proxy_pass http://backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# Auth routes with stricter limits
location /api/auth/login {
limit_req zone=login burst=3 nodelay;
proxy_pass http://backend;
}
# Frontend
location / {
proxy_pass http://frontend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
# Static files caching
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
expires 1y;
add_header Cache-Control "public, no-transform";
proxy_pass http://frontend;
}
}
}
```
### 5. Environment Configuration
```bash
# .env.production
NODE_ENV=production
PORT=5000
# Database
MONGODB_URI=mongodb://admin:secure_password@mongodb:27017/oldvinehotel?authSource=admin
MONGO_ROOT_USERNAME=admin
MONGO_ROOT_PASSWORD=secure_password
# Security
JWT_SECRET=your_super_secure_jwt_secret_key_for_production
JWT_EXPIRES_IN=7d
# External APIs
STRIPE_SECRET_KEY=sk_live_your_live_stripe_key
STRIPE_WEBHOOK_SECRET=whsec_your_webhook_secret
GOOGLE_MAPS_API_KEY=your_production_maps_key
# Email
EMAIL_HOST=smtp.sendgrid.net
EMAIL_PORT=587
EMAIL_USER=apikey
EMAIL_PASS=your_sendgrid_api_key
EMAIL_FROM=noreply@oldvinehotel.com
# Hotel Information
HOTEL_NAME=The Old Vine Hotel
HOTEL_ADDRESS=123 Luxury Avenue, Downtown District, City, State 12345
HOTEL_PHONE=+1 (555) 123-4567
HOTEL_EMAIL=info@oldvinehotel.com
# Integration APIs
OPERA_PMS_URL=https://your-opera-pms.com/api
OPERA_PMS_USERNAME=production_user
OPERA_PMS_PASSWORD=production_password
BOOKING_COM_API_URL=https://distribution-xml.booking.com
BOOKING_COM_USERNAME=your_booking_username
BOOKING_COM_PASSWORD=your_booking_password
# Redis
REDIS_URL=redis://redis:6379
# Logging
LOG_LEVEL=info
```
### 6. Deploy with Docker Compose
```bash
# Create production environment file
cp .env.example .env.production
# Edit .env.production with your production values
# Deploy
docker-compose -f docker-compose.prod.yml up -d
# Check status
docker-compose -f docker-compose.prod.yml ps
# View logs
docker-compose -f docker-compose.prod.yml logs -f backend
```
## ☁️ Cloud Deployment Options
### AWS Deployment
#### Using AWS ECS
```bash
# Install AWS CLI and ECS CLI
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip awscliv2.zip
sudo ./aws/install
# Configure AWS credentials
aws configure
# Create ECS cluster
aws ecs create-cluster --cluster-name old-vine-hotel
# Create task definition
aws ecs register-task-definition --cli-input-json file://ecs-task-definition.json
# Create service
aws ecs create-service \
--cluster old-vine-hotel \
--service-name hotel-website \
--task-definition hotel-website:1 \
--desired-count 2
```
#### ECS Task Definition
```json
{
"family": "hotel-website",
"networkMode": "awsvpc",
"requiresCompatibilities": ["FARGATE"],
"cpu": "512",
"memory": "1024",
"executionRoleArn": "arn:aws:iam::account:role/ecsTaskExecutionRole",
"containerDefinitions": [
{
"name": "hotel-backend",
"image": "your-account.dkr.ecr.us-east-1.amazonaws.com/hotel-backend:latest",
"portMappings": [
{
"containerPort": 5000,
"protocol": "tcp"
}
],
"environment": [
{
"name": "NODE_ENV",
"value": "production"
}
],
"secrets": [
{
"name": "MONGODB_URI",
"valueFrom": "arn:aws:secretsmanager:us-east-1:account:secret:hotel/mongodb-uri"
}
],
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "/ecs/hotel-website",
"awslogs-region": "us-east-1",
"awslogs-stream-prefix": "ecs"
}
}
}
]
}
```
### Google Cloud Platform
```bash
# Install gcloud CLI
curl https://sdk.cloud.google.com | bash
exec -l $SHELL
gcloud init
# Create GKE cluster
gcloud container clusters create hotel-cluster \
--num-nodes=3 \
--zone=us-central1-a
# Build and push images
gcloud builds submit --tag gcr.io/PROJECT_ID/hotel-backend ./server
gcloud builds submit --tag gcr.io/PROJECT_ID/hotel-frontend ./client
# Deploy to GKE
kubectl apply -f k8s/
```
### DigitalOcean App Platform
```yaml
# .do/app.yaml
name: old-vine-hotel
services:
- name: backend
source_dir: /server
github:
repo: your-username/old-vine-hotel
branch: main
run_command: npm start
environment_slug: node-js
instance_count: 2
instance_size_slug: basic-xxs
env:
- key: NODE_ENV
value: production
- key: MONGODB_URI
value: ${DB_CONNECTION_STRING}
type: SECRET
- name: frontend
source_dir: /client
github:
repo: your-username/old-vine-hotel
branch: main
build_command: npm run build
environment_slug: node-js
instance_count: 1
instance_size_slug: basic-xxs
routes:
- path: /
databases:
- name: hotel-db
engine: MONGODB
version: "5"
```
## 🔧 Manual Server Deployment
### 1. Server Setup
```bash
# Update system
sudo apt update && sudo apt upgrade -y
# Install Node.js
curl -fsSL https://deb.nodesource.com/setup_16.x | sudo -E bash -
sudo apt-get install -y nodejs
# Install MongoDB
wget -qO - https://www.mongodb.org/static/pgp/server-5.0.asc | sudo apt-key add -
echo "deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu focal/mongodb-org/5.0 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-5.0.list
sudo apt-get update
sudo apt-get install -y mongodb-org
# Install PM2 for process management
npm install -g pm2
# Install nginx
sudo apt install nginx
```
### 2. Application Deployment
```bash
# Clone repository
git clone <your-repo-url> /var/www/hotel
cd /var/www/hotel
# Install backend dependencies
cd server
npm install --production
# Install frontend dependencies and build
cd ../client
npm install
npm run build
# Copy build to nginx directory
sudo cp -r build/* /var/www/html/
```
### 3. PM2 Configuration
```javascript
// ecosystem.config.js
module.exports = {
apps: [
{
name: 'hotel-backend',
script: './server/index.js',
instances: 'max',
exec_mode: 'cluster',
env: {
NODE_ENV: 'production',
PORT: 5000
},
error_file: './logs/err.log',
out_file: './logs/out.log',
log_file: './logs/combined.log',
time: true
}
]
};
```
```bash
# Start application with PM2
pm2 start ecosystem.config.js
# Save PM2 configuration
pm2 save
# Setup PM2 startup
pm2 startup
sudo env PATH=$PATH:/usr/bin /usr/lib/node_modules/pm2/bin/pm2 startup systemd -u $USER --hp $HOME
```
### 4. Nginx Configuration
```nginx
# /etc/nginx/sites-available/hotel
server {
listen 80;
server_name oldvinehotel.com www.oldvinehotel.com;
root /var/www/html;
index index.html;
# API proxy
location /api/ {
proxy_pass http://localhost:5000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache_bypass $http_upgrade;
}
# Frontend
location / {
try_files $uri $uri/ /index.html;
}
# Static files
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
expires 1y;
add_header Cache-Control "public, no-transform";
}
}
```
```bash
# Enable site
sudo ln -s /etc/nginx/sites-available/hotel /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
```
## 🔐 SSL Certificate Setup
### Using Let's Encrypt (Certbot)
```bash
# Install Certbot
sudo apt install certbot python3-certbot-nginx
# Get SSL certificate
sudo certbot --nginx -d oldvinehotel.com -d www.oldvinehotel.com
# Test automatic renewal
sudo certbot renew --dry-run
```
### Manual SSL Certificate
```nginx
# Add to nginx configuration
server {
listen 443 ssl http2;
server_name oldvinehotel.com;
ssl_certificate /etc/ssl/certs/oldvinehotel.com.crt;
ssl_certificate_key /etc/ssl/private/oldvinehotel.com.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512;
ssl_prefer_server_ciphers off;
# ... rest of configuration
}
```
## 📊 Monitoring Setup
### Application Monitoring
```bash
# Install monitoring tools
npm install -g clinic
npm install newrelic
# Setup log rotation
sudo nano /etc/logrotate.d/hotel
```
```
# /etc/logrotate.d/hotel
/var/www/hotel/logs/*.log {
daily
missingok
rotate 52
compress
delaycompress
notifempty
create 644 www-data www-data
postrotate
pm2 reload hotel-backend
endscript
}
```
### Database Monitoring
```bash
# Enable MongoDB profiler
mongo
use oldvinehotel
db.setProfilingLevel(1, { slowms: 100 })
# Setup monitoring script
crontab -e
```
```bash
# Monitor disk space every hour
0 * * * * /usr/local/bin/check-disk-space.sh
# Backup database daily at 2 AM
0 2 * * * /usr/local/bin/backup-mongodb.sh
```
## 🔄 Backup Strategy
### Database Backup
```bash
#!/bin/bash
# backup-mongodb.sh
DATE=$(date +"%Y%m%d_%H%M%S")
BACKUP_DIR="/backups/mongodb"
DB_NAME="oldvinehotel"
# Create backup directory
mkdir -p $BACKUP_DIR
# Create backup
mongodump --db $DB_NAME --out $BACKUP_DIR/$DATE
# Compress backup
tar -czf $BACKUP_DIR/mongodb_backup_$DATE.tar.gz -C $BACKUP_DIR $DATE
# Remove uncompressed backup
rm -rf $BACKUP_DIR/$DATE
# Keep only last 7 days of backups
find $BACKUP_DIR -name "*.tar.gz" -mtime +7 -delete
# Upload to cloud storage (optional)
# aws s3 cp $BACKUP_DIR/mongodb_backup_$DATE.tar.gz s3://hotel-backups/
```
### Application Backup
```bash
#!/bin/bash
# backup-application.sh
DATE=$(date +"%Y%m%d_%H%M%S")
APP_DIR="/var/www/hotel"
BACKUP_DIR="/backups/application"
# Create backup
tar -czf $BACKUP_DIR/app_backup_$DATE.tar.gz \
--exclude="node_modules" \
--exclude=".git" \
--exclude="logs" \
$APP_DIR
# Keep only last 3 backups
ls -t $BACKUP_DIR/app_backup_*.tar.gz | tail -n +4 | xargs rm -f
```
## 🚨 Disaster Recovery
### Recovery Procedures
```bash
# Database Recovery
mongorestore --db oldvinehotel /path/to/backup/oldvinehotel
# Application Recovery
cd /var/www
tar -xzf /backups/application/app_backup_YYYYMMDD_HHMMSS.tar.gz
sudo chown -R www-data:www-data hotel
# Restart services
pm2 restart all
sudo systemctl restart nginx
```
### Health Check Automation
```bash
#!/bin/bash
# health-check.sh
# Check API health
if ! curl -f http://localhost:5000/health > /dev/null 2>&1; then
echo "API health check failed" | mail -s "Hotel API Down" admin@oldvinehotel.com
pm2 restart hotel-backend
fi
# Check database connection
if ! mongo --eval "db.adminCommand('ismaster')" > /dev/null 2>&1; then
echo "Database connection failed" | mail -s "Hotel DB Down" admin@oldvinehotel.com
sudo systemctl restart mongod
fi
# Check disk space
USAGE=$(df / | awk 'NR==2 {print $5}' | sed 's/%//')
if [ $USAGE -gt 80 ]; then
echo "Disk usage is at $USAGE%" | mail -s "High Disk Usage" admin@oldvinehotel.com
fi
```
## 📈 Performance Optimization
### Database Optimization
```bash
# MongoDB configuration
# /etc/mongod.conf
storage:
wiredTiger:
engineConfig:
cacheSizeGB: 2
collectionConfig:
blockCompressor: snappy
net:
bindIp: 127.0.0.1
port: 27017
maxIncomingConnections: 100
operationProfiling:
slowOpThresholdMs: 100
mode: slowOp
```
### Application Optimization
```javascript
// Add to server configuration
const compression = require('compression');
const helmet = require('helmet');
app.use(compression());
app.use(helmet());
// Connection pooling
mongoose.connect(process.env.MONGODB_URI, {
maxPoolSize: 10,
serverSelectionTimeoutMS: 5000,
socketTimeoutMS: 45000,
});
```
## 🔍 Troubleshooting
### Common Issues
```bash
# Check application logs
pm2 logs hotel-backend
# Check nginx logs
sudo tail -f /var/log/nginx/error.log
# Check MongoDB logs
sudo tail -f /var/log/mongodb/mongod.log
# Check system resources
top
df -h
free -h
```
### Performance Issues
```bash
# Monitor database performance
mongo
db.currentOp()
db.serverStatus()
# Check slow queries
db.system.profile.find().limit(5).sort({ts:-1}).pretty()
# Monitor Node.js performance
node --inspect server/index.js
# Then open chrome://inspect
```
This deployment guide provides comprehensive instructions for deploying The Old Vine Hotel website in production environments. Choose the deployment method that best fits your infrastructure and requirements.

799
docs/DEVELOPMENT.md Normal file
View File

@@ -0,0 +1,799 @@
# The Old Vine Hotel - Development Documentation
## 🏗️ Architecture Overview
This hotel management system follows a modern, scalable architecture designed for enterprise-level operations.
### System Architecture
```
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ React Client │────│ Express API │────│ MongoDB │
│ (Port 3000) │ │ (Port 5000) │ │ Database │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│ │ │
│ ┌─────────────────┐ │
│ │ Integrations │ │
│ └─────────────────┘ │
│ │ │
│ ┌─────────────────┐ │
└──────────────│ External APIs │──────────────┘
│ • Opera PMS │
│ • Booking.com │
│ • Expedia │
│ • Trip.com │
│ • Stripe │
└─────────────────┘
```
## 🛠️ Development Setup
### Prerequisites
```bash
# Install Node.js (v16+)
curl -fsSL https://deb.nodesource.com/setup_16.x | sudo -E bash -
sudo apt-get install -y nodejs
# Install MongoDB
wget -qO - https://www.mongodb.org/static/pgp/server-5.0.asc | sudo apt-key add -
echo "deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu focal/mongodb-org/5.0 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-5.0.list
sudo apt-get update
sudo apt-get install -y mongodb-org
sudo systemctl start mongod
sudo systemctl enable mongod
```
### Environment Setup
#### Development Database
```bash
# Create development database
mongo
use oldvinehotel_dev
db.createUser({
user: "hotel_admin",
pwd: "secure_password",
roles: ["readWrite"]
})
```
#### Environment Variables
**Server (.env)**
```bash
# Development configuration
NODE_ENV=development
PORT=5000
CLIENT_URL=http://localhost:3000
# Database
MONGODB_URI=mongodb://hotel_admin:secure_password@localhost:27017/oldvinehotel_dev
# Security
JWT_SECRET=your_super_secret_jwt_key_for_development
JWT_EXPIRES_IN=7d
# External APIs (Development keys)
STRIPE_SECRET_KEY=sk_test_your_development_key
GOOGLE_MAPS_API_KEY=your_development_maps_key
# Email (Development)
EMAIL_HOST=smtp.mailtrap.io
EMAIL_PORT=2525
EMAIL_USER=your_mailtrap_user
EMAIL_PASS=your_mailtrap_pass
```
**Client (.env)**
```bash
REACT_APP_API_URL=http://localhost:5000
REACT_APP_STRIPE_PUBLISHABLE_KEY=pk_test_your_development_key
REACT_APP_GOOGLE_MAPS_API_KEY=your_development_maps_key
```
## 📊 Database Schema
### Core Models
#### Room Model
```javascript
{
name: String, // "Deluxe Ocean Suite"
type: String, // "Suite", "Deluxe", etc.
roomNumber: String, // "101", "201A"
basePrice: Number, // 299.99
maxOccupancy: Number, // 4
amenities: [String], // ["WiFi", "Ocean View"]
status: String, // "Available", "Occupied"
operaRoomId: String, // PMS integration ID
images: [{
url: String,
alt: String,
isPrimary: Boolean
}]
}
```
#### Booking Model
```javascript
{
bookingNumber: String, // "OVH202512345"
confirmationCode: String, // "ABC12345"
guest: ObjectId, // Reference to Guest
room: ObjectId, // Reference to Room
checkInDate: Date,
checkOutDate: Date,
numberOfGuests: {
adults: Number,
children: Number
},
totalAmount: Number,
paymentStatus: String, // "Paid", "Pending"
status: String, // "Confirmed", "Cancelled"
stripePaymentIntentId: String
}
```
#### Guest Model
```javascript
{
firstName: String,
lastName: String,
email: String,
phone: String,
password: String, // Hashed
loyaltyProgram: {
tier: String, // "Bronze", "Silver", "Gold"
points: Number
},
preferences: {
roomType: String,
language: String
},
totalStays: Number,
isVIP: Boolean
}
```
### Database Indexes
```javascript
// Room indexes for performance
db.rooms.createIndex({ "roomNumber": 1 }, { unique: true })
db.rooms.createIndex({ "type": 1, "status": 1 })
db.rooms.createIndex({ "operaRoomId": 1 })
// Booking indexes
db.bookings.createIndex({ "bookingNumber": 1 }, { unique: true })
db.bookings.createIndex({ "confirmationCode": 1 })
db.bookings.createIndex({ "checkInDate": 1, "checkOutDate": 1 })
db.bookings.createIndex({ "guest": 1 })
db.bookings.createIndex({ "room": 1 })
// Guest indexes
db.guests.createIndex({ "email": 1 }, { unique: true })
db.guests.createIndex({ "loyaltyProgram.tier": 1 })
```
## 🔌 API Design
### RESTful API Structure
```
GET /api/rooms # List rooms with filters
GET /api/rooms/:id # Get specific room
POST /api/rooms/:id/availability # Check availability
POST /api/bookings # Create booking
GET /api/bookings/:bookingNumber # Get booking details
PUT /api/bookings/:id/cancel # Cancel booking
POST /api/auth/register # Guest registration
POST /api/auth/login # Guest login
GET /api/auth/me # Current user info
POST /api/contact # Send contact message
GET /api/contact/info # Hotel information
```
### Response Format
```javascript
// Success Response
{
"success": true,
"data": {
// Response data
},
"message": "Operation successful"
}
// Error Response
{
"success": false,
"message": "Error description",
"errors": [
{
"field": "email",
"message": "Invalid email format"
}
]
}
// Paginated Response
{
"success": true,
"data": {
"items": [...],
"pagination": {
"currentPage": 1,
"totalPages": 10,
"totalCount": 100,
"hasNextPage": true,
"hasPrevPage": false
}
}
}
```
## 🔐 Authentication Flow
### JWT Implementation
```javascript
// Token Generation
const generateToken = (payload) => {
return jwt.sign(payload, process.env.JWT_SECRET, {
expiresIn: process.env.JWT_EXPIRES_IN || '7d'
});
};
// Token Verification Middleware
const auth = async (req, res, next) => {
const token = req.header('Authorization')?.replace('Bearer ', '');
if (!token) {
return res.status(401).json({ success: false, message: 'Access denied' });
}
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
req.user = await User.findById(decoded.id);
next();
} catch (error) {
res.status(401).json({ success: false, message: 'Invalid token' });
}
};
```
### Password Security
```javascript
// Password Hashing (in model)
guestSchema.pre('save', async function(next) {
if (!this.isModified('password')) return next();
this.password = await bcrypt.hash(this.password, 12);
next();
});
// Password Comparison
guestSchema.methods.comparePassword = async function(candidatePassword) {
return bcrypt.compare(candidatePassword, this.password);
};
```
## 💳 Payment Integration
### Stripe Implementation
```javascript
// Create Payment Intent
const createPayment = async (amount, bookingData) => {
const paymentIntent = await stripe.paymentIntents.create({
amount: Math.round(amount * 100), // Convert to cents
currency: 'usd',
metadata: {
bookingNumber: bookingData.bookingNumber,
guestEmail: bookingData.guest.email
}
});
return paymentIntent;
};
// Handle Webhook
const handleStripeWebhook = (req, res) => {
const sig = req.headers['stripe-signature'];
let event;
try {
event = stripe.webhooks.constructEvent(
req.body,
sig,
process.env.STRIPE_WEBHOOK_SECRET
);
} catch (err) {
return res.status(400).send(`Webhook signature verification failed.`);
}
switch (event.type) {
case 'payment_intent.succeeded':
// Handle successful payment
break;
case 'payment_intent.payment_failed':
// Handle failed payment
break;
}
res.json({ received: true });
};
```
## 🔗 Integration Services
### Opera PMS Integration
```javascript
class OperaPMSService {
async createReservation(booking) {
const xmlRequest = this.generateXMLRequest('OTA_HotelResRQ', {
// XML structure for Opera PMS
});
const response = await this.client.post('/reservations', xmlRequest);
return this.parseXMLResponse(response.data);
}
async syncAvailability() {
// Sync room availability with Opera PMS
}
async updateRates(roomType, rates) {
// Update room rates in Opera PMS
}
}
```
### Booking Platform APIs
```javascript
// Booking.com Integration
class BookingComService {
async updateAvailability(roomType, dates, availability) {
const xmlRequest = this.buildAvailabilityXML(roomType, dates, availability);
return this.sendRequest(xmlRequest);
}
async processWebhook(webhookData) {
switch (webhookData.event_type) {
case 'booking_created':
await this.importBooking(webhookData);
break;
case 'booking_cancelled':
await this.cancelBooking(webhookData);
break;
}
}
}
```
## 🎨 Frontend Architecture
### React Component Structure
```
src/
├── components/
│ ├── common/ # Shared components
│ │ ├── Button/
│ │ ├── Modal/
│ │ └── Loading/
│ ├── layout/ # Layout components
│ │ ├── Header/
│ │ ├── Footer/
│ │ └── Sidebar/
│ └── booking/ # Booking-specific components
│ ├── RoomCard/
│ ├── BookingForm/
│ └── PaymentForm/
├── pages/ # Page components
├── services/ # API services
├── hooks/ # Custom React hooks
├── utils/ # Utility functions
└── context/ # React contexts
```
### State Management
```javascript
// React Query for server state
const { data: rooms, isLoading } = useQuery(
['rooms', filters],
() => roomsAPI.getRooms(filters),
{
staleTime: 5 * 60 * 1000, // 5 minutes
cacheTime: 10 * 60 * 1000, // 10 minutes
}
);
// Context for global state
const AuthContext = createContext();
export const AuthProvider = ({ children }) => {
const [user, setUser] = useState(null);
const [isAuthenticated, setIsAuthenticated] = useState(false);
return (
<AuthContext.Provider value={{ user, isAuthenticated, setUser }}>
{children}
</AuthContext.Provider>
);
};
```
### API Service Layer
```javascript
// API service structure
class RoomsAPI {
async getRooms(filters = {}) {
const params = new URLSearchParams(filters);
const response = await api.get(`/rooms?${params}`);
return response.data;
}
async checkAvailability(roomId, dates) {
const response = await api.post(`/rooms/${roomId}/availability`, dates);
return response.data;
}
async createBooking(bookingData) {
const response = await api.post('/bookings', bookingData);
return response.data;
}
}
export const roomsAPI = new RoomsAPI();
```
## 🧪 Testing Strategy
### Backend Testing
```javascript
// Unit Tests (Jest)
describe('Room Model', () => {
test('should calculate current price with seasonal pricing', () => {
const room = new Room({
basePrice: 200,
seasonalPricing: [{
season: 'summer',
startDate: new Date('2024-06-01'),
endDate: new Date('2024-08-31'),
priceMultiplier: 1.5
}]
});
expect(room.currentPrice).toBe(300);
});
});
// Integration Tests
describe('Booking API', () => {
test('POST /api/bookings should create booking', async () => {
const bookingData = {
roomId: 'room123',
checkInDate: '2024-06-01',
checkOutDate: '2024-06-03',
guestInfo: { /* guest data */ }
};
const response = await request(app)
.post('/api/bookings')
.send(bookingData)
.expect(201);
expect(response.body.success).toBe(true);
expect(response.body.data.booking).toBeDefined();
});
});
```
### Frontend Testing
```javascript
// Component Tests (React Testing Library)
import { render, screen, fireEvent } from '@testing-library/react';
import BookingForm from '../BookingForm';
test('should submit booking form with valid data', async () => {
render(<BookingForm />);
fireEvent.change(screen.getByLabelText(/check-in date/i), {
target: { value: '2024-06-01' }
});
fireEvent.click(screen.getByRole('button', { name: /book now/i }));
await waitFor(() => {
expect(screen.getByText(/booking confirmed/i)).toBeInTheDocument();
});
});
```
## 📊 Performance Optimization
### Database Optimization
```javascript
// Efficient aggregation for room availability
const getAvailableRooms = async (checkIn, checkOut) => {
return Room.aggregate([
{
$match: {
status: 'Available',
isActive: true
}
},
{
$lookup: {
from: 'bookings',
let: { roomId: '$_id' },
pipeline: [
{
$match: {
$expr: { $eq: ['$room', '$$roomId'] },
status: { $in: ['Confirmed', 'Checked In'] },
$or: [
{
checkInDate: { $lt: checkOut },
checkOutDate: { $gt: checkIn }
}
]
}
}
],
as: 'conflictingBookings'
}
},
{
$match: {
conflictingBookings: { $size: 0 }
}
}
]);
};
```
### Frontend Optimization
```javascript
// Code splitting and lazy loading
const Booking = lazy(() => import('./pages/Booking'));
const AdminDashboard = lazy(() => import('./pages/admin/Dashboard'));
// Memoization for expensive calculations
const ExpensiveComponent = memo(({ data }) => {
const processedData = useMemo(() => {
return expensiveCalculation(data);
}, [data]);
return <div>{processedData}</div>;
});
// Image optimization
const OptimizedImage = ({ src, alt, ...props }) => {
const [imageSrc, setImageSrc] = useState(src + '?w=50&q=10'); // Placeholder
useEffect(() => {
const img = new Image();
img.onload = () => setImageSrc(src);
img.src = src;
}, [src]);
return <img src={imageSrc} alt={alt} {...props} />;
};
```
## 🔍 Monitoring & Logging
### Application Logging
```javascript
// Winston logger configuration
const logger = winston.createLogger({
level: process.env.LOG_LEVEL || 'info',
format: winston.format.combine(
winston.format.timestamp(),
winston.format.errors({ stack: true }),
winston.format.json()
),
transports: [
new winston.transports.File({ filename: 'logs/error.log', level: 'error' }),
new winston.transports.File({ filename: 'logs/combined.log' }),
new winston.transports.File({
filename: 'logs/bookings.log',
format: winston.format((info) => {
return info.type === 'booking' ? info : false;
})()
})
]
});
// Usage in application
logger.info('Booking created', {
type: 'booking',
bookingId: booking._id,
guestEmail: booking.guest.email,
amount: booking.totalAmount
});
```
### Health Monitoring
```javascript
// Health check endpoint
app.get('/health', async (req, res) => {
const healthCheck = {
status: 'OK',
timestamp: new Date().toISOString(),
uptime: process.uptime(),
checks: {
database: await checkDatabaseHealth(),
redis: await checkRedisHealth(),
external_apis: await checkExternalAPIs()
}
};
const isHealthy = Object.values(healthCheck.checks).every(check => check.status === 'OK');
res.status(isHealthy ? 200 : 503).json(healthCheck);
});
```
## 🚀 Deployment
### Docker Configuration
```dockerfile
# Dockerfile for backend
FROM node:16-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
EXPOSE 5000
CMD ["npm", "start"]
```
```yaml
# docker-compose.yml
version: '3.8'
services:
frontend:
build: ./client
ports:
- "3000:3000"
environment:
- REACT_APP_API_URL=http://backend:5000
depends_on:
- backend
backend:
build: ./server
ports:
- "5000:5000"
environment:
- NODE_ENV=production
- MONGODB_URI=mongodb://mongodb:27017/oldvinehotel
depends_on:
- mongodb
mongodb:
image: mongo:5.0
ports:
- "27017:27017"
volumes:
- mongodb_data:/data/db
volumes:
mongodb_data:
```
### Production Deployment
```bash
# Build and deploy script
#!/bin/bash
# Build frontend
cd client
npm run build
# Deploy to CDN (e.g., AWS S3 + CloudFront)
aws s3 sync build/ s3://hotel-website-bucket --delete
aws cloudfront create-invalidation --distribution-id ABCD1234 --paths "/*"
# Deploy backend
cd ../server
# Build Docker image
docker build -t hotel-api:latest .
# Deploy to container registry
docker tag hotel-api:latest your-registry/hotel-api:latest
docker push your-registry/hotel-api:latest
# Deploy to production environment
kubectl apply -f k8s/
```
## 🔧 Troubleshooting
### Common Issues
#### Database Connection
```bash
# Check MongoDB status
sudo systemctl status mongod
# View MongoDB logs
sudo journalctl -u mongod
# Test connection
mongo --eval "db.adminCommand('ismaster')"
```
#### API Issues
```bash
# Check API health
curl http://localhost:5000/health
# Test specific endpoint
curl -X POST http://localhost:5000/api/rooms/availability \
-H "Content-Type: application/json" \
-d '{"checkIn":"2024-06-01","checkOut":"2024-06-03"}'
```
#### Frontend Issues
```bash
# Clear React cache
npm start -- --reset-cache
# Check bundle size
npm run build -- --analyze
# Test production build locally
npx serve -s build
```
### Performance Debugging
```javascript
// Database query profiling
db.setProfilingLevel(2); // Profile all operations
db.system.profile.find().limit(5).sort({ts:-1}).pretty();
// API response time monitoring
const responseTime = require('response-time');
app.use(responseTime((req, res, time) => {
logger.info('API Response Time', {
method: req.method,
url: req.originalUrl,
responseTime: time,
statusCode: res.statusCode
});
}));
```
---
This documentation provides a comprehensive guide for developers working on The Old Vine Hotel project. For specific implementation details, refer to the code comments and individual component documentation.