# cPanel deployment runbook

This runbook deploys the replacement Upharm API with Node.js, Prisma and a
cPanel-managed MySQL or MariaDB database. It intentionally does not run
`prisma migrate dev` on the server.

## Hosting requirements

- Node.js 20 or newer.
- A cPanel MySQL database and database user with all privileges on that database.
- SSH or cPanel Terminal access for installation and migration commands.
- Application startup file: `dist/app.js`.
- Reverse-proxy support for HTTPS and, if chat is enabled, WebSocket upgrades.

## Create the database

1. In **MySQL Databases**, create the database and user.
2. Add the user to the database with **ALL PRIVILEGES**.
3. Note the cPanel-prefixed names, for example `account_upharm` and
   `account_upharm_api`.
4. Build the connection string without committing it:

   ```text
   mysql://account_upharm_api:URL_ENCODED_PASSWORD@localhost:3306/account_upharm?connection_limit=5
   ```

Passwords containing `@`, `:`, `/`, `?`, `#` or `%` must be URL encoded.

## Application environment

Configure these in cPanel's Node.js application environment:

- `NODE_ENV=production`
- `PORT` (normally supplied by cPanel)
- `DATABASE_URL`
- `APP_ORIGIN=https://your-web-domain`
- `CORS_ORIGINS=https://your-web-domain,https://your-admin-domain`
- `BACKEND_URL=https://your-api-domain`
- `JWT_SECRET` and `JWT_REFRESH_SECRET` as different random values
- Resend and email-brand variables from `.env.example`
- OAuth variables only when those providers are enabled
- `FILE_STORAGE_DRIVER=local` or `gcs` plus the corresponding GCS variables

Do not upload `.env` into the public web root.

When the member application uses its built-in API bridge, set OAuth callback
URLs to the member domain so secure session cookies are created on that same
origin. Register these exact URLs with Google or Microsoft:

```text
GOOGLE_REDIRECT_URI=https://your-web-domain/api/backend/api/v1/auth/oauth/google/callback
MICROSOFT_REDIRECT_URI=https://your-web-domain/api/backend/api/v1/auth/oauth/microsoft/callback
```

Absolute callback URLs are accepted; the relative defaults in `.env.example`
remain suitable when the API is served on the same origin.

## First deployment

From the application root:

```bash
npm ci
npm run db:generate
npm run db:migrate:deploy
npm run build
```

Configure cPanel to start `dist/app.js`, then restart the application. Verify:

```text
GET https://your-api-domain/health
GET https://your-api-domain/ready
```

`/health` proves the Node.js process is responding. `/ready` additionally proves
that Prisma can query MySQL.

## Member and admin applications

Create separate cPanel Node.js applications for `app_frontend` and
`admin_frontend`. In each project:

```bash
npm ci
npm run build
cp -R public .next/standalone/
mkdir -p .next/standalone/.next
cp -R .next/static .next/standalone/.next/
```

Deploy the contents of `.next/standalone`, use `server.js` as the startup file,
and set `BACKEND_URL=https://your-api-domain`. Assign the member and admin apps
different domains or subdomains. The browser talks to each Next.js origin and
the included `/api/backend` handler forwards requests to the API.

## Routine deployment

1. Put the legacy API on standby, not offline.
2. Back up MySQL.
3. Upload or pull the reviewed release.
4. Run `npm ci`, `npm run db:migrate:deploy`, and `npm run build`.
5. Restart the cPanel Node.js application.
6. Check `/health`, `/ready`, login, one course read, and one admin read.
7. Review the cPanel application log for Prisma, Resend and WebSocket errors.

## Rollback

- Route traffic back to the legacy API if a release fails smoke testing.
- Restore the pre-deployment MySQL backup only when the release wrote
  incompatible data. Prisma migrations are forward-only in production.
- Keep uploaded files and the database backup together; restoring only one can
  leave file records pointing at missing objects.
