# Deployment

**Status: not deployed.** This project has only been run and tested locally. Nothing is live until you (or your
host) complete the steps below and point the domain at the server.

The previous version was a static site. This version needs a host that can run a **Python process** and keep a
**persistent disk** for `instance/` (database, uploads, backups). Pure static hosts (Netlify static, GitHub Pages,
plain cPanel without Python) cannot run it. cPanel **with** "Setup Python App" works: see Option C.

## Option C — cPanel with "Setup Python App"

Works on cPanel hosts that show **Setup Python App** under *Software* (CloudLinux / Passenger). Plain cPanel
hosting without that icon cannot run this site; ask your host to enable Python apps.

1. **File Manager** → open your home folder (`/home/<user>`, **not** `public_html`) → **Upload**
   `dune-echo-tours.zip` → right-click → **Extract**. You get the folder `/home/<user>/dune-echo-tours`.
2. Inside it, rename `.env.example` to `.env` (enable *Show Hidden Files* in File Manager settings) and edit:
   `DUNE_ENV=production`, `SECRET_KEY=` a long random string, `SITE_URL=https://www.your-domain.com`,
   `CONTACT_EMAIL=` your real mailbox, `TRUST_PROXY=1`.
3. **Setup Python App** → **Create Application**: Python **3.10 or newer** · Application root `dune-echo-tours` ·
   Application URL: your domain · Startup file `passenger_wsgi.py` · Entry point `application` → **Create**.
4. In the same screen, under *Configuration files*, add `requirements.txt` → **Run Pip Install** (Flask, Pillow,
   waitress). Then **Restart**.
5. Open your domain. On the first visit the database is created and filled automatically (in
   `dune-echo-tours/instance/`, which is outside `public_html` and not reachable from the web).
6. Create your admin login: cPanel **Terminal** → copy the "Enter to the virtual environment" command shown at the
   top of the Python App screen, paste it, then run:
   `ADMIN_PASSWORD='a long password' python manage.py create-admin you@your-domain.com`
   and sign in at `https://your-domain/admin/`.
7. Backups: cPanel **Cron Jobs**, daily:
   `cd ~/dune-echo-tours && <virtualenv>/bin/python manage.py backup` (the virtualenv path is shown in the Python
   App screen). Download `instance/backups/` from time to time.

After any change to the files, press **Restart** in Setup Python App.

## Option A — small Linux server (VPS), recommended

```bash
sudo adduser --system --group dune
sudo mkdir -p /srv/dune-echo /var/lib/dune-echo && sudo chown dune:dune /var/lib/dune-echo
# copy the project to /srv/dune-echo, then:
cd /srv/dune-echo
python3 -m venv .venv && .venv/bin/pip install -r requirements.txt gunicorn
cp .env.example .env    # then edit:
#   DUNE_ENV=production
#   SECRET_KEY=<long random string>
#   SITE_URL=https://www.your-domain.com
#   CONTACT_EMAIL=<real mailbox>
#   INSTANCE_DIR=/var/lib/dune-echo
#   TRUST_PROXY=1
sudo -u dune .venv/bin/python manage.py init-db
sudo -u dune .venv/bin/python manage.py create-admin owner@your-domain.com
```

systemd unit `/etc/systemd/system/dune-echo.service`:

```ini
[Unit]
Description=Dune Echo Tours
After=network.target

[Service]
User=dune
WorkingDirectory=/srv/dune-echo
ExecStart=/srv/dune-echo/.venv/bin/gunicorn -w 3 -b 127.0.0.1:8000 wsgi:app
Restart=always

[Install]
WantedBy=multi-user.target
```

nginx in front (HTTPS via certbot), serving static files directly:

```nginx
server {
    server_name www.your-domain.com;
    client_max_body_size 15m;
    location /static/ { alias /srv/dune-echo/app/static/; expires 7d; }
    location / { proxy_pass http://127.0.0.1:8000; proxy_set_header Host $host;
                 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                 proxy_set_header X-Forwarded-Proto $scheme; }
}
```

Daily backup (crontab of user `dune`):

```
30 2 * * * cd /srv/dune-echo && .venv/bin/python manage.py backup >/dev/null
```

and copy `/var/lib/dune-echo/backups/` off the server (e.g. rclone to cloud storage).

## Option B — platform hosting (Render, Railway, Fly.io, PythonAnywhere)

- Build: `pip install -r requirements.txt gunicorn` · Start: `gunicorn -w 2 -b 0.0.0.0:$PORT wsgi:app`
- Attach a **persistent volume** and set `INSTANCE_DIR` to its mount path. Without a volume, the database and
  inquiries are lost on every redeploy.
- Set the same environment variables as above, then run `python manage.py init-db` and `create-admin` once in the
  platform's shell.

## Windows server

`python manage.py serve --port 8000` runs the bundled production server (waitress). Put IIS or another reverse
proxy with HTTPS in front, and schedule `python manage.py backup` with Task Scheduler.

## After going live

1. Set `SITE_URL` to the final domain (canonical URLs, hreflang and the sitemap depend on it).
2. Submit `https://your-domain/sitemap.xml` in Google Search Console; check one tour and one activity in the Rich
   Results Test.
3. The old static URLs (`/tours/x.html`, `/activities/x.html`, `/blog/x.html`, `/plan-with-expert.html` …) answer with
   permanent redirects to the new pages, so existing links keep working.
4. Send a test inquiry and check it under Admin → Inquiries. The site stores inquiries; it does **not** send e-mail
   notifications (no mail provider is configured). Check the dashboard daily, or add an SMTP integration later.
