|
|
7 hours ago | |
|---|---|---|
| .. | ||
| README.md | 7 hours ago | |
| apache-radio.conf.example | 7 hours ago | |
| radio.service.example | 7 hours ago | |
The app lives at davidwindham.com/radio — a long-running Node process that
Apache proxies to, same shape as /chess and /ask. It is not served from the
web docroot.
Code is synced to the server by FTP, and the process is managed over SSH (pm2 or systemd), same as the other node apps.
Express serves app/, which is compiled from src/ by esbuild and is
gitignored. FTP uploads files from your disk, so:
Run
npm run buildlocally before every upload, and make sureapp/is included in what you FTP up.
If you forget, the server keeps serving the previous app/ (or none), and your
changes silently don't appear — no error, just stale files. src/ and the build
tooling (esbuild, bootstrap, handlebars) never need to go to the server at all;
they only exist to produce app/ on your machine.
Only four things, at runtime:
| On the server | Where it comes from |
|---|---|
app/ |
built locally, uploaded by FTP |
app.js |
uploaded by FTP |
package.json (+ package-lock.json) |
uploaded by FTP |
node_modules/ (prod only — 5 packages) |
npm ci --omit=dev on the server |
Not needed on the server: src/, build.mjs, esbuild, bootstrap,
handlebars. Those are build-time only.
Build locally.
npm run build # produces app/
FTP up the project folder, including app/. At minimum:
app/, app.js, package.json, package-lock.json,
ecosystem.config.cjs, deploy/. Skip node_modules/, .env, src/.
Install runtime deps on the server (SSH, once — and again only when
package.json changes):
cd /path/to/radio
npm ci --omit=dev # installs ONLY the 5 runtime deps, no build tooling
(No shell npm? Then also build node_modules locally with
npm ci --omit=dev and FTP it up. Slower — thousands of files — but works.)
Set the secret — a gitignored .env in the app root on the server:
echo 'LASTFM_API_KEY=your-key-here' > .env
Unset is fine — the sidebar lists just stay empty and the app still runs.
pm2 (matches chess-io):
pm2 start ecosystem.config.cjs
pm2 logs radio --lines 5 # confirm basePath":"/radio"
pm2 save # remember across reboots
pm2 startup # prints a sudo command to enable on boot
or systemd:
sudo cp deploy/radio.service.example /etc/systemd/system/radio.service
# edit User/Group/WorkingDirectory in it first
sudo systemctl daemon-reload
sudo systemctl enable --now radio
Add the Apache proxy. Paste the block from
deploy/apache-radio.conf.example into the existing davidwindham.com
vhost (both :80 and :443) — not a new vhost. Check the Apache version
first (apachectl -v); the file has Option A for 2.4.47+ and Option B for
older. Then:
apachectl configtest && sudo systemctl reload apache2 # or: apachectl -k graceful
sh
curl -s https://davidwindham.com/radio/health # {"ok":true,...}
curl -s https://davidwindham.com/radio/api/status # {"online":...}
Then load https://davidwindham.com/radio/ and confirm the chat connects
(network tab: a 101 Switching Protocols on /radio/socket.io/… means the
websocket upgraded; HTTP-200 long-polls mean the Apache upgrade rule isn't
matching).
# locally
npm run build
# FTP: upload the changed files — and app/ whenever you rebuilt
# on the server (SSH)
pm2 restart radio # or: sudo systemctl restart radio
Run npm ci --omit=dev on the server again only if package.json changed.
The node process serves the new app/ the moment it restarts — but it must
restart; it won't pick up files live.
FTP uploads but never deletes. Files removed from the project (this cleanup
dropped gulpfile.js, the whole webrtc-* bundle, and vendored libs) will
linger on the server after a plain upload — harmless clutter, except inside
app/, where a stale bundle could actually be served. npm run build wipes
app/ locally each time, so the safe move is to delete the server's app/
before uploading the fresh one (or use an FTP "mirror / delete orphans" mode
scoped to app/). For a first deploy there's nothing there yet, so it doesn't
matter.
--env-file-if-exists line in
ecosystem.config.cjs; on older Node, put LASTFM_API_KEY in the pm2 env
block or a systemd drop-in instead).proxy proxy_http rewrite (proxy_wstunnel too if you're on
Option B).stream.davidawindham.com over https — the
player and the /api/status proxy both need it, and http is blocked as mixed
content on the TLS page.REDIS_URL unset and the app runs
single-process, keeping chat state in memory. Only set it (and only then raise
pm2 instances) if you ever need to scale to multiple processes.DAW_ORIGIN must stay unset in production. It's a local-dev-only shim that
proxies /embed (the shared site chrome) to the main site when there's no
Apache in front of node. In production Apache serves /embed from the docroot.e12ea1d0…) is worth rotating —
it's read-only public data and now server-side only, so low stakes, but the
rotation is free.