195 lines
9.9 KiB
Markdown
195 lines
9.9 KiB
Markdown
# Deploying to cPanel (site administrator guide)
|
||||
|
|
|
|||
|
|
This guide assumes the standard cPanel layout `/home/USER/`. Replace `USER` with the account's
|
|||
|
|
username throughout. Nothing here changes DNS or publishes anything until step 8.
|
|||
|
|
|
|||
|
|
> **Current state (2026-09-22):** `visit.waterschurch.org` already resolves to the same server as
|
|||
|
|
> waterschurch.org (108.160.152.27) and currently 301-redirects to a Church Center form
|
|||
|
|
> (`waterschurch.churchcenter.com/people/forms/21186`). That redirect lives either in the main
|
|||
|
|
> site's `.htaccess` or in a cPanel **Redirects** entry and must be removed when this site goes live.
|
|||
|
|
|
|||
|
|
## Fastest path: `deploy/deploy.sh` (SSH)
|
|||
|
|
|
|||
|
|
If you have SSH access (alias `waterschurch` in `~/.ssh/config`), one command does steps 2, 5, 6 and 7 below:
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
deploy/deploy.sh --keep-redirect # deploy, but keep visitors on the Church Center form (pre-launch)
|
|||
|
|
deploy/deploy.sh # build, deploy and go live (removes the redirect)
|
|||
|
|
deploy/deploy.sh --no-build # re-upload the existing ./out
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
It preserves cPanel's own `.htaccess` blocks and `.user.ini`, never overwrites an existing
|
|||
|
|
`~/visit-private/.env`, and prints the health check when done. The manual steps below describe
|
|||
|
|
what it does, for File Manager users or troubleshooting.
|
|||
|
|
|
|||
|
|
**Status 2026-09-22 (evening): LIVE.** Deployed with `deploy/deploy.sh`; the Church Center redirect is
|
|||
|
|
gone. `INTEGRATION_MODE=live` with Mailchimp (audience "Waters Church", datacenter `us2`) and Sinch
|
|||
|
|
(all five campus sub-accounts verified). The 12 merge fields exist in the audience. A real email-only
|
|||
|
|
test lead succeeded end to end; the SMS path is verified against Sinch auth but a live text has not yet
|
|||
|
|
been sent. Journey "New Visit" (id 5842) is still a draft and triggers only on `Visit:Apollo Beach`.
|
|||
|
|
|
|||
|
|
## 0. What you will need
|
|||
|
|
|
|||
|
|
* cPanel login for the waterschurch.org account
|
|||
|
|
* The build package: run `npm run build && npm run package` → `dist/visit-waterschurch-<version>-<date>.zip`
|
|||
|
|
* Filled-in `.env` values (Mailchimp key, Sinch key/secret, etc.) — see `.env.example`
|
|||
|
|
|
|||
|
|
## 1. Check the account's PHP
|
|||
|
|
|
|||
|
|
cPanel → **Software → MultiPHP Manager** (or *Select PHP Version*).
|
|||
|
|
|
|||
|
|
* Set the domain (once created) to **PHP 8.1, 8.2 or 8.3**.
|
|||
|
|
* **MultiPHP INI Editor / Select PHP Version → Extensions**: ensure `curl`, `pdo_sqlite`, `sqlite3`,
|
|||
|
|
`mbstring`, `json`, `openssl` are enabled. (`pdo_mysql` only if you choose MySQL storage.)
|
|||
|
|
|
|||
|
|
Node.js is **not** required. If the account has "Setup Node.js App", ignore it.
|
|||
|
|
|
|||
|
|
## 2. Create the private configuration folder (outside the web root)
|
|||
|
|
|
|||
|
|
cPanel → **Files → File Manager** → navigate to `/home/USER/` (the home directory, *not*
|
|||
|
|
`public_html`).
|
|||
|
|
|
|||
|
|
1. Create folder **`visit-private`**.
|
|||
|
|
2. Inside it create folders **`data`** and **`logs`**.
|
|||
|
|
3. Upload `.env.example` from the package (`dist/env.example.txt`), rename it to **`.env`**, and edit
|
|||
|
|
it in File Manager: fill in real values, set `INTEGRATION_MODE=live` only when ready to go live,
|
|||
|
|
and set a long random `HASH_SALT`.
|
|||
|
|
4. Permissions (right-click → *Change Permissions*): folders `0750`, the `.env` file `0640`.
|
|||
|
|
PHP runs as your cPanel user under suPHP/LSAPI, so it can read and write these while the web
|
|||
|
|
server cannot serve them.
|
|||
|
|
|
|||
|
|
If your host runs PHP as a different user (rare), use `0755`/`0644` and rely on the folder being
|
|||
|
|
outside the document root.
|
|||
|
|
|
|||
|
|
## 3. Create the subdomain
|
|||
|
|
|
|||
|
|
cPanel → **Domains → Domains** (or *Subdomains* on older versions) → **Create a New Domain**:
|
|||
|
|
|
|||
|
|
* Domain: `visit.waterschurch.org`
|
|||
|
|
* Untick "Share document root"; set **Document Root** to `/home/USER/visit.waterschurch.org`
|
|||
|
|
* Submit. cPanel creates the folder and the DNS `A` record automatically because the zone is hosted
|
|||
|
|
here (`ns1/ns2.waterschurch.org`). The record already exists and points at this server, so nothing
|
|||
|
|
external changes.
|
|||
|
|
|
|||
|
|
Then remove the old redirect:
|
|||
|
|
|
|||
|
|
* cPanel → **Domains → Redirects**: delete any entry for `visit.waterschurch.org`, **or**
|
|||
|
|
* File Manager → `public_html/.htaccess`: remove the `RewriteCond %{HTTP_HOST} ^visit\.` block that
|
|||
|
|
redirects to churchcenter.com.
|
|||
|
|
|
|||
|
|
Do this at the moment you are ready to launch (step 8); until then the redirect keeps working.
|
|||
|
|
|
|||
|
|
## 4. Enable SSL
|
|||
|
|
|
|||
|
|
cPanel → **Security → SSL/TLS Status** → tick `visit.waterschurch.org` → **Run AutoSSL**.
|
|||
|
|
Wait for the green padlock (usually under 5 minutes). If AutoSSL fails with a DCV error, make sure
|
|||
|
|
the document root exists and nothing blocks `/.well-known/` (our `.htaccess` explicitly allows it).
|
|||
|
|
|
|||
|
|
Verify: `https://visit.waterschurch.org/BUILD.txt` shows the build id once files are uploaded
|
|||
|
|
(step 5). Force-HTTPS is handled by the site's own `.htaccess`; you do **not** need cPanel's
|
|||
|
|
"Force HTTPS Redirect" toggle, but enabling it is harmless.
|
|||
|
|
|
|||
|
|
## 5. Upload the build
|
|||
|
|
|
|||
|
|
1. File Manager → open `/home/USER/visit.waterschurch.org/`. Delete the placeholder `cgi-bin`
|
|||
|
|
folder if present.
|
|||
|
|
2. **Upload** the zip from `dist/`, then right-click → **Extract** into this folder. Delete the zip.
|
|||
|
|
3. Enable **Settings → Show Hidden Files (dotfiles)** and confirm `.htaccess` is present at the root
|
|||
|
|
and inside `api/lib`, `api/cron`, `api/config`.
|
|||
|
|
4. Tell the API where the private folder is. Either:
|
|||
|
|
* leave the default (`/home/USER/visit-private` — the folder *next to* the document root), **or**
|
|||
|
|
* edit `/home/USER/visit.waterschurch.org/.htaccess` and uncomment
|
|||
|
|
`SetEnv VISIT_PRIVATE_DIR /home/USER/visit-private` with the real path.
|
|||
|
|
5. Permissions: folders `0755`, files `0644` (File Manager's defaults after extraction are fine).
|
|||
|
|
|
|||
|
|
The upload never requires editing generated files. To update the site later, rebuild locally and
|
|||
|
|
repeat steps 5.2–5.3 (or use *File Manager → Upload* with "Overwrite existing files"). An alternative
|
|||
|
|
for frequent updates is cPanel's **Git Version Control** pointed at a repo whose `out/` is committed,
|
|||
|
|
or SFTP with `rsync --delete out/ USER@host:visit.waterschurch.org/`.
|
|||
|
|
|
|||
|
|
## 6. Verify the API
|
|||
|
|
|
|||
|
|
Open `https://visit.waterschurch.org/api/health.php`. You should see `"ok": true` and:
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
"private_dir_found": true, "env_file_found": true, "data_dir_writable": true,
|
|||
|
|
"database": "ok (sqlite)", "campuses_loaded": 6, "https": true
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
`integration_mode` shows `mock` until you change it in `.env`. Fix anything `false` before continuing
|
|||
|
|
(most often: wrong `VISIT_PRIVATE_DIR` path or `data` folder not writable).
|
|||
|
|
|
|||
|
|
Confirm private paths are blocked (each must return **403**):
|
|||
|
|
|
|||
|
|
* `https://visit.waterschurch.org/api/lib/Config.php`
|
|||
|
|
* `https://visit.waterschurch.org/api/config/campuses.json`
|
|||
|
|
* `https://visit.waterschurch.org/api/cron/retry-integrations.php`
|
|||
|
|
|
|||
|
|
## 7. Cron job for retries
|
|||
|
|
|
|||
|
|
cPanel → **Advanced → Cron Jobs** → add, every 5 minutes (`*/5 * * * *`):
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
VISIT_PRIVATE_DIR=/home/USER/visit-private /usr/local/bin/php /home/USER/visit.waterschurch.org/api/cron/retry-integrations.php >> /home/USER/visit-private/logs/cron.log 2>&1
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Check the PHP binary path with **MultiPHP Manager** (often `/usr/local/bin/php`, `/opt/cpanel/ea-php82/root/usr/bin/php`, or `/usr/local/bin/ea-php82`). The job re-sends any Mailchimp/Sinch
|
|||
|
|
delivery that failed transiently (up to 5 attempts with backoff) and, if `LEAD_RETENTION_DAYS` is set,
|
|||
|
|
deletes fully-processed leads older than that.
|
|||
|
|
|
|||
|
|
Optional weekly export for staff (contains personal data — download and delete promptly):
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
VISIT_PRIVATE_DIR=/home/USER/visit-private /usr/local/bin/php /home/USER/visit.waterschurch.org/api/cron/export-leads.php --since=$(date -d '7 days ago' +%F) > /home/USER/visit-private/data/leads-week.csv
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 8. Go live
|
|||
|
|
|
|||
|
|
1. Set `INTEGRATION_MODE=live` in `/home/USER/visit-private/.env`.
|
|||
|
|
2. Remove the Church Center redirect (step 3).
|
|||
|
|
3. Submit one real test lead with your own email and mobile: confirm the Mailchimp contact + tags
|
|||
|
|
and the confirmation text arrive. Reply STOP to test opt-out routing, then re-subscribe.
|
|||
|
|
4. Run through [LAUNCH-CHECKLIST.md](LAUNCH-CHECKLIST.md).
|
|||
|
|
|
|||
|
|
## 9. Updating content later
|
|||
|
|
|
|||
|
|
| Change | Where | Then |
|
|||
|
|
| --- | --- | --- |
|
|||
|
|
| Service times, address, phone, SMS number, campus status | `config/campuses.json` | `npm run build` → re-upload |
|
|||
|
|
| Logo | `assets/source/logos/` (same file names) | `npm run build` → re-upload |
|
|||
|
|
| Photos | `assets/source/photos/` (same file names) | `npm run build` → re-upload |
|
|||
|
|
| FAQs, kids info, headlines, consent text | `src/content/*.ts` (bump `consent.version` if consent text changes) | `npm run build` → re-upload |
|
|||
|
|
| Central contact number, privacy URL | `.env` (`NEXT_PUBLIC_*`) | `npm run build` → re-upload |
|
|||
|
|
| API keys, mode, providers | `/home/USER/visit-private/.env` | takes effect immediately, no rebuild |
|
|||
|
|
| SMS template | `SINCH_CONFIRMATION_TEMPLATE` in server `.env` | immediate |
|
|||
|
|
|
|||
|
|
## 10. Files, permissions and logs
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
/home/USER/
|
|||
|
|
├── visit.waterschurch.org/ document root (0755) — everything in out/
|
|||
|
|
│ ├── .htaccess HTTPS + canonical redirects, caching, security headers, 404
|
|||
|
|
│ ├── index.html, 404.html, _next/, images/
|
|||
|
|
│ └── api/ PHP endpoints (public) + lib/ cron/ config/ (403)
|
|||
|
|
└── visit-private/ NOT web accessible (0750)
|
|||
|
|
├── .env secrets (0640)
|
|||
|
|
├── data/leads.sqlite lead store (created automatically)
|
|||
|
|
├── data/retry.lock
|
|||
|
|
└── logs/app.log, cron.log JSON lines; personal data and secrets are redacted
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Rotate `app.log` occasionally (cPanel cron: `find /home/USER/visit-private/logs -name '*.log' -size +20M -delete`)
|
|||
|
|
or simply download and clear it.
|
|||
|
|
|
|||
|
|
## 11. Optional: MySQL instead of SQLite
|
|||
|
|
|
|||
|
|
cPanel → **Databases → MySQL Database Wizard**: create `USER_visit` + a user with ALL PRIVILEGES.
|
|||
|
|
In `.env`: `DATABASE_DSN=mysql:host=localhost;dbname=USER_visit;charset=utf8mb4`,
|
|||
|
|
`DATABASE_USER`, `DATABASE_PASSWORD`. Tables are created automatically on first request.
|
|||
|
|
|
|||
|
|
## 12. Content Security Policy
|
|||
|
|
|
|||
|
|
`.htaccess` ships a **report-only** CSP covering GTM, GA4, Meta, TikTok and Turnstile. After
|
|||
|
|
confirming the tags you actually use, change `Content-Security-Policy-Report-Only` to
|
|||
|
|
`Content-Security-Policy` and remove hosts you do not need.
|