ecosystem.config.cjs 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869
  1. // pm2 process config for daveo-radio.
  2. //
  3. // CommonJS (.cjs) on purpose: package.json has "type": "module", so a plain
  4. // ecosystem.config.js would be parsed as ESM and `module.exports` would throw.
  5. // pm2 reads .cjs fine.
  6. //
  7. // IMPORTANT — build LOCALLY, upload the result. Unlike chess-io (which renders
  8. // templates at runtime), this app serves the built bundle in app/, which is
  9. // gitignored. Code is synced by FTP, so app/ only reaches the server if you
  10. // build it first and include it in the upload. pm2 does NOT build.
  11. //
  12. // On the server (SSH), first time:
  13. // npm ci --omit=dev # 5 runtime deps only, no build tooling
  14. // pm2 start ecosystem.config.cjs
  15. // pm2 logs radio --lines 5 # confirm basePath":"/radio"
  16. // pm2 save # remember across restarts
  17. // pm2 startup # prints a sudo command to enable on boot
  18. //
  19. // Redeploy: `npm run build` locally -> FTP up app/ + changed files ->
  20. // pm2 restart radio (npm ci again only if package.json changed)
  21. //
  22. // Full runbook: deploy/README.md
  23. //
  24. module.exports = {
  25. apps: [
  26. {
  27. name: 'radio',
  28. script: 'app.js',
  29. cwd: __dirname,
  30. // Secrets / extra config come from a gitignored .env on the server
  31. // (LASTFM_API_KEY, and REDIS_URL if you enable it). Node reads it into
  32. // process.env; the inline env below still wins for anything set in both,
  33. // so the operational config here can't be overridden by a stale .env.
  34. // --env-file-if-exists needs Node 22.9+. On older Node, drop this line
  35. // and put LASTFM_API_KEY in the env block below instead.
  36. node_args: '--env-file-if-exists=.env',
  37. // Single process, fork mode -- NOT cluster. Per-connection chat state
  38. // (nicknames, presence) lives in memory in one process via socket.data;
  39. // multiple workers would each hold a different slice and socket.io would
  40. // need a shared adapter + sticky sessions. If you ever DO want to scale
  41. // out, set REDIS_URL (enables the socket.io Redis adapter) and raise
  42. // instances -- not before.
  43. instances: 1,
  44. exec_mode: 'fork',
  45. env: {
  46. NODE_ENV: 'production',
  47. PORT: 3000,
  48. BASE_PATH: '/radio',
  49. TRUST_PROXY: 1,
  50. // Icecast. Must be https on a TLS page (mixed content is blocked).
  51. STREAM_URL: 'https://stream.davidawindham.com/stream',
  52. STREAM_STATUS_URL: 'https://stream.davidawindham.com/status-json.xsl',
  53. STREAM_MOUNT: '/stream',
  54. // LASTFM_API_KEY: set it in .env on the server (see node_args above).
  55. // Unset => the sidebar lists just stay empty; the app still runs.
  56. LASTFM_USER: 'windhamdavid',
  57. // DAW_ORIGIN is intentionally omitted -- it is a local-dev-only shim for
  58. // the shared chrome and must stay UNSET in production, where Apache
  59. // serves /embed from the WordPress docroot.
  60. },
  61. },
  62. ],
  63. };