Added stats tracker script to ghost head (#20881)

closes
https://linear.app/tryghost/issue/ANAL-9/initial-tracker-in-ghost-head

- Given that all of the correct config is in place, output a tracking
script
- This allows us to send pageview events into tinybird
- All of the details (location of the script, destination etc) are kept
in config so that it's easy to change for different environments
This commit is contained in:
Hannah Wolfe 2024-08-29 21:40:41 +01:00 committed by GitHub
parent d30164df97
commit f79f5471b4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 357 additions and 7 deletions

View File

@ -141,6 +141,21 @@ function getWebmentionDiscoveryLink() {
}
}
function getTinybirdTrackerScript(dataRoot) {
const scriptUrl = config.get('tinybird:tracker:scriptUrl');
const endpoint = config.get('tinybird:tracker:endpoint');
const token = config.get('tinybird:tracker:token');
const tbParams = _.map({
site_uuid: config.get('tinybird:tracker:id'),
post_uuid: dataRoot.post?.uuid,
member_uuid: dataRoot.member?.uuid,
member_status: dataRoot.member?.status
}, (value, key) => `tb_${key}="${value}"`).join(' ');
return `<script defer src="${scriptUrl}" data-host="${endpoint}" data-token="${token}" ${tbParams}></script>`;
}
/**
* **NOTE**
* Express adds `_locals`, see https://github.com/expressjs/express/blob/4.15.4/lib/response.js#L962.
@ -319,6 +334,10 @@ module.exports = async function ghost_head(options) { // eslint-disable-line cam
if (!_.isEmpty(tagCodeInjection)) {
head.push(tagCodeInjection);
}
if (config.get('tinybird') && config.get('tinybird:tracker') && config.get('tinybird:tracker:scriptUrl')) {
head.push(getTinybirdTrackerScript(dataRoot));
}
}
debug('end');

View File

@ -959,6 +959,248 @@ Object {
}
`;
exports[`{{ghost_head}} helper includes tinybird tracker script when config is set Sets tb_post_uuid on post page 1 1`] = `
Object {
"rendered": "<link rel=\\"canonical\\" href=\\"http://127.0.0.1:2369/post/\\">
<meta name=\\"referrer\\" content=\\"no-referrer-when-downgrade\\">
<link rel=\\"amphtml\\" href=\\"http://127.0.0.1:2369/post/amp/\\">
<meta property=\\"og:site_name\\" content=\\"Ghost\\">
<meta property=\\"og:type\\" content=\\"article\\">
<meta property=\\"og:title\\" content=\\"Testing stats\\">
<meta property=\\"og:description\\" content=\\"Creating stats for the site\\">
<meta property=\\"og:url\\" content=\\"http://127.0.0.1:2369/post/\\">
<meta property=\\"og:image\\" content=\\"http://127.0.0.1:2369/content/images/site-cover.png\\">
<meta property=\\"article:published_time\\" content=\\"1970-01-01T00:00:00.000Z\\">
<meta property=\\"article:modified_time\\" content=\\"1970-01-01T00:00:00.000Z\\">
<meta property=\\"article:author\\" content=\\"https://www.facebook.com/testuser\\">
<meta name=\\"twitter:card\\" content=\\"summary_large_image\\">
<meta name=\\"twitter:title\\" content=\\"Testing stats\\">
<meta name=\\"twitter:description\\" content=\\"Creating stats for the site\\">
<meta name=\\"twitter:url\\" content=\\"http://127.0.0.1:2369/post/\\">
<meta name=\\"twitter:image\\" content=\\"http://127.0.0.1:2369/content/images/site-cover.png\\">
<meta name=\\"twitter:label1\\" content=\\"Written by\\">
<meta name=\\"twitter:data1\\" content=\\"Author name\\">
<meta name=\\"twitter:creator\\" content=\\"@testuser\\">
<script type=\\"application/ld+json\\">
{
\\"@context\\": \\"https://schema.org\\",
\\"@type\\": \\"Article\\",
\\"publisher\\": {
\\"@type\\": \\"Organization\\",
\\"name\\": \\"Ghost\\",
\\"url\\": \\"http://127.0.0.1:2369/\\",
\\"logo\\": {
\\"@type\\": \\"ImageObject\\",
\\"url\\": \\"http://127.0.0.1:2369/favicon.ico\\"
}
},
\\"author\\": {
\\"@type\\": \\"Person\\",
\\"name\\": \\"Author name\\",
\\"image\\": {
\\"@type\\": \\"ImageObject\\",
\\"url\\": \\"http://127.0.0.1:2369/content/images/test-author-image.png\\"
},
\\"url\\": \\"https://mysite.com/fakeauthor/\\",
\\"sameAs\\": [
\\"http://authorwebsite.com\\",
\\"https://www.facebook.com/testuser\\",
\\"https://twitter.com/testuser\\"
]
},
\\"headline\\": \\"Testing stats\\",
\\"url\\": \\"http://127.0.0.1:2369/post/\\",
\\"datePublished\\": \\"1970-01-01T00:00:00.000Z\\",
\\"dateModified\\": \\"1970-01-01T00:00:00.000Z\\",
\\"description\\": \\"Creating stats for the site\\",
\\"mainEntityOfPage\\": \\"http://127.0.0.1:2369/post/\\"
}
</script>
<meta name=\\"generator\\" content=\\"Ghost 0.3\\">
<link rel=\\"alternate\\" type=\\"application/rss+xml\\" title=\\"Ghost\\" href=\\"http://localhost:65530/rss/\\">
<script defer src=\\"https://cdn.jsdelivr.net/ghost/sodo-search@~[[VERSION]]/umd/sodo-search.min.js\\" data-key=\\"xyz\\" data-styles=\\"https://cdn.jsdelivr.net/ghost/sodo-search@~[[VERSION]]/umd/main.css\\" data-sodo-search=\\"http://127.0.0.1:2369/\\" crossorigin=\\"anonymous\\"></script>
<link href=\\"http://127.0.0.1:2369/webmentions/receive/\\" rel=\\"webmention\\">
<script defer src=\\"https://unpkg.com/@tinybirdco/flock.js\\" data-host=\\"https://api.tinybird.co\\" data-token=\\"tinybird_token\\" tb_site_uuid=\\"tb_test_site_uuid\\" tb_post_uuid=\\"post_uuid\\" tb_member_uuid=\\"undefined\\" tb_member_status=\\"undefined\\"></script>",
}
`;
exports[`{{ghost_head}} helper includes tinybird tracker script when config is set sets both tb_member_x variables and tb_post_uuid on logged in post page 1 1`] = `
Object {
"rendered": "<link rel=\\"canonical\\" href=\\"http://127.0.0.1:2369/post/\\">
<meta name=\\"referrer\\" content=\\"no-referrer-when-downgrade\\">
<link rel=\\"amphtml\\" href=\\"http://127.0.0.1:2369/post/amp/\\">
<meta property=\\"og:site_name\\" content=\\"Ghost\\">
<meta property=\\"og:type\\" content=\\"article\\">
<meta property=\\"og:title\\" content=\\"Testing stats\\">
<meta property=\\"og:description\\" content=\\"Creating stats for the site\\">
<meta property=\\"og:url\\" content=\\"http://127.0.0.1:2369/post/\\">
<meta property=\\"og:image\\" content=\\"http://127.0.0.1:2369/content/images/site-cover.png\\">
<meta property=\\"article:published_time\\" content=\\"1970-01-01T00:00:00.000Z\\">
<meta property=\\"article:modified_time\\" content=\\"1970-01-01T00:00:00.000Z\\">
<meta property=\\"article:author\\" content=\\"https://www.facebook.com/testuser\\">
<meta name=\\"twitter:card\\" content=\\"summary_large_image\\">
<meta name=\\"twitter:title\\" content=\\"Testing stats\\">
<meta name=\\"twitter:description\\" content=\\"Creating stats for the site\\">
<meta name=\\"twitter:url\\" content=\\"http://127.0.0.1:2369/post/\\">
<meta name=\\"twitter:image\\" content=\\"http://127.0.0.1:2369/content/images/site-cover.png\\">
<meta name=\\"twitter:label1\\" content=\\"Written by\\">
<meta name=\\"twitter:data1\\" content=\\"Author name\\">
<meta name=\\"twitter:creator\\" content=\\"@testuser\\">
<script type=\\"application/ld+json\\">
{
\\"@context\\": \\"https://schema.org\\",
\\"@type\\": \\"Article\\",
\\"publisher\\": {
\\"@type\\": \\"Organization\\",
\\"name\\": \\"Ghost\\",
\\"url\\": \\"http://127.0.0.1:2369/\\",
\\"logo\\": {
\\"@type\\": \\"ImageObject\\",
\\"url\\": \\"http://127.0.0.1:2369/favicon.ico\\"
}
},
\\"author\\": {
\\"@type\\": \\"Person\\",
\\"name\\": \\"Author name\\",
\\"image\\": {
\\"@type\\": \\"ImageObject\\",
\\"url\\": \\"http://127.0.0.1:2369/content/images/test-author-image.png\\"
},
\\"url\\": \\"https://mysite.com/fakeauthor/\\",
\\"sameAs\\": [
\\"http://authorwebsite.com\\",
\\"https://www.facebook.com/testuser\\",
\\"https://twitter.com/testuser\\"
]
},
\\"headline\\": \\"Testing stats\\",
\\"url\\": \\"http://127.0.0.1:2369/post/\\",
\\"datePublished\\": \\"1970-01-01T00:00:00.000Z\\",
\\"dateModified\\": \\"1970-01-01T00:00:00.000Z\\",
\\"description\\": \\"Creating stats for the site\\",
\\"mainEntityOfPage\\": \\"http://127.0.0.1:2369/post/\\"
}
</script>
<meta name=\\"generator\\" content=\\"Ghost 4.3\\">
<link rel=\\"alternate\\" type=\\"application/rss+xml\\" title=\\"Ghost\\" href=\\"http://localhost:65530/rss/\\">
<script defer src=\\"https://cdn.jsdelivr.net/ghost/sodo-search@~[[VERSION]]/umd/sodo-search.min.js\\" data-key=\\"xyz\\" data-styles=\\"https://cdn.jsdelivr.net/ghost/sodo-search@~[[VERSION]]/umd/main.css\\" data-sodo-search=\\"http://127.0.0.1:2369/\\" crossorigin=\\"anonymous\\"></script>
<link href=\\"http://127.0.0.1:2369/webmentions/receive/\\" rel=\\"webmention\\">
<script defer src=\\"https://unpkg.com/@tinybirdco/flock.js\\" data-host=\\"https://api.tinybird.co\\" data-token=\\"tinybird_token\\" tb_site_uuid=\\"tb_test_site_uuid\\" tb_post_uuid=\\"post_uuid\\" tb_member_uuid=\\"member_uuid\\" tb_member_status=\\"free\\"></script>",
}
`;
exports[`{{ghost_head}} helper includes tinybird tracker script when config is set sets tb_member_x variables on logged in home page 1 1`] = `
Object {
"rendered": "<meta name=\\"description\\" content=\\"site description\\">
<link rel=\\"canonical\\" href=\\"http://127.0.0.1:2369/\\">
<meta name=\\"referrer\\" content=\\"no-referrer-when-downgrade\\">
<meta property=\\"og:site_name\\" content=\\"Ghost\\">
<meta property=\\"og:type\\" content=\\"website\\">
<meta property=\\"og:title\\" content=\\"Ghost\\">
<meta property=\\"og:description\\" content=\\"site description\\">
<meta property=\\"og:url\\" content=\\"http://127.0.0.1:2369/\\">
<meta property=\\"og:image\\" content=\\"http://127.0.0.1:2369/content/images/site-cover.png\\">
<meta name=\\"twitter:card\\" content=\\"summary_large_image\\">
<meta name=\\"twitter:title\\" content=\\"Ghost\\">
<meta name=\\"twitter:description\\" content=\\"site description\\">
<meta name=\\"twitter:url\\" content=\\"http://127.0.0.1:2369/\\">
<meta name=\\"twitter:image\\" content=\\"http://127.0.0.1:2369/content/images/site-cover.png\\">
<script type=\\"application/ld+json\\">
{
\\"@context\\": \\"https://schema.org\\",
\\"@type\\": \\"WebSite\\",
\\"publisher\\": {
\\"@type\\": \\"Organization\\",
\\"name\\": \\"Ghost\\",
\\"url\\": \\"http://127.0.0.1:2369/\\",
\\"logo\\": {
\\"@type\\": \\"ImageObject\\",
\\"url\\": \\"http://127.0.0.1:2369/favicon.ico\\"
}
},
\\"url\\": \\"http://127.0.0.1:2369/\\",
\\"image\\": {
\\"@type\\": \\"ImageObject\\",
\\"url\\": \\"http://127.0.0.1:2369/content/images/site-cover.png\\"
},
\\"mainEntityOfPage\\": \\"http://127.0.0.1:2369/\\",
\\"description\\": \\"site description\\"
}
</script>
<meta name=\\"generator\\" content=\\"Ghost 4.3\\">
<link rel=\\"alternate\\" type=\\"application/rss+xml\\" title=\\"Ghost\\" href=\\"http://localhost:65530/rss/\\">
<script defer src=\\"https://cdn.jsdelivr.net/ghost/sodo-search@~[[VERSION]]/umd/sodo-search.min.js\\" data-key=\\"xyz\\" data-styles=\\"https://cdn.jsdelivr.net/ghost/sodo-search@~[[VERSION]]/umd/main.css\\" data-sodo-search=\\"http://127.0.0.1:2369/\\" crossorigin=\\"anonymous\\"></script>
<link href=\\"http://127.0.0.1:2369/webmentions/receive/\\" rel=\\"webmention\\">
<script defer src=\\"https://unpkg.com/@tinybirdco/flock.js\\" data-host=\\"https://api.tinybird.co\\" data-token=\\"tinybird_token\\" tb_site_uuid=\\"tb_test_site_uuid\\" tb_post_uuid=\\"undefined\\" tb_member_uuid=\\"member_uuid\\" tb_member_status=\\"paid\\"></script>",
}
`;
exports[`{{ghost_head}} helper includes tinybird tracker script when config is set with all tb_variables set to undefined on logged out home page 1 1`] = `
Object {
"rendered": "<meta name=\\"description\\" content=\\"site description\\">
<link rel=\\"canonical\\" href=\\"http://127.0.0.1:2369/\\">
<meta name=\\"referrer\\" content=\\"no-referrer-when-downgrade\\">
<meta property=\\"og:site_name\\" content=\\"Ghost\\">
<meta property=\\"og:type\\" content=\\"website\\">
<meta property=\\"og:title\\" content=\\"Ghost\\">
<meta property=\\"og:description\\" content=\\"site description\\">
<meta property=\\"og:url\\" content=\\"http://127.0.0.1:2369/\\">
<meta property=\\"og:image\\" content=\\"http://127.0.0.1:2369/content/images/site-cover.png\\">
<meta name=\\"twitter:card\\" content=\\"summary_large_image\\">
<meta name=\\"twitter:title\\" content=\\"Ghost\\">
<meta name=\\"twitter:description\\" content=\\"site description\\">
<meta name=\\"twitter:url\\" content=\\"http://127.0.0.1:2369/\\">
<meta name=\\"twitter:image\\" content=\\"http://127.0.0.1:2369/content/images/site-cover.png\\">
<script type=\\"application/ld+json\\">
{
\\"@context\\": \\"https://schema.org\\",
\\"@type\\": \\"WebSite\\",
\\"publisher\\": {
\\"@type\\": \\"Organization\\",
\\"name\\": \\"Ghost\\",
\\"url\\": \\"http://127.0.0.1:2369/\\",
\\"logo\\": {
\\"@type\\": \\"ImageObject\\",
\\"url\\": \\"http://127.0.0.1:2369/favicon.ico\\"
}
},
\\"url\\": \\"http://127.0.0.1:2369/\\",
\\"image\\": {
\\"@type\\": \\"ImageObject\\",
\\"url\\": \\"http://127.0.0.1:2369/content/images/site-cover.png\\"
},
\\"mainEntityOfPage\\": \\"http://127.0.0.1:2369/\\",
\\"description\\": \\"site description\\"
}
</script>
<meta name=\\"generator\\" content=\\"Ghost 4.3\\">
<link rel=\\"alternate\\" type=\\"application/rss+xml\\" title=\\"Ghost\\" href=\\"http://localhost:65530/rss/\\">
<script defer src=\\"https://cdn.jsdelivr.net/ghost/sodo-search@~[[VERSION]]/umd/sodo-search.min.js\\" data-key=\\"xyz\\" data-styles=\\"https://cdn.jsdelivr.net/ghost/sodo-search@~[[VERSION]]/umd/main.css\\" data-sodo-search=\\"http://127.0.0.1:2369/\\" crossorigin=\\"anonymous\\"></script>
<link href=\\"http://127.0.0.1:2369/webmentions/receive/\\" rel=\\"webmention\\">
<script defer src=\\"https://unpkg.com/@tinybirdco/flock.js\\" data-host=\\"https://api.tinybird.co\\" data-token=\\"tinybird_token\\" tb_site_uuid=\\"tb_test_site_uuid\\" tb_post_uuid=\\"undefined\\" tb_member_uuid=\\"undefined\\" tb_member_status=\\"undefined\\"></script>",
}
`;
exports[`{{ghost_head}} helper members scripts includes portal when members enabled 1 1`] = `
Object {
"rendered": "<meta name=\\"description\\" content=\\"site description\\">

View File

@ -340,6 +340,19 @@ describe('{{ghost_head}} helper', function () {
published_at: new Date(0),
updated_at: new Date(0)
}));
posts.push(createPost({ // Post 10
title: 'Testing stats',
uuid: 'post_uuid',
excerpt: 'Creating stats for the site',
mobiledoc: testUtils.DataGenerator.markdownToMobiledoc('Creating stats for the site'),
authors: [
authors[3]
],
primary_author: authors[3],
published_at: new Date(0),
updated_at: new Date(0)
}));
};
before(function () {
@ -1185,4 +1198,80 @@ describe('{{ghost_head}} helper', function () {
}));
});
});
describe('includes tinybird tracker script when config is set', function () {
beforeEach(function () {
configUtils.set({
tinybird: {
tracker: {
scriptUrl: 'https://unpkg.com/@tinybirdco/flock.js',
endpoint: 'https://api.tinybird.co',
token: 'tinybird_token',
id: 'tb_test_site_uuid'
}
}
});
});
it('with all tb_variables set to undefined on logged out home page', async function () {
await testGhostHead(testUtils.createHbsResponse({
locals: {
relativeUrl: '/',
context: ['home', 'index'],
safeVersion: '4.3'
}
}));
});
it('Sets tb_post_uuid on post page', async function () {
const renderObject = {
post: posts[10]
};
await testGhostHead(testUtils.createHbsResponse({
renderObject: renderObject,
locals: {
relativeUrl: '/post/',
context: ['post'],
safeVersion: '0.3'
}
}));
});
it('sets tb_member_x variables on logged in home page', async function () {
const renderObject = {
member: {
uuid: 'member_uuid',
status: 'paid'
}
};
await testGhostHead(testUtils.createHbsResponse({
renderObject: renderObject,
locals: {
relativeUrl: '/',
context: ['home', 'index'],
safeVersion: '4.3'
}
}));
});
it('sets both tb_member_x variables and tb_post_uuid on logged in post page', async function () {
const renderObject = {
member: {
uuid: 'member_uuid',
status: 'free'
},
post: posts[10]
};
await testGhostHead(testUtils.createHbsResponse({
renderObject: renderObject,
locals: {
relativeUrl: '/post/',
context: ['post'],
safeVersion: '4.3'
}
}));
});
});
});