# PRP-011 — cPanel deployment hardening

- **Status:** Draft
- **Plan:** Lite
- **Depends on:** all prior PRPs (deploys the assembled app)
- **Estimated effort:** ~1 day (incl. first real deploy)
- **Pro KB ref:** —

> **Audience:** junior dev with cPanel access (SSH + root available). This PRP ships Lite to
> **`lite.shiprexnow.com`** as a single Node app (API + built React) behind Passenger, with a
> repeatable release runbook. It also adds **auth rate limiting** as the last hardening item.

---

## 1. Goal / Why
Get Lite live reliably and make releases boring and repeatable, with a rollback path.

## 2. Scope
**In:** subdomain + Node app config; production env; DB provisioning; build/migrate/seed on the box;
SSL + force HTTPS; auth rate limiting; smoke checklist; release + rollback runbook.
**Out:** CI/CD, containers, multi-node scaling.

## 3. Prerequisites
- cPanel with **Setup Node.js App** (Passenger), Node 20–22, SSH access (root available if needed).
- DNS for `lite.shiprexnow.com` pointing at the cPanel server (A record).
- The repo available to the server (cPanel Git, or upload).

## 4. Step-by-step implementation (code hardening first)

### Step 1 — Add auth rate limiting
Protect login/register from brute force using the limiter from PRP-006.
In **`server/src/modules/auth/auth.routes.js`**:
```js
const { rateLimit } = require('../../middleware/rateLimit');
router.post('/login', rateLimit({ max: 10, key: 'login' }), asyncHandler(/* existing */));
router.post('/register', rateLimit({ max: 5, key: 'register' }), asyncHandler(/* existing */));
```
Build/test locally (`npm run dev:server`); 11 quick logins from one IP → `429 RATE_LIMITED`.

### Step 2 — Confirm production-safety items (already in place; verify)
- `helmet` enabled, `trust proxy` set, error stack hidden in prod (`isProd()` in `error.js`).
- `app.js` listens on `process.env.PORT` (Passenger provides it).
- SPA fallback returns `index.html` for non-`/api` GETs.
- `.env` is gitignored; secrets only via cPanel env.

Commit the rate-limit change (see §7 Commit 1) before deploying.

## 5. Deployment steps (on the cPanel box)

### A. Create the subdomain
cPanel → **Domains / Subdomains** → create `lite.shiprexnow.com`. Note its document root (you'll point
the Node app's **Application root** at the repo folder, not necessarily this docroot).

### B. Create the database + user
cPanel → **MySQL® Databases**:
1. Create DB → name becomes `cpuser_shiprexlite`.
2. Create user → `cpuser_shipuser` with a strong password.
3. **Add user to database** with **ALL PRIVILEGES**.

### C. Get the code onto the server
- **Git:** cPanel → *Git Version Control* → Create/Clone into e.g. `~/shiprex-lite`, **or**
- **Upload:** zip the repo (excluding `node_modules`, `client/dist`) and extract to `~/shiprex-lite`.

### D. Create the Node.js application
cPanel → **Setup Node.js App** → **Create Application**:
- Node version: 20.x or 22.x
- Application mode: **Production**
- Application root: `shiprex-lite` (folder containing `app.js`)
- Application URL: `lite.shiprexnow.com`
- Application startup file: `app.js` → **Create**.

### E. Set environment variables (Node App UI → Environment variables)
```
NODE_ENV=production
DB_HOST=localhost
DB_PORT=3306
DB_NAME=cpuser_shiprexlite
DB_USER=cpuser_shipuser
DB_PASSWORD=********
JWT_SECRET=<64+ random chars>
JWT_EXPIRES_IN=7d
ROOT_ADMIN_EMAIL=admin@shiprexnow.com
ROOT_ADMIN_PASSWORD=<strong>
ROOT_ADMIN_NAME=Shiprex Root
APP_URL=https://lite.shiprexnow.com
CORS_ORIGINS=
ORDER_DAILY_CAP=30
PRO_UPGRADE_URL=https://www.shiprexnow.com
BREVO_API_KEY=<key>
BREVO_SENDER_NAME=Shiprex Team
BREVO_SENDER_EMAIL=no-reply@notifications.shiprexnow.com
BREVO_CONTACT_LIST_ID=<id or blank>
SALES_NOTIFY_EMAIL=islam.baraka.90@gmail.com
```

### F. Install + build + migrate + seed
Enter the app's virtualenv (the command cPanel shows, e.g. `source /home/cpuser/nodevenv/.../bin/activate`),
then from the app root:
```bash
npm run cpanel:install
# = npm install && npm run build:client && npm run migrate && npm run seed
```
If `npm` isn't found in SSH, use the path cPanel prints, or the Node App UI **Run NPM Install** + a
**Run JS Script** for the build/migrate/seed steps.

### G. Start / restart
Click **Restart** in the Node App screen. Then smoke test (§6).

### H. SSL + force HTTPS
- cPanel → **SSL/TLS Status** → run **AutoSSL** for `lite.shiprexnow.com` (Let's Encrypt).
- Force HTTPS: enable the cPanel "Force HTTPS Redirect" for the subdomain (Domains screen), or add a
  redirect. Passenger fronts the Node app, so no custom `.htaccess` rewrite is needed for SPA routing.

## 6. Smoke test (run after every deploy)
```bash
# health
curl -s https://lite.shiprexnow.com/api/health
# features
curl -s https://lite.shiprexnow.com/api/features | head -c 120
# register a throwaway company (use a real inbox to confirm Brevo welcome), then delete it later
```
In a browser:
- `https://lite.shiprexnow.com/` loads the dashboard (login).
- Log in as root → see the companies console.
- Register a company → create an order → open public tracking.
- Confirm the welcome email arrives (Brevo).

**Acceptance criteria**
- [ ] `https://lite.shiprexnow.com/` serves the dashboard; `/api/health` OK over HTTPS.
- [ ] Register + login + create order + tracking work in production.
- [ ] Welcome email sends from the production box.
- [ ] Secrets only in env (no `.env` committed); prod error responses hide stack traces.
- [ ] Valid SSL cert; HTTP redirects to HTTPS.
- [ ] Auth endpoints rate-limited.

## 7. Release & rollback runbook (document in docs/DEPLOYMENT_CPANEL.md too)
**Release**
```bash
git pull                 # or upload changed files
npm install              # only if dependencies changed
npm run build:client     # only if client changed
npm run migrate          # only if new migrations
# Restart in the Node App UI, then run the §6 smoke test
```
**Rollback**
```bash
git checkout <previous-good-commit>
npm install && npm run build:client
npm run migrate:rollback # ONLY if a bad migration shipped (back up DB first!)
# Restart + smoke test
```
Always **back up the DB** before running migrations in production (cPanel → *Backup* or
`mysqldump cpuser_shiprexlite > backup.sql`).

## 8. Git commits
**Commit 1 — auth rate limiting**
```bash
git add server/src/modules/auth/auth.routes.js
git commit -m "feat(auth): PRP-011 rate-limit login/register

Applies the in-memory limiter to login (10/min) and register (5/min) per IP.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>"
```

**Commit 2 — deployment docs refresh + PRP status**
```bash
git add docs/DEPLOYMENT_CPANEL.md PRPs/PRP-011-cpanel-deployment.md PRPs/README.md
git commit -m "docs(prp): PRP-011 deploy runbook + mark done"
```

**Tag the first production release**
```bash
git tag -a v0.1.0 -m "Shiprex Lite v0.1.0 — first production deploy"
# push the tag yourself when you add a remote: git push origin v0.1.0
```

## 9. Done checklist
- [ ] Auth rate limiting added + verified.
- [ ] Subdomain + Node app created; env set; DB provisioned.
- [ ] install/build/migrate/seed run on the box; app restarts cleanly.
- [ ] SSL issued; HTTPS forced; smoke test passes.
- [ ] Release/rollback runbook documented.
- [ ] Commits made; release tagged; PRP marked Done.
