# Namecheap cPanel Deployment — crm.fxtltd.co.uk

Complete runbook for deploying FXT CRM Mailer to Namecheap shared hosting.
No root, no Supervisor, no systemd, no Node.js required on the server.

Throughout, replace `CPANEL_USER` with your actual cPanel username.

---

## 1. Check PHP in cPanel

1. cPanel → **Select PHP Version** (or *MultiPHP Manager*).
2. Choose **PHP 8.3** (8.2 minimum).
3. In **Extensions**, enable all of:
   `bcmath, ctype, curl, dom, fileinfo, gd, intl, json, mbstring, openssl,
   pdo, pdo_mysql, tokenizer, xml, zip`
   (most are on by default; typically you only need to tick `intl`, `gd`, `zip`).
4. In **Options**, set at least:
   - `upload_max_filesize` = 25M
   - `post_max_size` = 26M
   - `memory_limit` = 256M
   - `max_execution_time` = 120

## 2. Create the database

1. cPanel → **MySQL® Databases**.
2. Create database `fxtcrm` → becomes `CPANEL_USER_fxtcrm`.
3. Create user `fxtcrm` with a long random password → `CPANEL_USER_fxtcrm`.
4. Add the user to the database with **ALL PRIVILEGES**.
5. Note all three values for `.env`.

## 3. Point the subdomain

1. cPanel → **Domains** (or *Subdomains*): create/verify `crm.fxtltd.co.uk`.
2. Note its **document root** (e.g. `/home/CPANEL_USER/crm.fxtltd.co.uk`).
   - If cPanel lets you set the document root, you may point it at
     `/home/CPANEL_USER/fxt-crm-app/public` and skip the split layout in step 5.
     Namecheap shared plans usually **do** allow editing addon/subdomain roots.
3. DNS: `crm` A/CNAME record must point at the hosting server (already the case if the
   main domain is hosted here).

## 4. Upload the application

Upload `fxt-crm-app-production.zip` (contains `vendor/` and compiled assets — nothing to
build on the server):

1. cPanel → **File Manager** → go to `/home/CPANEL_USER/` (your home, **not** public_html).
2. Upload the zip, right-click → *Extract*. You should now have
   `/home/CPANEL_USER/fxt-crm-app/`.
3. Copy `deploy/app_root.htaccess` to `/home/CPANEL_USER/fxt-crm-app/.htaccess`
   (defence in depth — the folder is already outside the web root).

Via SSH instead (if enabled): `unzip fxt-crm-app-production.zip -d ~/`.

## 5. Wire the document root

**Option A — document root points at Laravel /public (preferred).**
Set the subdomain's document root to `/home/CPANEL_USER/fxt-crm-app/public`. Done.
Add the HTTPS-redirect block from `deploy/document_root/.htaccess` to
`fxt-crm-app/public/.htaccess` (the `RewriteCond %{HTTPS}` lines).

**Option B — fixed document root (fallback).**
If the document root cannot be changed (e.g. `/home/CPANEL_USER/crm.fxtltd.co.uk`):

1. Copy **the contents of** `fxt-crm-app/public/` into the document root
   (`build/`, `favicon.ico`, `robots.txt`, …).
2. Replace `index.php` in the document root with `fxt-crm-app/deploy/document_root/index.php`.
3. Edit its `FXT_APP_PATH` line to the absolute app path:
   `define('FXT_APP_PATH', '/home/CPANEL_USER/fxt-crm-app');`
4. Replace `.htaccess` in the document root with `fxt-crm-app/deploy/document_root/.htaccess`.
5. After every future update, re-copy `public/build/` to the document root.

## 6. Configure .env

```bash
cd ~/fxt-crm-app
cp .env.example .env
nano .env        # or edit in File Manager
```

Fill in: `DB_DATABASE`, `DB_USERNAME`, `DB_PASSWORD`, `FXT_COMPANY_ADDRESS`,
`FXT_WEBHOOK_TOKEN` (generate: `php -r "echo bin2hex(random_bytes(32));"`),
and your email-provider credentials (see EMAIL_PROVIDER_SETUP.md).
Keep `APP_ENV=production`, `APP_DEBUG=false`, `APP_URL=https://crm.fxtltd.co.uk`.

Then:

```bash
php artisan key:generate
```

> **PHP binary on Namecheap:** plain `php` usually works over SSH. If it is an old
> version, use the versioned binary, e.g. `/usr/local/bin/ea-php83`
> (`ls /usr/local/bin/ea-php*` to see what exists). Use the same binary in the cron job.

No SSH? Use cPanel **Terminal** (same commands). As a last resort every artisan step can
be run through cPanel's *Cron Jobs* one-off entries, but Terminal is available on all
current Namecheap shared plans.

## 7. Migrate and initialise

```bash
cd ~/fxt-crm-app
php artisan migrate --force        # creates all tables
php artisan db:seed --force        # roles + permissions (idempotent)
php artisan storage:link           # public storage symlink (logo uploads)
php artisan config:cache && php artisan route:cache && php artisan view:cache
```

If `storage:link` fails (symlinks disabled), skip it — only the branding logo feature
needs it; everything else works without.

## 8. Permissions

```bash
chmod -R u+rwX storage bootstrap/cache
find ~/fxt-crm-app -type d -exec chmod 755 {} \;
find ~/fxt-crm-app -type f -exec chmod 644 {} \;
chmod 600 ~/fxt-crm-app/.env
chmod -R 775 ~/fxt-crm-app/storage ~/fxt-crm-app/bootstrap/cache
```

(On cPanel the PHP process runs as your user, so 755/644 with writable
`storage` + `bootstrap/cache` is sufficient — never use 777.)

## 9. SSL

1. cPanel → **SSL/TLS Status** → tick `crm.fxtltd.co.uk` → *Run AutoSSL*.
2. The supplied `.htaccess` already redirects HTTP → HTTPS.
3. Verify: `https://crm.fxtltd.co.uk` shows the login page with a valid padlock.

## 10. Cron

Follow [CPANEL_CRON_SETUP.md](CPANEL_CRON_SETUP.md). Short version — cPanel → Cron Jobs:

```cron
* * * * * /usr/local/bin/php /home/CPANEL_USER/fxt-crm-app/artisan schedule:run >> /home/CPANEL_USER/fxt-crm-cron.log 2>&1
```

## 11. First administrator

```bash
cd ~/fxt-crm-app && php artisan admin:create
```

Interactive prompts: name, email, password (min 12 chars), role → choose **Super Admin**.
No hardcoded credentials exist anywhere.

## 12. Post-deployment verification checklist

| # | Check | Expected |
|---|---|---|
| 1 | `https://crm.fxtltd.co.uk` | Login page over HTTPS |
| 2 | `https://crm.fxtltd.co.uk/.env` | **404/403 — must NOT download** |
| 3 | `https://crm.fxtltd.co.uk/storage/logs/laravel.log` | 404/403 |
| 4 | `https://crm.fxtltd.co.uk/vendor/autoload.php` | 404/403 |
| 5 | Log in as the new admin | Dashboard loads |
| 6 | System Status page | "Cron: Active" within 2 minutes of adding the cron |
| 7 | Settings → Email Provider → save credentials | Saved, secrets shown masked |
| 8 | Templates → create → *Send test* | Test email arrives |
| 9 | Imports → upload a small CSV → map → start | Completes via cron; stats correct |
| 10 | Create a tiny campaign to yourself → launch | Email arrives with unsubscribe link |
| 11 | Click the unsubscribe link | Confirmation page; address appears under Suppression |
| 12 | Provider webhook test (see EMAIL_PROVIDER_SETUP.md) | Event visible, System Status clean |

## 13. Updating the application later

```bash
cd ~/fxt-crm-app
php artisan down
# upload/extract the new build over the top (keep .env and storage/)
composer install --no-dev --optimize-autoloader   # only if composer.lock changed
php artisan migrate --force
php artisan config:cache && php artisan route:cache && php artisan view:cache
php artisan queue:restart
php artisan up
```

If using layout Option B, re-copy `public/build/` into the document root.

## Troubleshooting

**500 error, blank page** — check `storage/logs/laravel-*.log`. Usual causes: missing
`.env`/APP_KEY (`php artisan key:generate`), stale caches (`php artisan config:clear
route:clear view:clear cache:clear`), or wrong `FXT_APP_PATH` in the document-root
`index.php`.

**Assets load without styling** — layout Option B and `public/build/` was not copied to
the document root.

**"Cron does not appear to be running" banner** — see CPANEL_CRON_SETUP.md
troubleshooting section.

**Campaign stuck in Queued** — cron not running, or the email provider is not configured
(System Status shows both). Also check Settings → Sending limits (daily limit reached?).

**Import stuck in Staging/Processing** — check System Status → last processing error;
oversized files are resumed automatically, so simply wait for the next cron runs.

**Permission denied on storage/** — re-run the chmod commands from step 8.
