ghost 5.116.0 → 5.116.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/components/{tryghost-api-framework-5.116.0.tgz → tryghost-api-framework-5.116.2.tgz} +0 -0
  2. package/components/tryghost-constants-5.116.2.tgz +0 -0
  3. package/components/tryghost-custom-fonts-5.116.2.tgz +0 -0
  4. package/components/{tryghost-custom-theme-settings-service-5.116.0.tgz → tryghost-custom-theme-settings-service-5.116.2.tgz} +0 -0
  5. package/components/tryghost-domain-events-5.116.2.tgz +0 -0
  6. package/components/tryghost-donations-5.116.2.tgz +0 -0
  7. package/components/{tryghost-email-addresses-5.116.0.tgz → tryghost-email-addresses-5.116.2.tgz} +0 -0
  8. package/components/{tryghost-email-service-5.116.0.tgz → tryghost-email-service-5.116.2.tgz} +0 -0
  9. package/components/tryghost-email-suppression-list-5.116.2.tgz +0 -0
  10. package/components/{tryghost-html-to-plaintext-5.116.0.tgz → tryghost-html-to-plaintext-5.116.2.tgz} +0 -0
  11. package/components/tryghost-i18n-5.116.2.tgz +0 -0
  12. package/components/{tryghost-job-manager-5.116.0.tgz → tryghost-job-manager-5.116.2.tgz} +0 -0
  13. package/components/tryghost-link-replacer-5.116.2.tgz +0 -0
  14. package/components/{tryghost-magic-link-5.116.0.tgz → tryghost-magic-link-5.116.2.tgz} +0 -0
  15. package/components/{tryghost-member-attribution-5.116.0.tgz → tryghost-member-attribution-5.116.2.tgz} +0 -0
  16. package/components/{tryghost-member-events-5.116.0.tgz → tryghost-member-events-5.116.2.tgz} +0 -0
  17. package/components/{tryghost-members-api-5.116.0.tgz → tryghost-members-api-5.116.2.tgz} +0 -0
  18. package/components/{tryghost-members-csv-5.116.0.tgz → tryghost-members-csv-5.116.2.tgz} +0 -0
  19. package/components/{tryghost-members-offers-5.116.0.tgz → tryghost-members-offers-5.116.2.tgz} +0 -0
  20. package/components/tryghost-milestones-5.116.2.tgz +0 -0
  21. package/components/{tryghost-mw-error-handler-5.116.0.tgz → tryghost-mw-error-handler-5.116.2.tgz} +0 -0
  22. package/components/{tryghost-mw-vhost-5.116.0.tgz → tryghost-mw-vhost-5.116.2.tgz} +0 -0
  23. package/components/tryghost-post-events-5.116.2.tgz +0 -0
  24. package/components/tryghost-post-revisions-5.116.2.tgz +0 -0
  25. package/components/tryghost-posts-service-5.116.2.tgz +0 -0
  26. package/components/{tryghost-prometheus-metrics-5.116.0.tgz → tryghost-prometheus-metrics-5.116.2.tgz} +0 -0
  27. package/components/tryghost-security-5.116.2.tgz +0 -0
  28. package/components/{tryghost-tiers-5.116.0.tgz → tryghost-tiers-5.116.2.tgz} +0 -0
  29. package/components/tryghost-webmentions-5.116.2.tgz +0 -0
  30. package/core/built/admin/assets/admin-x-activitypub/admin-x-activitypub.js +9776 -11013
  31. package/core/built/admin/assets/admin-x-demo/admin-x-demo.js +2 -2
  32. package/core/built/admin/assets/admin-x-demo/{index-a9601514.mjs → index-09325f38.mjs} +4 -4
  33. package/core/built/admin/assets/admin-x-demo/{modals-c1789d04.mjs → modals-1a9ae8b7.mjs} +2 -2
  34. package/core/built/admin/assets/admin-x-settings/{CodeEditorView-e9c9deb8.mjs → CodeEditorView-550846e0.mjs} +2 -2
  35. package/core/built/admin/assets/admin-x-settings/admin-x-settings.js +2 -2
  36. package/core/built/admin/assets/admin-x-settings/{index-f744cab7.mjs → index-4ce2fcd1.mjs} +78 -77
  37. package/core/built/admin/assets/admin-x-settings/{index-84580c3a.mjs → index-f3cb3f4d.mjs} +2 -2
  38. package/core/built/admin/assets/admin-x-settings/{modals-d9ca60c5.mjs → modals-6bc20529.mjs} +8 -8
  39. package/core/built/admin/assets/{chunk.524.8371443ef8f60db429d0.js → chunk.524.578de86e5014b911b05a.js} +8 -8
  40. package/core/built/admin/assets/{chunk.582.f90151775f2e53dd21d9.js → chunk.582.21bf3e37b5d84ac4b58a.js} +9 -9
  41. package/core/built/admin/assets/{chunk.713.e9027c0cc3c56110f5da.js → chunk.713.761d11035fe0bf3e557c.js} +2 -2
  42. package/core/built/admin/assets/{ghost-03b64c086f3c60cabc85fe7a7e2b640a.js → ghost-868c537d5c02ca65323d0122596a67ec.js} +57 -55
  43. package/core/built/admin/assets/posts/posts.js +5185 -5096
  44. package/core/built/admin/assets/stats/stats.js +6630 -6541
  45. package/core/built/admin/assets/{vendor-72026232b36d97babc6320917c16c321.js → vendor-8e3ee8261528bb429cfe78ce79a4a82a.js} +5 -5
  46. package/core/built/admin/index.html +5 -5
  47. package/core/frontend/helpers/ghost_head.js +9 -5
  48. package/core/frontend/public/ghost-stats.js +41 -74
  49. package/core/server/data/tinybird/datasources/_mv_hits.datasource +23 -0
  50. package/core/server/data/tinybird/datasources/analytics_events.datasource +14 -0
  51. package/core/server/data/tinybird/endpoints/api_kpis.pipe +161 -0
  52. package/core/server/data/tinybird/endpoints/api_top_browsers.pipe +52 -0
  53. package/core/server/data/tinybird/endpoints/api_top_devices.pipe +51 -0
  54. package/core/server/data/tinybird/endpoints/api_top_locations.pipe +51 -0
  55. package/core/server/data/tinybird/endpoints/api_top_os.pipe +51 -0
  56. package/core/server/data/tinybird/endpoints/api_top_pages.pipe +52 -0
  57. package/core/server/data/tinybird/endpoints/api_top_sources.pipe +24 -0
  58. package/core/server/data/tinybird/fixtures/analytics_events.ndjson +31 -0
  59. package/core/server/data/tinybird/pipes/filtered_sessions.pipe +42 -0
  60. package/core/server/data/tinybird/pipes/mv_hits.pipe +89 -0
  61. package/core/server/data/tinybird/pipes/mv_session_data.pipe +28 -0
  62. package/core/server/data/tinybird/readme.md +47 -0
  63. package/core/server/data/tinybird/tests/api_kpis.yaml +207 -0
  64. package/core/server/data/tinybird/tests/api_top_browsers.yaml +89 -0
  65. package/core/server/data/tinybird/tests/api_top_devices.yaml +69 -0
  66. package/core/server/data/tinybird/tests/api_top_locations.yaml +95 -0
  67. package/core/server/data/tinybird/tests/api_top_os.yaml +73 -0
  68. package/core/server/data/tinybird/tests/api_top_pages.yaml +86 -0
  69. package/core/server/data/tinybird/tests/api_top_sources.yaml +118 -0
  70. package/package.json +59 -59
  71. package/yarn.lock +37 -56
  72. package/components/tryghost-constants-5.116.0.tgz +0 -0
  73. package/components/tryghost-custom-fonts-5.116.0.tgz +0 -0
  74. package/components/tryghost-domain-events-5.116.0.tgz +0 -0
  75. package/components/tryghost-donations-5.116.0.tgz +0 -0
  76. package/components/tryghost-email-suppression-list-5.116.0.tgz +0 -0
  77. package/components/tryghost-i18n-5.116.0.tgz +0 -0
  78. package/components/tryghost-link-replacer-5.116.0.tgz +0 -0
  79. package/components/tryghost-milestones-5.116.0.tgz +0 -0
  80. package/components/tryghost-post-events-5.116.0.tgz +0 -0
  81. package/components/tryghost-post-revisions-5.116.0.tgz +0 -0
  82. package/components/tryghost-posts-service-5.116.0.tgz +0 -0
  83. package/components/tryghost-security-5.116.0.tgz +0 -0
  84. package/components/tryghost-webmentions-5.116.0.tgz +0 -0
  85. /package/core/built/admin/assets/{chunk.713.e9027c0cc3c56110f5da.js.LICENSE.txt → chunk.713.761d11035fe0bf3e557c.js.LICENSE.txt} +0 -0
@@ -0,0 +1,51 @@
1
+ TOKEN "stats_page" READ
2
+
3
+ NODE _top_os_0
4
+ SQL >
5
+
6
+ %
7
+ select
8
+ os,
9
+ uniqExact(session_id) as visits
10
+ from _mv_hits h
11
+ inner join filtered_sessions fs
12
+ on fs.session_id = h.session_id
13
+ where
14
+ site_uuid = {{String(site_uuid, 'mock_site_uuid', description="Tenant ID", required=True)}}
15
+ {% if defined(date_from) %}
16
+ and toDate(toTimezone(timestamp, {{String(timezone, 'Etc/UTC', description="Site timezone", required=True)}}))
17
+ >=
18
+ {{ Date(date_from, description="Starting day for filtering a date range", required=False) }}
19
+ {% else %}
20
+ and toDate(toTimezone(timestamp, {{String(timezone, 'Etc/UTC', description="Site timezone", required=True)}}))
21
+ >=
22
+ timestampAdd(today(), interval -1 year)
23
+ {% end %}
24
+ {% if defined(date_to) %}
25
+ and toDate(toTimezone(timestamp, {{String(timezone, 'Etc/UTC', description="Site timezone", required=True)}}))
26
+ <=
27
+ {{ Date(date_to, description="Finishing day for filtering a date range", required=False) }}
28
+ {% else %}
29
+ and toDate(toTimezone(timestamp, {{String(timezone, 'Etc/UTC', description="Site timezone", required=True)}}))
30
+ <=
31
+ today()
32
+ {% end %}
33
+ {% if defined(member_status) %}
34
+ and member_status IN (
35
+ select arrayJoin(
36
+ {{ Array(member_status, "'undefined', 'free', 'paid'", description="Member status to filter on", required=False) }}
37
+ || if('paid' IN {{ Array(member_status) }}, ['comped'], [])
38
+ )
39
+ )
40
+ {% end %}
41
+ {% if defined(device) %} and device = {{ String(device, description="Device to filter on", required=False) }} {% end %}
42
+ {% if defined(browser) %} and browser = {{ String(browser, description="Browser to filter on", required=False) }} {% end %}
43
+ {% if defined(os) %} and os = {{ String(os, description="Operating system to filter on", required=False) }} {% end %}
44
+ {% if defined(source) %} and source = {{ String(source, description="Source to filter on", required=False) }} {% end %}
45
+ {% if defined(location) %} and location = {{ String(location, description="Location to filter on", required=False) }} {% end %}
46
+ {% if defined(pathname) %} and pathname = {{ String(pathname, description="Pathname to filter on", required=False) }} {% end %}
47
+ group by os
48
+ order by visits desc
49
+ limit {{ Int32(skip, 0) }},{{ Int32(limit, 50) }}
50
+
51
+ TYPE ENDPOINT
@@ -0,0 +1,52 @@
1
+ TOKEN "stats_page" READ
2
+
3
+ NODE _top_pages_0
4
+ SQL >
5
+
6
+ %
7
+ select
8
+ pathname,
9
+ uniqExact(session_id) as visits
10
+ from _mv_hits h
11
+ inner join filtered_sessions fs
12
+ on fs.session_id = h.session_id
13
+ where
14
+ site_uuid = {{String(site_uuid, 'mock_site_uuid', description="Tenant ID", required=True)}}
15
+ {% if defined(date_from) %}
16
+ and toDate(toTimezone(timestamp, {{String(timezone, 'Etc/UTC', description="Site timezone", required=True)}}))
17
+ >=
18
+ {{ Date(date_from, description="Starting day for filtering a date range", required=False) }}
19
+ {% else %}
20
+ and toDate(toTimezone(timestamp, {{String(timezone, 'Etc/UTC', description="Site timezone", required=True)}}))
21
+ >=
22
+ timestampAdd(today(), interval -7 day)
23
+ {% end %}
24
+ {% if defined(date_to) %}
25
+ and toDate(toTimezone(timestamp, {{String(timezone, 'Etc/UTC', description="Site timezone", required=True)}}))
26
+ <=
27
+ {{ Date(date_to, description="Finishing day for filtering a date range", required=False) }}
28
+ {% else %}
29
+ and toDate(toTimezone(timestamp, {{String(timezone, 'Etc/UTC', description="Site timezone", required=True)}}))
30
+ <=
31
+ today()
32
+ {% end %}
33
+ {% if defined(member_status) %}
34
+ and member_status IN (
35
+ select arrayJoin(
36
+ {{ Array(member_status, "'undefined', 'free', 'paid'", description="Member status to filter on", required=False) }}
37
+ || if('paid' IN {{ Array(member_status) }}, ['comped'], [])
38
+ )
39
+ )
40
+ {% end %}
41
+ {% if defined(device) %} and device = {{ String(device, description="Device to filter on", required=False) }} {% end %}
42
+ {% if defined(browser) %} and browser = {{ String(browser, description="Browser to filter on", required=False) }} {% end %}
43
+ {% if defined(os) %} and os = {{ String(os, description="Operating system to filter on", required=False) }} {% end %}
44
+ -- we do filtering on source in the filtered_sessions pipe
45
+ # --{% if defined(source) %} and source = {{ String(source, description="Source to filter on", required=False) }} {% end %}
46
+ {% if defined(location) %} and location = {{ String(location, description="Location to filter on", required=False) }} {% end %}
47
+ {% if defined(pathname) %} and pathname = {{ String(pathname, description="Pathname to filter on", required=False) }} {% end %}
48
+ group by pathname
49
+ order by visits desc
50
+ limit {{ Int32(skip, 0) }},{{ Int32(limit, 50) }}
51
+
52
+ TYPE ENDPOINT
@@ -0,0 +1,24 @@
1
+ TOKEN "stats_page" READ
2
+
3
+ NODE top_sources
4
+ SQL >
5
+ %
6
+ select
7
+ source,
8
+ count() as visits
9
+ from mv_session_data sd
10
+ inner join filtered_sessions fs
11
+ on fs.session_id = sd.session_id
12
+ where
13
+ site_uuid = {{ String(site_uuid, 'mock_site_uuid', description="Tenant ID", required=True) }}
14
+ {% if defined(date_from) and day_diff(date_from, date_to) == 0 %}
15
+ and toDate(toTimezone(first_pageview, {{String(timezone, 'Etc/UTC', description="Site timezone", required=True)}})) = {{ Date(date_from) }}
16
+ {% else %}
17
+ {% if defined(date_from) %} and toDate(toTimezone(first_pageview, {{String(timezone, 'Etc/UTC', description="Site timezone", required=True)}})) >= {{ Date(date_from) }} {% else %} and toDate(toTimezone(first_pageview, {{String(timezone, 'Etc/UTC', description="Site timezone", required=True)}})) >= timestampAdd(today(), interval -7 day) {% end %}
18
+ {% if defined(date_to) %} and toDate(toTimezone(first_pageview, {{String(timezone, 'Etc/UTC', description="Site timezone", required=True)}})) <= {{ Date(date_to) }} {% else %} and toDate(toTimezone(first_pageview, {{String(timezone, 'Etc/UTC', description="Site timezone", required=True)}})) <= today() {% end %}
19
+ {% end %}
20
+ group by source
21
+ order by visits desc
22
+ limit {{ Int32(skip, 0) }},{{ Int32(limit, 50) }}
23
+
24
+ TYPE ENDPOINT
@@ -0,0 +1,31 @@
1
+ {"timestamp":"2100-01-01 00:06:15","session_id":"e5c37e25-ed9e-4940-a2be-bc49149d991a","action":"page_hit","version":"1","payload":{"site_uuid":"mock_site_uuid","member_uuid":"undefined","member_status":"undefined","post_uuid":"6b8635fb-292f-4422-9fe4-d76cfab2ba31","user-agent":"AhrefsBot/7.0; +http://ahrefs.com/robot/","locale":"en-GB","location":"GB","referrer":"https://petty-queen.com","pathname":"/blog/hello-world/","href":"https://my-ghost-site.com/blog/hello-world/"}}
2
+ {"timestamp":"2100-01-01 01:21:17","session_id":"1267b782-e5a1-4334-8cf6-771d72bbc28e","action":"page_hit","version":"1","payload":{"site_uuid":"mock_site_uuid","member_uuid":"d4678fdf-824c-4d5f-a5fe-c713d409faac","member_status":"free","post_uuid":"undefined","user-agent":"Mozilla/5.0 (Windows; U; Windows NT 5.2) AppleWebKit/533.2.1 (KHTML, like Gecko) Chrome/13.0.868.0 Safari/533.2.1","locale":"es-ES","location":"ES","referrer":"","pathname":"/","href":"https://my-ghost-site.com/"}}
3
+ {"timestamp":"2100-01-01 01:39:48","session_id":"1267b782-e5a1-4334-8cf6-771d72bbc28e","action":"page_hit","version":"1","payload":{"site_uuid":"mock_site_uuid","member_uuid":"d4678fdf-824c-4d5f-a5fe-c713d409faac","member_status":"free","post_uuid":"undefined","user-agent":"Mozilla/5.0 (Windows; U; Windows NT 5.2) AppleWebKit/533.2.1 (KHTML, like Gecko) Chrome/13.0.868.0 Safari/533.2.1","locale":"es-ES","location":"ES","referrer":"https://my-ghost-site.com","pathname":"/","href":"https://my-ghost-site.com/"}}
4
+ {"timestamp":"2100-01-01 02:21:13","session_id":"2a31286e-53b4-41da-a7fd-89d966072af5","action":"page_hit","version":"1","payload":{"site_uuid":"mock_site_uuid","member_uuid":"df8343d2-e89d-45b7-ba12-988734efcc56","member_status":"free","post_uuid":"06b1b0c9-fb53-4a15-a060-3db3fde7b1fc","user-agent":"Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.0; Trident/4.0)","locale":"en-GB","location":"GB","referrer":"https://www.bing.com/","pathname":"/about/","href":"https://my-ghost-site.com/about/"}}
5
+ {"timestamp":"2100-01-01 02:31:43","session_id":"2a31286e-53b4-41da-a7fd-89d966072af5","action":"page_hit","version":"1","payload":{"site_uuid":"mock_site_uuid","member_uuid":"df8343d2-e89d-45b7-ba12-988734efcc56","member_status":"free","post_uuid":"undefined","user-agent":"Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.0; Trident/4.0)","locale":"en-GB","location":"GB","referrer":"https://my-ghost-site.com","pathname":"/","href":"https://my-ghost-site.com/"}}
6
+ {"timestamp":"2100-01-02 00:59:45","session_id":"f253b9b7-0a1a-4168-8fcf-b20a1668ce4d","action":"page_hit","version":"1","payload":{"site_uuid":"mock_site_uuid","member_uuid":"65bacac2-8122-4ed0-a11f-ac52aa82beb0","member_status":"paid","post_uuid":"06b1b0c9-fb53-4a15-a060-3db3fde7b1fc","user-agent":"Mozilla/5.0 (Windows NT 5.3; Win64; x64; rv:11.6) Gecko/20100101 Firefox/11.6.2","locale":"en-GB","location":"GB","referrer":"https://www.google.com/","pathname":"/about/","href":"https://my-ghost-site.com/about/"}}
7
+ {"timestamp":"2100-01-02 01:12:56","session_id":"f253b9b7-0a1a-4168-8fcf-b20a1668ce4d","action":"page_hit","version":"1","payload":{"site_uuid":"mock_site_uuid","member_uuid":"65bacac2-8122-4ed0-a11f-ac52aa82beb0","member_status":"paid","post_uuid":"undefined","user-agent":"Mozilla/5.0 (Windows NT 5.3; Win64; x64; rv:11.6) Gecko/20100101 Firefox/11.6.2","locale":"en-GB","location":"GB","referrer":"https://my-ghost-site.com","pathname":"/","href":"https://my-ghost-site.com/"}}
8
+ {"timestamp":"2100-01-02 01:16:52","session_id":"f253b9b7-0a1a-4168-8fcf-b20a1668ce4d","action":"page_hit","version":"1","payload":{"site_uuid":"mock_site_uuid","member_uuid":"65bacac2-8122-4ed0-a11f-ac52aa82beb0","member_status":"paid","post_uuid":"undefined","user-agent":"Mozilla/5.0 (Windows NT 5.3; Win64; x64; rv:11.6) Gecko/20100101 Firefox/11.6.2","locale":"en-GB","location":"GB","referrer":"https://my-ghost-site.com","pathname":"/","href":"https://my-ghost-site.com/"}}
9
+ {"timestamp":"2100-01-03 00:01:24","session_id":"9c15f99e-c8b1-4145-a073-e7f8649d2fa4","action":"page_hit","version":"1","payload":{"site_uuid":"mock_site_uuid","member_uuid":"4c14393f-d792-403e-bbdc-aa5af3abbdd9","member_status":"free","post_uuid":"undefined","user-agent":"Mozilla/5.0 (Windows NT 5.0; rv:10.7) Gecko/20100101 Firefox/10.7.1","locale":"en-US","location":"US","referrer":"https://duckduckgo.com/","pathname":"/","href":"https://my-ghost-site.com/"}}
10
+ {"timestamp":"2100-01-03 01:28:09","session_id":"9c15f99e-c8b1-4145-a073-e7f8649d2fa4","action":"page_hit","version":"1","payload":{"site_uuid":"mock_site_uuid","member_uuid":"4c14393f-d792-403e-bbdc-aa5af3abbdd9","member_status":"free","post_uuid":"6b8635fb-292f-4422-9fe4-d76cfab2ba31","user-agent":"Mozilla/5.0 (Windows NT 5.0; rv:10.7) Gecko/20100101 Firefox/10.7.1","locale":"en-US","location":"US","referrer":"https://my-ghost-site.com","pathname":"/blog/hello-world/","href":"https://my-ghost-site.com/blog/hello-world/"}}
11
+ {"timestamp":"2100-01-03 01:41:44","session_id":"8a2461a8-91cd-4f01-b066-3de6dc946995","action":"page_hit","version":"1","payload":{"site_uuid":"mock_site_uuid","member_uuid":"f4c738bc-7327-440c-8007-6a0b306c05e3","member_status":"comped","post_uuid":"06b1b0c9-fb53-4a15-a060-3db3fde7b1fc","user-agent":"Mozilla/5.0 (Windows; U; Windows NT 5.0) AppleWebKit/533.2.0 (KHTML, like Gecko) Chrome/39.0.887.0 Safari/533.2.0","locale":"de-DE","location":"DE","referrer":"https://www.bing.com/","pathname":"/about/","href":"https://my-ghost-site.com/about/"}}
12
+ {"timestamp":"2100-01-03 01:53:31","session_id":"8a2461a8-91cd-4f01-b066-3de6dc946995","action":"page_hit","version":"1","payload":{"site_uuid":"mock_site_uuid","member_uuid":"f4c738bc-7327-440c-8007-6a0b306c05e3","member_status":"comped","post_uuid":"6b8635fb-292f-4422-9fe4-d76cfab2ba31","user-agent":"Mozilla/5.0 (Windows; U; Windows NT 5.0) AppleWebKit/533.2.0 (KHTML, like Gecko) Chrome/39.0.887.0 Safari/533.2.0","locale":"de-DE","location":"DE","referrer":"https://my-ghost-site.com","pathname":"/blog/hello-world/","href":"https://my-ghost-site.com/blog/hello-world/"}}
13
+ {"timestamp":"2100-01-03 02:00:19","session_id":"8a2461a8-91cd-4f01-b066-3de6dc946995","action":"page_hit","version":"1","payload":{"site_uuid":"mock_site_uuid","member_uuid":"f4c738bc-7327-440c-8007-6a0b306c05e3","member_status":"comped","post_uuid":"06b1b0c9-fb53-4a15-a060-3db3fde7b1fc","user-agent":"Mozilla/5.0 (Windows; U; Windows NT 5.0) AppleWebKit/533.2.0 (KHTML, like Gecko) Chrome/39.0.887.0 Safari/533.2.0","locale":"de-DE","location":"DE","referrer":"https://my-ghost-site.com","pathname":"/about/","href":"https://my-ghost-site.com/about/"}}
14
+ {"timestamp":"2100-01-03 02:51:20","session_id":"50785df1-3232-4ff7-8495-d93e06d63f5c","action":"page_hit","version":"1","payload":{"site_uuid":"mock_site_uuid","member_uuid":"3675e750-09bf-44c9-bc3f-b9aebac37c5d","member_status":"paid","post_uuid":"undefined","user-agent":"Mozilla/5.0 (Windows NT 6.3; rv:14.7) Gecko/20100101 Firefox/14.7.1","locale":"fr-FR","location":"FR","referrer":"https://search.yahoo.com/","pathname":"/","href":"https://my-ghost-site.com/"}}
15
+ {"timestamp":"2100-01-03 03:52:39","session_id":"50785df1-3232-4ff7-8495-d93e06d63f5c","action":"page_hit","version":"1","payload":{"site_uuid":"mock_site_uuid","member_uuid":"3675e750-09bf-44c9-bc3f-b9aebac37c5d","member_status":"paid","post_uuid":"undefined","user-agent":"Mozilla/5.0 (Windows NT 6.3; rv:14.7) Gecko/20100101 Firefox/14.7.1","locale":"fr-FR","location":"FR","referrer":"https://my-ghost-site.com","pathname":"/","href":"https://my-ghost-site.com/"}}
16
+ {"timestamp":"2100-01-04 00:25:39","session_id":"59478d87-ce95-40fd-a081-65d1e497bcfc","action":"page_hit","version":"1","payload":{"site_uuid":"mock_site_uuid","member_uuid":"97c79891-2ae9-4eb2-ada8-89d2a998747d","member_status":"paid","post_uuid":"6b8635fb-292f-4422-9fe4-d76cfab2ba31","user-agent":"Mozilla/5.0 (Windows; U; Windows NT 6.3) AppleWebKit/531.2.2 (KHTML, like Gecko) Chrome/31.0.808.0 Safari/531.2.2","locale":"en-GB","location":"GB","referrer":"","pathname":"/blog/hello-world/","href":"https://my-ghost-site.com/blog/hello-world/"}}
17
+ {"timestamp":"2100-01-04 01:10:48","session_id":"a6b6c4e6-19e3-47a9-afc6-d9870592652e","action":"page_hit","version":"1","payload":{"site_uuid":"mock_site_uuid","member_uuid":"undefined","member_status":"undefined","post_uuid":"6b8635fb-292f-4422-9fe4-d76cfab2ba31","user-agent":"Mozilla/5.0 (Windows; U; Windows NT 5.2) AppleWebKit/533.0.1 (KHTML, like Gecko) Chrome/32.0.856.0 Safari/533.0.1","locale":"en-GB","location":"GB","referrer":"","pathname":"/blog/hello-world/","href":"https://my-ghost-site.com/blog/hello-world/"}}
18
+ {"timestamp":"2100-01-04 01:16:10","session_id":"a6b6c4e6-19e3-47a9-afc6-d9870592652e","action":"page_hit","version":"1","payload":{"site_uuid":"mock_site_uuid","member_uuid":"undefined","member_status":"undefined","post_uuid":"06b1b0c9-fb53-4a15-a060-3db3fde7b1fc","user-agent":"Mozilla/5.0 (Windows; U; Windows NT 5.2) AppleWebKit/533.0.1 (KHTML, like Gecko) Chrome/32.0.856.0 Safari/533.0.1","locale":"en-GB","location":"GB","referrer":"https://my-ghost-site.com","pathname":"/about/","href":"https://my-ghost-site.com/about/"}}
19
+ {"timestamp":"2100-01-04 01:20:15","session_id":"a6b6c4e6-19e3-47a9-afc6-d9870592652e","action":"page_hit","version":"1","payload":{"site_uuid":"mock_site_uuid","member_uuid":"undefined","member_status":"undefined","post_uuid":"06b1b0c9-fb53-4a15-a060-3db3fde7b1fc","user-agent":"Mozilla/5.0 (Windows; U; Windows NT 5.2) AppleWebKit/533.0.1 (KHTML, like Gecko) Chrome/32.0.856.0 Safari/533.0.1","locale":"en-GB","location":"GB","referrer":"https://my-ghost-site.com","pathname":"/about/","href":"https://my-ghost-site.com/about/"}}
20
+ {"timestamp":"2100-01-04 01:35:41","session_id":"e22a7f6f-28da-4715-a199-6f0338b593d4","action":"page_hit","version":"1","payload":{"site_uuid":"mock_site_uuid","member_uuid":"5369031a-a5cd-4176-83d8-d6ffcb3bcfb8","member_status":"free","post_uuid":"6b8635fb-292f-4422-9fe4-d76cfab2ba31","user-agent":"Mozilla/5.0 (Windows; U; Windows NT 6.1) AppleWebKit/538.0.1 (KHTML, like Gecko) Chrome/16.0.814.0 Safari/538.0.1","locale":"en-GB","location":"GB","referrer":"","pathname":"/blog/hello-world/","href":"https://my-ghost-site.com/blog/hello-world/"}}
21
+ {"timestamp":"2100-01-04 01:36:33","session_id":"e22a7f6f-28da-4715-a199-6f0338b593d4","action":"page_hit","version":"1","payload":{"site_uuid":"mock_site_uuid","member_uuid":"5369031a-a5cd-4176-83d8-d6ffcb3bcfb8","member_status":"free","post_uuid":"undefined","user-agent":"Mozilla/5.0 (Windows; U; Windows NT 6.1) AppleWebKit/538.0.1 (KHTML, like Gecko) Chrome/16.0.814.0 Safari/538.0.1","locale":"en-GB","location":"GB","referrer":"https://my-ghost-site.com","pathname":"/","href":"https://my-ghost-site.com/"}}
22
+ {"timestamp":"2100-01-04 01:54:50","session_id":"e22a7f6f-28da-4715-a199-6f0338b593d4","action":"page_hit","version":"1","payload":{"site_uuid":"mock_site_uuid","member_uuid":"5369031a-a5cd-4176-83d8-d6ffcb3bcfb8","member_status":"free","post_uuid":"06b1b0c9-fb53-4a15-a060-3db3fde7b1fc","user-agent":"Mozilla/5.0 (Windows; U; Windows NT 6.1) AppleWebKit/538.0.1 (KHTML, like Gecko) Chrome/16.0.814.0 Safari/538.0.1","locale":"en-GB","location":"GB","referrer":"https://my-ghost-site.com","pathname":"/about/","href":"https://my-ghost-site.com/about/"}}
23
+ {"timestamp":"2100-01-05 01:51:00","session_id":"d8e4622f-95cc-4fba-b31b-f38ff72e0975","action":"page_hit","version":"1","payload":{"site_uuid":"mock_site_uuid","member_uuid":"75a190eb-62da-46d2-972d-a9763c954f42","member_status":"paid","post_uuid":"06b1b0c9-fb53-4a15-a060-3db3fde7b1fc","user-agent":"Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/3.0)","locale":"es-ES","location":"ES","referrer":"","pathname":"/about/","href":"https://my-ghost-site.com/about/"}}
24
+ {"timestamp":"2100-01-05 01:53:03","session_id":"d8e4622f-95cc-4fba-b31b-f38ff72e0975","action":"page_hit","version":"1","payload":{"site_uuid":"mock_site_uuid","member_uuid":"75a190eb-62da-46d2-972d-a9763c954f42","member_status":"paid","post_uuid":"6b8635fb-292f-4422-9fe4-d76cfab2ba31","user-agent":"Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/3.0)","locale":"es-ES","location":"ES","referrer":"https://my-ghost-site.com","pathname":"/blog/hello-world/","href":"https://my-ghost-site.com/blog/hello-world/"}}
25
+ {"timestamp":"2100-01-05 00:29:59","session_id":"490475f1-1fb7-4672-9edd-daa1b411b5f9","action":"page_hit","version":"1","payload":{"site_uuid":"mock_site_uuid","member_uuid":"undefined","member_status":"undefined","post_uuid":"6b8635fb-292f-4422-9fe4-d76cfab2ba31","user-agent":"Mozilla/5.0 (Windows; U; Windows NT 5.1) AppleWebKit/532.2.0 (KHTML, like Gecko) Chrome/20.0.898.0 Safari/532.2.0","locale":"en-GB","location":"GB","referrer":"https://www.baidu.com/","pathname":"/blog/hello-world/","href":"https://my-ghost-site.com/blog/hello-world/"}}
26
+ {"timestamp":"2100-01-05 00:37:42","session_id":"490475f1-1fb7-4672-9edd-daa1b411b5f9","action":"page_hit","version":"1","payload":{"site_uuid":"mock_site_uuid","member_uuid":"undefined","member_status":"undefined","post_uuid":"undefined","user-agent":"Mozilla/5.0 (Windows; U; Windows NT 5.1) AppleWebKit/532.2.0 (KHTML, like Gecko) Chrome/20.0.898.0 Safari/532.2.0","locale":"en-GB","location":"GB","referrer":"https://my-ghost-site.com","pathname":"/","href":"https://my-ghost-site.com/"}}
27
+ {"timestamp":"2100-01-05 00:38:12","session_id":"490475f1-1fb7-4672-9edd-daa1b411b5f9","action":"page_hit","version":"1","payload":{"site_uuid":"mock_site_uuid","member_uuid":"undefined","member_status":"undefined","post_uuid":"6b8635fb-292f-4422-9fe4-d76cfab2ba31","user-agent":"Mozilla/5.0 (Windows; U; Windows NT 5.1) AppleWebKit/532.2.0 (KHTML, like Gecko) Chrome/20.0.898.0 Safari/532.2.0","locale":"en-GB","location":"GB","referrer":"https://my-ghost-site.com","pathname":"/blog/hello-world/","href":"https://my-ghost-site.com/blog/hello-world/"}}
28
+ {"timestamp":"2100-01-06 00:51:26","session_id":"8d975128-2027-40c6-834a-972cc0293d21","action":"page_hit","version":"1","payload":{"site_uuid":"mock_site_uuid","member_uuid":"b7e0fca6-27ce-46c0-af57-c591f20dcd51","member_status":"free","post_uuid":"06b1b0c9-fb53-4a15-a060-3db3fde7b1fc","user-agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_7 rv:2.0; KW) AppleWebKit/537.0.1 (KHTML, like Gecko) Version/5.0.10 Safari/537.0.1","locale":"fr-FR","location":"FR","referrer":"","pathname":"/about/","href":"https://my-ghost-site.com/about/"}}
29
+ {"timestamp":"2100-01-06 01:28:38","session_id":"61a2896b-7cf8-4853-86a6-a0e4f87c1e21","action":"page_hit","version":"1","payload":{"site_uuid":"mock_site_uuid","member_uuid":"undefined","member_status":"undefined","post_uuid":"6b8635fb-292f-4422-9fe4-d76cfab2ba31","user-agent":"Mozilla/5.0 (Windows; U; Windows NT 5.1) AppleWebKit/533.1.0 (KHTML, like Gecko) Chrome/18.0.852.0 Safari/533.1.0","locale":"en-GB","location":"GB","referrer":"https://search.yahoo.com/","pathname":"/blog/hello-world/","href":"https://my-ghost-site.com/blog/hello-world/"}}
30
+ {"timestamp":"2100-01-07 01:44:10","session_id":"7f1e88e1-da8e-46df-bc69-d04fb29d603d","action":"page_hit","version":"1","payload":{"site_uuid":"mock_site_uuid","member_uuid":"undefined","member_status":"undefined","post_uuid":"06b1b0c9-fb53-4a15-a060-3db3fde7b1fc","user-agent":"Mozilla/5.0 (Windows NT 5.0; WOW64; rv:13.9) Gecko/20100101 Firefox/13.9.7","locale":"en-US","location":"US","referrer":"http://wilted-tick.com","pathname":"/about/","href":"https://my-ghost-site.com/about/"}}
31
+ {"timestamp":"2100-01-07 02:23:19","session_id":"98159299-8111-4dc8-9156-bb339fe9508c","action":"page_hit","version":"1","payload":{"site_uuid":"mock_site_uuid","member_uuid":"undefined","member_status":"undefined","post_uuid":"06b1b0c9-fb53-4a15-a060-3db3fde7b1dd","user-agent":"Mozilla/5.0 (Windows NT 5.0; WOW64; rv:13.9) Gecko/20100101 Firefox/13.9.7","locale":"en-US","location":"US","referrer":"https://my-ghost-site.com","pathname":"/blog/hello-world/","href":"https://my-ghost-site.com/blog/hello-world/"}}
@@ -0,0 +1,42 @@
1
+ NODE query_filters
2
+ DESCRIPTION >
3
+ Get sessions that match the filter criteria
4
+
5
+ SQL >
6
+ %
7
+ select distinct session_id
8
+ from _mv_hits
9
+ where
10
+ site_uuid = {{ String(site_uuid, 'mock_site_uuid', description="Tenant ID", required=True) }}
11
+ {% if defined(date_from) %} and toDate(toTimezone(timestamp, {{String(timezone, 'Etc/UTC', description="Site timezone", required=True)}})) >= {{ Date(date_from) }} {% else %} and toDate(toTimezone(timestamp, {{String(timezone, 'Etc/UTC', description="Site timezone", required=True)}})) >= timestampAdd(today(), interval -7 day) {% end %}
12
+ {% if defined(date_to) %} and toDate(toTimezone(timestamp, {{String(timezone, 'Etc/UTC', description="Site timezone", required=True)}})) <= {{ Date(date_to) }} {% else %} and toDate(toTimezone(timestamp, {{String(timezone, 'Etc/UTC', description="Site timezone", required=True)}})) <= today() {% end %}
13
+ {% if defined(member_status) %}
14
+ and member_status IN (
15
+ select arrayJoin(
16
+ {{ Array(member_status, "'undefined', 'free', 'paid'", description="Member status to filter on", required=False) }}
17
+ || if('paid' IN {{ Array(member_status) }}, ['comped'], [])
18
+ )
19
+ )
20
+ {% end %}
21
+ {% if defined(device) %} and device = {{ String(device, description="Device to filter on", required=False) }} {% end %}
22
+ {% if defined(browser) %} and browser = {{ String(browser, description="Browser to filter on", required=False) }} {% end %}
23
+ {% if defined(os) %} and os = {{ String(os, description="Operating system to filter on", required=False) }} {% end %}
24
+ {% if defined(source) %} and source = {{ String(source, description="Source to filter on", required=False) }} {% end %}
25
+ {% if defined(location) %} and location = {{ String(location, description="Location to filter on", required=False) }} {% end %}
26
+ {% if defined(pathname) %} and pathname = {{ String(pathname, description="Pathname to filter on", required=False) }} {% end %}
27
+
28
+
29
+ NODE sessions_filtered_by_source
30
+ DESCRIPTION >
31
+ Further filter by source when using the source filter. This is necessary because the source is specific to the first hit in a session,
32
+ whereas other filters are on hits.
33
+
34
+ SQL >
35
+ %
36
+ select session_id
37
+ from mv_session_data sd
38
+ inner join query_filters qf
39
+ on qf.session_id = sd.session_id
40
+ where
41
+ site_uuid = {{ String(site_uuid, 'mock_site_uuid', description="Tenant ID", required=True) }}
42
+ {% if defined(source) %} and source = {{ String(source, description="Source to filter on", required=False) }} {% end %}
@@ -0,0 +1,89 @@
1
+ NODE mv_hits_0
2
+ SQL >
3
+
4
+ SELECT timestamp,
5
+ action,
6
+ version,
7
+ coalesce(session_id, '0') as session_id,
8
+ toString(payload.locale) as locale,
9
+ toString(payload.location) as location,
10
+ case -- this avoids really crappy null handling by the JSON type and downstream fns
11
+ when isNull(payload.referrer) then ''
12
+ else toString(payload.referrer)
13
+ end as referrer,
14
+ toString(payload.pathname) as pathname,
15
+ toString(payload.href) as href,
16
+ site_uuid,
17
+ toString(payload.member_uuid) as member_uuid,
18
+ toString(payload.member_status) as member_status,
19
+ toString(payload.post_uuid) as post_uuid,
20
+ toString(payload.post_type) as post_type,
21
+ lower(toString(getSubcolumn(payload,'user-agent'))) as user_agent
22
+ FROM analytics_events
23
+ where action = 'page_hit'
24
+
25
+
26
+
27
+ NODE mv_hits_1
28
+ SQL >
29
+
30
+ SELECT
31
+ site_uuid,
32
+ timestamp,
33
+ action,
34
+ version,
35
+ session_id,
36
+ member_uuid,
37
+ member_status,
38
+ post_uuid,
39
+ post_type,
40
+ location,
41
+ case
42
+ -- domainWithoutWWW returns '' if the referrer is not a valid domain
43
+ -- we want to maintain refs like 'ghost-newsletter'
44
+ when domainWithoutWWW(referrer) = '' then coalesce(referrer, '')
45
+ else domainWithoutWWW(referrer)
46
+ end as source,
47
+ pathname,
48
+ href,
49
+ case
50
+ when match(user_agent, 'wget|ahrefsbot|curl|urllib|bitdiscovery|\+https://|googlebot')
51
+ then 'bot'
52
+ when match(user_agent, 'android')
53
+ then 'mobile-android'
54
+ when match(user_agent, 'ipad|iphone|ipod')
55
+ then 'mobile-ios'
56
+ else 'desktop'
57
+ END as device,
58
+ case
59
+ when match(user_agent, 'windows')
60
+ then 'windows'
61
+ when match(user_agent, 'mac')
62
+ then 'macos'
63
+ when match(user_agent, 'linux')
64
+ then 'linux'
65
+ when match(user_agent, 'android')
66
+ then 'android'
67
+ when match(user_agent, 'iphone|ipad|ipod')
68
+ then 'ios'
69
+ else 'Unknown'
70
+ END as os,
71
+ case
72
+ when match(user_agent, 'firefox')
73
+ then 'firefox'
74
+ when match(user_agent, 'chrome|crios')
75
+ then 'chrome'
76
+ when match(user_agent, 'opera')
77
+ then 'opera'
78
+ when match(user_agent, 'msie|trident')
79
+ then 'ie'
80
+ when match(user_agent, 'iphone|ipad|safari')
81
+ then 'safari'
82
+ else 'Unknown'
83
+ END as browser
84
+ FROM mv_hits_0
85
+
86
+ TYPE materialized
87
+ DATASOURCE _mv_hits
88
+
89
+
@@ -0,0 +1,28 @@
1
+ NODE mv_hits_0
2
+ SQL >
3
+
4
+ SELECT
5
+ site_uuid,
6
+ session_id,
7
+ finalizeAggregation(countState()) as pageviews,
8
+ finalizeAggregation(minState(timestamp)) as first_pageview,
9
+ finalizeAggregation(maxState(timestamp)) as last_pageview,
10
+ argMin(source, timestamp) as source
11
+ FROM _mv_hits
12
+ GROUP BY site_uuid, session_id
13
+
14
+
15
+
16
+ NODE data
17
+ SQL >
18
+
19
+ SELECT
20
+ site_uuid,
21
+ session_id,
22
+ pageviews,
23
+ first_pageview,
24
+ last_pageview,
25
+ last_pageview - first_pageview AS duration,
26
+ pageviews = 1 AS is_bounce,
27
+ source
28
+ FROM mv_hits_0
@@ -0,0 +1,47 @@
1
+ ## Tinybird Analytics
2
+ This is the web analytics implementation using [Tinybird Forward](https://www.tinybird.co/docs/forward).
3
+
4
+ ### Usage
5
+ *Only for the first time: run `yarn tbf:install` from `/ghost/`*.
6
+
7
+ Run `yarn tbf` from `/ghost/`. This will start/install the TB Local Docker container, change the path, and start `tb dev`.
8
+
9
+ ### Local
10
+ Run `yarn tb:local` from `/ghost/` to spin up a Docker container running Tinybird local.
11
+
12
+ In order to use `tb local` with Ghost, the config local blocks should be updated with your tokens: `tb token ls`.
13
+
14
+ Simply disable local to use the cloud version by switching `local: false` - this is switchable both via tracker script (ideally not used with the cloud data except with a unique id) and stats page.
15
+
16
+ ### Config
17
+ Sample config:
18
+ ```
19
+ "tinybird": {
20
+ "tracker": {
21
+ "endpoint": "https://e.ghost.org/tb/web_analytics",
22
+ "token": "xxxxx",
23
+ "id": "local-ghost",
24
+ "datasource": "analytics_events",
25
+ "local": {
26
+ "enabled": true,
27
+ "token": "xxxxx",
28
+ "endpoint": "http://localhost:7181/v0/events",
29
+ "datasource": "analytics_events"
30
+ }
31
+ },
32
+ "stats": {
33
+ "endpoint": "https://api.tinybird.co",
34
+ "token": "xxxxx",
35
+ "id": "local-ghost",
36
+ "local": {
37
+ "enabled": true,
38
+ "token": "xxxxx",
39
+ "endpoint": "http://localhost:7181",
40
+ "datasource": "analytics_events"
41
+ }
42
+ }
43
+ }
44
+ ```
45
+
46
+ ### Testing
47
+ Tests are executed using `tb test run`.