feat: set up GA4 with GTM
This commit is contained in:
Robert Helmer 2023-02-10 16:52:05 -08:00 коммит произвёл GitHub
Родитель 0c1f2ef77e
Коммит ff51c14d84
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
9 изменённых файлов: 46 добавлений и 12 удалений

Просмотреть файл

@ -101,4 +101,6 @@ EDUCATION_VIDEO_URL_VPN=https://monitor.cdn.mozilla.net/videos/Mozilla_VPN.mp4
ADMINS=
# Enable monthly cron-job, currently for sending unresolved breach reminder emails
MONTHLY_CRON_ENABLED=
MONTHLY_CRON_ENABLED=
GA4_MEASUREMENT_ID=test

Просмотреть файл

@ -46,7 +46,8 @@ const requiredEnvVars = [
'DELETE_UNVERIFIED_SUBSCRIBERS_TIMER',
'EXPERIMENT_ACTIVE',
'MAX_NUM_ADDRESSES',
'SUPPORTED_LOCALES'
'SUPPORTED_LOCALES',
'GA4_MEASUREMENT_ID'
]
const optionalEnvVars = [

Просмотреть файл

@ -2,6 +2,8 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import crypto from 'node:crypto'
import express from 'express'
import session from 'express-session'
import connectRedis from 'connect-redis'
@ -87,15 +89,31 @@ if (AppConstants.FXA_ENABLED) {
imgSrc.push(fxaSrc)
}
// Support GA4 per https://developers.google.com/tag-platform/tag-manager/web/csp
imgSrc.push('www.googletagmanager.com')
// disable forced https to allow localhost on Safari
app.use(
app.use((_req, res, _next) => {
res.locals.nonce = crypto.randomBytes(16).toString('hex')
helmet.contentSecurityPolicy({
directives: {
upgradeInsecureRequests: isDev ? null : [],
scriptSrc: [
"'self'",
// Support GA4 per https://developers.google.com/tag-platform/tag-manager/web/csp
`'nonce-${res.locals.nonce}'`
],
imgSrc,
upgradeInsecureRequests: isDev ? null : []
connectSrc: [
"'self'",
// Support GA4 per https://developers.google.com/tag-platform/tag-manager/web/csp
'https://*.google-analytics.com',
'https://*.analytics.google.com',
'https://*.googletagmanager.com'
]
}
})
)
})(_req, res, _next)
})
// fallback to default 'no-referrer' only when 'strict-origin-when-cross-origin' not available
app.use(

Просмотреть файл

@ -20,7 +20,8 @@ async function breachesPage (req, res) {
emailCount,
partial: breaches,
csrfToken: generateToken(res),
fxaProfile: req.user.fxa_profile_json
fxaProfile: req.user.fxa_profile_json,
nonce: res.locals.nonce
}
res.send(mainLayout(data))

Просмотреть файл

@ -8,7 +8,8 @@ import { dashboard } from '../views/partials/dashboard.js'
function dashboardPage (req, res) {
const data = {
fxaProfile: req.user.fxa_profile_json,
partial: dashboard
partial: dashboard,
nonce: res.locals.nonce
}
res.send(mainLayout(data))

Просмотреть файл

@ -8,7 +8,8 @@ import { dataRemoval } from '../views/partials/data-removal.js'
function dataRemovalPage (req, res) {
const data = {
fxaProfile: req.user.fxa_profile_json,
partial: dataRemoval
partial: dataRemoval,
nonce: res.locals.nonce
}
res.send(mainLayout(data))

Просмотреть файл

@ -7,7 +7,8 @@ import { landing } from '../views/partials/landing.js'
function landingPage (req, res) {
const data = {
partial: landing
partial: landing,
nonce: res.locals.nonce
}
res.send(mainLayout(data))

Просмотреть файл

@ -62,7 +62,8 @@ async function settingsPage (req, res) {
emails,
breachCounts,
limit: AppConstants.MAX_NUM_ADDRESSES,
csrfToken: generateToken(res)
csrfToken: generateToken(res),
nonce: res.locals.nonce
}
res.send(mainLayout(data))

Просмотреть файл

@ -36,7 +36,15 @@ const mainLayout = data => `
<link rel='apple-touch-icon' href='/images/apple-touch-icon.webp' sizes='180x180'>
<script src='/js/index.js' type='module'></script>
</head>
<!-- Google Tag Manager -->
<script nonce='${data.nonce}'>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'https://www.googletagmanager.com/gtm.js?id='+i+dl;var n=d.querySelector('[nonce]');
n&&j.setAttribute('nonce',n.nonce||n.getAttribute('nonce'));f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','${AppConstants.GA4_MEASUREMENT_ID}');</script>
<!-- End Google Tag Manager -->
</head>
<body>
${data.partial.name === 'landing' ? landingHeader(data) : mainHeader(data)}
<main data-partial='${data.partial.name}'>