← 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**