{"id":"69e411dc4d0935332cfbed8d","title":"2026 04 19","path":"how-to/committee/release-notes/2026-04-19","contentMarkdown":"# 19-Apr-2026 — centralise worker secrets, harden paste flows, and bypass headless-bot detection\n\n## [build 615](https://github.com/nbarrett/ngx-ramblers/actions/runs/24615893808) — [commit b70e39a](https://github.com/nbarrett/ngx-ramblers/commit/b70e39a18a89401e6673f5bd958a2c3744cfdb7c)\n\n_____\n\n## Centralise worker wiring in Global Application Secrets\n\n`INTEGRATION_WORKER_URL`, `INTEGRATION_WORKER_SHARED_SECRET` and\n`INTEGRATION_WORKER_ENCRYPTION_KEY` are the same value for every env —\nthey belong in the root `config.environments.value.secrets` section\n(the Global Application Secrets panel in Admin), not sprinkled across\neach `environments[].secrets`. `buildSecretsFromDatabaseConfig` already\nmerges root secrets into every env deploy, so no code change is needed\nfor the shared trio to propagate.\n\n`INTEGRATION_WORKER_CALLBACK_BASE_URL` genuinely differs per env (each\nwebsite has its own fly URL). Auto-derive it in\n`buildSecretsFromDatabaseConfig` from `envConfig.flyio.appName` when no\nexplicit value is set, so any new env is worker-ready on first deploy\nwithout manual per-env secret curation.\n\nData already migrated in staging DB: three shared keys added under\nglobals; `staging`, `ekwg`, `ekwg-dev-2-staging` per-env duplicates\nremoved. Each now only carries `INTEGRATION_WORKER_CALLBACK_BASE_URL`.\n\n## Paste-URL hardening (CMS dynamic content text field)\n\n`isBase64Image` at `url.service.ts` already accepts any `data:` URI\n(fixes GIF preview `GET /api/aws/s3/data:image/...` errors), and the\nimage cropper enables save for non-croppable files (GIFs) with\n`awsFileName` falling back to `originalFile.name`.\n\n`PageContentService.allAlbumIndexParents` now returns early for an\nundefined `childPath` (happens during initial CMS page load before\n`pageContent` resolves) and guards against non-string `contentPath`\nvalues with `isString` from `es-toolkit/compat`, fixing\n`TypeError: Cannot read properties of undefined (reading 'startsWith')`\nreported in the dynamic content editor.\n\n## Worker browser fingerprint\n\n`integration-worker-browser-routes` now creates pages via a\n`browser.newContext({ userAgent, viewport, locale, timezoneId,\nextraHTTPHeaders })` helper rather than `browser.newPage()` directly,\nso sites behind Cloudflare/Akamai (e.g. ramblers.org.uk) don't serve\n403 Forbidden on fingerprint. Applied to both `html-fetch` and\n`flickr-user-albums` routes.\n\n## Misc\n\n`navigate-with-dom-loaded` task added in the previous commit is used\ndeclaratively from `walks-upload.ts`; removed the explanatory comment\nblock to comply with the project's no-comments rule.","contentHtml":"<h1>19-Apr-2026 — centralise worker secrets, harden paste flows, and bypass headless-bot detection</h1>\n<h2><a href=\"https://github.com/nbarrett/ngx-ramblers/actions/runs/24615893808\">build 615</a> — <a href=\"https://github.com/nbarrett/ngx-ramblers/commit/b70e39a18a89401e6673f5bd958a2c3744cfdb7c\">commit b70e39a</a></h2>\n<hr>\n<h2>Centralise worker wiring in Global Application Secrets</h2>\n<p><code>INTEGRATION_WORKER_URL</code>, <code>INTEGRATION_WORKER_SHARED_SECRET</code> and\n<code>INTEGRATION_WORKER_ENCRYPTION_KEY</code> are the same value for every env —\nthey belong in the root <code>config.environments.value.secrets</code> section\n(the Global Application Secrets panel in Admin), not sprinkled across\neach <code>environments[].secrets</code>. <code>buildSecretsFromDatabaseConfig</code> already\nmerges root secrets into every env deploy, so no code change is needed\nfor the shared trio to propagate.</p>\n<p><code>INTEGRATION_WORKER_CALLBACK_BASE_URL</code> genuinely differs per env (each\nwebsite has its own fly URL). Auto-derive it in\n<code>buildSecretsFromDatabaseConfig</code> from <code>envConfig.flyio.appName</code> when no\nexplicit value is set, so any new env is worker-ready on first deploy\nwithout manual per-env secret curation.</p>\n<p>Data already migrated in staging DB: three shared keys added under\nglobals; <code>staging</code>, <code>ekwg</code>, <code>ekwg-dev-2-staging</code> per-env duplicates\nremoved. Each now only carries <code>INTEGRATION_WORKER_CALLBACK_BASE_URL</code>.</p>\n<h2>Paste-URL hardening (CMS dynamic content text field)</h2>\n<p><code>isBase64Image</code> at <code>url.service.ts</code> already accepts any <code>data:</code> URI\n(fixes GIF preview <code>GET /api/aws/s3/data:image/...</code> errors), and the\nimage cropper enables save for non-croppable files (GIFs) with\n<code>awsFileName</code> falling back to <code>originalFile.name</code>.</p>\n<p><code>PageContentService.allAlbumIndexParents</code> now returns early for an\nundefined <code>childPath</code> (happens during initial CMS page load before\n<code>pageContent</code> resolves) and guards against non-string <code>contentPath</code>\nvalues with <code>isString</code> from <code>es-toolkit/compat</code>, fixing\n<code>TypeError: Cannot read properties of undefined (reading &#39;startsWith&#39;)</code>\nreported in the dynamic content editor.</p>\n<h2>Worker browser fingerprint</h2>\n<p><code>integration-worker-browser-routes</code> now creates pages via a\n<code>browser.newContext({ userAgent, viewport, locale, timezoneId, extraHTTPHeaders })</code> helper rather than <code>browser.newPage()</code> directly,\nso sites behind Cloudflare/Akamai (e.g. ramblers.org.uk) don&#39;t serve\n403 Forbidden on fingerprint. Applied to both <code>html-fetch</code> and\n<code>flickr-user-albums</code> routes.</p>\n<h2>Misc</h2>\n<p><code>navigate-with-dom-loaded</code> task added in the previous commit is used\ndeclaratively from <code>walks-upload.ts</code>; removed the explanatory comment\nblock to comply with the project&#39;s no-comments rule.</p>\n"}