← back to Jill Website
README.md
642 lines
# Nosara Beachfront Rentals
A luxury beachfront vacation rental website for Nosara, Costa Rica. This application provides property listings, guest inquiry management, and email notifications for vacation rental properties.
## Tech Stack
- **Backend**: Node.js with Express.js
- **Language**: TypeScript
- **Database**: PostgreSQL
- **Template Engine**: EJS
- **Email**: Nodemailer (SMTP)
- **Testing**: Jest with Supertest
- **Process Management**: PM2 (recommended for production)
## Prerequisites
Before installation, ensure you have the following installed:
- Node.js (v16 or higher)
- PostgreSQL (v12 or higher)
- npm or yarn package manager
## Installation
### 1. Clone the Repository
```bash
git clone <repository-url>
cd jill-website
```
### 2. Install Dependencies
```bash
npm install
```
### 3. Environment Configuration
Copy the example environment file and configure your settings:
```bash
cp .env.example .env
```
Edit `.env` with your specific configuration:
```env
# Database Configuration
DB_HOST=localhost
DB_PORT=5432
DB_NAME=jill_website
DB_USER=postgres
DB_PASSWORD=your_password_here
# Server Configuration
PORT=8200
NODE_ENV=development
# Email Configuration (SMTP)
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SMTP_SECURE=false
SMTP_USER=your_email@gmail.com
SMTP_PASS=your_app_password_here
SMTP_FROM="Nosara Beachfront Rentals <noreply@nosarabeachfront.com>"
```
#### Gmail SMTP Setup
If using Gmail for email notifications:
1. Enable 2-factor authentication on your Google account
2. Generate an App Password: [https://myaccount.google.com/apppasswords](https://myaccount.google.com/apppasswords)
3. Use the App Password as your `SMTP_PASS` value
### 4. Database Setup
Create the PostgreSQL database:
```bash
createdb jill_website
```
Run database migrations to create tables and indexes:
```bash
npm run migrate
```
This will create the following tables:
- `inquiries` - Guest inquiry submissions
- `properties` - Vacation rental property listings
For detailed database schema information, see [DATABASE.md](./DATABASE.md).
### 5. Build the Project
Compile TypeScript to JavaScript:
```bash
npm run build
```
## Running the Application
### Development Mode
Run with hot-reload using ts-node:
```bash
npm run dev
```
The server will start on `http://localhost:8200` (or your configured PORT).
### Production Mode
Build and run the compiled JavaScript:
```bash
npm run build
npm start
```
### Type Checking
Verify TypeScript types without compilation:
```bash
npm run typecheck
```
## Testing
### Run All Tests
```bash
npm test
```
### Watch Mode (for development)
```bash
npm run test:watch
```
### Test Coverage
```bash
npm run test:coverage
```
Test files are located in `src/__tests__/` and cover:
- Database connection and models
- API route handlers
- Inquiry and Property creation/validation
- Email service integration
## API Endpoints
### Health Check
**GET** `/health`
Returns server health status.
**Response:**
```json
{
"status": "ok",
"timestamp": "2025-01-14T12:00:00.000Z"
}
```
---
### Inquiries
#### Create Inquiry
**POST** `/api/inquiries`
Submit a new guest inquiry for property rental.
**Request Body:**
```json
{
"name": "John Doe",
"email": "john@example.com",
"phone": "+1-555-0123",
"check_in_date": "2025-02-01",
"check_out_date": "2025-02-08",
"guest_count": 4,
"message": "Looking to book Casita del Sol for a week"
}
```
**Success Response (201 Created):**
```json
{
"success": true,
"data": {
"id": 1,
"inquiry": {
"id": 1,
"name": "John Doe",
"email": "john@example.com",
"phone": "+1-555-0123",
"check_in_date": "2025-02-01T00:00:00.000Z",
"check_out_date": "2025-02-08T00:00:00.000Z",
"guest_count": 4,
"message": "Looking to book Casita del Sol for a week",
"created_at": "2025-01-14T12:00:00.000Z",
"updated_at": "2025-01-14T12:00:00.000Z"
}
},
"message": "Inquiry created successfully"
}
```
**Error Response (400 Bad Request):**
```json
{
"success": false,
"error": "Validation error",
"message": "Validation failed: Email is required"
}
```
**Validation Rules:**
- All fields are required
- Email must be valid format
- Check-out date must be after check-in date
- Guest count must be at least 1
**Side Effects:**
- Sends confirmation email to guest (non-blocking)
- Email failures are logged but don't affect response
#### Get All Inquiries
**GET** `/api/inquiries?limit=100&offset=0`
Retrieve all inquiries with optional pagination.
**Query Parameters:**
- `limit` (optional): Number of results to return (default: 100)
- `offset` (optional): Number of results to skip (default: 0)
**Success Response (200 OK):**
```json
{
"success": true,
"data": [
{
"id": 1,
"name": "John Doe",
"email": "john@example.com",
"phone": "+1-555-0123",
"check_in_date": "2025-02-01T00:00:00.000Z",
"check_out_date": "2025-02-08T00:00:00.000Z",
"guest_count": 4,
"message": "Looking to book Casita del Sol for a week",
"created_at": "2025-01-14T12:00:00.000Z",
"updated_at": "2025-01-14T12:00:00.000Z"
}
],
"count": 1
}
```
#### Get Inquiry by ID
**GET** `/api/inquiries/:id`
Retrieve a specific inquiry by ID.
**Success Response (200 OK):**
```json
{
"success": true,
"data": {
"id": 1,
"name": "John Doe",
"email": "john@example.com",
"phone": "+1-555-0123",
"check_in_date": "2025-02-01T00:00:00.000Z",
"check_out_date": "2025-02-08T00:00:00.000Z",
"guest_count": 4,
"message": "Looking to book Casita del Sol for a week",
"created_at": "2025-01-14T12:00:00.000Z",
"updated_at": "2025-01-14T12:00:00.000Z"
}
}
```
**Error Response (404 Not Found):**
```json
{
"success": false,
"error": "Not found",
"message": "Inquiry not found"
}
```
---
### Properties
#### Get All Properties
**GET** `/api/properties?limit=100&offset=0`
Retrieve all property listings with optional pagination.
**Query Parameters:**
- `limit` (optional): Number of results to return (default: 100)
- `offset` (optional): Number of results to skip (default: 0)
**Success Response (200 OK):**
```json
{
"success": true,
"data": [
{
"id": 1,
"name": "Casita del Sol",
"description": "First floor oceanfront villa with private entrance",
"amenities": ["Saltwater Pool", "Beach Access", "Full Kitchen", "WiFi"],
"images": [
"https://example.com/image1.jpg",
"https://example.com/image2.jpg"
],
"airbnb_url": "https://airbnb.com/rooms/12345",
"capacity": 4,
"bedrooms": 1,
"bathrooms": 1.5,
"size_sqm": 76,
"price_per_night": 250,
"created_at": "2025-01-14T12:00:00.000Z",
"updated_at": "2025-01-14T12:00:00.000Z"
}
],
"count": 1
}
```
#### Get Property by ID
**GET** `/api/properties/:id`
Retrieve a specific property by ID.
**Success Response (200 OK):**
```json
{
"success": true,
"data": {
"id": 1,
"name": "Casita del Sol",
"description": "First floor oceanfront villa with private entrance",
"amenities": ["Saltwater Pool", "Beach Access", "Full Kitchen", "WiFi"],
"images": [
"https://example.com/image1.jpg",
"https://example.com/image2.jpg"
],
"airbnb_url": "https://airbnb.com/rooms/12345",
"capacity": 4,
"bedrooms": 1,
"bathrooms": 1.5,
"size_sqm": 76,
"price_per_night": 250,
"created_at": "2025-01-14T12:00:00.000Z",
"updated_at": "2025-01-14T12:00:00.000Z"
}
}
```
**Error Response (404 Not Found):**
```json
{
"success": false,
"error": "Not found",
"message": "Property not found"
}
```
**Error Response (400 Bad Request - Invalid ID):**
```json
{
"success": false,
"error": "Invalid ID",
"message": "Property ID must be a number"
}
```
---
## Project Structure
```
jill-website/
├── src/
│ ├── config/
│ │ └── database.ts # PostgreSQL connection pool
│ ├── migrations/
│ │ ├── 001_create_tables.sql # Initial schema
│ │ └── migrate.ts # Migration runner
│ ├── models/
│ │ ├── Inquiry.ts # Inquiry model with validation
│ │ └── Property.ts # Property model with validation
│ ├── routes/
│ │ ├── inquiries.ts # Inquiry API endpoints
│ │ ├── properties.ts # Property API endpoints
│ │ └── unsubscribe.ts # Email unsubscribe handler
│ ├── services/
│ │ └── emailService.ts # Email notification service
│ ├── __tests__/ # Test suites
│ ├── index.ts # Application entry point
│ └── server.ts # Express server configuration
├── public/ # Static assets
├── views/ # EJS templates
├── dist/ # Compiled JavaScript (generated)
├── .env # Environment variables (not in git)
├── .env.example # Example environment configuration
├── package.json # Node.js dependencies
├── tsconfig.json # TypeScript configuration
├── jest.config.js # Jest test configuration
└── README.md # This file
```
## Deployment
### Using PM2 (Recommended)
PM2 is a production process manager for Node.js applications.
1. **Install PM2 globally:**
```bash
npm install -g pm2
```
2. **Build the application:**
```bash
npm run build
```
3. **Start with PM2:**
```bash
pm2 start dist/server.js --name nosara-rentals
```
4. **Configure PM2 to restart on server reboot:**
```bash
pm2 startup
pm2 save
```
5. **Useful PM2 commands:**
```bash
pm2 status # View process status
pm2 logs nosara-rentals # View logs
pm2 restart nosara-rentals # Restart application
pm2 stop nosara-rentals # Stop application
pm2 delete nosara-rentals # Remove from PM2
```
### Environment Variables in Production
Ensure all environment variables are properly configured:
```bash
# Set NODE_ENV to production
NODE_ENV=production
# Use production database credentials
DB_HOST=your-production-db-host
DB_NAME=jill_website_prod
DB_USER=your-production-user
DB_PASSWORD=your-secure-password
# Configure production SMTP
SMTP_HOST=smtp.gmail.com
SMTP_USER=your-production-email@gmail.com
SMTP_PASS=your-app-password
```
### Firewall Configuration
Open the application port:
```bash
sudo ufw allow 8200/tcp
```
### Nginx Reverse Proxy (Optional)
If using Nginx as a reverse proxy:
```nginx
server {
listen 80;
server_name yourdomain.com;
location / {
proxy_pass http://localhost:8200;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
```
### Database Backup
Regular database backups are recommended:
```bash
# Backup database
pg_dump jill_website > backup_$(date +%Y%m%d).sql
# Restore database
psql jill_website < backup_20250114.sql
```
## Development Workflow
1. Create a new branch for features:
```bash
git checkout -b feature/your-feature-name
```
2. Make changes and test:
```bash
npm run dev # Run development server
npm test # Run tests
npm run typecheck # Verify TypeScript types
```
3. Build and verify:
```bash
npm run build
npm start
```
4. Commit and push:
```bash
git add .
git commit -m "feat: your feature description"
git push origin feature/your-feature-name
```
## Troubleshooting
### Database Connection Issues
If you encounter database connection errors:
1. Verify PostgreSQL is running:
```bash
sudo systemctl status postgresql
```
2. Check database exists:
```bash
psql -l | grep jill_website
```
3. Test connection manually:
```bash
psql -h localhost -U postgres -d jill_website
```
4. Verify `.env` credentials match your PostgreSQL configuration
### Email Sending Issues
If emails aren't sending:
1. Verify SMTP credentials in `.env`
2. Check Gmail App Password is correctly generated
3. Ensure "Less secure app access" is NOT enabled (use App Passwords instead)
4. Review application logs for email errors
### Port Already in Use
If the port is already in use:
```bash
# Find process using port 8200
lsof -ti:8200
# Kill the process
lsof -ti:8200 | xargs kill -9
```
### TypeScript Compilation Errors
If TypeScript won't compile:
1. Clear the build directory:
```bash
rm -rf dist
```
2. Reinstall dependencies:
```bash
rm -rf node_modules package-lock.json
npm install
```
3. Run type checking:
```bash
npm run typecheck
```
## Contributing
1. Fork the repository
2. Create a feature branch
3. Make your changes with tests
4. Ensure all tests pass: `npm test`
5. Ensure typecheck passes: `npm run typecheck`
6. Submit a pull request
## License
MIT
## Support
For issues or questions, please contact the development team or create an issue in the repository.
---
**Built with ❤️ for luxury vacation rentals in Nosara, Costa Rica**