# DEPLOY.md — cPanel/WHM Deployment

Reference for setting up and deploying the app on the self-managed cPanel/WHM
server described in ARCHITECTURE.md. This is written for whoever has SSH/WHM
access to that box — it can't be executed from this repo alone.

## Automated deploy layout

`.github/workflows/deploy.yml` (push to `main`) and `.cpanel.yml` (cPanel's
"Deploy HEAD Commit" button) both use a **two-tree layout**:

- a plain git clone/checkout (the "repo clone"), kept up to date by either
  the GitHub Actions SSH step or cPanel's Git Version Control feature
- the **live document root** — a separate directory that the deploy scripts
  copy the repo clone into on every deploy (`cp` plus a `find`/`comm`-based
  prune step for files deleted upstream — this box has no `rsync`); this is
  the tree PHP-FPM, the queue worker, and Reverb actually run from

Everything below that talks about cron, systemd, or "the app directory"
means **the live document root**, not the repo clone — the repo clone is
just a git mirror and is never served or executed directly.

**This server has no Node.js at all.** `public/build` is committed to git
(it's intentionally not in `.gitignore`) instead of being built on the
server. Before pushing to `main`, always run `npm ci && npm run build`
locally and commit the resulting `public/build` — the deploy scripts only
copy files, they never run `npm`.

## One-time server setup

1. **cPanel account** — create the account, confirm `ea-php83` (or newer) is
   installed via MultiPHP Manager, and set it as the PHP version for the
   account.
2. **Document root** — point the domain/subdomain at `<live-doc-root>/public`,
   not `<live-doc-root>` itself (e.g. `smarty.easyweb360.com/public`).
3. **Redis** — install/activate at the server level (WHM → Redis, or
   `dnf install redis` / `apt install redis-server` depending on the OS, then
   `systemctl enable --now redis`). Confirm with `redis-cli ping`.
4. **Database** — create a MySQL or PostgreSQL database + user via
   cPanel → MySQL/PostgreSQL Databases, matching whichever `DB_CONNECTION`
   is set in `.env`.
5. **Clone + install** (one-time, into the live document root — later
   deploys sync into this same directory, so `.env` created here survives).
   No `npm` step — this server has no Node.js; `public/build` comes from
   git, already built by whoever last pushed (see "Automated deploy
   layout" above).
   ```
   git clone <repo> ~/smarty.easyweb360.com
   cd ~/smarty.easyweb360.com
   composer install --no-dev --optimize-autoloader
   cp .env.example .env   # then fill in real values
   php artisan key:generate
   php artisan migrate --force
   ```
6. **Cron** — cPanel → Cron Jobs → add, running every minute, pointed at the
   live document root:
   ```
   * * * * * cd /home/CPANEL_USERNAME/smarty.easyweb360.com && php artisan schedule:run >> /dev/null 2>&1
   ```
7. **Queue worker + Reverb** — copy the two unit files in
   `deploy/systemd/` to `/etc/systemd/system/`, replace `CPANEL_USERNAME`
   and the PHP binary path with the real values (`WorkingDirectory` already
   points at the live document root, e.g. `smarty.easyweb360.com` — adjust
   it if your account's document root differs), then:
   ```
   systemctl daemon-reload
   systemctl enable --now smarty-queue smarty-reverb
   ```
   Supervisor is an equally valid alternative to systemd here if that's
   already how the box is managed — the unit files are a starting point,
   not a requirement.
8. **Reverb reverse proxy** — add a LiteSpeed rewrite rule (or a
   `<VirtualHost>`/`.htaccess` proxy block, depending on the web server in
   front) forwarding `wss://<domain>/app/*` and `/apps/*` to
   `127.0.0.1:8080`, so Reverb is reachable through the normal domain/port
   instead of exposing 8080 directly to the internet.
9. **Storage** — `php artisan storage:link` so `public/storage` resolves;
   certificates/OG images and uploads live on local disk under the cPanel
   account (see ARCHITECTURE.md — Hosting).

## Staging vs. production

Use a separate subdomain (e.g. `staging.<domain>`) pointed at a second
cPanel account/database, deployed from a `staging` branch. Same systemd
units, same Redis instance is fine as long as `REDIS_PREFIX`/`CACHE_PREFIX`
and the queue connection name differ from production so keys don't collide.

## Backups

- **Database** — cPanel's built-in MySQL/PostgreSQL backup job (WHM →
  Backup Configuration), scheduled daily, retained on a separate disk/offsite
  target, not just the same account.
- **Files** — `storage/app` (certificates, uploads) backed up the same way.
- **Verify restorability** periodically by actually restoring a backup into
  a scratch database, not just confirming the backup job "succeeded."

## Deploying an update

Normally this happens automatically — push to `main` (GitHub Actions) or
click "Deploy HEAD Commit" in cPanel's Git Version Control (`.cpanel.yml`).
Both copy the repo clone into the live document root, no `rsync`/`npm`
involved (see "Automated deploy layout" above). **Before pushing**, run
`npm ci && npm run build` locally and commit `public/build` — the server
never builds it. To do the deploy step itself by hand:

```
cd ~/smarty.easyweb360.com
cp -R --no-preserve=all ~/repositories/smarty/. .
composer install --no-dev --optimize-autoloader
php artisan migrate --force
php artisan config:cache && php artisan route:cache && php artisan view:cache
php artisan queue:restart
```

Note: the `easyweb3` deploy account has no `systemctl` access at all (not
a PATH issue — shared/managed cPanel accounts generally don't get systemd
control even when the account owner has separate root/WHM access to the
box). `php artisan queue:restart` needs no root — it flags the cache, and
the running worker exits after its current job and gets respawned by
`Restart=always` in `deploy/systemd/smarty-queue.service`. Reverb has no
equivalent signal-based restart; only restart it (via WHM/root SSH —
`systemctl restart smarty-reverb`) when Reverb's own server code changes,
which is rare for an ordinary app deploy.

## Why this isn't marked done in PHASES.md

The Phase 0 "Hosting & environment" checklist items describe actions on the
real server (provisioning the cPanel account, confirming Redis is reachable,
adding the actual cron entry, verifying a real backup restore, deciding the
staging strategy for the live domain). This document and the files under
`deploy/` are the concrete deliverables for that work, but they need to be
carried out and verified by whoever has access to the production box —
PHASES.md stays unchecked on those specific items until that happens, per
the "mark `[x]` only when actually done and verified" rule in CLAUDE.md.
