feat: replace Tinybird tracking with PostHog

- Remove Tinybird fetch pipeline from pipe.ts, keep only session/lifetime copy counters
- Wire session_copy_count and lifetime_copy_count into posthog.capture calls
- Remove Tinybird calls from sponsor click tracking, use posthog only
- Hardcode PostHog project ID and reverse proxy host (hogpost.samber.dev)
This commit is contained in:
Samuel Berthe 2026-04-17 12:07:50 +02:00
parent 5a5976c9a3
commit b4324742be
No known key found for this signature in database
GPG key ID: 64863511FFBD0E3C
9 changed files with 481 additions and 126 deletions

1
site/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
.env

429
site/package-lock.json generated
View file

@ -16,6 +16,7 @@
"astro-icon": "^1.0.0", "astro-icon": "^1.0.0",
"js-yaml": "^4.1.0", "js-yaml": "^4.1.0",
"pagefind": "^1.0.0", "pagefind": "^1.0.0",
"posthog-js": "^1.369.2",
"tailwindcss": "^4.2.2", "tailwindcss": "^4.2.2",
"yaml": "^2.8.3" "yaml": "^2.8.3"
}, },
@ -1235,6 +1236,252 @@
"@jridgewell/sourcemap-codec": "^1.4.14" "@jridgewell/sourcemap-codec": "^1.4.14"
} }
}, },
"node_modules/@opentelemetry/api": {
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.1.tgz",
"integrity": "sha512-gLyJlPHPZYdAk1JENA9LeHejZe1Ti77/pTeFm/nMXmQH/HFZlcS/O2XJB+L8fkbrNSqhdtlvjBVjxwUYanNH5Q==",
"license": "Apache-2.0",
"engines": {
"node": ">=8.0.0"
}
},
"node_modules/@opentelemetry/api-logs": {
"version": "0.208.0",
"resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.208.0.tgz",
"integrity": "sha512-CjruKY9V6NMssL/T1kAFgzosF1v9o6oeN+aX5JB/C/xPNtmgIJqcXHG7fA82Ou1zCpWGl4lROQUKwUNE1pMCyg==",
"license": "Apache-2.0",
"dependencies": {
"@opentelemetry/api": "^1.3.0"
},
"engines": {
"node": ">=8.0.0"
}
},
"node_modules/@opentelemetry/core": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.2.0.tgz",
"integrity": "sha512-FuabnnUm8LflnieVxs6eP7Z383hgQU4W1e3KJS6aOG3RxWxcHyBxH8fDMHNgu/gFx/M2jvTOW/4/PHhLz6bjWw==",
"license": "Apache-2.0",
"dependencies": {
"@opentelemetry/semantic-conventions": "^1.29.0"
},
"engines": {
"node": "^18.19.0 || >=20.6.0"
},
"peerDependencies": {
"@opentelemetry/api": ">=1.0.0 <1.10.0"
}
},
"node_modules/@opentelemetry/exporter-logs-otlp-http": {
"version": "0.208.0",
"resolved": "https://registry.npmjs.org/@opentelemetry/exporter-logs-otlp-http/-/exporter-logs-otlp-http-0.208.0.tgz",
"integrity": "sha512-jOv40Bs9jy9bZVLo/i8FwUiuCvbjWDI+ZW13wimJm4LjnlwJxGgB+N/VWOZUTpM+ah/awXeQqKdNlpLf2EjvYg==",
"license": "Apache-2.0",
"dependencies": {
"@opentelemetry/api-logs": "0.208.0",
"@opentelemetry/core": "2.2.0",
"@opentelemetry/otlp-exporter-base": "0.208.0",
"@opentelemetry/otlp-transformer": "0.208.0",
"@opentelemetry/sdk-logs": "0.208.0"
},
"engines": {
"node": "^18.19.0 || >=20.6.0"
},
"peerDependencies": {
"@opentelemetry/api": "^1.3.0"
}
},
"node_modules/@opentelemetry/otlp-exporter-base": {
"version": "0.208.0",
"resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.208.0.tgz",
"integrity": "sha512-gMd39gIfVb2OgxldxUtOwGJYSH8P1kVFFlJLuut32L6KgUC4gl1dMhn+YC2mGn0bDOiQYSk/uHOdSjuKp58vvA==",
"license": "Apache-2.0",
"dependencies": {
"@opentelemetry/core": "2.2.0",
"@opentelemetry/otlp-transformer": "0.208.0"
},
"engines": {
"node": "^18.19.0 || >=20.6.0"
},
"peerDependencies": {
"@opentelemetry/api": "^1.3.0"
}
},
"node_modules/@opentelemetry/otlp-transformer": {
"version": "0.208.0",
"resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.208.0.tgz",
"integrity": "sha512-DCFPY8C6lAQHUNkzcNT9R+qYExvsk6C5Bto2pbNxgicpcSWbe2WHShLxkOxIdNcBiYPdVHv/e7vH7K6TI+C+fQ==",
"license": "Apache-2.0",
"dependencies": {
"@opentelemetry/api-logs": "0.208.0",
"@opentelemetry/core": "2.2.0",
"@opentelemetry/resources": "2.2.0",
"@opentelemetry/sdk-logs": "0.208.0",
"@opentelemetry/sdk-metrics": "2.2.0",
"@opentelemetry/sdk-trace-base": "2.2.0",
"protobufjs": "^7.3.0"
},
"engines": {
"node": "^18.19.0 || >=20.6.0"
},
"peerDependencies": {
"@opentelemetry/api": "^1.3.0"
}
},
"node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/resources": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.2.0.tgz",
"integrity": "sha512-1pNQf/JazQTMA0BiO5NINUzH0cbLbbl7mntLa4aJNmCCXSj0q03T5ZXXL0zw4G55TjdL9Tz32cznGClf+8zr5A==",
"license": "Apache-2.0",
"dependencies": {
"@opentelemetry/core": "2.2.0",
"@opentelemetry/semantic-conventions": "^1.29.0"
},
"engines": {
"node": "^18.19.0 || >=20.6.0"
},
"peerDependencies": {
"@opentelemetry/api": ">=1.3.0 <1.10.0"
}
},
"node_modules/@opentelemetry/resources": {
"version": "2.6.1",
"resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.6.1.tgz",
"integrity": "sha512-lID/vxSuKWXM55XhAKNoYXu9Cutoq5hFdkbTdI/zDKQktXzcWBVhNsOkiZFTMU9UtEWuGRNe0HUgmsFldIdxVA==",
"license": "Apache-2.0",
"dependencies": {
"@opentelemetry/core": "2.6.1",
"@opentelemetry/semantic-conventions": "^1.29.0"
},
"engines": {
"node": "^18.19.0 || >=20.6.0"
},
"peerDependencies": {
"@opentelemetry/api": ">=1.3.0 <1.10.0"
}
},
"node_modules/@opentelemetry/resources/node_modules/@opentelemetry/core": {
"version": "2.6.1",
"resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.6.1.tgz",
"integrity": "sha512-8xHSGWpJP9wBxgBpnqGL0R3PbdWQndL1Qp50qrg71+B28zK5OQmUgcDKLJgzyAAV38t4tOyLMGDD60LneR5W8g==",
"license": "Apache-2.0",
"dependencies": {
"@opentelemetry/semantic-conventions": "^1.29.0"
},
"engines": {
"node": "^18.19.0 || >=20.6.0"
},
"peerDependencies": {
"@opentelemetry/api": ">=1.0.0 <1.10.0"
}
},
"node_modules/@opentelemetry/sdk-logs": {
"version": "0.208.0",
"resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.208.0.tgz",
"integrity": "sha512-QlAyL1jRpOeaqx7/leG1vJMp84g0xKP6gJmfELBpnI4O/9xPX+Hu5m1POk9Kl+veNkyth5t19hRlN6tNY1sjbA==",
"license": "Apache-2.0",
"dependencies": {
"@opentelemetry/api-logs": "0.208.0",
"@opentelemetry/core": "2.2.0",
"@opentelemetry/resources": "2.2.0"
},
"engines": {
"node": "^18.19.0 || >=20.6.0"
},
"peerDependencies": {
"@opentelemetry/api": ">=1.4.0 <1.10.0"
}
},
"node_modules/@opentelemetry/sdk-logs/node_modules/@opentelemetry/resources": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.2.0.tgz",
"integrity": "sha512-1pNQf/JazQTMA0BiO5NINUzH0cbLbbl7mntLa4aJNmCCXSj0q03T5ZXXL0zw4G55TjdL9Tz32cznGClf+8zr5A==",
"license": "Apache-2.0",
"dependencies": {
"@opentelemetry/core": "2.2.0",
"@opentelemetry/semantic-conventions": "^1.29.0"
},
"engines": {
"node": "^18.19.0 || >=20.6.0"
},
"peerDependencies": {
"@opentelemetry/api": ">=1.3.0 <1.10.0"
}
},
"node_modules/@opentelemetry/sdk-metrics": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-2.2.0.tgz",
"integrity": "sha512-G5KYP6+VJMZzpGipQw7Giif48h6SGQ2PFKEYCybeXJsOCB4fp8azqMAAzE5lnnHK3ZVwYQrgmFbsUJO/zOnwGw==",
"license": "Apache-2.0",
"dependencies": {
"@opentelemetry/core": "2.2.0",
"@opentelemetry/resources": "2.2.0"
},
"engines": {
"node": "^18.19.0 || >=20.6.0"
},
"peerDependencies": {
"@opentelemetry/api": ">=1.9.0 <1.10.0"
}
},
"node_modules/@opentelemetry/sdk-metrics/node_modules/@opentelemetry/resources": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.2.0.tgz",
"integrity": "sha512-1pNQf/JazQTMA0BiO5NINUzH0cbLbbl7mntLa4aJNmCCXSj0q03T5ZXXL0zw4G55TjdL9Tz32cznGClf+8zr5A==",
"license": "Apache-2.0",
"dependencies": {
"@opentelemetry/core": "2.2.0",
"@opentelemetry/semantic-conventions": "^1.29.0"
},
"engines": {
"node": "^18.19.0 || >=20.6.0"
},
"peerDependencies": {
"@opentelemetry/api": ">=1.3.0 <1.10.0"
}
},
"node_modules/@opentelemetry/sdk-trace-base": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.2.0.tgz",
"integrity": "sha512-xWQgL0Bmctsalg6PaXExmzdedSp3gyKV8mQBwK/j9VGdCDu2fmXIb2gAehBKbkXCpJ4HPkgv3QfoJWRT4dHWbw==",
"license": "Apache-2.0",
"dependencies": {
"@opentelemetry/core": "2.2.0",
"@opentelemetry/resources": "2.2.0",
"@opentelemetry/semantic-conventions": "^1.29.0"
},
"engines": {
"node": "^18.19.0 || >=20.6.0"
},
"peerDependencies": {
"@opentelemetry/api": ">=1.3.0 <1.10.0"
}
},
"node_modules/@opentelemetry/sdk-trace-base/node_modules/@opentelemetry/resources": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.2.0.tgz",
"integrity": "sha512-1pNQf/JazQTMA0BiO5NINUzH0cbLbbl7mntLa4aJNmCCXSj0q03T5ZXXL0zw4G55TjdL9Tz32cznGClf+8zr5A==",
"license": "Apache-2.0",
"dependencies": {
"@opentelemetry/core": "2.2.0",
"@opentelemetry/semantic-conventions": "^1.29.0"
},
"engines": {
"node": "^18.19.0 || >=20.6.0"
},
"peerDependencies": {
"@opentelemetry/api": ">=1.3.0 <1.10.0"
}
},
"node_modules/@opentelemetry/semantic-conventions": {
"version": "1.40.0",
"resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.40.0.tgz",
"integrity": "sha512-cifvXDhcqMwwTlTK04GBNeIe7yyo28Mfby85QXFe1Yk8nmi36Ab/5UQwptOx84SsoGNRg+EVSjwzfSZMy6pmlw==",
"license": "Apache-2.0",
"engines": {
"node": ">=14"
}
},
"node_modules/@oslojs/encoding": { "node_modules/@oslojs/encoding": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/@oslojs/encoding/-/encoding-1.1.0.tgz", "resolved": "https://registry.npmjs.org/@oslojs/encoding/-/encoding-1.1.0.tgz",
@ -1332,6 +1579,82 @@
"win32" "win32"
] ]
}, },
"node_modules/@posthog/core": {
"version": "1.25.2",
"resolved": "https://registry.npmjs.org/@posthog/core/-/core-1.25.2.tgz",
"integrity": "sha512-h2FO7ut/BbfwpAXWpwdDHTzQgUo9ibDFEs6ZO+3cI3KPWQt5XwczK1OLAuPprcjm8T/jl0SH8jSFo5XdU4RbTg==",
"license": "MIT"
},
"node_modules/@posthog/types": {
"version": "1.369.2",
"resolved": "https://registry.npmjs.org/@posthog/types/-/types-1.369.2.tgz",
"integrity": "sha512-PJqkqPCFnnbCZslH2jHSvXlasRqvke6YAsYPhPALy4zy2hldor8A0O2wIlpAefEJ7fVz6wR5ZbRJzQP6nwujyw==",
"license": "MIT"
},
"node_modules/@protobufjs/aspromise": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
"integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==",
"license": "BSD-3-Clause"
},
"node_modules/@protobufjs/base64": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz",
"integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==",
"license": "BSD-3-Clause"
},
"node_modules/@protobufjs/codegen": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz",
"integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==",
"license": "BSD-3-Clause"
},
"node_modules/@protobufjs/eventemitter": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz",
"integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==",
"license": "BSD-3-Clause"
},
"node_modules/@protobufjs/fetch": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz",
"integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==",
"license": "BSD-3-Clause",
"dependencies": {
"@protobufjs/aspromise": "^1.1.1",
"@protobufjs/inquire": "^1.1.0"
}
},
"node_modules/@protobufjs/float": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz",
"integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==",
"license": "BSD-3-Clause"
},
"node_modules/@protobufjs/inquire": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz",
"integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==",
"license": "BSD-3-Clause"
},
"node_modules/@protobufjs/path": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz",
"integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==",
"license": "BSD-3-Clause"
},
"node_modules/@protobufjs/pool": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz",
"integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==",
"license": "BSD-3-Clause"
},
"node_modules/@protobufjs/utf8": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz",
"integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==",
"license": "BSD-3-Clause"
},
"node_modules/@rollup/plugin-yaml": { "node_modules/@rollup/plugin-yaml": {
"version": "4.1.2", "version": "4.1.2",
"resolved": "https://registry.npmjs.org/@rollup/plugin-yaml/-/plugin-yaml-4.1.2.tgz", "resolved": "https://registry.npmjs.org/@rollup/plugin-yaml/-/plugin-yaml-4.1.2.tgz",
@ -2156,6 +2479,13 @@
"@types/node": "*" "@types/node": "*"
} }
}, },
"node_modules/@types/trusted-types": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz",
"integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==",
"license": "MIT",
"optional": true
},
"node_modules/@types/unist": { "node_modules/@types/unist": {
"version": "3.0.3", "version": "3.0.3",
"resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz",
@ -2707,6 +3037,17 @@
"integrity": "sha512-lXVyvUvrNXblMqzIRrxHb57UUVmqsSWlxqt3XIjCkUP0wDAf6uicO6KMbEgYrMNtEvWgWHwe42CKxPu9MYAnWw==", "integrity": "sha512-lXVyvUvrNXblMqzIRrxHb57UUVmqsSWlxqt3XIjCkUP0wDAf6uicO6KMbEgYrMNtEvWgWHwe42CKxPu9MYAnWw==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/core-js": {
"version": "3.49.0",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.49.0.tgz",
"integrity": "sha512-es1U2+YTtzpwkxVLwAFdSpaIMyQaq0PBgm3YD1W3Qpsn1NAmO3KSgZfu+oGSWVu6NvLHoHCV/aYcsE5wiB7ALg==",
"hasInstallScript": true,
"license": "MIT",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/core-js"
}
},
"node_modules/crossws": { "node_modules/crossws": {
"version": "0.3.5", "version": "0.3.5",
"resolved": "https://registry.npmjs.org/crossws/-/crossws-0.3.5.tgz", "resolved": "https://registry.npmjs.org/crossws/-/crossws-0.3.5.tgz",
@ -2949,6 +3290,15 @@
"url": "https://github.com/fb55/domhandler?sponsor=1" "url": "https://github.com/fb55/domhandler?sponsor=1"
} }
}, },
"node_modules/dompurify": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.0.tgz",
"integrity": "sha512-nolgK9JcaUXMSmW+j1yaSvaEaoXYHwWyGJlkoCTghc97KgGDDSnpoU/PlEnw63Ah+TGKFOyY+X5LnxaWbCSfXg==",
"license": "(MPL-2.0 OR Apache-2.0)",
"optionalDependencies": {
"@types/trusted-types": "^2.0.7"
}
},
"node_modules/domutils": { "node_modules/domutils": {
"version": "3.2.2", "version": "3.2.2",
"resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz",
@ -3154,6 +3504,12 @@
} }
} }
}, },
"node_modules/fflate": {
"version": "0.4.8",
"resolved": "https://registry.npmjs.org/fflate/-/fflate-0.4.8.tgz",
"integrity": "sha512-FJqqoDBR00Mdj9ppamLa/Y7vxm+PRmNWA67N846RvsoYVMKB4q3y/de5PA7gUmRMYK/8CMz2GDZQmCRN1wBcWA==",
"license": "MIT"
},
"node_modules/flattie": { "node_modules/flattie": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/flattie/-/flattie-1.1.1.tgz", "resolved": "https://registry.npmjs.org/flattie/-/flattie-1.1.1.tgz",
@ -3898,6 +4254,12 @@
"url": "https://github.com/sponsors/antfu" "url": "https://github.com/sponsors/antfu"
} }
}, },
"node_modules/long": {
"version": "5.3.2",
"resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz",
"integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==",
"license": "Apache-2.0"
},
"node_modules/longest-streak": { "node_modules/longest-streak": {
"version": "3.1.0", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz",
@ -5131,6 +5493,37 @@
"node": "^10 || ^12 || >=14" "node": "^10 || ^12 || >=14"
} }
}, },
"node_modules/posthog-js": {
"version": "1.369.2",
"resolved": "https://registry.npmjs.org/posthog-js/-/posthog-js-1.369.2.tgz",
"integrity": "sha512-pY+SvNvRp3C2XW80h/jwVLTgoruK15C6klo9bYYoO6DCK9EbcwS6YzjgxBHx1dIN0XBZM3KWJPmuaSimU65HQQ==",
"license": "SEE LICENSE IN LICENSE",
"dependencies": {
"@opentelemetry/api": "^1.9.0",
"@opentelemetry/api-logs": "^0.208.0",
"@opentelemetry/exporter-logs-otlp-http": "^0.208.0",
"@opentelemetry/resources": "^2.2.0",
"@opentelemetry/sdk-logs": "^0.208.0",
"@posthog/core": "1.25.2",
"@posthog/types": "1.369.2",
"core-js": "^3.38.1",
"dompurify": "^3.3.2",
"fflate": "^0.4.8",
"preact": "^10.28.2",
"query-selector-shadow-dom": "^1.0.1",
"web-vitals": "^5.1.0"
}
},
"node_modules/preact": {
"version": "10.29.1",
"resolved": "https://registry.npmjs.org/preact/-/preact-10.29.1.tgz",
"integrity": "sha512-gQCLc/vWroE8lIpleXtdJhTFDogTdZG9AjMUpVkDf2iTCNwYNWA+u16dL41TqUDJO4gm2IgrcMv3uTpjd4Pwmg==",
"license": "MIT",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/preact"
}
},
"node_modules/prismjs": { "node_modules/prismjs": {
"version": "1.30.0", "version": "1.30.0",
"resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz", "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz",
@ -5163,6 +5556,30 @@
"url": "https://github.com/sponsors/wooorm" "url": "https://github.com/sponsors/wooorm"
} }
}, },
"node_modules/protobufjs": {
"version": "7.5.5",
"resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.5.5.tgz",
"integrity": "sha512-3wY1AxV+VBNW8Yypfd1yQY9pXnqTAN+KwQxL8iYm3/BjKYMNg4i0owhEe26PWDOMaIrzeeF98Lqd5NGz4omiIg==",
"hasInstallScript": true,
"license": "BSD-3-Clause",
"dependencies": {
"@protobufjs/aspromise": "^1.1.2",
"@protobufjs/base64": "^1.1.2",
"@protobufjs/codegen": "^2.0.4",
"@protobufjs/eventemitter": "^1.1.0",
"@protobufjs/fetch": "^1.1.0",
"@protobufjs/float": "^1.0.2",
"@protobufjs/inquire": "^1.1.0",
"@protobufjs/path": "^1.1.2",
"@protobufjs/pool": "^1.1.0",
"@protobufjs/utf8": "^1.1.0",
"@types/node": ">=13.7.0",
"long": "^5.0.0"
},
"engines": {
"node": ">=12.0.0"
}
},
"node_modules/pump": { "node_modules/pump": {
"version": "3.0.4", "version": "3.0.4",
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz",
@ -5189,6 +5606,12 @@
], ],
"license": "MIT" "license": "MIT"
}, },
"node_modules/query-selector-shadow-dom": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/query-selector-shadow-dom/-/query-selector-shadow-dom-1.0.1.tgz",
"integrity": "sha512-lT5yCqEBgfoMYpf3F2xQRK7zEr1rhIIZuceDK6+xRkJQ4NMbHTwXqk4NkwDwQMNqXgG9r9fyHnzwNVs6zV5KRw==",
"license": "MIT"
},
"node_modules/radix3": { "node_modules/radix3": {
"version": "1.1.2", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/radix3/-/radix3-1.1.2.tgz", "resolved": "https://registry.npmjs.org/radix3/-/radix3-1.1.2.tgz",
@ -6761,6 +7184,12 @@
"url": "https://github.com/sponsors/wooorm" "url": "https://github.com/sponsors/wooorm"
} }
}, },
"node_modules/web-vitals": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-5.2.0.tgz",
"integrity": "sha512-i2z98bEmaCqSDiHEDu+gHl/dmR4Q+TxFmG3/13KkMO+o8UxQzCqWaDRCiLgEa41nlO4VpXSI0ASa1xWmO9sBlA==",
"license": "Apache-2.0"
},
"node_modules/whatwg-encoding": { "node_modules/whatwg-encoding": {
"version": "3.1.1", "version": "3.1.1",
"resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz",

View file

@ -11,13 +11,14 @@
}, },
"dependencies": { "dependencies": {
"@astrojs/sitemap": "^3.0.0", "@astrojs/sitemap": "^3.0.0",
"@tailwindcss/vite": "^4.2.2",
"@iconify-json/lucide": "^1.2.102", "@iconify-json/lucide": "^1.2.102",
"@rollup/plugin-yaml": "^4.0.0", "@rollup/plugin-yaml": "^4.0.0",
"@tailwindcss/vite": "^4.2.2",
"astro": "^5.0.0", "astro": "^5.0.0",
"astro-icon": "^1.0.0", "astro-icon": "^1.0.0",
"js-yaml": "^4.1.0", "js-yaml": "^4.1.0",
"pagefind": "^1.0.0", "pagefind": "^1.0.0",
"posthog-js": "^1.369.2",
"tailwindcss": "^4.2.2", "tailwindcss": "^4.2.2",
"yaml": "^2.8.3" "yaml": "^2.8.3"
}, },

View file

@ -41,5 +41,13 @@ const base = import.meta.env.BASE_URL.replace(/\/$/, '');
} else { } else {
initPagefind(); initPagefind();
} }
// Track search queries via PostHog
searchEl.addEventListener('pagefind:search', (e: Event) => {
const query = (e as CustomEvent<{ query: string }>).detail?.query;
if (query) {
window.posthog?.capture('search_performed', { query });
}
});
} }
</script> </script>

View file

@ -0,0 +1,10 @@
---
// PostHog analytics snippet
---
<script is:inline>
!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+"/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||[],u.toString=function(t){var e="posthog";return"posthog"!==a&&(e+="."+a),t||(e+=" (stub)"),e},u.people.toString=function(){return u.toString(1)+".people (stub)"},o="capture identify alias people.set people.set_once set_config register register_once unregister opt_out_capturing has_opted_out_capturing opt_in_capturing reset isFeatureEnabled onFeatureFlags getFeatureFlag getFeatureFlagPayload reloadFeatureFlags group updateEarlyAccessFeatureEnrollment getEarlyAccessFeatures getActiveMatchingSurveys getSurveys getNextSurveyStep onSessionId".split(" "),n=0;n<o.length;n++)g(u,o[n]);e._i.push([i,s,a])},e.__SV=1)}(document,window.posthog||[]);
posthog.init('phc_rzLVwu5RRWhbJqQqpgcsNHyIaZOUs3Pw5laOq1sTdtI', {
api_host: 'https://hogpost.samber.dev',
defaults: '2026-01-30',
});
</script>

10
site/src/env.d.ts vendored Normal file
View file

@ -0,0 +1,10 @@
/// <reference types="astro/client" />
interface Window {
posthog?: {
capture: (event: string, properties?: Record<string, unknown>) => void;
identify: (distinctId: string, properties?: Record<string, unknown>) => void;
reset: () => void;
captureException: (error: unknown) => void;
};
}

View file

@ -4,6 +4,7 @@ import Header from '../components/Header.astro';
import Footer from '../components/Footer.astro'; import Footer from '../components/Footer.astro';
import StarToast from '../components/StarToast.astro'; import StarToast from '../components/StarToast.astro';
import SEO from '../components/SEO.astro'; import SEO from '../components/SEO.astro';
import PostHog from '../components/posthog.astro';
import { SITE_ORIGIN, AUTHOR_NAME } from '../data/site'; import { SITE_ORIGIN, AUTHOR_NAME } from '../data/site';
interface Props { interface Props {
@ -84,6 +85,8 @@ const canonical = canonicalUrl ?? `${SITE_ORIGIN}${base}${Astro.url.pathname.rep
gtag('js', new Date()); gtag('js', new Date());
gtag('config', 'G-GDF25KKVNL'); gtag('config', 'G-GDF25KKVNL');
</script> </script>
<PostHog />
</head> </head>
<body class="min-h-screen flex flex-col bg-white dark:bg-slate-950 text-slate-900 dark:text-slate-100 transition-colors duration-150"> <body class="min-h-screen flex flex-col bg-white dark:bg-slate-950 text-slate-900 dark:text-slate-100 transition-colors duration-150">
<a href="#main-content" class="sr-only focus:not-sr-only focus:absolute focus:top-2 focus:left-2 bg-brand text-white px-3 py-1 rounded z-50"> <a href="#main-content" class="sr-only focus:not-sr-only focus:absolute focus:top-2 focus:left-2 bg-brand text-white px-3 py-1 rounded z-50">
@ -99,10 +102,17 @@ const canonical = canonicalUrl ?? `${SITE_ORIGIN}${base}${Astro.url.pathname.rep
<Footer base={base} /> <Footer base={base} />
<StarToast /> <StarToast />
<script> <script>
import { recordCopy } from '../scripts/pipe'; import { bumpSessionCount, bumpLifetimeCount } from '../scripts/pipe';
window.addEventListener('apa-copy', (e: Event) => { window.addEventListener('apa-copy', (e: Event) => {
const { name, ...data } = (e as CustomEvent<Record<string, unknown>>).detail; const { name, ...data } = (e as CustomEvent<Record<string, unknown>>).detail;
recordCopy(name as string, data); const counts = { session_copy_count: bumpSessionCount(), lifetime_copy_count: bumpLifetimeCount() };
if (name === 'rule_copy' && data.scope === 'rule') {
window.posthog?.capture('rule_copied', { ...data, ...counts });
} else if (name === 'rule_copy' && data.scope === 'exporter') {
window.posthog?.capture('all_rules_copied', { ...data, ...counts });
} else if (name === 'wget_copy') {
window.posthog?.capture('wget_copied', { ...data, ...counts });
}
}); });
</script> </script>
</body> </body>

View file

@ -1,36 +1,4 @@
// First-party ingest pipe — records copy events to Tinybird. export function bumpSessionCount(): number {
// Naming deliberately avoids ad-blocker filter-list keywords
// (track, analytics, telemetry, metrics, beacon, pixel, collect, stat, signal).
// const PIPE_URL = 'https://api.eu-west-1.aws.tinybird.co/v0/events?name=';
const PIPE_URL = 'https://tb.samber.dev/?name=';
const PIPE_KEY = 'p.eyJ1IjogIjQ1MzY3NjRjLTNiY2MtNDU0My04M2ZjLWM0MDUxZGFhMGM5ZiIsICJpZCI6ICJmOWZjOGQ3Yi05ZGE1LTRiZjEtYjg4YS1mNGFlNTRkNTU3YWUiLCAiaG9zdCI6ICJhd3MtZXUtd2VzdC0xIn0.-zLRexgT8W2-derRM6jVCXzUkz54sMsiOy45WO6GglM';
function uid(): string {
if (typeof crypto !== 'undefined' && crypto.randomUUID) return crypto.randomUUID();
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
const r = (Math.random() * 16) | 0;
return (c === 'x' ? r : (r & 0x3) | 0x8).toString(16);
});
}
function getUserId(): string {
try {
let id = localStorage.getItem('apa_uid');
if (!id) { id = uid(); localStorage.setItem('apa_uid', id); }
return id;
} catch { return 'anon'; }
}
function getSessionId(): string {
try {
let id = sessionStorage.getItem('apa_sid');
if (!id) { id = uid(); sessionStorage.setItem('apa_sid', id); }
return id;
} catch { return 'anon'; }
}
function bumpSessionCount(): number {
try { try {
const n = parseInt(sessionStorage.getItem('apa_sc') ?? '0', 10); const n = parseInt(sessionStorage.getItem('apa_sc') ?? '0', 10);
sessionStorage.setItem('apa_sc', String(n + 1)); sessionStorage.setItem('apa_sc', String(n + 1));
@ -38,87 +6,10 @@ function bumpSessionCount(): number {
} catch { return 0; } } catch { return 0; }
} }
function bumpLifetimeCount(): number { export function bumpLifetimeCount(): number {
try { try {
const n = parseInt(localStorage.getItem('apa_lc') ?? '0', 10); const n = parseInt(localStorage.getItem('apa_lc') ?? '0', 10);
localStorage.setItem('apa_lc', String(n + 1)); localStorage.setItem('apa_lc', String(n + 1));
return n + 1; return n + 1;
} catch { return 0; } } catch { return 0; }
} }
function getPageContext() {
const path = location.pathname;
let page_type: 'service' | 'home' | 'guide' | 'other' = 'other';
if (path.includes('/rules/') && path.split('/').filter(Boolean).length >= 4) {
page_type = 'service';
} else if (path.split('/').filter(Boolean).length <= 1) {
page_type = 'home';
} else if (['alertmanager', 'blackbox-exporter', 'sleep-peacefully'].some((g) => path.includes(g))) {
page_type = 'guide';
}
return {
page_path: path,
page_type,
referrer: document.referrer || undefined,
anchor_hash: location.hash || undefined,
};
}
// Eagerly init IDs on module load so they exist before first copy
getUserId();
getSessionId();
function buildPayload(name: string, data: Record<string, unknown>): object {
return {
timestamp: new Date().toISOString(),
transaction_id: uid(),
name: "awesome_prometheus_alerts_" + name,
user_id: getUserId(),
session_id: getSessionId(),
...data,
...getPageContext(),
language: navigator.language,
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
viewport_w: window.innerWidth,
viewport_h: window.innerHeight,
color_scheme: document.documentElement.classList.contains('dark') ? 'dark' : 'light',
user_agent: navigator.userAgent,
is_bot: /bot|crawl|spider/i.test(navigator.userAgent),
};
}
export function record(name: string, data: Record<string, unknown>): void {
const payload = buildPayload(name, data);
fetch(PIPE_URL + "awesome_prometheus_alerts_" + name, {
method: 'POST',
body: JSON.stringify(payload),
headers: { Authorization: `Bearer ${PIPE_KEY}` },
keepalive: true,
}).catch(console.error);
}
export function recordCopy(name: string, data: Record<string, unknown>): void {
record(name, {
session_copy_count: bumpSessionCount(),
lifetime_copy_count: bumpLifetimeCount(),
...data,
});
}
export async function recordAndWait(name: string, data: Record<string, unknown>): Promise<void> {
const payload = buildPayload(name, data);
const ctrl = new AbortController();
const timer = setTimeout(() => ctrl.abort(), 1500);
try {
await fetch(PIPE_URL + "awesome_prometheus_alerts_" + name, {
method: 'POST',
body: JSON.stringify(payload),
headers: { Authorization: `Bearer ${PIPE_KEY}` },
signal: ctrl.signal,
});
} catch {
// swallow — a failed event must never break sponsor navigation
} finally {
clearTimeout(timer);
}
}

View file

@ -1,8 +1,6 @@
import { record, recordAndWait } from './pipe';
export function initSponsorClickTracking(): void { export function initSponsorClickTracking(): void {
document.querySelectorAll<HTMLAnchorElement>('a[data-sponsor-name]').forEach((a) => { document.querySelectorAll<HTMLAnchorElement>('a[data-sponsor-name]').forEach((a) => {
a.addEventListener('click', async (e) => { a.addEventListener('click', (e) => {
const me = e as MouseEvent; const me = e as MouseEvent;
const href = a.href; const href = a.href;
const sponsorName = a.dataset.sponsorName!; const sponsorName = a.dataset.sponsorName!;
@ -11,22 +9,19 @@ export function initSponsorClickTracking(): void {
// Modifier / non-primary clicks: track fire-and-forget, let browser handle navigation // Modifier / non-primary clicks: track fire-and-forget, let browser handle navigation
if (me.button !== 0 || me.metaKey || me.ctrlKey || me.shiftKey) { if (me.button !== 0 || me.metaKey || me.ctrlKey || me.shiftKey) {
record('sponsor_click', eventData); window.posthog?.capture('sponsor_clicked', eventData);
return; return;
} }
// Plain left-click: block navigation until event is recorded // Plain left-click: block navigation until event is recorded
e.preventDefault(); e.preventDefault();
window.posthog?.capture('sponsor_clicked', eventData);
// Open blank tab now (inside user gesture) to avoid popup-blocker after await // Open blank tab now (inside user gesture) to avoid popup-blocker after await
const w = a.target === '_blank' ? window.open('', '_blank') : null; const w = a.target === '_blank' ? window.open('', '_blank') : null;
try { if (w) {
await recordAndWait('sponsor_click', eventData); w.location.href = href;
} finally { } else {
if (w) { window.open(href, '_blank') ?? (window.location.href = href);
w.location.href = href;
} else {
window.open(href, '_blank') ?? (window.location.href = href);
}
} }
}); });
}); });