Files
Waters ChurchandClaude Fable 5.1 78cd0cc550 Plan Your Visit landing page for visit.waterschurch.org
Next.js 16 static export + PHP 8 API for cPanel/Apache hosting.

- Location-aware campus finder (IP estimate, optional precise location, ZIP search)
- Three-step Plan Your Visit form with separate email/SMS consent
- PHP API: validation, anti-spam, rate limiting, SQLite lead store,
  Mailchimp and Sinch Engage adapters, idempotent retries via cron
- Attribution capture (utm_*, gclid/gbraid/wbraid, fbclid, ttclid, twclid) and
  consent-aware analytics hooks
- Vitest and PHP test suites, cPanel deployment script and docs

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-22 13:22:26 -04:00

195 lines
9.9 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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.