Merge remote-tracking branch 'upstream/master'
This commit is contained in:
		
						commit
						23f1fdbb3f
					
				
							
								
								
									
										30
									
								
								GUIDE.md
									
									
									
									
									
								
							
							
						
						
									
										30
									
								
								GUIDE.md
									
									
									
									
									
								
							@ -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`.
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										18
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								README.md
									
									
									
									
									
								
							@ -1,14 +1,8 @@
 | 
			
		||||
<p align="center">
 | 
			
		||||
  <h3 align="center">🔭 Shynet 🔭</h3>
 | 
			
		||||
<img src="images/logo.png" height="50" alt="Shynet logo">
 | 
			
		||||
 | 
			
		||||
  <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>
 | 
			
		||||
Modern, privacy-friendly, and cookie-free web analytics.
 | 
			
		||||
 | 
			
		||||
<br>
 | 
			
		||||
 | 
			
		||||
## 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
									
								
							
							
						
						
									
										
											BIN
										
									
								
								images/logo.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 12 KiB  | 
@ -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>
 | 
			
		||||
@ -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",
 | 
			
		||||
@ -21,8 +24,8 @@ window.onload = function () {
 | 
			
		||||
            window.performance.timing.navigationStart,
 | 
			
		||||
        })
 | 
			
		||||
      );
 | 
			
		||||
    } catch {}
 | 
			
		||||
    } catch { }
 | 
			
		||||
  }
 | 
			
		||||
  setInterval(sendUpdate, 5000);
 | 
			
		||||
  setInterval(sendUpdate, parseInt("{{heartbeat_frequency}}"));
 | 
			
		||||
  sendUpdate();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -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",
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -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(","):
 | 
			
		||||
 | 
			
		||||
@ -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>
 | 
			
		||||
@ -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>
 | 
			
		||||
 | 
			
		||||
@ -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>
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
 | 
			
		||||
@ -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"))
 | 
			
		||||
 | 
			
		||||
@ -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>
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user