Skip to content

Commit

Permalink
Site code improvements
Browse files Browse the repository at this point in the history
Better documentation, allow callbacks, ignore all local networks.

Fixes part of #10
  • Loading branch information
arp242 committed Sep 5, 2019
1 parent cdef2d8 commit fbb2dcd
Show file tree
Hide file tree
Showing 8 changed files with 708 additions and 544 deletions.
36 changes: 36 additions & 0 deletions bin/proxy
@@ -0,0 +1,36 @@
#!/usr/bin/env python3
#
# mitmproxy <https://mitmproxy.org> addon to serve count.js locally, instead of
# the live version. It's the easiest way to test it manually for now (I need to
# figure out how to unit test this stuff in a way that I like).
#
# Start with: ~/mitproxy -s ./proxy
#
# Can also use mitmweb with the same arguments for a web interface.
#
# TODO: looks like we can also use standard "replace" with "@path/to/file"?

import mimetypes
import mitmproxy.http
import mitmproxy.ctx

REPLACE = {
'static.goatcounter.com/count.min.js': './public/count.js',
'zgo.at/': '../zgo.at/index.html',
}

class InterceptCount:
def request(self, flow):
for remote, local in REPLACE.items():
if remote in flow.request.pretty_url:
with open(local) as fp:
data = fp.read()
ct = mimetypes.MimeTypes().guess_type(local)[0]
flow.response = mitmproxy.http.HTTPResponse.make(200, data, {
'Content-Type': ct,
})
mitmproxy.ctx.log.info('Replaced {} with {} ({})'.format(remote, local, ct))
break


addons = [InterceptCount()]
1,078 changes: 582 additions & 496 deletions handlers/pack.go

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion hit.go
Expand Up @@ -229,7 +229,7 @@ func cleanURL(ref string, refURL *url.URL) (string, *string, bool, bool) {
i := strings.Index(ref, "?")
if i == -1 {
// No parameters so no work.
return refURL.String()[2:], nil, changed, false
return strings.TrimLeft(refURL.String(), "/"), nil, changed, false
}
eq := ref[i+1:]
ref = ref[:i]
Expand Down
56 changes: 42 additions & 14 deletions public/count.js
Expand Up @@ -2,26 +2,44 @@
// This file is part of GoatCounter and published under the terms of the AGPLv3,
// which can be found in the LICENSE file or at gnu.org/licenses/agpl.html

// See /bin/proxy on how to test this locally.
(function() {
'use strict';

var vars = window.vars || {};

// Find canonical location of the current page.
var get_location = function() {
var loc = window.location,
c = document.querySelector('link[rel="canonical"][href]');
// Parse in a tag to a Location object (canonical URL may be relative).
if (c) {
var a = document.createElement('a');
a.href = c.href;
loc = a;
var results = {p: vars.path, r: vars.referrer};

var rcb, pcb;
if (typeof(results.r) === 'function')
rcb = results.r;
if (typeof(results.p) === 'function')
pcb = results.p;

// Get referrer.
if (results.r === null || results.r === undefined)
results.r = document.referrer;
if (rcb)
results.r = rcb(results.r);

// Get path.
if (results.p === null || results.p === undefined) {
var loc = window.location,
c = document.querySelector('link[rel="canonical"][href]');
// Parse in a tag to a Location object (canonical URL may be relative).
if (c) {
var a = document.createElement('a');
a.href = c.href;
loc = a;
}
results.p = (loc.pathname + loc.search) || '/';
}
if (pcb)
results.p = pcb(results.p);

return {
p: (vars.path || (loc.pathname + loc.search) || '/'),
r: (vars.referrer || document.referrer),
};
return results;
};

// Convert parameters to urlencoded string, starting with a ?
Expand All @@ -38,15 +56,25 @@
var count = function() {
// Don't track pages fetched with the browser's prefetch algorithm.
// See https://github.com/usefathom/fathom/issues/13
if ('visibilityState' in document && document.visibilityState === 'prerender') {
if ('visibilityState' in document && document.visibilityState === 'prerender')
return;

// Don't track private networks.
if (window.location.hostname.match(/localhost$/) ||
window.location.hostname.match(/^(127\.|10\.|172\.16\.|192\.168\.)/))
return;

var loc = get_location();

// null returned from user callback.
if (loc.p === null)
return;
}

// Add image to send request.
var img = document.createElement('img');
img.setAttribute('alt', '');
img.setAttribute('aria-hidden', 'true');
img.src = window.counter + to_params(get_location());
img.src = window.counter + to_params(loc);
img.addEventListener('load', function() {
document.body.removeChild(img)
}, false);
Expand Down
2 changes: 1 addition & 1 deletion public/count.min.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

45 changes: 39 additions & 6 deletions tpl/_backend_sitecode.gohtml
@@ -1,9 +1,5 @@
&lt;script&gt;
<pre>&lt;script&gt;
(function() {
if (window.location.hostname.indexOf('localhost') &gt; -1) {
return;
}

var script = document.createElement('script');
window.counter = 'https://{{.Site.Code}}.{{.Domain}}/count'
script.async = 1;
Expand All @@ -12,4 +8,41 @@
var ins = document.getElementsByTagName('script')[0];
ins.parentNode.insertBefore(script, ins)
})();
&lt;/script&gt;
&lt;/script&gt;</pre>

{{if eq .Path "/settings"}}
<p>You can optionally pass variables manually by using the <code>vars</code> object. Supported keys:</p>

<ul>
<li><code>path</code> – Path to the current page that's recorded, without
domain (e.g. <code>/path/page.html</code>).</li>

<li><code>referrer</code> – Referrer value; can be URL
(<code>https://example.com</code>) or any string
(<code>June Newsletter</code>). Default is to use the Referer
header.</li>
</ul>

<p>The default value will be used if the value is <code>null</code> or
<code>undefined</code> (but <em>not</em> on empty string!) The value can be used
as a callback too: the default value is passed and the return is used. The view
will be ignored if the return value from the <code>path</code> callback is
<code>null</code>.</p>

<p>Example:</p>

<pre>window.vars = {
path: function(p) {
// Don't track the home page.
if (p === '/')
return null;
// Remove .html from all other page links.
else
return p.replace(/\.html$/, '');
},

// Very simplistic method to get referrer from URL (e.g. ?ref=Newsletter)
referrer: (window.location.search ? window.location.search.split('=')[1] : null),
};</pre>

{{end}}
6 changes: 4 additions & 2 deletions tpl/backend.gohtml
Expand Up @@ -6,10 +6,12 @@
data yet.<br>
Make sure the site is set up correctly with the code below inserted just
before the closing &lt;/body&gt; tag:</p>
<pre>{{template "_backend_sitecode.gohtml" .}}</pre>
{{template "_backend_sitecode.gohtml" .}}

<p><small>This message will disappear once we receive data; you will
still be able to see the site code in <a href="/settings">settings</a>.</small></p>
still be able to see the site code in
<a href="/settings#site-code">settings</a>, which also contains
further documentation.</small></p>
</div>
{{end}}

Expand Down
27 changes: 3 additions & 24 deletions tpl/backend_settings.gohtml
@@ -1,6 +1,6 @@
{{template "_backend_top.gohtml" .}}

<h2 id="settings">Settings</h2>
<h2>Settings</h2>
<div class="form-wrap">
<form method="post" action="/save" class="vertical">
<input type="hidden" name="csrf" value="{{.User.CSRFToken}}">
Expand Down Expand Up @@ -75,30 +75,9 @@

{{end}}

<h2 id="code">Site code</h2>
<h2 id="site-code">Site code</h2>
<p>Insert the code below just before the closing &lt;/body&gt; tag:</p>
<pre>{{template "_backend_sitecode.gohtml" .}}</pre>

<p><code>window.counter</code> is the endpoint which records the path and
referrer.</p>

<p><code>window.vars</code> is an optional setting to set the <code>path</code>
and/or <code>referrer</code>; for example:</p>

<pre>
(function() {
if (window.location.hostname.indexOf('localhost') &gt; -1) {
return;
}

window.vars = {
path: '/foo',
referrer: 'bar',
};

// .. code above ..
})();
</pre>
{{template "_backend_sitecode.gohtml" .}}

{{/*
<h2 id="domain">Custom domain</h2>
Expand Down

0 comments on commit fbb2dcd

Please sign in to comment.