# Deploying QC Recognition to a cPanel / WHM server (VPS or dedicated, root access)

Both halves run on the **same** box:

- **PHP admin panel** (CodeIgniter 4) → a cPanel subdomain, HTTPS.
- **Python ML service** (FastAPI + FAISS + PyTorch) → a **systemd service on `127.0.0.1:8001`**, never exposed to the internet.

The Android app talks only to the admin panel over HTTPS. The admin panel talks to the ML
service over loopback with a shared token.

> Replace `qcuser`, `yourdomain.com`, and `qc.yourdomain.com` throughout with your real values.

---

## 0. Prerequisites (once, as root)

Install the native libs OpenCV needs, plus a modern Python:

```bash
# CloudLinux / CentOS / AlmaLinux (WHM default):
sudo yum install -y mesa-libGL glib2

# Debian / Ubuntu:
sudo apt-get update && sudo apt-get install -y libgl1 libglib2.0-0
```

Python 3.11 (skip if `python3.11 --version` already works). On CloudLinux you can use the
bundled alt-python, or install from your OS packages.

**Sizing:** give the box at least **2 GB RAM** — torch resnet50 inference holds ~1 GB resident.

---

## 1. Upload the code

Put the repo under the cPanel account home. Cleanest split:

```
/home/qcuser/component-qc/admin      → PHP app (public/ becomes the subdomain docroot)
/home/qcuser/qc-ml                   → copy of the ml-service/ folder
```

Via SSH:

```bash
cd /home/qcuser
git clone <your-repo> component-qc          # or upload+unzip
cp -r component-qc/ml-service qc-ml          # ml-service runs from its own dir
```

---

## 2. Python ML service (systemd)

```bash
cd /home/qcuser/qc-ml
python3.11 -m venv .venv
.venv/bin/pip install --upgrade pip wheel
# CPU-only torch wheels (much smaller than the default CUDA build):
.venv/bin/pip install --extra-index-url https://download.pytorch.org/whl/cpu -r requirements.txt
```

Create the production `.env` in `/home/qcuser/qc-ml/.env`:

```ini
ML_SERVICE_TOKEN=<a long random secret — must match the admin .env below>
DATA_DIR=/home/qcuser/qc-ml/data
EMBED_MODEL=resnet50
HOST=127.0.0.1
PORT=8001
```

> `data/` holds the FAISS index (`index.faiss`), `idmap.json`, and stored image copies.
> If you already built an index locally, copy your local `ml-service/data/` up so recognition
> works immediately; otherwise it starts empty and fills as you (re)embed images from the admin.

Install the service:

```bash
sudo cp /home/qcuser/component-qc/deploy/qc-ml.service /etc/systemd/system/qc-ml.service
sudo nano /etc/systemd/system/qc-ml.service   # set User/Group/WorkingDirectory/paths
sudo systemctl daemon-reload
sudo systemctl enable --now qc-ml
sudo systemctl status qc-ml                    # should be active (running)
```

Verify it answers on loopback (first start is slow — it downloads the resnet50 weights once):

```bash
curl -s http://127.0.0.1:8001/health
# -> {"status":"ok", ...}
```

Watch logs: `journalctl -u qc-ml -f`

---

## 3. PHP admin panel (cPanel subdomain)

1. **WHM/cPanel → Domains → Create a subdomain** `qc.yourdomain.com` and set its
   **Document Root** to `/home/qcuser/component-qc/admin/public`.
2. **MultiPHP Manager** → set that subdomain to **PHP 8.1+**.
   **Select PHP Version → Extensions**: enable `intl, mbstring, curl, gd, fileinfo, mysqlnd, json`.
3. **MySQL Databases** → create a DB + user, add user to DB with all privileges.
4. Install dependencies + set env (SSH):

   ```bash
   cd /home/qcuser/component-qc/admin
   composer install --no-dev --optimize-autoloader
   cp env .env    # if not already present
   ```

5. Edit `/home/qcuser/component-qc/admin/.env`:

   ```ini
   CI_ENVIRONMENT = production
   app.baseURL = 'https://qc.yourdomain.com/'

   database.default.hostname = localhost
   database.default.database = qcuser_qc
   database.default.username = qcuser_qc
   database.default.password = '******'
   database.default.DBDriver = MySQLi

   # ML service — same box, loopback, token must equal qc-ml/.env
   ML_SERVICE_URL   = 'http://127.0.0.1:8001'
   ML_SERVICE_TOKEN = '<same secret as step 2>'
   ```

6. **Fix the RewriteBase** — the local `.htaccess` is set for a subfolder. At a subdomain
   root it must be `/`. Edit `admin/public/.htaccess`:

   ```apache
   RewriteBase /
   ```

7. Run migrations, set permissions:

   ```bash
   cd /home/qcuser/component-qc/admin
   php spark migrate
   chmod -R 775 writable
   ```

8. **Enable HTTPS**: cPanel → **SSL/TLS Status → Run AutoSSL** for the subdomain.

Load `https://qc.yourdomain.com/` — you should get the admin login. The dashboard's
ML-service health card should show **reachable** (that confirms PHP → loopback → uvicorn works).

---

## 4. Firewall / security

- The ML service is bound to `127.0.0.1` — **do not** open port 8001 in CSF/the firewall.
- If CSF is installed and blocks loopback, ensure `lo` traffic is allowed (it is by default).
- Keep `ML_SERVICE_TOKEN` long and secret; it's the second lock behind loopback-only binding.
- In the admin **Security** settings, set the IP allowlist / geofence / Wi-Fi BSSID to your
  real premises before handing out the app.

---

## 5. Rebuild the Android app for production

In the app, point it at the live URL and rebuild the APK:

- `app/src/config.ts` (and the `QcChecker/src/config.ts` build copy):
  ```ts
  export const API_BASE_URL = 'https://qc.yourdomain.com/';
  ```
- Remove any localhost / `adb reverse` assumptions (those were dev-only).
- Rebuild the release APK and distribute it to the checkers' phones.

Since the app now hits a real HTTPS domain, no USB tunneling is involved in production.

---

## 6. Day-2 operations

| Task | Command |
|------|---------|
| Restart ML service | `sudo systemctl restart qc-ml` |
| ML logs | `journalctl -u qc-ml -f` |
| Update ML code | `git pull` in `component-qc`, `cp -r` into `qc-ml`, `systemctl restart qc-ml` |
| Update PHP code | `git pull`, `composer install --no-dev`, `php spark migrate` |
| Back up | MySQL dump **+** `qc-ml/data/` (the FAISS index + image copies) |
| Rebuild index | admin → re-embed, or `qc-ml/.venv/bin/python cli.py` (see cli.py) |

---

## Alternative: Docker (if the box has Docker)

The repo ships a `Dockerfile` for the ML service. If Docker is available you can skip the
venv/systemd steps:

```bash
cd /home/qcuser/qc-ml
docker build -t qc-ml .
docker run -d --name qc-ml --restart unless-stopped \
  -p 127.0.0.1:8001:8001 \
  -e ML_SERVICE_TOKEN='<secret>' \
  -v /home/qcuser/qc-ml/data:/data \
  qc-ml
```

Note `-p 127.0.0.1:8001:8001` — publish to loopback only, never `0.0.0.0`. Most WHM/CloudLinux
servers don't run Docker, so systemd (section 2) is the primary path.
