diff --git a/cloudflare/map-site/public/404.html b/cloudflare/map-site/public/404.html index d7e540b..e5cd432 100644 --- a/cloudflare/map-site/public/404.html +++ b/cloudflare/map-site/public/404.html @@ -18,6 +18,7 @@ + diff --git a/cloudflare/map-site/public/_headers b/cloudflare/map-site/public/_headers index fb40807..dff85b6 100644 --- a/cloudflare/map-site/public/_headers +++ b/cloudflare/map-site/public/_headers @@ -6,25 +6,25 @@ X-Frame-Options: DENY / - Content-Security-Policy: default-src 'self'; script-src 'self' https://static.cloudflareinsights.com; style-src 'self' https://fonts.googleapis.com; font-src https://fonts.gstatic.com; img-src 'self' data:; connect-src 'self' https://cloudflareinsights.com; frame-ancestors 'none'; base-uri 'none'; form-action 'none' + Content-Security-Policy: default-src 'self'; script-src 'self' https://static.cloudflareinsights.com https://*.googletagmanager.com https://*.posthog.com; style-src 'self' https://fonts.googleapis.com; font-src https://fonts.gstatic.com; img-src 'self' data: https://*.google-analytics.com https://*.googletagmanager.com; connect-src 'self' https://cloudflareinsights.com https://*.google-analytics.com https://*.analytics.google.com https://*.googletagmanager.com https://*.posthog.com; worker-src 'self' blob: data:; frame-ancestors 'none'; base-uri 'none'; form-action 'none' /index.html - Content-Security-Policy: default-src 'self'; script-src 'self' https://static.cloudflareinsights.com; style-src 'self' https://fonts.googleapis.com; font-src https://fonts.gstatic.com; img-src 'self' data:; connect-src 'self' https://cloudflareinsights.com; frame-ancestors 'none'; base-uri 'none'; form-action 'none' + Content-Security-Policy: default-src 'self'; script-src 'self' https://static.cloudflareinsights.com https://*.googletagmanager.com https://*.posthog.com; style-src 'self' https://fonts.googleapis.com; font-src https://fonts.gstatic.com; img-src 'self' data: https://*.google-analytics.com https://*.googletagmanager.com; connect-src 'self' https://cloudflareinsights.com https://*.google-analytics.com https://*.analytics.google.com https://*.googletagmanager.com https://*.posthog.com; worker-src 'self' blob: data:; frame-ancestors 'none'; base-uri 'none'; form-action 'none' /learn/* - Content-Security-Policy: default-src 'self'; script-src 'self' https://static.cloudflareinsights.com; style-src 'self' https://fonts.googleapis.com; font-src https://fonts.gstatic.com; img-src 'self' data:; connect-src 'self' https://cloudflareinsights.com; frame-ancestors 'none'; base-uri 'none'; form-action 'none' + Content-Security-Policy: default-src 'self'; script-src 'self' https://static.cloudflareinsights.com https://*.googletagmanager.com https://*.posthog.com; style-src 'self' https://fonts.googleapis.com; font-src https://fonts.gstatic.com; img-src 'self' data: https://*.google-analytics.com https://*.googletagmanager.com; connect-src 'self' https://cloudflareinsights.com https://*.google-analytics.com https://*.analytics.google.com https://*.googletagmanager.com https://*.posthog.com; worker-src 'self' blob: data:; frame-ancestors 'none'; base-uri 'none'; form-action 'none' /blake2b/* - Content-Security-Policy: default-src 'self'; script-src 'self' https://static.cloudflareinsights.com; style-src 'self' https://fonts.googleapis.com; font-src https://fonts.gstatic.com; img-src 'self' data:; connect-src 'self' https://cloudflareinsights.com; frame-ancestors 'none'; base-uri 'none'; form-action 'none' + Content-Security-Policy: default-src 'self'; script-src 'self' https://static.cloudflareinsights.com https://*.googletagmanager.com https://*.posthog.com; style-src 'self' https://fonts.googleapis.com; font-src https://fonts.gstatic.com; img-src 'self' data: https://*.google-analytics.com https://*.googletagmanager.com; connect-src 'self' https://cloudflareinsights.com https://*.google-analytics.com https://*.analytics.google.com https://*.googletagmanager.com https://*.posthog.com; worker-src 'self' blob: data:; frame-ancestors 'none'; base-uri 'none'; form-action 'none' /ecosystem/* - Content-Security-Policy: default-src 'self'; script-src 'self' https://static.cloudflareinsights.com; style-src 'self' https://fonts.googleapis.com; font-src https://fonts.gstatic.com; img-src 'self' data:; connect-src 'self' https://cloudflareinsights.com; frame-ancestors 'none'; base-uri 'none'; form-action 'none' + Content-Security-Policy: default-src 'self'; script-src 'self' https://static.cloudflareinsights.com https://*.googletagmanager.com https://*.posthog.com; style-src 'self' https://fonts.googleapis.com; font-src https://fonts.gstatic.com; img-src 'self' data: https://*.google-analytics.com https://*.googletagmanager.com; connect-src 'self' https://cloudflareinsights.com https://*.google-analytics.com https://*.analytics.google.com https://*.googletagmanager.com https://*.posthog.com; worker-src 'self' blob: data:; frame-ancestors 'none'; base-uri 'none'; form-action 'none' /stream/* - Content-Security-Policy: default-src 'self'; script-src 'self' https://static.cloudflareinsights.com; style-src 'self' https://fonts.googleapis.com; font-src https://fonts.gstatic.com; img-src 'self' data:; connect-src 'self' https://cloudflareinsights.com; frame-ancestors 'none'; base-uri 'none'; form-action 'none' + Content-Security-Policy: default-src 'self'; script-src 'self' https://static.cloudflareinsights.com https://*.googletagmanager.com https://*.posthog.com; style-src 'self' https://fonts.googleapis.com; font-src https://fonts.gstatic.com; img-src 'self' data: https://*.google-analytics.com https://*.googletagmanager.com; connect-src 'self' https://cloudflareinsights.com https://*.google-analytics.com https://*.analytics.google.com https://*.googletagmanager.com https://*.posthog.com; worker-src 'self' blob: data:; frame-ancestors 'none'; base-uri 'none'; form-action 'none' /faq/* - Content-Security-Policy: default-src 'self'; script-src 'self' https://static.cloudflareinsights.com; style-src 'self' https://fonts.googleapis.com; font-src https://fonts.gstatic.com; img-src 'self' data:; connect-src 'self' https://cloudflareinsights.com; frame-ancestors 'none'; base-uri 'none'; form-action 'none' + Content-Security-Policy: default-src 'self'; script-src 'self' https://static.cloudflareinsights.com https://*.googletagmanager.com https://*.posthog.com; style-src 'self' https://fonts.googleapis.com; font-src https://fonts.gstatic.com; img-src 'self' data: https://*.google-analytics.com https://*.googletagmanager.com; connect-src 'self' https://cloudflareinsights.com https://*.google-analytics.com https://*.analytics.google.com https://*.googletagmanager.com https://*.posthog.com; worker-src 'self' blob: data:; frame-ancestors 'none'; base-uri 'none'; form-action 'none' /key/app X-Content-Type-Options: nosniff @@ -33,19 +33,19 @@ X-Content-Type-Options: nosniff /key/* - Content-Security-Policy: default-src 'self'; script-src 'self' https://static.cloudflareinsights.com; style-src 'self' https://fonts.googleapis.com; font-src https://fonts.gstatic.com; img-src 'self' data:; connect-src 'self' https://cloudflareinsights.com; frame-ancestors 'none'; base-uri 'none'; form-action 'none' + Content-Security-Policy: default-src 'self'; script-src 'self' https://static.cloudflareinsights.com https://*.googletagmanager.com https://*.posthog.com; style-src 'self' https://fonts.googleapis.com; font-src https://fonts.gstatic.com; img-src 'self' data: https://*.google-analytics.com https://*.googletagmanager.com; connect-src 'self' https://cloudflareinsights.com https://*.google-analytics.com https://*.analytics.google.com https://*.googletagmanager.com https://*.posthog.com; worker-src 'self' blob: data:; frame-ancestors 'none'; base-uri 'none'; form-action 'none' /404.html - Content-Security-Policy: default-src 'self'; script-src 'self' https://static.cloudflareinsights.com; style-src 'self' https://fonts.googleapis.com; font-src https://fonts.gstatic.com; img-src 'self' data:; connect-src 'self' https://cloudflareinsights.com; frame-ancestors 'none'; base-uri 'none'; form-action 'none' + Content-Security-Policy: default-src 'self'; script-src 'self' https://static.cloudflareinsights.com https://*.googletagmanager.com https://*.posthog.com; style-src 'self' https://fonts.googleapis.com; font-src https://fonts.gstatic.com; img-src 'self' data: https://*.google-analytics.com https://*.googletagmanager.com; connect-src 'self' https://cloudflareinsights.com https://*.google-analytics.com https://*.analytics.google.com https://*.googletagmanager.com https://*.posthog.com; worker-src 'self' blob: data:; frame-ancestors 'none'; base-uri 'none'; form-action 'none' /assets/* - Content-Security-Policy: default-src 'self'; script-src 'self' https://static.cloudflareinsights.com; style-src 'self' https://fonts.googleapis.com; font-src https://fonts.gstatic.com; img-src 'self' data:; connect-src 'self' https://cloudflareinsights.com; frame-ancestors 'none'; base-uri 'none'; form-action 'none' + Content-Security-Policy: default-src 'self'; script-src 'self' https://static.cloudflareinsights.com https://*.googletagmanager.com https://*.posthog.com; style-src 'self' https://fonts.googleapis.com; font-src https://fonts.gstatic.com; img-src 'self' data: https://*.google-analytics.com https://*.googletagmanager.com; connect-src 'self' https://cloudflareinsights.com https://*.google-analytics.com https://*.analytics.google.com https://*.googletagmanager.com https://*.posthog.com; worker-src 'self' blob: data:; frame-ancestors 'none'; base-uri 'none'; form-action 'none' /assets/* Cache-Control: public, max-age=604800 /map/* - Content-Security-Policy: default-src 'self'; script-src 'self' https://static.cloudflareinsights.com 'sha256-vlGx9AvjSJDahRHjiKwqKUG7BedPk8gKirI6r9RF/rI='; style-src 'self' https://fonts.googleapis.com; font-src https://fonts.gstatic.com; img-src 'self' data: blob:; connect-src 'self' https://cloudflareinsights.com; worker-src 'self' blob:; frame-ancestors 'none'; base-uri 'none'; form-action 'none' + Content-Security-Policy: default-src 'self'; script-src 'self' https://static.cloudflareinsights.com https://*.googletagmanager.com https://*.posthog.com 'sha256-vlGx9AvjSJDahRHjiKwqKUG7BedPk8gKirI6r9RF/rI='; style-src 'self' https://fonts.googleapis.com; font-src https://fonts.gstatic.com; img-src 'self' data: https://*.google-analytics.com https://*.googletagmanager.com blob:; connect-src 'self' https://cloudflareinsights.com https://*.google-analytics.com https://*.analytics.google.com https://*.googletagmanager.com https://*.posthog.com; worker-src 'self' blob: data:; frame-ancestors 'none'; base-uri 'none'; form-action 'none' # three.js is versioned by folder and the page's own files by ?v=, so they can be held a while. /map/vendor/* diff --git a/cloudflare/map-site/public/assets/analytics.js b/cloudflare/map-site/public/assets/analytics.js new file mode 100644 index 0000000..c894b9f --- /dev/null +++ b/cloudflare/map-site/public/assets/analytics.js @@ -0,0 +1,31 @@ +// Google Analytics 4 and PostHog for lazarus-xbt.xyz and pool.lazarus-xbt.xyz. +// +// One GA4 property ("Lazarus Pool websites", stream G-CFKT50ZPQE) and one PostHog project +// ("Lazarus sites", 625142) cover both hosts. Both set their cookies on .lazarus-xbt.xyz, so a visit +// that goes from the hub to the pool stays one visitor and one session, and reports split by host. +// The hub serves this file as /assets/analytics.js and the pool as /static/analytics.js; the two +// copies must stay identical (cloudflare/pool-site/tests/indexing.py checks). +(function () { + // Only the production hosts report: *.pages.dev previews, local servers and the node stay out. + var host = location.hostname; + if (host !== "lazarus-xbt.xyz" && host !== "pool.lazarus-xbt.xyz") return; + + var GA4 = "G-CFKT50ZPQE"; + var tag = document.createElement("script"); + tag.async = true; + tag.src = "https://www.googletagmanager.com/gtag/js?id=" + GA4; + document.head.appendChild(tag); + window.dataLayer = window.dataLayer || []; + window.gtag = function () { window.dataLayer.push(arguments); }; + window.gtag("js", new Date()); + window.gtag("config", GA4); + + // PostHog's loader as published at posthog.com/docs/libraries/js: it queues calls until + // array.js arrives from us-assets.i.posthog.com. + !function(t,e){var o,n,p,r;e.__SV||(window.posthog=e,e._i=[],e.init=function(i,s,a){function g(t,e){var o=e.split(".");2==o.length&&(t=t[o[0]],e=o[1]),t[e]=function(){t.push([e].concat(Array.prototype.slice.call(arguments,0)))}}(p=t.createElement("script")).type="text/javascript",p.crossOrigin="anonymous",p.async=!0,p.src=s.api_host.replace(".i.posthog.com","-assets.i.posthog.com")+"/static/array.js",(r=t.getElementsByTagName("script")[0]).parentNode.insertBefore(p,r);var u=e;for(void 0!==a?u=e[a]=[]:a="posthog",u.people=u.people||[],Object.defineProperty(u,"toString",{configurable:!0,enumerable:!0,writable:!0,value:function(t){var e="posthog";return"posthog"!==a&&(e+="."+a),t||(e+=" (stub)"),e}}),Object.defineProperty(u.people,"toString",{configurable:!0,enumerable:!0,writable:!0,value:function(){return u.toString(1)+".people (stub)"}}),o="init capture register register_once register_for_session unregister unregister_for_session getFeatureFlag getFeatureFlagResult isFeatureEnabled reloadFeatureFlags updateEarlyAccessFeatureEnrollment getEarlyAccessFeatures on onFeatureFlags onSessionId getSurveys getActiveMatchingSurveys renderSurvey canRenderSurvey getNextSurveyStep identify setPersonProperties group resetGroups setPersonPropertiesForFlags resetPersonPropertiesForFlags setGroupPropertiesForFlags resetGroupPropertiesForFlags reset get_distinct_id getGroups get_session_id get_session_replay_url alias set_config startSessionRecording stopSessionRecording sessionRecordingStarted captureException loadToolbar get_property getSessionProperty createPersonProfile opt_in_capturing opt_out_capturing has_opted_in_capturing has_opted_out_capturing clear_opt_in_out_capturing debug".split(" "),n=0;n + + + + + + diff --git a/cloudflare/map-site/public/stream/index.html b/cloudflare/map-site/public/stream/index.html index f0fffb2..07f2399 100644 --- a/cloudflare/map-site/public/stream/index.html +++ b/cloudflare/map-site/public/stream/index.html @@ -30,6 +30,7 @@ + '), `${url}: no analytics`); } for (const path of ['/naughtylist', '/naughtylist/index.html']) { const r = await worker.fetch(new Request('https://lazarus-xbt.xyz'+path+'?pool=Lazarus'), {}, {}); diff --git a/cloudflare/map-site/src/shell.js b/cloudflare/map-site/src/shell.js index c518a2a..6433353 100644 --- a/cloudflare/map-site/src/shell.js +++ b/cloudflare/map-site/src/shell.js @@ -38,6 +38,7 @@ export const HEAD = (title, description, canonical, current = "", css = "") => ` ${css ? `` : ""} + diff --git a/cloudflare/map-site/src/worker.js b/cloudflare/map-site/src/worker.js index 743263e..22ef469 100644 --- a/cloudflare/map-site/src/worker.js +++ b/cloudflare/map-site/src/worker.js @@ -254,7 +254,7 @@ async function naughtyAddress(env, address) { }; } -const PAGE_CSP = "default-src 'self'; script-src 'self' https://static.cloudflareinsights.com; style-src 'self' https://fonts.googleapis.com; font-src https://fonts.gstatic.com; img-src 'self' data:; connect-src 'self' https://cloudflareinsights.com; frame-ancestors 'none'; base-uri 'none'; form-action 'self'"; +const PAGE_CSP = "default-src 'self'; script-src 'self' https://static.cloudflareinsights.com https://*.googletagmanager.com https://*.posthog.com; style-src 'self' https://fonts.googleapis.com; font-src https://fonts.gstatic.com; img-src 'self' data: https://*.google-analytics.com https://*.googletagmanager.com; connect-src 'self' https://cloudflareinsights.com https://*.google-analytics.com https://*.analytics.google.com https://*.googletagmanager.com https://*.posthog.com; worker-src 'self' blob: data:; frame-ancestors 'none'; base-uri 'none'; form-action 'self'"; /** The naughty list as HTML, held at the edge for a minute per URL. */ async function naughtyPage(request, env, ctx, url) { diff --git a/cloudflare/pool-site/404.html b/cloudflare/pool-site/404.html index 3335f62..4d3e93c 100644 --- a/cloudflare/pool-site/404.html +++ b/cloudflare/pool-site/404.html @@ -15,6 +15,7 @@ a{color:#dbb565} ul{padding-left:1.1rem} +
diff --git a/cloudflare/pool-site/tests/indexing.py b/cloudflare/pool-site/tests/indexing.py index 38c5abd..b36b73e 100644 --- a/cloudflare/pool-site/tests/indexing.py +++ b/cloudflare/pool-site/tests/indexing.py @@ -49,6 +49,7 @@ def handle_starttag(self, tag, attrs): for key, target in {'home':'/', 'mine':'/connect', 'payouts':'/blocks', 'learn':'/how'}.items(): assert page.nav[key] == prefix + target, (url, key, page.nav) assert set(page.alts) == {'en', 'zh-CN', 'x-default'}, url + assert '' in text, (url, 'no analytics') for alt in page.alts.values(): assert alt in urls, (url, alt) count += 1 assert count == 30, count # 15 documents in two languages @@ -62,5 +63,10 @@ def handle_starttag(self, tag, attrs): for route in ['/static/', '/static/index.html', '/static/index']: assert f'{route} / 301' in (out/'_redirects').read_text() assert (out/'404.html').exists(), 'Pages would enable SPA fallback without 404.html' +for name in ('404.html', 'miner.html'): + assert '/static/analytics.js' in (out/name).read_text(), (name, 'no analytics') +# The hub and the pool load the same analytics file; one copy must not drift from the other. +hub_analytics = Path(__file__).resolve().parents[2] / 'map-site/public/assets/analytics.js' +assert (out/'static/analytics.js').read_bytes() == hub_analytics.read_bytes(), 'pool and hub analytics.js differ' assert '/*\n Strict-Transport-Security: max-age=' in (out/'_headers').read_text(), 'HSTS missing from _headers /*' print(f'PASS: {count} canonical HTML pages, languages, document navigation, sitemap and template exclusion') diff --git a/pool/static/analytics.js b/pool/static/analytics.js new file mode 100644 index 0000000..c894b9f --- /dev/null +++ b/pool/static/analytics.js @@ -0,0 +1,31 @@ +// Google Analytics 4 and PostHog for lazarus-xbt.xyz and pool.lazarus-xbt.xyz. +// +// One GA4 property ("Lazarus Pool websites", stream G-CFKT50ZPQE) and one PostHog project +// ("Lazarus sites", 625142) cover both hosts. Both set their cookies on .lazarus-xbt.xyz, so a visit +// that goes from the hub to the pool stays one visitor and one session, and reports split by host. +// The hub serves this file as /assets/analytics.js and the pool as /static/analytics.js; the two +// copies must stay identical (cloudflare/pool-site/tests/indexing.py checks). +(function () { + // Only the production hosts report: *.pages.dev previews, local servers and the node stay out. + var host = location.hostname; + if (host !== "lazarus-xbt.xyz" && host !== "pool.lazarus-xbt.xyz") return; + + var GA4 = "G-CFKT50ZPQE"; + var tag = document.createElement("script"); + tag.async = true; + tag.src = "https://www.googletagmanager.com/gtag/js?id=" + GA4; + document.head.appendChild(tag); + window.dataLayer = window.dataLayer || []; + window.gtag = function () { window.dataLayer.push(arguments); }; + window.gtag("js", new Date()); + window.gtag("config", GA4); + + // PostHog's loader as published at posthog.com/docs/libraries/js: it queues calls until + // array.js arrives from us-assets.i.posthog.com. + !function(t,e){var o,n,p,r;e.__SV||(window.posthog=e,e._i=[],e.init=function(i,s,a){function g(t,e){var o=e.split(".");2==o.length&&(t=t[o[0]],e=o[1]),t[e]=function(){t.push([e].concat(Array.prototype.slice.call(arguments,0)))}}(p=t.createElement("script")).type="text/javascript",p.crossOrigin="anonymous",p.async=!0,p.src=s.api_host.replace(".i.posthog.com","-assets.i.posthog.com")+"/static/array.js",(r=t.getElementsByTagName("script")[0]).parentNode.insertBefore(p,r);var u=e;for(void 0!==a?u=e[a]=[]:a="posthog",u.people=u.people||[],Object.defineProperty(u,"toString",{configurable:!0,enumerable:!0,writable:!0,value:function(t){var e="posthog";return"posthog"!==a&&(e+="."+a),t||(e+=" (stub)"),e}}),Object.defineProperty(u.people,"toString",{configurable:!0,enumerable:!0,writable:!0,value:function(){return u.toString(1)+".people (stub)"}}),o="init capture register register_once register_for_session unregister unregister_for_session getFeatureFlag getFeatureFlagResult isFeatureEnabled reloadFeatureFlags updateEarlyAccessFeatureEnrollment getEarlyAccessFeatures on onFeatureFlags onSessionId getSurveys getActiveMatchingSurveys renderSurvey canRenderSurvey getNextSurveyStep identify setPersonProperties group resetGroups setPersonPropertiesForFlags resetPersonPropertiesForFlags setGroupPropertiesForFlags resetGroupPropertiesForFlags reset get_distinct_id getGroups get_session_id get_session_replay_url alias set_config startSessionRecording stopSessionRecording sessionRecordingStarted captureException loadToolbar get_property getSessionProperty createPersonProfile opt_in_capturing opt_out_capturing has_opted_in_capturing has_opted_out_capturing clear_opt_in_out_capturing debug".split(" "),n=0;n + diff --git a/pool/static/miner.html b/pool/static/miner.html index b189f48..15ad978 100644 --- a/pool/static/miner.html +++ b/pool/static/miner.html @@ -18,6 +18,7 @@ if (zh) { document.documentElement.lang = "zh-CN"; document.documentElement.classList.add("lang-zh"); } })(); +