Compare commits
8
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bf887720a1 | ||
|
|
6de0b5525b | ||
|
|
e261705214 | ||
|
|
4ef9cea634 | ||
|
|
6f18e40f06 | ||
|
|
7fdd2b8b9d | ||
|
|
8e2cb7eb4d | ||
|
|
bc73d7e3af |
@@ -7,6 +7,24 @@ and this project follows [Semantic Versioning](https://semver.org/).
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [1.41.3] - 2026-10-27
|
||||
|
||||
### Changed
|
||||
|
||||
- **The platform is now called Cross Events** (and the Hub, Cross Events Hub): Site settings →
|
||||
Licence, the "update available" banner, the setup wizard, the audit log, help and the docs.
|
||||
Technical names stay as they are (`hope-events` repo, PM2 and nginx names, install folder, browser
|
||||
storage keys), so existing installs and sign-ins keep working.
|
||||
|
||||
## [1.41.2] - 2026-10-27
|
||||
|
||||
### Fixed
|
||||
|
||||
- **Background jobs page showed last-run times two hours ahead** (e.g. 12:55 at 10:55). Prisma's
|
||||
Postgres adapter dropped the time-zone offset of pg-boss's timestamps; the query now reads them
|
||||
as UTC. Job history itself was never wrong. `APP_TIMEZONE` is now in `backend/.env.example` and
|
||||
SETTINGS.md.
|
||||
|
||||
## [1.41.1] - 2026-10-27
|
||||
|
||||
### Changed
|
||||
|
||||
+342
@@ -0,0 +1,342 @@
|
||||
# Deploying Cross Events behind frp
|
||||
|
||||
This guide is for a server that is **not reachable from the internet directly**: it runs
|
||||
`frpc`, which opens a tunnel to `frps` on a public server (the "VPS"). Visitors reach the VPS,
|
||||
and frp carries the traffic through to this server. The frp client config lives at
|
||||
**`/etc/frp/frpc.ini`**.
|
||||
|
||||
For a server with a public IP, `scripts/install.sh` does everything (see the README). Behind frp
|
||||
it needs a few changes, because:
|
||||
|
||||
- **HTTPS can't be set up by the installer.** DNS points at the VPS, not at this server, so
|
||||
`certbot --nginx` can't work the usual way. HTTPS ends either on the VPS or in `frpc`.
|
||||
- **There are more proxies in the way.** The app must be told which of them to trust, or it sees
|
||||
every visitor as `127.0.0.1` (rate limiting then treats everyone as one person, and PayFast
|
||||
payment notifications fail their IP check).
|
||||
- **The app must know the site is HTTPS** even though nginx on this server only speaks HTTP;
|
||||
otherwise it builds `http://` links (event images, payment return URLs).
|
||||
|
||||
## 1. How the traffic flows
|
||||
|
||||
Pick the setup that matches how your other frp sites get HTTPS.
|
||||
|
||||
**A. HTTPS ends on the VPS** (nginx/Caddy on the VPS holds the certificate) — recommended if the
|
||||
VPS already does this for other sites:
|
||||
|
||||
```
|
||||
visitor ──https──▶ VPS nginx/Caddy ──http──▶ frps (vhost_http_port) ══tunnel══▶ frpc ──▶ nginx :80 ──▶ app
|
||||
```
|
||||
|
||||
**B. HTTPS ends in frpc** (the VPS only runs frps; the certificate lives on this server and
|
||||
frpc's `https2http` plugin uses it):
|
||||
|
||||
```
|
||||
visitor ──https──▶ frps (vhost_https_port) ══tunnel══▶ frpc https2http ──▶ nginx 127.0.0.1:8080 ──▶ app
|
||||
visitor ──http───▶ frps (vhost_http_port) ══tunnel══▶ frpc ──▶ nginx :80 (certificate check + redirect to https)
|
||||
```
|
||||
|
||||
On this server, nginx sends `/api`, `/uploads`, `/health` and `/docs` to the backend (port 5000)
|
||||
and everything else to the Next.js site (port 3000), the same as a normal install.
|
||||
|
||||
## 2. Before you start
|
||||
|
||||
- Ubuntu 22.04 or 24.04 on this server, with `frpc` already installed and connected (it runs as
|
||||
a systemd service, usually `frpc.service`).
|
||||
- The site's DNS record (e.g. `events.yourchurch.org`) points at the **VPS**.
|
||||
- On the VPS, `frps.ini` has `vhost_http_port` set (and `vhost_https_port` for setup B). Note
|
||||
the values; you need them below.
|
||||
- The licence key for this site from the Cross Events Hub.
|
||||
|
||||
## 3. Install the app
|
||||
|
||||
On this server, as root, run the installer **without** its certificate step:
|
||||
|
||||
```bash
|
||||
curl -fsSLO https://c-git.crosscode.co.za/joshua/hope-events/raw/branch/main/scripts/install.sh
|
||||
sudo bash install.sh --domain events.yourchurch.org --no-ssl --version v1.41.2
|
||||
```
|
||||
|
||||
It installs Node 20, PostgreSQL, PM2 and nginx, creates the database, writes the config, builds
|
||||
and starts the app (in `/opt/hope-events`, run by the `hopeevents` user). Use `--name`, `--dir`,
|
||||
`--web-port` and `--api-port` if other apps already use those names or ports; adjust the ports
|
||||
below to match.
|
||||
|
||||
### Switch the URLs to https
|
||||
|
||||
With `--no-ssl` the installer writes `http://` addresses. Change them to `https://`:
|
||||
|
||||
```bash
|
||||
cd /opt/hope-events
|
||||
sudo sed -i 's#=http://#=https://#' backend/.env frontend/.env.production
|
||||
grep -E 'URL=' backend/.env frontend/.env.production # all should now start with https://
|
||||
```
|
||||
|
||||
While `backend/.env` is open, set which proxies to trust (all of them run on loopback in both
|
||||
setups; see [step 7](#7-check-that-visitor-addresses-come-through) to confirm):
|
||||
|
||||
```bash
|
||||
sudo sed -i 's#^TRUST_PROXY=.*#TRUST_PROXY=loopback#' backend/.env
|
||||
```
|
||||
|
||||
`NEXT_PUBLIC_API_URL` is baked in when the site is built, so rebuild the frontend and restart:
|
||||
|
||||
```bash
|
||||
sudo -u hopeevents -H bash -lc 'cd /opt/hope-events/frontend && npm run build'
|
||||
sudo -u hopeevents -H bash -lc 'cd /opt/hope-events && pm2 reload ecosystem.config.js --update-env'
|
||||
```
|
||||
|
||||
Keep `JWT_SECRET` in `backend/.env` as it is: it also encrypts the secrets stored in the
|
||||
database (SMTP, payment keys).
|
||||
|
||||
## 4. nginx on this server
|
||||
|
||||
The installer wrote `/etc/nginx/sites-available/hope-events`. Replace it with the version for
|
||||
your setup, changing the domain (and the ports if you changed them).
|
||||
|
||||
### Setup A (HTTPS on the VPS)
|
||||
|
||||
```nginx
|
||||
# Shows the forwarded-for chain in the access log (see step 7). Safe to keep.
|
||||
log_format hope_proxy '$remote_addr xff="$http_x_forwarded_for" "$request" $status';
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
server_name events.yourchurch.org;
|
||||
client_max_body_size 25m;
|
||||
access_log /var/log/nginx/hope-events.access.log hope_proxy;
|
||||
|
||||
# Visitors only reach this through the VPS over HTTPS, so tell the app so.
|
||||
location ~ ^/(api|uploads|health|docs)(/|$) {
|
||||
proxy_pass http://127.0.0.1:5000;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto https;
|
||||
}
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:3000;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto https;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The VPS nginx/Caddy must redirect plain HTTP to HTTPS and pass the `Host` header through to frps
|
||||
unchanged (frps picks the site by its host name). For nginx on the VPS, roughly:
|
||||
|
||||
```nginx
|
||||
server {
|
||||
listen 443 ssl;
|
||||
server_name events.yourchurch.org;
|
||||
# ssl_certificate / ssl_certificate_key from certbot on the VPS
|
||||
client_max_body_size 25m;
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:8080; # frps vhost_http_port
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto https;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Setup B (HTTPS in frpc)
|
||||
|
||||
Port 80 only answers certificate checks and redirects everything else; the real site is on
|
||||
`127.0.0.1:8080`, which only frpc's `https2http` plugin reaches.
|
||||
|
||||
```nginx
|
||||
log_format hope_proxy '$remote_addr xff="$http_x_forwarded_for" "$request" $status';
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
server_name events.yourchurch.org;
|
||||
location /.well-known/acme-challenge/ { root /var/www/certbot; }
|
||||
location / { return 301 https://$host$request_uri; }
|
||||
}
|
||||
|
||||
server {
|
||||
listen 127.0.0.1:8080;
|
||||
server_name events.yourchurch.org;
|
||||
client_max_body_size 25m;
|
||||
access_log /var/log/nginx/hope-events.access.log hope_proxy;
|
||||
|
||||
location ~ ^/(api|uploads|health|docs)(/|$) {
|
||||
proxy_pass http://127.0.0.1:5000;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto https;
|
||||
}
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:3000;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto https;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Then, for either setup:
|
||||
|
||||
```bash
|
||||
sudo mkdir -p /var/www/certbot # setup B only
|
||||
sudo nginx -t && sudo systemctl reload nginx
|
||||
```
|
||||
|
||||
## 5. frpc: `/etc/frp/frpc.ini`
|
||||
|
||||
Back it up first, then **add** a section for the site below the existing ones. Leave `[common]`
|
||||
(server address, port, token) and the other sections as they are. Section names must be unique
|
||||
across everything connected to your frps.
|
||||
|
||||
```bash
|
||||
sudo cp /etc/frp/frpc.ini /etc/frp/frpc.ini.bak-$(date +%F)
|
||||
sudo nano /etc/frp/frpc.ini
|
||||
```
|
||||
|
||||
### Setup A
|
||||
|
||||
```ini
|
||||
[hope-events-http]
|
||||
type = http
|
||||
local_ip = 127.0.0.1
|
||||
local_port = 80
|
||||
custom_domains = events.yourchurch.org
|
||||
```
|
||||
|
||||
### Setup B
|
||||
|
||||
```ini
|
||||
# Plain HTTP: certificate checks and the redirect to https
|
||||
[hope-events-http]
|
||||
type = http
|
||||
local_ip = 127.0.0.1
|
||||
local_port = 80
|
||||
custom_domains = events.yourchurch.org
|
||||
|
||||
# HTTPS: frpc holds the certificate and passes plain HTTP to nginx on 127.0.0.1:8080
|
||||
[hope-events-https]
|
||||
type = https
|
||||
custom_domains = events.yourchurch.org
|
||||
plugin = https2http
|
||||
plugin_local_addr = 127.0.0.1:8080
|
||||
plugin_crt_path = /etc/letsencrypt/live/events.yourchurch.org/fullchain.pem
|
||||
plugin_key_path = /etc/letsencrypt/live/events.yourchurch.org/privkey.pem
|
||||
plugin_header_X-Forwarded-Proto = https
|
||||
```
|
||||
|
||||
Restart frpc and check it registered the new sections without errors:
|
||||
|
||||
```bash
|
||||
sudo systemctl restart frpc
|
||||
sudo journalctl -u frpc -n 30 --no-pager # look for "start proxy success" for each section
|
||||
```
|
||||
|
||||
> Newer frp releases (0.52+) prefer `frpc.toml`, but still read `.ini`. Don't mix the two: if
|
||||
> your service already uses `frpc.ini`, keep adding to it.
|
||||
|
||||
### Certificate (setup B only)
|
||||
|
||||
Request it through the tunnel with the webroot method (not `--nginx`, which would rewrite the
|
||||
nginx config), then have renewals restart frpc so it loads the new certificate:
|
||||
|
||||
```bash
|
||||
sudo apt-get install -y certbot
|
||||
sudo certbot certonly --webroot -w /var/www/certbot -d events.yourchurch.org \
|
||||
-m you@yourchurch.org --agree-tos --non-interactive
|
||||
echo 'systemctl restart frpc' | sudo tee /etc/letsencrypt/renewal-hooks/deploy/restart-frpc.sh
|
||||
sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/restart-frpc.sh
|
||||
sudo systemctl restart frpc
|
||||
```
|
||||
|
||||
frpc needs to be able to read the certificate files (it does when it runs as root, the usual
|
||||
setup). If the `[hope-events-https]` section fails to start, check `journalctl -u frpc`.
|
||||
|
||||
## 6. Finish in the browser
|
||||
|
||||
1. Open `https://events.yourchurch.org/health` — it should answer with the version.
|
||||
2. Open `https://events.yourchurch.org/setup`: create the admin account.
|
||||
3. **Site settings → Licence**: enter the licence key to connect to the Hub. The site checks in
|
||||
every 15 minutes from then on (outgoing HTTPS to `events-hub.crosscode.co.za`, which frp
|
||||
doesn't affect).
|
||||
4. Configure SMTP, payments (Yoco/PayFast webhooks use the `https://` `BACKEND_URL`), branding,
|
||||
etc. in Site settings.
|
||||
|
||||
## 7. Check that visitor addresses come through
|
||||
|
||||
Open the site from your phone (on mobile data, so it isn't your own network), then on the
|
||||
server:
|
||||
|
||||
```bash
|
||||
sudo tail -n 5 /var/log/nginx/hope-events.access.log
|
||||
```
|
||||
|
||||
The `xff="…"` part is what the app sees, with nginx's own entry added at the end:
|
||||
|
||||
- `xff="<your phone's IP>, 127.0.0.1"` (only `127.0.0.1` after the real address): good,
|
||||
`TRUST_PROXY=loopback` is right.
|
||||
- `xff="<your phone's IP>, 203.0.113.10"` (a non-local address after it, e.g. the VPS's): add
|
||||
that address: `TRUST_PROXY=loopback,203.0.113.10`, then
|
||||
`pm2 reload ecosystem.config.js --update-env`.
|
||||
- Your phone's IP doesn't appear at all: frp isn't passing the visitor's address on (can happen
|
||||
with setup B on some frp versions). Rate limiting and the PayFast IP check will then misbehave.
|
||||
Fix it by moving TLS to the VPS (setup A), or with frp's `proxy_protocol_version = v2` on the
|
||||
frpc section plus nginx's `listen … proxy_protocol` and `set_real_ip_from 127.0.0.1;
|
||||
real_ip_header proxy_protocol;`.
|
||||
|
||||
The backend logs the setting it uses at startup: `pm2 logs hope-events-backend | grep trust`.
|
||||
|
||||
## 8. Updating
|
||||
|
||||
Nothing about frp changes for updates. On the server:
|
||||
|
||||
```bash
|
||||
cd /opt/hope-events
|
||||
sudo -u hopeevents -H bash scripts/deploy.sh v1.41.2 # or no tag for the latest main
|
||||
```
|
||||
|
||||
It backs up the database, checks out the release, installs, migrates, builds and reloads PM2,
|
||||
and goes back to the previous version if anything fails first. `backend/.env`,
|
||||
`frontend/.env.production`, `ecosystem.config.js` and the nginx/frpc configs are not in git, so
|
||||
updates keep your changes from this guide.
|
||||
|
||||
### Deploying from CI over SSH
|
||||
|
||||
The Gitea workflow deploys on a `v*` tag by SSHing to `DEPLOY_HOST`. Behind frp, that needs an
|
||||
SSH tunnel too, e.g. in `frpc.ini`:
|
||||
|
||||
```ini
|
||||
[hope-events-ssh]
|
||||
type = tcp
|
||||
local_ip = 127.0.0.1
|
||||
local_port = 22
|
||||
remote_port = 6022
|
||||
```
|
||||
|
||||
Then use the VPS as `DEPLOY_HOST`. The workflow's `ssh` call (`.gitea/workflows/ci.yml`) doesn't
|
||||
take a port yet, so add `-p 6022` there (or a `DEPLOY_PORT` secret) before relying on it. Only
|
||||
open this if you need it: it exposes this server's SSH on the VPS. Use key-only login. Deploying
|
||||
by hand (above) needs none of this.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
| Symptom | Likely cause |
|
||||
| --- | --- |
|
||||
| frps shows "404 Not Found" page | The `custom_domains` value doesn't match the address typed, or frpc didn't start that section (`journalctl -u frpc`). |
|
||||
| 502 Bad Gateway | nginx is up but the app isn't: `sudo -u hopeevents pm2 ls` and `pm2 logs`. |
|
||||
| Site loads but login/API calls fail (CORS) | `FRONTEND_URL` in `backend/.env` is still `http://`, or the frontend wasn't rebuilt after changing `NEXT_PUBLIC_API_URL`. |
|
||||
| Images or links use `http://` (browser "mixed content" warnings) | nginx isn't sending `X-Forwarded-Proto https`, or `TRUST_PROXY` isn't set. |
|
||||
| "Too many requests" for everyone at once | All visitors look like one address: see step 7. |
|
||||
| PayFast payments stay pending | Same as above: the PayFast notification's IP check sees the proxy, not PayFast. |
|
||||
| Uploads over ~1 MB fail | `client_max_body_size` missing on this server's nginx or the VPS's. |
|
||||
| Redirect loop (setup B) | The site's `server` block is on port 80 instead of `127.0.0.1:8080`, or `plugin_local_addr` points at port 80. |
|
||||
+3
-3
@@ -1,4 +1,4 @@
|
||||
# Hope Events — Feature Overview
|
||||
# Cross Events — Feature Overview
|
||||
|
||||
A complete map of what the platform does, grouped by who uses it. For setup and
|
||||
deployment see [README.md](./README.md); for the API see
|
||||
@@ -642,7 +642,7 @@ can't start, the same jobs run on in-process timers.
|
||||
| `consent` — parental consent reminders, and deleting medical details after events | 09:00 daily (`CONSENT_JOBS_ENABLED=false`) |
|
||||
| `privacy` — remind admins of privacy requests due within a week or overdue | 08:00 daily (`PRIVACY_JOBS_ENABLED=false`) |
|
||||
| `retention` — delete/anonymise personal information past its retention period (Admin → Privacy → Retention) | 03:30 daily (`RETENTION_JOBS_ENABLED=false`) |
|
||||
| `hub-heartbeat` — check in with the Hope Events Hub and pick up licence changes (only once connected) | Every 15 minutes (`HUB_HEARTBEAT_ENABLED=false`) |
|
||||
| `hub-heartbeat` — check in with the Cross Events Hub and pick up licence changes (only once connected) | Every 15 minutes (`HUB_HEARTBEAT_ENABLED=false`) |
|
||||
| `analytics-rollup` — roll raw visit rows older than 90 days into daily totals | 04:15 daily (`ANALYTICS_ROLLUP_ENABLED=false`) |
|
||||
| `backup` — database backup (`pg_dump`, gzipped, 14-day retention) | 02:00 daily (`BACKUP_ENABLED=false`) |
|
||||
| `temp-cleanup` — temp files older than 7 days in `/temp` | 03:00 daily |
|
||||
@@ -653,7 +653,7 @@ can't start, the same jobs run on in-process timers.
|
||||
|
||||
- **Install script** (`scripts/install.sh`) — a fresh Ubuntu server to a running site: Node 20,
|
||||
PostgreSQL, PM2, nginx, HTTPS, configuration, migrations and build; then `/setup`.
|
||||
- **Hope Events Hub** — every install connects with a licence key (`/setup`, or Site settings →
|
||||
- **Cross Events Hub** — every install connects with a licence key (`/setup`, or Site settings →
|
||||
Licence) and checks in every 15 minutes with its version, health and totals (no personal
|
||||
information; income as a range). Licences are signed tokens checked on the site, so a Hub outage
|
||||
never switches it off: past the end date there's a grace period (banner for site admins only),
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Hope Events Platform
|
||||
# Cross Events Platform
|
||||
|
||||
Hope Events is a full-stack event management platform that allows users to browse events, register for them, make payments, and manage their tickets.
|
||||
Cross Events is a full-stack event management platform that allows users to browse events, register for them, make payments, and manage their tickets.
|
||||
|
||||
## Project Structure
|
||||
|
||||
@@ -111,9 +111,11 @@ It installs Node 20, PostgreSQL (with its own database and a random password), P
|
||||
API, uploads, `/health` and `/docs` to the backend, everything else to the site) and an HTTPS
|
||||
certificate, writes `backend/.env` and `frontend/.env.production`, migrates, builds and starts the
|
||||
app. Then open `https://<domain>/setup`: create the admin account and enter the **licence key**
|
||||
to connect the site to the Hope Events Hub. Several installs can share a server with different
|
||||
to connect the site to the Cross Events Hub. Several installs can share a server with different
|
||||
`--name`, `--dir`, `--web-port` and `--api-port`. Run `--help` for all options.
|
||||
|
||||
Behind an frp tunnel (no public IP on the server)? See [DEPLOYMENT.md](DEPLOYMENT.md).
|
||||
|
||||
### Updating
|
||||
|
||||
To update a running install, on the server in the repo folder:
|
||||
|
||||
+2
-1
@@ -17,6 +17,7 @@ in-process settings cache TTL) without a server restart.
|
||||
| `org_email` | *(blank)* | No fallback in most places. **Exception:** the Terms of Use / Privacy Policy pages default to `privacy@example.com` (an obvious placeholder) if unset — see the Legal section below. |
|
||||
| `org_phone` / `org_address` | *(blank)* | No fallback — simply omitted wherever shown if unset. |
|
||||
| `app_base_url` | `APP_BASE_URL` or `FRONTEND_URL` env var, else `http://localhost:3001` | Used to build links in emails (password reset, ticket delivery, etc.). Should always be set in production. |
|
||||
| `APP_TIMEZONE` (env) | `Africa/Johannesburg` | The site's time zone (an IANA name, e.g. `Europe/London`): background job schedules (e.g. the 07:00 daily summary), session dates and printed times. Also sent to the Hub in the heartbeat. Needs a backend restart. |
|
||||
|
||||
## Branding
|
||||
|
||||
@@ -84,7 +85,7 @@ Managed in the Payments tab (through `/api/payments/providers/settings` and `/ap
|
||||
| `consent_medical_retention_days` | `30` | Youth events: medical and emergency details from parental consent forms are deleted this many days after the event ends (daily 09:00 job). The consent record itself — who agreed, when, to which text — is kept. Set under Site Settings → Legal. |
|
||||
| `org_email` (as used on legal pages specifically) | `"privacy@example.com"` | Different fallback than `org_email`'s general default (blank) — also rendered as a `mailto:` link, so it needs to stay a valid-looking address. |
|
||||
|
||||
## Hope Events Hub (licence)
|
||||
## Cross Events Hub (licence)
|
||||
|
||||
| Setting | Default when unset | Notes |
|
||||
|---|---|---|
|
||||
|
||||
@@ -13,6 +13,10 @@ JWT_SECRET=your_jwt_secret_here_minimum_32_characters
|
||||
PORT=5000
|
||||
NODE_ENV=development
|
||||
|
||||
# Time zone for background job schedules (e.g. the 07:00 daily summary), session dates
|
||||
# and printed times. An IANA name, e.g. Africa/Johannesburg or Europe/London.
|
||||
APP_TIMEZONE=Africa/Johannesburg
|
||||
|
||||
# Set this if the app runs behind a reverse proxy (e.g. nginx on a separate
|
||||
# server) so rate limiting reads the real client IP instead of the proxy's.
|
||||
# Accepts a hop count ("1"), "true"/"false", or comma-separated IP(s)/CIDR(s)
|
||||
@@ -77,7 +81,7 @@ PRIVACY_JOBS_ENABLED=true
|
||||
# accounts are anonymised after 5 years by default (with a warning 30 days before).
|
||||
RETENTION_JOBS_ENABLED=true
|
||||
|
||||
# ─── Hope Events Hub (licence and health) ─────────────────────────────────────
|
||||
# ─── Cross Events Hub (licence and health) ─────────────────────────────────────
|
||||
# Where the Hub runs. The site checks in every 15 minutes once connected (Site settings → Licence).
|
||||
# HUB_URL=https://events-hub.crosscode.co.za
|
||||
# For a local or test Hub only: its public key (the release has the production key built in).
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Hope Events — API Documentation
|
||||
# Cross Events — API Documentation
|
||||
|
||||
Full interactive docs (with request/response examples) are available at `GET /docs?token=<admin-jwt>` on the running backend.
|
||||
|
||||
@@ -839,7 +839,7 @@ Manages the WAWP WhatsApp API instance. Credentials are stored in `AppSetting` (
|
||||
|
||||
## Hub and licence
|
||||
|
||||
The connection to the Hope Events Hub (`src/hub/`, protocol in `docs/hub-protocol.md`).
|
||||
The connection to the Cross Events Hub (`src/hub/`, protocol in `docs/hub-protocol.md`).
|
||||
|
||||
| Method | Path | Auth | Description |
|
||||
|--------|------|------|-------------|
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
# Hope Events — Backend API
|
||||
# Cross Events — Backend API
|
||||
|
||||
Node.js + Express REST API for the Hope Events platform used by Hope Family Church (`events.hopehenley.co.za`).
|
||||
Node.js + Express REST API for the Cross Events platform used by Hope Family Church (`events.hopehenley.co.za`).
|
||||
|
||||
---
|
||||
|
||||
|
||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "event-management-backend",
|
||||
"version": "1.41.1",
|
||||
"version": "1.41.3",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "event-management-backend",
|
||||
"version": "1.41.1",
|
||||
"version": "1.41.3",
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"@prisma/adapter-pg": "^7.10.1",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "event-management-backend",
|
||||
"version": "1.41.1",
|
||||
"version": "1.41.3",
|
||||
"description": "Event Management System Backend",
|
||||
"main": "src/index.js",
|
||||
"scripts": {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Talking to the Hope Events Hub (docs/hub-protocol.md): register with a licence key, then a
|
||||
* Talking to the Cross Events Hub (docs/hub-protocol.md): register with a licence key, then a
|
||||
* signed heartbeat every 15 minutes. Install → Hub only. Stored in AppSetting:
|
||||
* hub_install_id, hub_install_secret (encrypted), hub_licence (signed token),
|
||||
* hub_last_heartbeat_at, hub_last_error, hub_latest_version.
|
||||
@@ -62,7 +62,7 @@ async function post(path, body, headers = {}) {
|
||||
/** Register this install with a licence key. Returns the licence claims. */
|
||||
async function connect(licenceKey, adminContact) {
|
||||
if (!require('./publicKey').hubPublicKey()) {
|
||||
throw new HubError('This version of Hope Events has no Hub key built in, so it can\'t check licences yet.', 'NO_PUBLIC_KEY', 500);
|
||||
throw new HubError('This version of Cross Events has no Hub key built in, so it can\'t check licences yet.', 'NO_PUBLIC_KEY', 500);
|
||||
}
|
||||
const install = await report.installInfo();
|
||||
if (!/^https?:\/\//.test(install.url)) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* The Hope Events Hub's public licence key (Ed25519). Installs check every licence token with
|
||||
* The Cross Events Hub's public licence key (Ed25519). Installs check every licence token with
|
||||
* it, so a licence can't be faked by editing the database. The matching private key exists
|
||||
* only on the Hub server (events-hub repo, `npm run generate-keys`).
|
||||
*
|
||||
|
||||
@@ -136,7 +136,7 @@ async function installInfo() {
|
||||
require('../payments').availableProviders().then(list => list.map(p => p.id)).catch(() => []),
|
||||
]);
|
||||
return {
|
||||
name: name || 'Hope Events',
|
||||
name: name || 'Cross Events',
|
||||
url: String(url || process.env.FRONTEND_URL || '').split(',')[0].trim().replace(/\/$/, ''),
|
||||
version: APP_VERSION,
|
||||
timezone: process.env.APP_TIMEZONE || 'Africa/Johannesburg',
|
||||
|
||||
@@ -916,7 +916,7 @@ app.get('/docs', async (req, res) => {
|
||||
responses:[{ status:200, desc:'Healthy', body:{ status:'ok', version:'1.41.0', db:true, jobs:{ mode:'queue', failedLast24h:0 }, lastBackupAt:'2026-10-26T00:00:12.000Z' }}]},
|
||||
{ method:'GET', path:'/api/hub/status', auth:'admin', desc:'Licence state (active, grace, suspended, revoked, unlicensed), Hub connection, version and update',
|
||||
responses:[{ status:200, desc:'Success', body:{ state:'grace', readOnly:false, daysLeft:9, connected:true, version:'1.41.0', latestVersion:'1.42.0', updateAvailable:true }}]},
|
||||
{ method:'POST', path:'/api/hub/connect', auth:'admin', desc:'Connect to the Hope Events Hub with a licence key (also the /setup licence step)',
|
||||
{ method:'POST', path:'/api/hub/connect', auth:'admin', desc:'Connect to the Cross Events Hub with a licence key (also the /setup licence step)',
|
||||
request:{ body:{ licenceKey:'CEH-7K2M-9QXA-P4RT-WD8C' } },
|
||||
responses:[{ status:200, desc:'Connected (status as above)' }, { status:404, desc:'Unknown key', body:{ code:'LICENCE_NOT_FOUND', message:'That licence key was not found.' }}]},
|
||||
{ method:'POST', path:'/api/hub/heartbeat', auth:'admin', desc:'Check in with the Hub now (picks up licence changes)',
|
||||
|
||||
@@ -57,7 +57,7 @@ const JOBS = [
|
||||
run: () => require('../utils/privacy').remindDueRequests(new Date()),
|
||||
},
|
||||
{
|
||||
name: 'hub-heartbeat', label: 'Hope Events Hub: heartbeat and licence refresh', cron: '*/15 * * * *', env: 'HUB_HEARTBEAT_ENABLED',
|
||||
name: 'hub-heartbeat', label: 'Cross Events Hub: heartbeat and licence refresh', cron: '*/15 * * * *', env: 'HUB_HEARTBEAT_ENABLED',
|
||||
run: () => require('../hub/client').heartbeat(),
|
||||
},
|
||||
{
|
||||
@@ -247,8 +247,14 @@ async function runNow(name) {
|
||||
async function jobStatus(prisma) {
|
||||
const recent = new Map();
|
||||
if (mode === 'queue') {
|
||||
// pg-boss columns are timestamptz, and Prisma's pg adapter drops their offset in raw queries
|
||||
// (10:55+02 came back as 10:55Z). As plain UTC timestamps they come back right.
|
||||
const rows = await prisma.$queryRawUnsafe(`
|
||||
SELECT name, state, created_on AS "createdOn", started_on AS "startedOn", completed_on AS "completedOn", output
|
||||
SELECT name, state,
|
||||
created_on AT TIME ZONE 'UTC' AS "createdOn",
|
||||
started_on AT TIME ZONE 'UTC' AS "startedOn",
|
||||
completed_on AT TIME ZONE 'UTC' AS "completedOn",
|
||||
output
|
||||
FROM (
|
||||
SELECT name, state, created_on, started_on, completed_on, output,
|
||||
row_number() OVER (PARTITION BY name ORDER BY created_on DESC) AS rn
|
||||
|
||||
@@ -3,7 +3,7 @@ const router = express.Router();
|
||||
const { getStatus, connect, heartbeatNow } = require('../controllers/hubController');
|
||||
const { protect, admin } = require('../middleware/authMiddleware');
|
||||
|
||||
// Licence and the Hope Events Hub (docs/hub-protocol.md). Admins only; the setup wizard's
|
||||
// Licence and the Cross Events Hub (docs/hub-protocol.md). Admins only; the setup wizard's
|
||||
// admin token counts, so the licence step can run during /setup.
|
||||
router.get('/status', protect, admin, getStatus);
|
||||
router.post('/connect', protect, admin, connect);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Hope Events — New Features
|
||||
# Cross Events — New Features
|
||||
|
||||
Planned features for the next round of development. Each section covers what
|
||||
the feature does, who uses it, how it fits the existing platform, and the edge
|
||||
|
||||
+4
-3
@@ -1,6 +1,6 @@
|
||||
# Handover — where things stand and how to carry on
|
||||
|
||||
Last updated: 2026-10-27, after **1.41.1** (and Hub 1.0.0). The plan itself is in
|
||||
Last updated: 2026-10-27, after **1.41.2** (and Hub 1.0.0). The plan itself is in
|
||||
[ROADMAP-v2.md](./ROADMAP-v2.md) (decisions table at the end); the previous round of
|
||||
features is in [FEATURES-ROUND-1.md](./FEATURES-ROUND-1.md).
|
||||
|
||||
@@ -8,8 +8,9 @@ features is in [FEATURES-ROUND-1.md](./FEATURES-ROUND-1.md).
|
||||
|
||||
| Version | What |
|
||||
|---------|------|
|
||||
| 1.41.2 | Background jobs page: last-run times were 2 h ahead (Prisma's pg adapter drops the offset of timestamptz in raw queries — read pg-boss times `AT TIME ZONE 'UTC'`); `APP_TIMEZONE` documented |
|
||||
| 1.41.1 | Licence fixes — no licence (never connected, or the Hub answers `INSTALL_NOT_FOUND`, which now drops the old licence) is read-only like suspended; read-only also blocks editing events/options/variants; heartbeat 30 s after start and after connecting; `LICENCE_CHECK_DISABLED` for e2e |
|
||||
| 1.41.0 | Hope Events Hub connection — licence step in `/setup` and Site settings → Licence, 15-minute heartbeat (health, totals, income bands), licence states checked offline (grace banner for site admins only, read-only when suspended/revoked), `/health`, "update available", `scripts/install.sh`. The Hub itself is `joshua/events-hub` 1.0.0 |
|
||||
| 1.41.0 | Cross Events Hub connection — licence step in `/setup` and Site settings → Licence, 15-minute heartbeat (health, totals, income bands), licence states checked offline (grace banner for site admins only, read-only when suspended/revoked), `/health`, "update available", `scripts/install.sh`. The Hub itself is `joshua/events-hub` 1.0.0 |
|
||||
| 1.40.0 | POPIA toolkit part 3 — readiness checklist with score, retention policies + nightly `retention` job (inactive accounts anonymised after 5 years with a 30-day warning, on by default), breach register with notification drafts, children's/medical flag in the access log, `{retention}` in the notice |
|
||||
| 1.39.0 | POPIA toolkit part 2 — Admin → Privacy: privacy notice editor (built-in text as the start, placeholders, drafts, numbered versions, re-acceptance after login), operator register, consent log, access log; Information Officer on the contact page |
|
||||
| 1.38.0 | POPIA toolkit part 1 — Privacy page (news opt-in + history, download my data, correction and deletion requests), Event update vs News & announcements on every bulk send, unsubscribe links + one-click headers, WhatsApp STOP, one-time news question, Admin → Data requests, thorough anonymising |
|
||||
@@ -171,7 +172,7 @@ features is in [FEATURES-ROUND-1.md](./FEATURES-ROUND-1.md).
|
||||
- Frontend: `components/admin/LicenceTab.tsx` (Site settings → Licence; its `ConnectForm` is also on
|
||||
the last `/setup` screen), `components/shared/LicenceBanner.tsx` (site admins only).
|
||||
- Local testing: run the Hub (`../events-hub`, ports 5200/3200), create a licence there, and start
|
||||
Hope Events with `HUB_URL=http://localhost:5200` and `HUB_PUBLIC_KEY` = the key on Hub → Settings.
|
||||
Cross Events with `HUB_URL=http://localhost:5200` and `HUB_PUBLIC_KEY` = the key on Hub → Settings.
|
||||
- `scripts/install.sh` — fresh Ubuntu server to a running site (written and syntax-checked; not yet
|
||||
run on a real server).
|
||||
|
||||
|
||||
+9
-9
@@ -1,6 +1,6 @@
|
||||
# Hope Events — Road to 2.0.0
|
||||
# Cross Events — Road to 2.0.0
|
||||
|
||||
Planned features and platform work to take Hope Events from 1.32.x to 2.0.0.
|
||||
Planned features and platform work to take Cross Events from 1.32.x to 2.0.0.
|
||||
Each section covers what the feature does, who uses it, how it fits the
|
||||
existing platform, and the edge cases to handle. See [FEATURES.md](../FEATURES.md)
|
||||
for what already exists, and [FEATURES-ROUND-1.md](./FEATURES-ROUND-1.md) for the
|
||||
@@ -48,7 +48,7 @@ notifications. SnapScan and Zapper are dropped — Yoco and PayFast are enough (
|
||||
10. [Languages (Afrikaans first)](#10-languages-afrikaans-first)
|
||||
11. [Accessibility pass (WCAG 2.2 AA)](#11-accessibility-pass-wcag-22-aa)
|
||||
12. [Multi-campus (one install per church)](#12-multi-campus-one-install-per-church)
|
||||
13. [Central management platform (Hope Events Hub)](#13-central-management-platform-hope-events-hub)
|
||||
13. [Central management platform (Cross Events Hub)](#13-central-management-platform-cross-events-hub)
|
||||
14. [Foundations (platform work)](#14-foundations-platform-work)
|
||||
15. [AI helpers (2.0 headline)](#15-ai-helpers-20-headline)
|
||||
16. [AI: later stages](#16-ai-later-stages)
|
||||
@@ -464,7 +464,7 @@ Plan who serves where, for an event or each session.
|
||||
|
||||
## 8. Outbound webhooks
|
||||
|
||||
Let other systems react to what happens in Hope Events.
|
||||
Let other systems react to what happens in Cross Events.
|
||||
|
||||
- Admin adds endpoints and picks events: `registration.created`,
|
||||
`registration.paid`, `registration.cancelled`, `payment.refunded`,
|
||||
@@ -580,13 +580,13 @@ Because every church is a separate install, a few tools make that manageable:
|
||||
migrations, build, restart; roll back on failure.
|
||||
- **Version and health:** current version shown in admin and on a `/health`
|
||||
endpoint (version, DB, jobs, last backup), reported to the
|
||||
[Hub](#13-central-management-platform-hope-events-hub).
|
||||
[Hub](#13-central-management-platform-cross-events-hub).
|
||||
|
||||
---
|
||||
|
||||
## 13. Central management platform (Hope Events Hub)
|
||||
## 13. Central management platform (Cross Events Hub)
|
||||
|
||||
A separate app, run by Joshua, to see and manage **every Hope Events
|
||||
A separate app, run by Joshua, to see and manage **every Cross Events
|
||||
install** from one place: who the customers are, which version each runs,
|
||||
whether they're healthy, basic usage stats, and their **licence** status.
|
||||
|
||||
@@ -603,7 +603,7 @@ whether they're healthy, basic usage stats, and their **licence** status.
|
||||
- Hub repo: dashboard, customers, licences, licence **signing** (private
|
||||
key — only ever on the Hub server), `/api/installs/register` and
|
||||
`/api/installs/heartbeat` endpoints, alerts.
|
||||
- Hope Events repo: the Hub **client** — licence step in `/setup`,
|
||||
- Cross Events repo: the Hub **client** — licence step in `/setup`,
|
||||
"Connect to Hub" in Site settings, heartbeat job, licence verification
|
||||
with the Hub's **public** key, read-only mode, banners.
|
||||
- **The contract between them** (register / heartbeat payloads, licence token
|
||||
@@ -896,7 +896,7 @@ feature.
|
||||
| 6 | Selling model | **One install per church** — data separation, any server. No multi-tenant. **Multi-campus** inside an install is in scope |
|
||||
| — | Pro-rata rounding | Nearest R10 |
|
||||
| — | Multi-campus | In scope (§12), inside one install |
|
||||
| — | Central management | **Hope Events Hub** in its **own git repo** (`joshua/events-hub`): installs register on setup, send heartbeats (health, version, aggregate stats), licences switched on/off centrally |
|
||||
| — | Central management | **Cross Events Hub** in its **own git repo** (`joshua/events-hub`): installs register on setup, send heartbeats (health, version, aggregate stats), licences switched on/off centrally |
|
||||
| 7 | Payment provider order | Yoco · PayFast. Joshua can supply documentation links |
|
||||
| — | SnapScan, Zapper | Dropped (2026-10-23): Yoco and PayFast are enough. Later versions moved up by two |
|
||||
| — | Provider fees | Not deducted or estimated automatically; handled by people during cashup. No "pass fee to payer" |
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Hub protocol (version 1)
|
||||
|
||||
The contract between a **Hope Events install** (the church's site) and the **Hope Events
|
||||
The contract between a **Cross Events install** (the church's site) and the **Cross Events
|
||||
Hub** (`events-hub.crosscode.co.za`). A copy of this file lives in both repositories
|
||||
(`joshua/hope-events` → `docs/hub-protocol.md`, `joshua/events-hub` → `docs/hub-protocol.md`);
|
||||
change both together.
|
||||
@@ -156,5 +156,5 @@ grace starts). Blocking the Hub can't keep a suspended licence active for more t
|
||||
|
||||
## 6. Later (not in v1)
|
||||
|
||||
- **AI proxy** (Hope Events 2.0): `POST /api/ai/complete`, signed like the heartbeat; the Hub holds
|
||||
- **AI proxy** (Cross Events 2.0): `POST /api/ai/complete`, signed like the heartbeat; the Hub holds
|
||||
the Claude API key, meters each call in rand and enforces `limits.aiMonthlyCapCents`.
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
# Hope Events — Frontend
|
||||
# Cross Events — Frontend
|
||||
|
||||
Next.js 15 (App Router) frontend for the Hope Events platform used by Hope Family Church (`events.hopehenley.co.za`).
|
||||
Next.js 15 (App Router) frontend for the Cross Events platform used by Hope Family Church (`events.hopehenley.co.za`).
|
||||
|
||||
---
|
||||
|
||||
|
||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "hope-events-frontend",
|
||||
"version": "1.41.1",
|
||||
"version": "1.41.3",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "hope-events-frontend",
|
||||
"version": "1.41.1",
|
||||
"version": "1.41.3",
|
||||
"dependencies": {
|
||||
"@dnd-kit/core": "^6.3.1",
|
||||
"@hookform/resolvers": "^5.2.1",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "hope-events-frontend",
|
||||
"version": "1.41.1",
|
||||
"version": "1.41.3",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev --turbopack",
|
||||
|
||||
@@ -95,7 +95,7 @@ const ACTION_LABELS: Record<string, string> = {
|
||||
retention_run: "Retention job deleted/anonymised data",
|
||||
breach_recorded: "Security compromise recorded",
|
||||
breach_updated: "Security compromise updated",
|
||||
hub_connected: "Connected to the Hope Events Hub",
|
||||
hub_connected: "Connected to the Cross Events Hub",
|
||||
};
|
||||
|
||||
interface AuditLogEntry {
|
||||
|
||||
@@ -594,14 +594,14 @@ export default function SetupPage() {
|
||||
Email delivery is not configured yet. Set it up under Admin → Site Settings after logging in.
|
||||
</p>
|
||||
)}
|
||||
{/* Licence: connect to the Hope Events Hub now, or later in Site settings → Licence. */}
|
||||
{/* Licence: connect to the Cross Events Hub now, or later in Site settings → Licence. */}
|
||||
<div className="text-left border rounded-xl p-4 space-y-2">
|
||||
<h3 className="text-sm font-semibold text-gray-900">Licence</h3>
|
||||
{licenceConnected ? (
|
||||
<p className="text-sm text-green-700">✓ Connected — licensed to {licenceConnected}.</p>
|
||||
) : (
|
||||
<>
|
||||
<p className="text-xs text-gray-500">Enter the licence key you received to connect this site to the Hope Events Hub. Until a licence is connected, events can't be created or edited. You can also do this later under Admin → Site Settings → Licence.</p>
|
||||
<p className="text-xs text-gray-500">Enter the licence key you received to connect this site to the Cross Events Hub. Until a licence is connected, events can't be created or edited. You can also do this later under Admin → Site Settings → Licence.</p>
|
||||
<ConnectForm token={setupToken} compact onConnected={s => setLicenceConnected(s.licence?.customer || "your organisation")} />
|
||||
</>
|
||||
)}
|
||||
|
||||
@@ -100,7 +100,7 @@ export function LicenceTab({ active }: { active: boolean }) {
|
||||
<div className="space-y-5">
|
||||
<div>
|
||||
<h2 className="text-lg font-semibold text-gray-900">Licence</h2>
|
||||
<p className="text-sm text-gray-500">This site checks in with the Hope Events Hub 30 seconds after starting and every 15 minutes after that: version, health and totals only — never anyone's personal information.</p>
|
||||
<p className="text-sm text-gray-500">This site checks in with the Cross Events Hub 30 seconds after starting and every 15 minutes after that: version, health and totals only — never anyone's personal information.</p>
|
||||
</div>
|
||||
|
||||
<div className={`border rounded-xl p-4 ${st.tone}`}>
|
||||
@@ -114,7 +114,7 @@ export function LicenceTab({ active }: { active: boolean }) {
|
||||
|
||||
{!status.keyConfigured && (
|
||||
<p className="text-sm text-amber-800 bg-amber-50 border border-amber-200 rounded-lg p-3">
|
||||
This version has no Hub key built in yet, so it can't check licences. Update Hope Events, or set HUB_PUBLIC_KEY on the server for testing.
|
||||
This version has no Hub key built in yet, so it can't check licences. Update Cross Events, or set HUB_PUBLIC_KEY on the server for testing.
|
||||
</p>
|
||||
)}
|
||||
|
||||
@@ -147,7 +147,7 @@ export function LicenceTab({ active }: { active: boolean }) {
|
||||
|
||||
<div className="border rounded-xl p-4 text-sm">
|
||||
<div className="flex items-center gap-2 font-medium text-gray-900 mb-1"><ArrowUpCircle className="w-4 h-4" /> Version</div>
|
||||
<p>This site runs Hope Events <strong>{status.version}</strong>.{status.latestVersion && !status.updateAvailable && " It's up to date."}</p>
|
||||
<p>This site runs Cross Events <strong>{status.version}</strong>.{status.latestVersion && !status.updateAvailable && " It's up to date."}</p>
|
||||
{status.updateAvailable && (
|
||||
<p className="mt-1 text-brand-700">
|
||||
Version {status.latestVersion} is available. On the server: <code className="bg-gray-100 px-1 rounded">bash scripts/deploy.sh v{status.latestVersion}</code>
|
||||
|
||||
@@ -54,7 +54,7 @@ export function LicenceBanner() {
|
||||
return (
|
||||
<div role="status" className="bg-brand-50 border-b border-brand-100 text-brand-800 text-sm px-4 py-2 flex items-center gap-2">
|
||||
<ArrowUpCircle className="w-4 h-4 shrink-0" />
|
||||
<span>Hope Events {status.latestVersion} is available (this site runs {status.version}). {link}</span>
|
||||
<span>Cross Events {status.latestVersion} is available (this site runs {status.version}). {link}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -145,7 +145,7 @@ export const siteSettingsHelpContent: HelpContent = {
|
||||
icon: KeyRound,
|
||||
content: (
|
||||
<div className="space-y-4">
|
||||
<p>The site's licence and its connection to the Hope Events Hub. The site checks in every 15 minutes with its version, health and totals — never anyone's personal information.</p>
|
||||
<p>The site's licence and its connection to the Cross Events Hub. The site checks in every 15 minutes with its version, health and totals — never anyone's personal information.</p>
|
||||
<div className="space-y-4">
|
||||
<GuideItem icon={KeyRound} title="Connecting" tone="brand">
|
||||
Enter the licence key you received (CEH-XXXX-XXXX-XXXX-XXXX). If the site moves to a new address, ask your provider to release the licence, then connect again.
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
# Hope Events — Event Management Platform for Churches
|
||||
# Cross Events — Event Management Platform for Churches
|
||||
|
||||
## What is it?
|
||||
|
||||
Hope Events is a complete event management platform built specifically for churches and faith-based organisations. It handles everything from advertising upcoming events and taking online registrations to collecting payments and donations, sending QR-coded tickets, checking people in at the door, and balancing the books afterwards.
|
||||
Cross Events is a complete event management platform built specifically for churches and faith-based organisations. It handles everything from advertising upcoming events and taking online registrations to collecting payments and donations, sending QR-coded tickets, checking people in at the door, and balancing the books afterwards.
|
||||
|
||||
It runs in any web browser — on a phone, tablet, or computer — with nothing to install.
|
||||
|
||||
@@ -76,7 +76,7 @@ It runs in any web browser — on a phone, tablet, or computer — with nothing
|
||||
|
||||
## Pricing
|
||||
|
||||
Hope Events is offered as a **hosted service**. You don't need to worry about servers, databases, or software updates — we handle everything.
|
||||
Cross Events is offered as a **hosted service**. You don't need to worry about servers, databases, or software updates — we handle everything.
|
||||
|
||||
| Plan | Details |
|
||||
|------|---------|
|
||||
|
||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "hope-events",
|
||||
"version": "1.41.1",
|
||||
"version": "1.41.3",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "hope-events",
|
||||
"version": "1.41.1",
|
||||
"version": "1.41.3",
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"@playwright/test": "^1.64.0",
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "hope-events",
|
||||
"version": "1.41.1",
|
||||
"version": "1.41.3",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"dev:backend": "cd backend && npm run dev",
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
# Installs Hope Events on a fresh Ubuntu server (22.04 or 24.04): Node 20, PostgreSQL, PM2,
|
||||
# Installs Cross Events on a fresh Ubuntu server (22.04 or 24.04): Node 20, PostgreSQL, PM2,
|
||||
# nginx and an HTTPS certificate, then the app itself. Finish in the browser at /setup.
|
||||
#
|
||||
# Usage (as root, or with sudo):
|
||||
|
||||
Reference in New Issue
Block a user