# cPanel production deployment

This guide deploys the Flask app with cPanel **Setup Python App** and Passenger.
The scraper needs outbound HTTPS access. Blog topic drafting uses scraping only;
there is no OpenAI or Ollama requirement.

## 1. Prepare the project

Keep these project items together: `app.py`, `passenger_wsgi.py`, `cron_sync.py`,
`db.py`, `catalog.py`, `ingestion.py`, `scraper.py`, `blog_writer.py`,
`blog_research.py`, `requirements.txt`, `templates/`, and `static/`.

Upload the existing `cars.db` if you want the current catalogue and posts. If you
start without it, the app creates an empty database and the admin sync can populate it.
Do not upload `.runtime-deps/`, `.local-ai/`, `__pycache__/`, or test files.

## 2. Create the Python application

In cPanel:

1. Open **Setup Python App** → **Create Application**.
2. Select Python 3.10 or newer, matching the versions supported by your host.
3. Set the application root to a private directory such as `carcompare`.
4. Set the application URL to your domain or a temporary subdomain.
5. Set the startup file to `passenger_wsgi.py`.
6. Set the entry point to `application`.
7. Create the application.

cPanel displays the virtual-environment path. Copy it for the install and cron steps.

## 3. Install dependencies

Open cPanel Terminal or SSH and run the commands using the virtual environment path
shown by cPanel. Replace both paths with your real values:

```bash
source /home/CPANEL_USER/virtualenv/carcompare/3.11/bin/activate
cd /home/CPANEL_USER/carcompare
python -m pip install --upgrade pip
python -m pip install -r requirements.txt
mkdir -p static/media
chmod 755 static static/media
```

If the host does not offer Terminal or SSH, use the **Run Pip Install** button in
Setup Python App and enter `-r requirements.txt`.

## 4. Set production environment variables

In **Setup Python App → Environment variables**, add:

```text
SECRET_KEY=<a-long-random-value>
ADMIN_USER=<your-admin-username>
ADMIN_PASSWORD=<a-long-random-password>
```

Generate a secret locally with:

```bash
python -c "import secrets; print(secrets.token_urlsafe(48))"
```

Never commit these values, put them in a public file, or use the development default
`admin123`. The existing SQLite database and `static/media/` directory must be writable
by the cPanel application user because admin edits, scraping, and media caching write there.

## 5. Initialise and restart

After uploading the files and setting variables, run once from the project directory:

```bash
source /home/CPANEL_USER/virtualenv/carcompare/3.11/bin/activate
cd /home/CPANEL_USER/carcompare
python -c "import db, catalog; db.init_db(); catalog.init(); print('database ready')"
```

Then click **Restart** in Setup Python App. Visit `/admin/login` and sign in with the
production credentials. Change site content and confirm that `/`, `/bikes`, `/blog`, and
`/compare` load.

## 6. Run imports with Cron

Passenger web workers can be recycled, so scheduled imports belong in cPanel **Cron Jobs**.
For example, run missing car and bike data every six hours:

```text
17 */6 * * * /home/CPANEL_USER/virtualenv/carcompare/3.11/bin/python /home/CPANEL_USER/carcompare/cron_sync.py --kind all --mode missing >> /home/CPANEL_USER/carcompare/sync.log 2>&1
```

Run a weekly full refresh:

```text
31 3 * * 0 /home/CPANEL_USER/virtualenv/carcompare/3.11/bin/python /home/CPANEL_USER/carcompare/cron_sync.py --kind all --mode full >> /home/CPANEL_USER/carcompare/sync.log 2>&1
```

Run blog topic/feed scraping separately when needed:

```text
47 */6 * * * /home/CPANEL_USER/virtualenv/carcompare/3.11/bin/python /home/CPANEL_USER/carcompare/cron_sync.py --kind blog --mode full >> /home/CPANEL_USER/carcompare/sync.log 2>&1
```

The admin sync button remains useful for one-off imports. If the host blocks outbound
HTTPS or a source returns 403, the run is recorded as an error and saved records remain.

## 7. Secure the deployment

Enable HTTPS in cPanel **SSL/TLS Status** and force HTTPS in the domain settings. Keep
`cars.db`, `sync.log`, and environment values outside `public_html` when your hosting
layout allows it. If the application root must be inside `public_html`, add a deny rule
for database/log files in the public directory and test that direct requests cannot read
them. Keep the admin password long and unique.

## 8. Production checks

Check these after deployment:

- `/` loads without a debug traceback.
- `/admin/login` rejects the development password.
- `/admin?tab=sources` lists the configured sources.
- `/admin?tab=sync` records a successful test run.
- `/bikes` and `/compare?type=bike&cars=...` work.
- `/blog` and an article page use local links and local cached images.
- `static/media/` remains writable after a scrape.
- `sync.log` contains no repeated permission, timeout, or database-lock errors.

## Common cPanel problems

**500 / application failed to start:** check Passenger error logs, confirm the startup
file is `passenger_wsgi.py`, and run `python -c "import app"` inside the cPanel virtualenv.

**ModuleNotFoundError:** install `requirements.txt` into the exact virtualenv selected by
Setup Python App, then restart the application.

**Database is read-only:** give the cPanel account write permission to `cars.db`, its
containing directory, and `static/media/`.

**Scraping times out:** use cron for full imports, lower the source count, and inspect the
sync history. Do not expose source URLs in public templates; the app stores them for backend
scraping and editor diagnostics only.

