# API deploy — cPanel one-time setup

The GitHub Actions workflow at `.github/workflows/deploy-api.yml` ships `services/api` to Novatrend cPanel via SSH on every push to `main` that touches the API. This doc covers the **one-time setup** you do on cPanel + GitHub before the first deploy.

## 1. Confirm Node.js ≥ 22 LTS is available

Open cPanel → **Setup Node.js App** → **Create Application**. The Node version dropdown should include **22.x LTS** (Node 24 also fine). Pick **22.22.0** — Maintenance LTS through April 2027, most battle-tested with Drizzle/Hono/mysql2.

## 2. Create the subdomain

cPanel → **Domains** → **Create A New Domain** → `api.banzerathletics.com`.

- Document Root: leave the default suggestion (e.g. `/home/<youruser>/api.banzerathletics.com/public_html`) — Passenger will replace public-html serving with the Node app.
- SSL: cPanel usually auto-provisions a Let's Encrypt cert. Confirm under **SSL/TLS Status**.

## 3. Create the Node.js app

cPanel → **Setup Node.js App** → **Create Application**:

| Field | Value |
|---|---|
| Node.js version | **22.22.0** |
| Application mode | `production` |
| Application root | `api.banzerathletics.com` (path relative to home, e.g. `/home/<youruser>/api.banzerathletics.com`) |
| Application URL | `api.banzerathletics.com` |
| Application startup file | `dist/server.js` |

Click **Create**. The app root folder gets created with a virtualenv. **Don't put any files in it yet** — the deploy workflow rsyncs them.

## 4. Set environment variables in cPanel

Same screen (**Setup Node.js App** → click your app → "Environment variables"):

| Name | Value |
|---|---|
| `NODE_ENV` | `production` |
| `CORS_ORIGINS` | `https://banzerathletics.com,https://app.banzerathletics.com` |
| `DATABASE_HOST` | `localhost` |
| `DATABASE_PORT` | `3306` |
| `DATABASE_USER` | the cPanel-created MySQL username |
| `DATABASE_PASSWORD` | the cPanel-created MySQL password |
| `DATABASE_DB` | the cPanel-created database name |
| `WEB_BASE_URL` | `https://banzerathletics.com` (used in email links) |
| `SMTP_HOST` | the cPanel SMTP hostname (e.g. `mail.banzerathletics.com`) |
| `SMTP_PORT` | typically `587` for STARTTLS, `465` for implicit TLS |
| `SMTP_SECURE` | `false` for port 587, `true` for port 465 |
| `SMTP_USER` | the cPanel mailbox username (e.g. `hello@banzerathletics.com`) |
| `SMTP_PASSWORD` | the mailbox password |
| `SMTP_FROM` | display sender, e.g. `Banzer Athletics <hello@banzerathletics.com>` |

> The API reads these from `process.env` at boot — Passenger injects them. **Don't upload a `.env` file**; it's intentionally excluded from the rsync.

`PORT` is set automatically by Passenger — don't override it.

## 5. Create a `.env.production` for migrations *(optional, for SSH-run migrations)*

The deploy step that runs `pnpm db:migrate` over SSH sources `.env.production` from the app root because env vars set in cPanel's UI only reach Passenger-spawned processes, not ad-hoc SSH commands.

Via cPanel **File Manager** create `~/api.banzerathletics.com/.env.production` with the same `DATABASE_*` values as above. Permissions 600.

```
DATABASE_HOST=localhost
DATABASE_PORT=3306
DATABASE_USER=...
DATABASE_PASSWORD=...
DATABASE_DB=...
```

## 6. Set up SSH access for CI

cPanel → **SSH Access** (enable if not already) → **Manage SSH Keys** → **Generate a New Key** *(or import the public half of a key you generated locally)*. Authorize the key.

Locally, generate a deploy key for GitHub:

```bash
ssh-keygen -t ed25519 -f ~/.ssh/banzer_api_deploy -N "" -C "github-actions deploy"
```

- Copy the contents of `~/.ssh/banzer_api_deploy.pub` into cPanel → **Manage SSH Keys** → "Import Key" (public key).
- Authorize the imported key.
- Copy the contents of `~/.ssh/banzer_api_deploy` (private — long, multi-line) — this is the `CPANEL_SSH_PRIVATE_KEY` GitHub secret.

Test from your laptop first:

```bash
ssh -i ~/.ssh/banzer_api_deploy -p <ssh-port> <cpanel-user>@<ssh-host>
```

If that drops you at a shell, GitHub Actions can use the same key.

## 7. Add GitHub Secrets

Repo → **Settings → Secrets and variables → Actions → New repository secret**:

| Name | Value |
|---|---|
| `CPANEL_SSH_HOST` | hostname Novatrend gave you for SSH (often the same as your cPanel control panel host) |
| `CPANEL_SSH_PORT` | usually `22`, sometimes `2222` or similar — check Novatrend's docs |
| `CPANEL_SSH_USER` | your cPanel username |
| `CPANEL_SSH_PRIVATE_KEY` | the **full private key** including `-----BEGIN OPENSSH PRIVATE KEY-----` lines |
| `CPANEL_API_APP_PATH` | absolute path on the server, e.g. `/home/<youruser>/api.banzerathletics.com` |

And one **variable** (Variables tab, not Secrets):

| Name | Value |
|---|---|
| `API_PUBLIC_URL` | `https://api.banzerathletics.com` (used for the post-deploy smoke test) |

## 8. First deploy

```
git push                                                       # auto-deploy
# or via UI:
# Actions → "Build & Deploy API to cPanel" → Run workflow
```

Watch the action. The first deploy needs ~2 minutes for `pnpm install` of API deps. Subsequent deploys are faster (cached).

## 9. First migration

The very first time, run this manually so the DB tables exist:

- Actions → "Build & Deploy API to cPanel" → **Run workflow** → check **"Run DB migrations after deploy"** → Run.

Alternatively, SSH in once and run by hand:

```bash
ssh <user>@<host>
cd ~/api.banzerathletics.com
set -a; source .env.production; set +a
node dist/db/migrate.js
```

After that's green, `curl https://api.banzerathletics.com/health/db` should return `{"status":"ok","latencyMs":<n>}`.

## Day-to-day

- Push to `main` → workflow auto-deploys (no migrations).
- Schema changed? After merging, dispatch the workflow with **"Run DB migrations after deploy"** checked.
- Need to roll back? `git revert` the bad commit and push — the workflow re-deploys the previous version.

## Troubleshooting

| Symptom | Likely cause |
|---|---|
| `Error: ENETUNREACH` from CI's rsync | Wrong `CPANEL_SSH_HOST` or port closed; verify with manual SSH first |
| `Permission denied (publickey)` | Public key not authorized in cPanel, or wrong user/host secret |
| API serves a Passenger error page | Restart didn't pick up — check the `tmp/restart.txt` mod-time, then check `~/api.banzerathletics.com/stderr.log` |
| `/health` 200 but `/health/db` 503 | DB env vars not set in cPanel UI, or MySQL user has no perms on the DB |
