diff --git a/nuxt.config.js b/nuxt.config.js index 28c038a34..816a9cdea 100644 --- a/nuxt.config.js +++ b/nuxt.config.js @@ -139,6 +139,7 @@ export default { { src: '~/plugins/globals.js' }, { src: '~/plugins/auth.js' }, { src: '~/plugins/datepicker.js', ssr: false }, + { src: '~/plugins/track.js', ssr: false } ], components: true, buildModules: [], @@ -163,11 +164,16 @@ export default { }, plausible: { domain: process.env.PLAUSIBLE_DOMAIN || translations.domain, + // NOTE(privacy): Disables automatic tracking of page views, meaning we have to do it manually + // If it's not done manually, a privacy issue occurs, which we *do not want* + // - tecc + enableAutoPageviews: false }, publicRuntimeConfig: { plausible: { domain: process.env.PLAUSIBLE_DOMAIN || translations.domain, - apiHost: process.env.PLAUSIBLE_API_HOST + apiHost: process.env.PLAUSIBLE_API_HOST, + enableAutoPageviews: false // see previous comment } }, build: { diff --git a/plugins/track.js b/plugins/track.js new file mode 100644 index 000000000..67b8c9616 --- /dev/null +++ b/plugins/track.js @@ -0,0 +1,70 @@ +function defaultHandler({plausible, to, from}) { + // console.debug("[analytics] Tracking default handler"); + plausible.trackPageview() +} +function eventHandler(eventName) { + return function ({plausible, to, from}) { + plausible.trackEvent(eventName, {}, {}); + } +} +/** + * @param {(value: string) => string} redactor + * @param {(ctx) => void} base + * @return {(ctx) => void} + */ +function redact(redactor, base = defaultHandler) { + return (ctx) => base({ + ...ctx, + to: redactor(ctx) + }) +} + +const TRACKER_OVERRIDES = [ + { + test(v) { + return /^\/@.+/.test(v); + }, + handling: redact((v) => { + const parts = v.split('/').filter((v) => v.length > 0); + for (const i in parts) { // NOTE(tecc): This might be overengineering but that's fine + const part = parts[i]; + if (/^@.+$/.test(part)) { + parts[i] = '@--redacted--'; + } + } + return '/' + parts.join('/'); + }) + } +] + +export const plugin = function ({app}) { + const plausible = app.$plausible; + + app.router.afterEach((to, from) => { + let handler = defaultHandler; + for (const trackerOverride of TRACKER_OVERRIDES) { + if (!trackerOverride.test(to.fullPath)) { + continue; + } + + if (trackerOverride.handling === false) { + console.debug("[analytics] Page is blocked from tracking"); + return; + } else if (typeof trackerOverride.handling === "function") { + handler = trackerOverride.handling; + } else { + throw new Error("Tracking override handling is invalid"); + } + break; + } + + // console.log("[analytics] Tracking pageview") + try { + handler({plausible, to, from}); + } catch (e) { + console.error("Error whilst trying to handle navigation: %O", e); + } + }) +} + +export default plugin; \ No newline at end of file