Comparing version 0.0.1 to 1.0.0
30
cfga.js
@@ -1,7 +0,9 @@ | ||
(function (ga, url, window, document, navigator) { | ||
(function (window, document, navigator) { | ||
var screen = window.screen, | ||
encode = encodeURIComponent, | ||
max = Math.max, | ||
min = Math.min, | ||
t = window.performance.timing; | ||
//min = Math.min, | ||
performance = window.performance, | ||
t = performance && performance.timing; | ||
filterNumber = function (num) { return isNaN(num) || num == Infinity || num < 0 ? void 0 : num; }; | ||
@@ -12,3 +14,3 @@ // sendGA: collect data and send. | ||
// GA tid | ||
'ga=' + ga, | ||
'ga=' + window.ga_tid, | ||
// Title | ||
@@ -31,21 +33,21 @@ 'dt=' + encode(document.title), | ||
// (window.onload) | ||
'plt=' + encode(t.loadEventStart - t.navigationStart || 0), | ||
'plt=' + filterNumber(t.loadEventStart - t.navigationStart || 0), | ||
// dns: DNS Time | ||
'dns=' + encode(t.domainLookupEnd - t.domainLookupStart || 0), | ||
'dns=' + filterNumber(t,domainLookupEnd - t.domainLookupStart || 0), | ||
// pdt: Page Dowenload Time | ||
// start download time => finish download time | ||
'pdt=' + encode(t.responseEnd - t.responseStart || 0), | ||
'pdt=' + filterNumber(t,responseEnd - t.responseStart || 0), | ||
// rrt: Redirect Time | ||
'rrt=' + encode(t.redirectEnd - t.redirectStart || 0), | ||
'rrt=' + filterNumber(t,redirectEnd - t.redirectStart || 0), | ||
// tcp: TCP Time | ||
'tcp=' + encode(t.connectEnd - t.connectStart || 0), | ||
'tcp=' + filterNumber(t,connectEnd - t.connectStart || 0), | ||
// srt: Server Response Time | ||
// start request => server send first byte | ||
// (TTFB - TCP - DNS) | ||
'srt=' + encode(t.responseStart - t.requestStart || 0), | ||
'srt=' + filterNumber(t,responseStart - t.requestStart || 0), | ||
// dit: DOM Interactive Time | ||
'dit=' + encode(t.domInteractive - t.domLoading || 0), | ||
'dit=' + filterNumber(t,domInteractive - t.domLoading || 0), | ||
// clt: Content Loading Time | ||
// open the page => DOMContentLoaded | ||
'clt=' + encode(t.domContentLoadedEventStart - t.navigationStart || 0), | ||
'clt=' + filterNumber(t,domContentLoadedEventStart - t.navigationStart || 0), | ||
'z=' + Date.now() | ||
@@ -55,3 +57,3 @@ ]; | ||
window.__ga_img = new Image(); | ||
window.__ga_img.src = url + '?' + pv_data.join('&');; | ||
window.__ga_img.src = window.ga_api + '?' + pv_data.join('&'); | ||
} | ||
@@ -64,2 +66,2 @@ | ||
} | ||
})(window.ga_tid, window.ga_api, window, document, navigator); | ||
})(window, document, navigator); |
@@ -1,1 +0,1 @@ | ||
!function(t,e,n,a,r){var o=n.screen,i=encodeURIComponent,d=Math.max,c=(Math.min,n.performance.timing);function g(){var g=["ga="+t,"dt="+i(a.title),"de="+i(a.characterSet||a.charset),"dr="+i(a.referrer),"ul="+(r.language||r.browserLanguage||r.userLanguage),"sd="+o.colorDepth+"-bit","sr="+o.width+"x"+o.height,"vp="+d(a.documentElement.clientWidth,n.innerWidth||0)+"x"+d(a.documentElement.clientHeight,n.innerHeight||0),"plt="+i(c.loadEventStart-c.navigationStart||0),"dns="+i(c.domainLookupEnd-c.domainLookupStart||0),"pdt="+i(c.responseEnd-c.responseStart||0),"rrt="+i(c.redirectEnd-c.redirectStart||0),"tcp="+i(c.connectEnd-c.connectStart||0),"srt="+i(c.responseStart-c.requestStart||0),"dit="+i(c.domInteractive-c.domLoading||0),"clt="+i(c.domContentLoadedEventStart-c.navigationStart||0),"z="+Date.now()];n.__ga_img=new Image,n.__ga_img.src=e+"?"+g.join("&")}"complete"===a.readyState?g():n.addEventListener("load",g)}(window.ga_tid,window.ga_api,window,document,navigator); | ||
!function(t,e,r){var n=t.screen,a=encodeURIComponent,i=Math.max,o=t.performance,d=o&&o.timing;function m(){var o=["ga="+t.ga_tid,"dt="+a(e.title),"de="+a(e.characterSet||e.charset),"dr="+a(e.referrer),"ul="+(r.language||r.browserLanguage||r.userLanguage),"sd="+n.colorDepth+"-bit","sr="+n.width+"x"+n.height,"vp="+i(e.documentElement.clientWidth,t.innerWidth||0)+"x"+i(e.documentElement.clientHeight,t.innerHeight||0),"plt="+filterNumber(d.loadEventStart-d.navigationStart||0),"dns="+filterNumber(d,domainLookupEnd-d.domainLookupStart||0),"pdt="+filterNumber(d,responseEnd-d.responseStart||0),"rrt="+filterNumber(d,redirectEnd-d.redirectStart||0),"tcp="+filterNumber(d,connectEnd-d.connectStart||0),"srt="+filterNumber(d,responseStart-d.requestStart||0),"dit="+filterNumber(d,domInteractive-d.domLoading||0),"clt="+filterNumber(d,domContentLoadedEventStart-d.navigationStart||0),"z="+Date.now()];t.__ga_img=new Image,t.__ga_img.src=t.ga_api+"?"+o.join("&")}filterNumber=function(t){return isNaN(t)||t==1/0||t<0?void 0:t},"complete"===e.readyState?m():t.addEventListener("load",m)}(window,document,navigator); |
{ | ||
"name": "cfga", | ||
"version": "0.0.1", | ||
"version": "1.0.0", | ||
"description": "The Cloudflare Workers implementation of an async Google Analytics", | ||
@@ -5,0 +5,0 @@ "main": "cfga.min.js", |
# cloudflare-workers-async-google-analytics | ||
[![Author Sukka](https://img.shields.io/badge/author-Sukka-b68469.svg?style=flat-square)](https://skk.moe) | ||
[![License MIT](https://img.shields.io/github/license/sukkaw/cloudflare-workers-async-google-analytics.svg?style=flat-square)](./LICENSE) | ||
![NPM Version](https://img.shields.io/npm/v/cfga) | ||
[![Build with Cloudflare Workers](https://img.shields.io/badge/build%20with-cloudflare%20workers-f38020.svg?style=flat-square)](https://workers.cloudflare.com/) | ||
![Gzip size of cfga.min.js](https://img.badgesize.io/sukkaw/cloudflare-workers-async-google-analytics/master/cfga.min.js.svg?compression=gzip&style=flat-square) | ||
[![](https://data.jsdelivr.com/v1/package/npm/cfga/badge)](https://www.jsdelivr.com/package/npm/cfga) | ||
The Cloudflare Workers implementation of an async Google Analytics | ||
## Introduction | ||
This project is based on [Google Analytics Measurement Protocol](https://developers.google.com/analytics/devguides/collection/protocol/v1/), using [Cloudflare Workers](https://workers.cloudflare.com/) with a less than 1KB gzipped tiny `cfga.min.js` to accelerate the Google Analytics, rather than a heavy (45KB gzipped) `analytics.js` from Google. | ||
## Get Start | ||
### 1. Import into Cloudflare Workers | ||
Login into Cloudflare Dashboard and enter `Workers` App. Create a new script, delete default code in the editor, and then copy [the `woker.js` content](https://github.com/SukkaW/cloudflare-workers-async-google-analytics/blob/master/worker.js) into the editor. After saving the workers script, do not forget to register a route for the scripts. | ||
Now you can test your workers with a simple HTTP request. You should able to see `403 Forbidden`. Then you can deploy the scripts. | ||
### 2. Insert the `cfga.min.js` into your website | ||
Just add those few lines of the code to your website, right before `</body>`. Do not forget to replace the default configuration with your own! | ||
```html | ||
<script> | ||
window.ga_tid = "UA-XXXXX-Y"; // {String} The trackerID of your site. | ||
window.ga_url = "https://example.com/xxx/"; // {String} The route of your cloudflare workers you just registered before. | ||
</script> | ||
<script src="https://cdn.jsdelivr.net/npm/cfga@1.0.0" async></script> | ||
``` | ||
### 3. Watch this repo with `Releases Only`. | ||
Click the `watch` button at the top of the repo and choose `Releases Only`, so you can get notice of release update in time. | ||
## Advanced | ||
### Data type the `cfga.min.js` collected and sent | ||
Currently, `cloudflare-workers-async-google-analytics` and `cfga.min.js` only support collect those types of data listed below. If you want to collect more, you should use Google Analytics official track code. | ||
- [`dl`](https://developers.google.com/analytics/devguides/collection/protocol/v1/parameters#dl): Document location URL | ||
- [`uip`](https://developers.google.com/analytics/devguides/collection/protocol/v1/parameters#uip): User real IP | ||
- [`ua`](https://developers.google.com/analytics/devguides/collection/protocol/v1/parameters#ua): User Agent | ||
- [`dt`](https://developers.google.com/analytics/devguides/collection/protocol/v1/parameters#dt): Document Title | ||
- [`de`](https://developers.google.com/analytics/devguides/collection/protocol/v1/parameters#de): Document Encoding | ||
- [`dr`](https://developers.google.com/analytics/devguides/collection/protocol/v1/parameters#dr): Document Referrer | ||
- [`ul`](https://developers.google.com/analytics/devguides/collection/protocol/v1/parameters#ul): User Language | ||
- [`sd`](https://developers.google.com/analytics/devguides/collection/protocol/v1/parameters#sd): Screen Colors Depth | ||
- [`sr`](https://developers.google.com/analytics/devguides/collection/protocol/v1/parameters#sr): Screen Resolution | ||
- [`plt`](https://developers.google.com/analytics/devguides/collection/protocol/v1/parameters#plt): Page Load Time | ||
- [`dns`](https://developers.google.com/analytics/devguides/collection/protocol/v1/parameters#dns): DNS Time | ||
- [`pdt`](https://developers.google.com/analytics/devguides/collection/protocol/v1/parameters#pdt): Page Downloaad Time | ||
- [`rrt`](https://developers.google.com/analytics/devguides/collection/protocol/v1/parameters#rrt): Redirect Response Time | ||
- [`tcp`](https://developers.google.com/analytics/devguides/collection/protocol/v1/parameters#tcp): TCP Connect Time | ||
- [`srt`](https://developers.google.com/analytics/devguides/collection/protocol/v1/parameters#srt): Server Response Time | ||
- [`dit`](https://developers.google.com/analytics/devguides/collection/protocol/v1/parameters#dit): DOM Interactive Time | ||
- [`clt`](https://developers.google.com/analytics/devguides/collection/protocol/v1/parameters#clt): Content Load Time | ||
### Security | ||
`cloudflare-workers-async-google-analytics` blocks those types of request by default: | ||
- No `User-Agent` in request headers | ||
- No `Referer` in request headers | ||
- No Tracker ID given in request headers | ||
- Use some other measurements from Cloudflare WAF | ||
And if you want to restrict your workers only for your website, all you need to do is to edit a few lines of your workers: | ||
- Launch Cloudflare Workers Editor again. | ||
- You can see some commented out code at the first line like this: | ||
```javascript | ||
//const AllowedReferrer = 'skk.moe'; | ||
``` | ||
- replace your domain with `skk.moe`, then remove `//`. | ||
> **Notice**: set `AllowedReferrer` value to `skk.moe` means all the subdomains of `skk.moe` will be allowed as well. | ||
## Author | ||
**cloudflare-workers-async-google-analytics** © [Sukka](https://github.com/SukkaW), Released under the [MIT](./LICENSE) License.<br> | ||
Authored and maintained by Sukka with help from contributors ([list](https://github.com/SukkaW/cloudflare-workers-async-google-analytics/graphs/contributors)). | ||
> [Personal Website](https://skk.moe) · [Blog](https://blog.skk.moe) · GitHub [@SukkaW](https://github.com/SukkaW) · Telegram Channel [@SukkaChannel](https://t.me/SukkaChannel) · Twitter [@isukkaw](https://twitter.com/isukkaw) · Keybase [@sukka](https://keybase.io/sukka) |
106
worker.js
//const AllowedReferrer = 'skk.moe'; | ||
const createUuid = () => { | ||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => { | ||
let r = Math.random() * 16 | 0, | ||
v = (c == 'x') ? r : (r & 0x3 | 0x8); | ||
return v.toString(16); | ||
}); | ||
}; | ||
const encode = (data) => encodeURIComponent(decodeURIComponent(data)); | ||
addEventListener('fetch', (event) => { | ||
@@ -22,2 +13,16 @@ event.respondWith(response(event)); | ||
async function response(event) { | ||
const createUuid = () => { | ||
let s = []; | ||
const hexDigits = '0123456789abcdef'; | ||
for (let i = 0; i < 36; i++) { | ||
s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1); | ||
} | ||
s[14] = '4'; // bits 12-15 of the time_hi_and_version field to 0010 | ||
s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1); // bits 6-7 of the clock_seq_hi_and_reserved to 01 | ||
s[8] = s[13] = s[18] = s[23] = '-'; | ||
return s.join(''); | ||
}; | ||
const encode = (data) => encodeURIComponent(decodeURIComponent(data)); | ||
const url = new URL(event.request.url); | ||
@@ -37,4 +42,8 @@ | ||
const Referer = getReqHeader('Referer'); | ||
const user_agent = getReqHeader('User-Agent'); | ||
const ga_tid = getQueryString('ga'); | ||
const hasUuid = getCookie('uuid'); | ||
const uuid = (hasUuid) ? getCookie('uuid') : createUuid(); | ||
const uuid = (hasUuid) ? hasUuid : createUuid(); | ||
@@ -44,14 +53,11 @@ let response; | ||
let needBlock = false; | ||
(!getReqHeader('Referer') || !getReqHeader('User-Agent') || !getQueryString('ga')) ? needBlock = true : needBlock = false; | ||
(!Referer || !user_agent || !ga_tid) ? needBlock = true : needBlock = false; | ||
if (typeof AllowedReferrer !== 'undefined' && AllowedReferrer !== null) { | ||
if (AllowedReferrer && getReqHeader('Referer')) { | ||
(!(getReqHeader('Referer').indexOf(AllowedReferrer) > -1)) ? needBlock = true : needBlock = false; | ||
} | ||
if (typeof AllowedReferrer !== 'undefined' && AllowedReferrer !== null && AllowedReferrer && Referer) { | ||
(!(Referer.indexOf(AllowedReferrer) > -1)) ? needBlock = true : needBlock = false; | ||
} | ||
// Block Request that have no referer, no user-agent and no ga query. | ||
if (needBlock) { | ||
response = new Response('<html>403 Forbidden</html>', { | ||
response = new Response('403 Forbidden', { | ||
headers: { 'Content-Type': 'text/html' }, | ||
@@ -64,53 +70,8 @@ status: 403, | ||
} else { | ||
const pvData = [ | ||
// UA-XXXXXX-Y | ||
'tid=' + encode(getQueryString('ga')), | ||
// UUID Version 4 | ||
'cid=' + uuid, | ||
// Document URL | ||
'dl=' + encode(getReqHeader('Referer')), | ||
// Real IP - Collect from CF-Connecting-IP | ||
'uip=' + getReqHeader('CF-Connecting-IP'), | ||
// Title | ||
'dt=' + encode(getQueryString('dt')), | ||
// Document Encoding | ||
'de=' + encode(getQueryString('de')), | ||
// Referrer | ||
'dr=' + encode(getQueryString('dr')), | ||
// Language | ||
'ul=' + encode(getQueryString('ul')), | ||
// Color Depth | ||
'sd=' + encode(getQueryString('sd')), | ||
// Screen Size | ||
'sr=' + encode(getQueryString('sr')), | ||
// Display | ||
'vp=' + encode(getQueryString('vp')), | ||
'z=' + getQueryString('z') | ||
]; | ||
const pvData = `tid=${encode(ga_tid)}&cid=${uuid}&dl=${encode(Referer)}&uip=${getReqHeader('CF-Connecting-IP')}&ua=${user_agent}&dt=${encode(getQueryString('dt'))}&de=${encode(getQueryString('de'))}&dr=${encode(getQueryString('dr'))}&ul=${getQueryString('ul')}&sd=${getQueryString('sd')}&sr=${getQueryString('sr')}&vp=${getQueryString('vp')}`; | ||
const perfData = [ | ||
// plt: Page Loading Time | ||
'plt=' + getQueryString('plt'), | ||
// dns: DNS Time | ||
'dns=' + getQueryString('dns'), | ||
// pdt: Page Dowenload Time | ||
// start download time => finish download time | ||
'pdt=' + getQueryString('pdt'), | ||
// rrt: Redirect Time | ||
'rrt=' + getQueryString('rrt'), | ||
// tcp: TCP Time | ||
'tcp=' + getQueryString('tcp'), | ||
// srt: Server Response Time | ||
// start request => server send first byte | ||
// (TTFB - TCP - DNS) | ||
'srt=' + getQueryString('srt'), | ||
// dit: DOM Interactive Time | ||
'dit=' + getQueryString('dit'), | ||
// clt: Content Loading Time | ||
// open the page => DOMContentLoaded | ||
'clt=' + getQueryString('clt') | ||
]; | ||
const perfData = `plt=${getQueryString('plt')}&dns=${getQueryString('dns')}&pdt=${getQueryString('pdt')}&rrt=${getQueryString('rrt')}&tcp=${getQueryString('tcp')}&srt=${getQueryString('srt')}&dit=${getQueryString('dit')}&clt=${getQueryString('clt')}` | ||
const pvUrl = `https://www.google-analytics.com/collect?v=1&t=pageview&${pvData.join('&')}`; | ||
const perfUrl = `https://www.google-analytics.com/collect?v=1&t=timing&${pvData.concat(perfData).join('&')}` | ||
const pvUrl = `https://www.google-analytics.com/collect?v=1&t=pageview&${pvData}&z=${getQueryString('z')}`; | ||
const perfUrl = `https://www.google-analytics.com/collect?v=1&t=timing&${pvData}&${perfData}&z=${getQueryString('z')}` | ||
@@ -120,3 +81,3 @@ let parameter = { | ||
'Host': 'www.google-analytics.com', | ||
'User-Agent': getReqHeader('User-Agent'), | ||
'User-Agent': user_agent, | ||
'Accept': getReqHeader('Accept'), | ||
@@ -129,8 +90,4 @@ 'Accept-Language': getReqHeader('Accept-Language'), | ||
if (event.request.headers.has('Referer')) { | ||
parameter.headers.Referer = getReqHeader('Referer'); | ||
} | ||
if (event.request.headers.has('Origin')) { | ||
parameter.headers.Origin = getReqHeader('Origin'); | ||
} | ||
// To sent data to google analytics after response id finished | ||
event.waitUntil(senData(pvUrl, perfUrl, parameter)); | ||
@@ -148,7 +105,4 @@ // Return an 204 to speed up: No need to download a gif | ||
// To sent data to google analytics after response id finished | ||
event.waitUntil(senData(pvUrl, perfUrl, parameter)); | ||
return response | ||
} | ||
} |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
15661
1
93
141