# Deploy on cPanel (Setup Node.js App)

One Node.js app serves the web panel **and** the API. Startup file: `server.js`.
You upload the **built bundle** `deploy.zip` — never the source zip. Nothing is compiled on the server.

> Where `deploy.zip` comes from: it is delivered with each release, or you build it on your laptop:
> `npm install` → `npm run build` → `npm run deploy:zip` (PowerShell, in the source folder).

## 1. Database

cPanel → **MySQL® Databases**: create a database (e.g. `vertexso_iescollege`) and a user with **All privileges** on it.
Your `DATABASE_URL` is then `mysql://vertexso_USER:PASSWORD@localhost:3306/vertexso_iescollege`
(if the password has special characters, URL-encode them: `@` → `%40`, `#` → `%23`, `&` → `%26`).

## 2. Clean the application root

File Manager → open the application root (e.g. `iescollege.vertexsoft.lk`).
If you extracted the **source** zip there earlier, delete those files: **Select All**, then *un-select* `.htaccess`, `node_modules`, `public`, `tmp`, `.well-known`, `cgi-bin`, `php.ini`, `.user.ini` — and **Delete** the rest (`apps`, `packages`, `scripts`, `*.md`, `*.zip`, `package*.json`, `server.js`, `Caddyfile`, `docker-compose.yml`, `stderr.log`).

## 3. Upload the bundle

**Upload `deploy.zip`** into the application root → right-click → **Extract** (files land at top level: `server.js` and `package.json` directly inside the root) → delete `deploy.zip`.

## 4. Setup Node.js App

| Field | Value |
|---|---|
| Node.js version | 20.x |
| Application mode | **Production** |
| Application root | `iescollege.vertexsoft.lk` |
| Application URL | `iescollege.vertexsoft.lk` |
| Application startup file | `server.js` |

**Environment variables** (ADD VARIABLE → SAVE):

| Name | Value | |
|---|---|---|
| `DATABASE_URL` | `mysql://user:pass@localhost:3306/dbname` | required |
| `DEFAULT_SCHOOL_SLUG` | `iescollege` | required — the one school this install serves |
| `PUBLIC_URL` | `https://iescollege.vertexsoft.lk` | used in printed links and SMS |
| `SEED_SCHOOL_NAME` | `IES College` | first run only |
| `SEED_SCHOOL_SHORT` | `IES` | first run only |
| `SEED_OWNER_PASSWORD` | a strong password | first run only (default `Owner-2026!`) |
| `SEED_OWNER_PHONE` | `9477…` | first run only |

Secrets (`JWT_ACCESS_SECRET`, `JWT_REFRESH_SECRET`, `ENCRYPTION_KEY`) are **generated automatically** on first start and kept in `.alphazoft-secrets.json` in the app root. Set them yourself only if you want to (`npm run secrets` prints fresh ones). Never delete that file — it invalidates all logins and encrypted data.

## 5. Install, create tables, seed

1. **Run NPM Install** (button). Wait for "completed". (cPanel installs into `~/nodevenv/…/lib/node_modules` and links it into the app root — that's expected.)
2. **Restart** the app, then open `https://iescollege.vertexsoft.lk`. On this first start `server.js` generates the Prisma database client for this server (10–30 s, it downloads the engine once from `binaries.prisma.sh`) and adds a guard block to `.htaccess`. Until the tables exist you'll see the landing page and `/health` says `db: down` — that's fine.
3. Create the tables and the school in cPanel **Terminal** (the first line is shown at the top of your Node.js app page):
   ```bash
   source /home/vertexso/nodevenv/iescollege.vertexsoft.lk/20/bin/activate && cd /home/vertexso/iescollege.vertexsoft.lk
   npm run db:push      # creates all tables from the schema
   npm run db:seed      # platform admin + your school + owner user — prints the login link
   ```
   (The **Run JS script** button may also work for `db:push` / `db:seed`, but the Terminal always does.)
4. **Restart** the app once more.

The seed prints the **secret login link** `https://iescollege.vertexsoft.lk/s/<loginSlug>/login`. Lost it? `npm run login-url` prints it again (also available in the dev console → Schools).

| What | URL | Credentials |
|---|---|---|
| School owner | `/s/<loginSlug>/login` | `owner` / the `SEED_OWNER_PASSWORD` you set — 2FA setup is asked on first login |
| Alphazoft dev console | `/dev/login` | `admin@alphazoft.lk` / `ChangeMe-Platform-2026!` — change it |
| Landing page | `/` | — |

## Updating later

Upload the new `deploy.zip` → Extract over the existing files (same top-level layout) → **Run NPM Install** only if `package.json` changed → `npm run db:push` only if the schema changed (the release notes say so) → **Restart**.

## What the server does for you

| Situation | Behaviour |
|---|---|
| Files incomplete / `DATABASE_URL` missing / dependency missing | The site shows a **setup page** that names the problem (instead of a blank error) — `stderr.log` has the detail. |
| Database down at boot | App still starts; `/health` reports `db: down`; recovers when MySQL is back. |
| No Redis on shared hosting | Rate limiting + permission cache are in-memory (one process). |
| WebSockets blocked by the proxy | Socket.IO falls back to long-polling — notifications and chat still arrive within ~1 s. |
| App sleeps when idle | First request after idle takes 3–5 s. A cron `curl -s https://iescollege.vertexsoft.lk/health` every 5 min keeps it warm. |
| Low memory | Nothing is built on the server. |

## Troubleshooting

| Symptom | Fix |
|---|---|
| "package.json file is required" | The bundle was not extracted at top level — `package.json` must sit next to `server.js` in the app root. |
| "Index of /" directory listing | App isn't running yet. Check Setup Node.js App shows it started; open `stderr.log` in the app root. |
| Setup page: "API is not built" | You uploaded the source zip. Upload `deploy.zip` (step 2–3). |
| Setup page: "Cannot find module …" | Click **Run NPM Install**, then Restart. |
| `npm run db:push` → "Can't reach database server" / access denied | Check `DATABASE_URL` user, password (URL-encoded) and that the user has privileges on the database. |
| `prisma generate` / engine download failed | The server must reach `binaries.prisma.sh`. Ask the host to allow outbound HTTPS, then `npm run db:generate`. |
| Login page says not found | The `/s/<loginSlug>/login` path must match the school's `loginSlug` exactly — `npm run login-url`. |
| `Tenant context missing` in `stderr.log` | `DEFAULT_SCHOOL_SLUG` does not match the school `slug` in the DB (the seed uses it, so set it **before** seeding). |
