Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Jason Carpenter 2020-04-24 15:10:56 -04:00
commit 23f1fdbb3f
13 changed files with 85 additions and 30 deletions

View File

@ -1,11 +1,4 @@
<p align="center">
<h3 align="center">🔭 Shynet 🔭</h3>
<p align="center">
Web analytics that's self hosted, cookie free, privacy friendly, and useful(?)
<br>
</p>
</p>
# Getting Started
## Table of Contents
@ -50,6 +43,8 @@ DB_PORT=<your db port>
DJANGO_SECRET_KEY=<your Django secret key; just a random string>
# Don't leak error details to visitors, very important
DEBUG=False
# Unless you are using an external Celery task queue, make sure this
# is set to True.
CELERY_TASK_ALWAYS_EAGER=True
# For better security, set this to your deployment's domain. Comma separated.
ALLOWED_HOSTS=*
@ -74,6 +69,14 @@ SERVER_EMAIL=Shynet <noreply@shynet.example.com>
REDIS_CACHE_LOCATION=redis://redis.default.svc.cluster.local/0
# If set, make sure CELERY_TASK_ALWAYS_EAGER is False
CELERY_BROKER_URL=redis://redis.default.svc.cluster.local/1
# Other Shynet settings
# How frequently should the monitoring script "phone home" (in ms)?
SCRIPT_HEARTBEAT_FREQUENCY=5000
# Should only superusers (admins) be able to create services? This is helpful
# when you'd like to invite others to your Shynet instance but don't want
# them to be able to create services of their own.
ONLY_SUPERUSERS_CREATE=False
```
4. Setup the Shynet database by running `docker run --env-file=<your env file> milesmcc/shynet:latest python manage.py migrate`.
@ -138,6 +141,8 @@ DB_PORT=<your db port>
DJANGO_SECRET_KEY=<your Django secret key; just a random string>
# Don't leak error details to visitors, very important
DEBUG=False
# Unless you are using an external Celery task queue, make sure this
# is set to True.
CELERY_TASK_ALWAYS_EAGER=True
# For better security, set this to your deployment's domain. Comma separated.
ALLOWED_HOSTS=*
@ -162,6 +167,15 @@ SERVER_EMAIL=Shynet <noreply@shynet.example.com>
REDIS_CACHE_LOCATION=redis://redis.default.svc.cluster.local/0
# If set, make sure CELERY_TASK_ALWAYS_EAGER is False
CELERY_BROKER_URL=redis://redis.default.svc.cluster.local/1
# Other Shynet settings
# How frequently should the monitoring script "phone home" (in ms)?
SCRIPT_HEARTBEAT_FREQUENCY=5000
# Should only superusers (admins) be able to create services? This is helpful
# when you'd like to invite others to your Shynet instance but don't want
# them to be able to create services of their own.
ONLY_SUPERUSERS_CREATE=False
```
9. Setup the Shynet database by running `docker run --env-file=<your env file> milesmcc/shynet:latest-ssl python manage.py migrate`.

View File

@ -1,14 +1,8 @@
<p align="center">
<h3 align="center">🔭 Shynet 🔭</h3>
<img src="images/logo.png" height="50" alt="Shynet logo">
Modern, privacy-friendly, and cookie-free web analytics.
<p align="center">
Web analytics that's self hosted, cookie free, privacy friendly, and useful(?)
<br>
<br>
<a href="#installation"><strong>Getting started »</strong></a>
<br>
</p>
</p>
## Motivation
@ -85,7 +79,7 @@ Shynet is pretty simple, but there are a few key terms you need to know in order
## Installation
You can find installation instructions in our [Getting Started Guide](GUIDE.md#installation)!
You can find installation instructions in our [Getting Started Guide](GUIDE.md#installation).
## FAQ
@ -118,4 +112,4 @@ Shynet is made available under the [Apache License, version 2.0](LICENSE).
---
a17t was created by [Miles McCain](https://miles.land) at the [Recurse Center](https://recurse.com) using [a17t](https://a17t.miles.land).
Shynet was created by [Miles McCain](https://miles.land) ([@MilesMcCain](https://twitter.com/MilesMcCain)) at the [Recurse Center](https://recurse.com) using [a17t](https://a17t.miles.land).

BIN
images/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -1,3 +1,10 @@
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/a17t@0.1.3/dist/a17t.css">
<script async src="https://use.fontawesome.com/releases/v5.3.1/js/all.js"></script>
<link href="https://cdn.jsdelivr.net/npm/tailwindcss@^1.0/dist/tailwind.min.css" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap" rel="stylesheet">
<style>
:root {
--family-primary: "Inter", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
--family-secondary: var(--family-primary);
}
</style>

View File

@ -4,6 +4,9 @@ window.onload = function () {
Math.random().toString(36).substring(2, 15);
function sendUpdate() {
try {
if (document.hidden) {
return;
}
var xhr = new XMLHttpRequest();
xhr.open(
"POST",
@ -23,6 +26,6 @@ window.onload = function () {
);
} catch { }
}
setInterval(sendUpdate, 5000);
setInterval(sendUpdate, parseInt("{{heartbeat_frequency}}"));
sendUpdate();
};

View File

@ -8,7 +8,9 @@ from django.utils import timezone
from django.utils.decorators import method_decorator
from django.views.decorators.csrf import csrf_exempt
from django.views.generic import TemplateView, View
from django.core.cache import cache
from ipware import get_client_ip
from core.models import Service
from ..tasks import ingress_request
@ -58,8 +60,15 @@ class PixelView(View):
@method_decorator(csrf_exempt, name="dispatch")
class ScriptView(View):
def dispatch(self, request, *args, **kwargs):
service_uuid = self.kwargs.get("service_uuid")
origins = cache.get(f"service_origins_{service_uuid}")
if origins is None:
service = Service.objects.get(uuid=service_uuid)
origins = service.origins
cache.set(f"service_origins_{service_uuid}", origins, timeout=3600)
resp = super().dispatch(request, *args, **kwargs)
resp["Access-Control-Allow-Origin"] = "*"
resp["Access-Control-Allow-Origin"] = origins
resp["Access-Control-Allow-Methods"] = "GET,HEAD,OPTIONS,POST"
resp[
"Access-Control-Allow-Headers"
@ -82,10 +91,15 @@ class ScriptView(View):
},
)
)
heartbeat_frequency = settings.SCRIPT_HEARTBEAT_FREQUENCY
return render(
self.request,
"analytics/scripts/page.js",
context={"endpoint": endpoint, "protocol": protocol},
context={
"endpoint": endpoint,
"protocol": protocol,
"heartbeat_frequency": heartbeat_frequency,
},
content_type="application/javascript",
)

View File

@ -8,7 +8,7 @@ from allauth.account.admin import EmailAddress
class ServiceForm(forms.ModelForm):
class Meta:
model = Service
fields = ["name", "link", "respect_dnt", "collaborators"]
fields = ["name", "link", "respect_dnt", "origins", "collaborators"]
widgets = {
"name": forms.TextInput(),
"origins": forms.TextInput(),
@ -29,7 +29,6 @@ class ServiceForm(forms.ModelForm):
collaborators = forms.CharField(help_text="Which users should have read-only access to this service? (Comma separated list of emails.)", required=False)
def clean_collaborators(self):
collaborators = []
for collaborator_email in self.cleaned_data["collaborators"].split(","):

View File

@ -0,0 +1,12 @@
{% load a17t_tags %}
{{form.name|a17t}}
{{form.link|a17t}}
{{form.collaborators|a17t}}
<details class="p-4 border rounded">
<summary class="cursor-pointer text-sm">Advanced settings</summary>
<hr class="sep h-4">
{{form.respect_dnt|a17t}}
{{form.origins|a17t}}
</details>

View File

@ -10,7 +10,7 @@
<form class="card ~neutral !low p-0 max-w-xl" method="POST">
{% csrf_token %}
<div class="p-4">
{{form|a17t}}
{% include 'dashboard/includes/service_form.html' %}
</div>
<div class="section ~urge !normal p-4">
<button type="submit" class="button ~urge !high">Create</button>

View File

@ -23,7 +23,7 @@
<form class="card ~neutral !low p-0" method="POST">
{% csrf_token %}
<div class="p-4">
{{form|a17t}}
{% include 'dashboard/includes/service_form.html' %}
</div>
<div class="section ~neutral !normal p-4 flex justify-between">
<div>

View File

@ -12,6 +12,7 @@ from django.views.generic import (
UpdateView,
)
from rules.contrib.views import PermissionRequiredMixin
from django.core.cache import cache
from analytics.models import Session
from core.models import Service
@ -77,6 +78,13 @@ class ServiceUpdateView(
def get_success_url(self):
return reverse("dashboard:service", kwargs={"pk": self.object.uuid})
def form_valid(self, *args, **kwargs):
resp = super().form_valid(*args, **kwargs)
cache.set(
f"service_origins_{self.object.uuid}", self.object.origins, timeout=3600
)
return resp
class ServiceDeleteView(
LoginRequiredMixin, PermissionRequiredMixin, SuccessMessageMixin, DeleteView

View File

@ -256,3 +256,7 @@ ONLY_SUPERUSERS_CREATE = os.getenv("ONLY_SUPERUSERS_CREATE", "True") == "True"
# Should the script use HTTPS to send the POST requests? The hostname is from
# the django SITE default. (Edit it using the admin panel.)
SCRIPT_USE_HTTPS = os.getenv("SCRIPT_USE_HTTPS", "True") == "True"
# How frequently should the tracking script "phone home" with a heartbeat, in
# milliseconds?
SCRIPT_HEARTBEAT_FREQUENCY = int(os.getenv("SCRIPT_HEARTBEAT_FREQUENCY", "5000"))

View File

@ -7,8 +7,8 @@
</head>
<body>
<noscript><img src="//localhost:8000/ingress/211410e6-b401-4fe8-8740-7926368590be/pixel.gif"></noscript>
<script src="//localhost:8000/ingress/211410e6-b401-4fe8-8740-7926368590be/script.js"></script>
<noscript><img src="//localhost:8000/ingress/test_uuid/pixel.gif"></noscript>
<script src="//localhost:8000/ingress/test_uuid/script.js"></script>
</body>
</html>