# Deploy to TrueHost cPanel (Setup Python App)

TrueHost shared hosting serves Django via **cPanel "Setup Python App"** (CloudLinux
Python Selector + Phusion Passenger). There is **no gunicorn** involved — Passenger
talks directly to the WSGI app via `passenger_wsgi.py`.

> Read TrueHost's own guide first: https://truehost.com/support/knowledge-base/how-to-deploy-django-web-application-on-shared-hosting-cpanel/

---

## 0. Choose your database (do this BEFORE configuring DB vars)

Your project supports both via env vars (`DB_ENGINE`, `DB_NAME`, ...), defaulting to
SQLite. cPanel MySQL is the solid choice.

- **Option A — MySQL (recommended, typical cPanel).**
  In cPanel → **MySQL Databases**, create a database + user, add the user to the db
  with **ALL PRIVILEGES**. You'll get 4 values: db name, user, password, host.
- **Option B — SQLite.** Keep it on the server disk. Simpler but fragile; the DB file
  lives inside your app folder.

The steps below work for either. For MySQL replace the `DB_*` env values accordingly.

---

## 1. Create the subdomain

cPanel → **Domains** → **Create a New Domain** (or **Subdomains**).

- Enter your subdomain, e.g. `app.yourdomain.com`.
- Set its Document Root, e.g. `app` (leave **outside** `public_html` for security).
- If you don't own a domain yet, use a free subdomain under TrueHost's domain.

---

## 2. Upload your code

The quickest reliable path is a **zip of the GitHub repo**, or git clone.

1. On GitHub, go to `SimonMuturi123/personal-site` → **Code** → **Download ZIP**.
2. cPanel → **File Manager** → open the Document Root folder (e.g. `app`).
3. Upload the ZIP, then right-click it and **Extract**.
4. Move the contents (the folder named `personal-site-master/...`) UP into the
   Document Root so that `passenger_wsgi.py`, `manage.py`, and `requirements.txt`
   sit **directly** in the Document Root (not a nested subfolder).

Final layout should be:

```
app/                     <- your subdomain's Document Root = Application Root
├── personal_site/
├── static/
├── manage.py
├── passenger_wsgi.py
├── requirements.txt
└── .env
```

If you have SSH access (cPanel → **Terminal**), an alternative is:

```bash
cd ~/app
git clone https://github.com/SimonMuturi123/personal-site.git .
```

---

## 3. Create the Python application

cPanel → **Software** → **Setup Python App** → **Create Application**.

| Field | Value |
|---|---|
| **Python version** | `3.12` (matches Django 6.1) |
| **Application root** | your Document Root, e.g. `app` |
| **Application URL** | select your subdomain (e.g. `app.yourdomain.com`) |
| **Application startup file** | `passenger_wsgi.py` |
| **Application entry point** | `application` |
| **Application mode** | Production |

Click **Create**. cPanel builds a virtualenv and a placeholder `passenger_wsgi.py`.

**Edit `passenger_wsgi.py`** under your Application Root and make sure it imports the
project WSGI app (the repo already ships one — just make sure it wasn't overwritten):

```python
import os, sys
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "personal_site.settings")
from django.core.wsgi import get_wsgi_application
application = get_wsgi_application()
```

---

## 4. Configure environment variables

**Important:** cPanel's Python manager does **NOT** auto-load a `.env` file, but your
project DOES load one at startup via `python-dotenv` (`load_dotenv(BASE_DIR / ".env")`).
Two options:

**Option A — upload a `.env` file** into the Application Root. `.env` is gitignored
locally and the repo one is NOT the production one. Create a production `.env`:

```
DEBUG=False
SECRET_KEY=<a long random string>
ALLOWED_HOSTS=app.yourdomain.com

DB_ENGINE=django.db.backends.mysql
DB_NAME=<your_mysql_db_name>
DB_USER=<your_mysql_user>
DB_PASSWORD=<your_mysql_password>
DB_HOST=localhost
DB_PORT=
```

> If you kept SQLite, omit the `DB_*` lines entirely.

**Option B — use cPanel's "Add Variable"** in Setup Python App for each key. This is
more secure (never stored in a file), but does not include `.env`. If you use this,
still keep `SECRET_KEY` etc. as cPanel variables.

Use your real domain in `ALLOWED_HOSTS`. Add both the bare subdomain and, if used,
`www`.

---

## 5. Install dependencies

In **Setup Python App** for your app, add `requirements.txt` under **Configuration
files**, then click **Run Pip Install**.

Or, if you prefer Terminal/SSH:

```bash
source /home/USERNAME/virtualenv/app/3.12/bin/activate
cd ~/app
pip install -r requirements.txt
```

> If `mysqlclient` fails to build (needs `default-libmysqlclient-dev`), your host may
> not have MySQL dev headers. Alternative MySQL-purconn: `pip install mysqlclient`
> usually works on CloudLinux (TrueHost). If not, fall back to SQLite, or contact
> support to confirm Python MySQL support.

---

## 6. Run migrations, collect static, create superuser

In the virtualenv, from the Application Root:

```bash
python manage.py migrate
python manage.py collectstatic --noinput
python manage.py createsuperuser
```

If you later **import your existing SQLite data**, see the "Migrate data" note below.

---

## 7. Objects/permissions (dates & site)

Your project uses `django.contrib.sites` (`SITE_ID = 1`) and a `SiteSettings` model
with a `canonical_domain`. After deploying:

- Visit **admin** (`https://app.yourdomain.com/admin/`) and log in as the superuser.
- Update **Sites** → set the example.com domain to your real subdomain/domain.
- Update **Site Settings** → set `canonical_domain` to your real domain.

---

## 8. Restart & test

In **Setup Python App**, click **Restart** for your app. Then visit:

- `https://app.yourdomain.com/` – homepage
- `https://app.yourdomain.com/admin/` – admin login
- `https://app.yourdomain.com/static/...` – static assets (WhiteNoise serves these)

If you get a **503**, recreate the app with a different Python version (TrueHost KB
suggestion). For **500s**, check the app's error log / `passenger.log` in the
Application Root, or run `python manage.py runserver` in the virtualenv to see the
traceback with `DEBUG=True`.

---

## Migrating existing local data (optional)

If you have content in your local SQLite you want preserved:

1. Dump locally: `python manage.py dumpdata --natural-foreign --natural-primary -e contenttypes -e auth.permission -o data.json` (adapt as needed).
2. After migrations on the server, load it: `python manage.py loaddata data.json`.

Note: uploaded files under `/media/` also need to be uploaded (they are gitignored).
Zip your local `media/` folder, upload to the server's Application Root, and extract
so it lands in `<root>/media/`.

---

## Updating later

- Upload new files / `git pull` into the Application Root.
- `source .../activate && pip install -r requirements.txt` if deps changed.
- `python manage.py migrate` if models changed.
- `python manage.py collectstatic --noinput` (CSS/JS already carry `?v=` cache-busts).
- Restart the app in Setup Python App.