Polish analytics & add collaboration
This commit is contained in:
parent
e486397c9d
commit
cfe3dac408
@ -6,6 +6,16 @@
|
||||
{{ field }}
|
||||
<span>{{ field.label }}</span>
|
||||
</label>
|
||||
{% elif field|is_multiple_checkbox %}
|
||||
{% if field.auto_id %}
|
||||
<label class="label my-1" for="{{field.auto_id}}">{{ field.label }}</label>
|
||||
{% endif %}
|
||||
{% for choice in field %}
|
||||
<label class="switch my-1 block">
|
||||
{{ choice.tag }}
|
||||
<span>{{ choice.choice_label }}</span>
|
||||
</label>
|
||||
{% endfor %}
|
||||
{% elif field|is_radio %}
|
||||
{% if field.auto_id %}
|
||||
<label class="label my-1" for="{{field.auto_id}}">{{ field.label }}</label>
|
||||
|
@ -1,5 +1,7 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class CoreConfig(AppConfig):
|
||||
name = "core"
|
||||
|
||||
# def ready(self):
|
||||
# import core.rules
|
||||
|
@ -7,10 +7,11 @@ from .models import Service
|
||||
class ServiceForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = Service
|
||||
fields = ["name", "link", "origins"]
|
||||
fields = ["name", "link", "origins", "collaborators"]
|
||||
widgets = {
|
||||
"name": forms.TextInput(),
|
||||
"origins": forms.TextInput(),
|
||||
"collaborators": forms.CheckboxSelectMultiple(),
|
||||
}
|
||||
labels = {
|
||||
"origins": "Allowed Hostnames",
|
||||
|
@ -10,13 +10,13 @@ def is_service_creator(user):
|
||||
|
||||
|
||||
@rules.predicate
|
||||
def is_service_owner(service, user):
|
||||
def is_service_owner(user, service):
|
||||
return service.owner == user
|
||||
|
||||
|
||||
@rules.predicate
|
||||
def is_service_collaborator(service, user):
|
||||
return user in service.collaborators.all()
|
||||
def is_service_collaborator(user, service):
|
||||
return service.collaborators.filter(pk=user.pk).exists()
|
||||
|
||||
|
||||
rules.add_perm("core.view_service", is_service_owner | is_service_collaborator)
|
||||
|
@ -6,7 +6,7 @@ This message can be safely ignored if you did not request a password reset. Clic
|
||||
|
||||
{{ password_reset_url }}
|
||||
|
||||
{% endif %}{% blocktrans with site_name=current_site.name site_domain=current_site.domain %}Thank you,
|
||||
{% blocktrans with site_name=current_site.name site_domain=current_site.domain %}Thank you,
|
||||
{{ site_name }}
|
||||
{% endblocktrans %}
|
||||
{% endautoescape %}
|
||||
|
@ -13,7 +13,7 @@
|
||||
<form method="POST" action="{{ action_url }}" class="max-w-lg">
|
||||
{% csrf_token %}
|
||||
{{ form|a17t }}
|
||||
<button type="submit" name="action" class="button ~urge !high">{% trans 'Change Password'}</button>
|
||||
<button type="submit" name="action" class="button ~urge !high">{% trans 'Change Password' %}</button>
|
||||
</form>
|
||||
{% else %}
|
||||
<p>{% trans 'Your password is now changed.' %}</p>
|
||||
|
@ -6,4 +6,5 @@
|
||||
|
||||
{% block card %}
|
||||
<p>{% trans 'Your password is now changed.' %}</p>
|
||||
<a href="{% url 'account_login' %}" class="button ~urge !high">Log In</a>
|
||||
{% endblock %}
|
||||
|
@ -48,19 +48,23 @@
|
||||
{% if can_create %}
|
||||
{% url 'core:service_create' as url %}
|
||||
{% include 'core/includes/sidebar_portal.html' with label="+ Create" url=url %}
|
||||
|
||||
<hr class="sep h-8">
|
||||
{% endif %}
|
||||
|
||||
|
||||
{% if user.collaborating_services.all %}
|
||||
<p class="ml-2 mt-8 mb-1 supra font-medium text-gray-500 pointer-events-none">Collaborations</p>
|
||||
<p class="ml-2 mb-1 supra font-medium text-gray-500 pointer-events-none">Collaborations</p>
|
||||
|
||||
{% for service in user.collaborating_services.all %}
|
||||
{% url 'core:service' service.uuid as url %}
|
||||
{% include 'core/includes/sidebar_portal.html' with label=service.name url=url %}
|
||||
{% endfor %}
|
||||
|
||||
<hr class="sep h-8">
|
||||
{% endif %}
|
||||
|
||||
<p class="ml-2 mt-8 mb-1 supra font-medium text-gray-500 pointer-events-none">Account</p>
|
||||
<p class="ml-2 mb-1 supra font-medium text-gray-500 pointer-events-none">Account</p>
|
||||
|
||||
{% if user.is_authenticated %}
|
||||
|
||||
|
35
shynet/core/templates/core/includes/session_list.html
Normal file
35
shynet/core/templates/core/includes/session_list.html
Normal file
@ -0,0 +1,35 @@
|
||||
{% load humanize helpers %}
|
||||
|
||||
<table class="table">
|
||||
<thead>
|
||||
<th>Session Start</th>
|
||||
<th>Identity</th>
|
||||
<th>Network</th>
|
||||
<th class="rf">Duration</th>
|
||||
<th class="rf">Hits</th>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for session in object_list %}
|
||||
<tr>
|
||||
<td>
|
||||
<a href="{% url 'core:service_session' object.pk session.pk %}" class="font-medium text-purple-700">
|
||||
{{session.start_time|date:"M j Y, g:i a"|capfirst}}
|
||||
{% if session.is_currently_active %}
|
||||
<span class="badge ~positive">Online</span>
|
||||
{% endif %}
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
{% if session.identifier %}
|
||||
<span class="chip ~neutral">{{session.identifier}}</span>
|
||||
{% else %}
|
||||
<span class="text-gray-600">—</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>{{session.country|flag_emoji}} {{session.asn|default:"Unknown"}}</td>
|
||||
<td class="rf">{{session.duration|naturaldelta}}</td>
|
||||
<td class="rf">{{session.hit_set.count|intcomma}}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
@ -21,6 +21,6 @@
|
||||
{% for object in services %}
|
||||
{% include 'core/includes/service_overview.html' %}
|
||||
{% empty %}
|
||||
<p>You don't have any services.</p>
|
||||
<p>You don't have any services on this Shynet instance yet.</p>
|
||||
{% endfor %}
|
||||
{% endblock %}
|
@ -178,6 +178,10 @@
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<a href="{% url 'core:service_session_list' service.uuid %}" class="button field w-auto">View individual sessions
|
||||
→</a>
|
||||
<div class="card ~neutral !low">
|
||||
{% include 'core/includes/session_list.html' %}
|
||||
<hr class="sep h-8">
|
||||
<a href="{% url 'core:service_session_list' service.uuid %}" class="button ~neutral w-auto">View more sessions
|
||||
→</a>
|
||||
</div>
|
||||
{% endblock %}
|
@ -11,39 +11,7 @@
|
||||
|
||||
{% block service_content %}
|
||||
<div class="card ~neutral !low mb-8 pt-2 max-w-full overflow-x-scroll">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<th>Time</th>
|
||||
<th>Identity</th>
|
||||
<th>Network</th>
|
||||
<th class="rf">Duration</th>
|
||||
<th class="rf">Hits</th>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for session in object_list %}
|
||||
<tr>
|
||||
<td>
|
||||
<a href="{% url 'core:service_session' object.pk session.pk %}" class="font-medium text-purple-700">
|
||||
{{session.start_time|date:"M j Y, g:i a"|capfirst}}
|
||||
{% if session.is_currently_active %}
|
||||
<span class="badge ~positive">Online</span>
|
||||
{% endif %}
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
{% if session.identifier %}
|
||||
<span class="chip ~neutral">{{session.identifier}}</span>
|
||||
{% else %}
|
||||
<span class="text-gray-600">—</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>{{session.country|flag_emoji}} {{session.asn|default:"Unknown"}}</td>
|
||||
<td class="rf">{{session.duration|naturaldelta}}</td>
|
||||
<td class="rf">{{session.hit_set.count|intcomma}}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% include 'core/includes/session_list.html' %}
|
||||
</div>
|
||||
{% pagination page_obj request %}
|
||||
{% endblock %}
|
@ -11,24 +11,29 @@
|
||||
{% block service_content %}
|
||||
<div class="max-w-xl content">
|
||||
<h5>Installation</h5>
|
||||
<p>Place the following snippet at the end of the <code><body></code> tag on any page you'd like to track.</p>
|
||||
<div class="card ~neutral !high font-mono text-sm">
|
||||
{% filter force_escape %}<noscript><img src="{{base_url}}/ingress/fc4008d3-f2fa-4500-9968-d96719e3819c/pixel.gif"></noscript><script src="{{base_url}}/ingress/fc4008d3-f2fa-4500-9968-d96719e3819c/identifier/script.js"></script>{% endfilter %}
|
||||
{% filter force_escape %}<noscript><img
|
||||
src="{{base_url}}/ingress/fc4008d3-f2fa-4500-9968-d96719e3819c/pixel.gif"></noscript>
|
||||
<script src="{{base_url}}/ingress/fc4008d3-f2fa-4500-9968-d96719e3819c/identifier/script.js"></script>
|
||||
{% endfilter %}
|
||||
</div>
|
||||
<hr class="sep h-4">
|
||||
<h5>Settings</h5>
|
||||
<form class="card ~neutral !low p-0" method="POST">
|
||||
{% csrf_token %}
|
||||
<div class="p-4">
|
||||
{{form|a17t}}
|
||||
</div>
|
||||
<div class="section ~neutral !normal p-4 flex justify-between">
|
||||
<div>
|
||||
<button type="submit" class="button ~neutral !high">Save</button>
|
||||
<a href="{% url 'core:service' object.uuid %}" class="button ~neutral !low">Cancel</a>
|
||||
</div>
|
||||
<div>
|
||||
<a href="{% url 'core:service_delete' object.uuid %}" class="button ~critical !high">Delete</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<hr class="sep">
|
||||
<form class="card ~neutral !low p-0 max-w-xl" method="POST">
|
||||
{% csrf_token %}
|
||||
<div class="p-4">
|
||||
{{form|a17t}}
|
||||
</div>
|
||||
<div class="section ~neutral !normal p-4 flex justify-between">
|
||||
<div>
|
||||
<button type="submit" class="button ~neutral !high">Save</button>
|
||||
<a href="{% url 'core:service' object.uuid %}" class="button ~neutral !low">Cancel</a>
|
||||
</div>
|
||||
<div>
|
||||
<a href="{% url 'core:service_delete' object.uuid %}" class="button ~critical !high">Delete</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
{% endblock %}
|
@ -1,9 +1,16 @@
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.shortcuts import get_object_or_404, reverse
|
||||
from django.utils import timezone
|
||||
from django.views.generic import (CreateView, DeleteView, DetailView, ListView,
|
||||
TemplateView, UpdateView)
|
||||
from django.views.generic import (
|
||||
CreateView,
|
||||
DeleteView,
|
||||
DetailView,
|
||||
ListView,
|
||||
TemplateView,
|
||||
UpdateView,
|
||||
)
|
||||
from rules.contrib.views import PermissionRequiredMixin
|
||||
from django.db.models import Q
|
||||
|
||||
from analytics.models import Session
|
||||
|
||||
@ -21,7 +28,9 @@ class DashboardView(LoginRequiredMixin, DateRangeMixin, TemplateView):
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
data = super().get_context_data(**kwargs)
|
||||
data["services"] = Service.objects.filter(owner=self.request.user)
|
||||
data["services"] = Service.objects.filter(
|
||||
Q(owner=self.request.user) | Q(collaborators__in=[self.request.user])
|
||||
)
|
||||
for service in data["services"]:
|
||||
service.stats = service.get_core_stats(data["start_date"], data["end_date"])
|
||||
return data
|
||||
@ -51,6 +60,11 @@ class ServiceView(
|
||||
def get_context_data(self, **kwargs):
|
||||
data = super().get_context_data(**kwargs)
|
||||
data["stats"] = self.object.get_core_stats(data["start_date"], data["end_date"])
|
||||
data["object_list"] = Session.objects.filter(
|
||||
service=self.get_object(),
|
||||
start_time__lt=self.get_end_date(),
|
||||
start_time__gt=self.get_start_date(),
|
||||
).order_by("-start_time")[:10]
|
||||
return data
|
||||
|
||||
|
||||
@ -92,7 +106,7 @@ class ServiceSessionsListView(
|
||||
service=self.get_object(),
|
||||
start_time__lt=self.get_end_date(),
|
||||
start_time__gt=self.get_start_date(),
|
||||
)
|
||||
).order_by("-start_time")
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
data = super().get_context_data(**kwargs)
|
||||
|
@ -26,9 +26,9 @@ BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
SECRET_KEY = "q@@928+gjkhmcdpuwse0awn@#ygm#0etg11jlny+b*^cm5m-x!"
|
||||
|
||||
# SECURITY WARNING: don't run with debug turned on in production!
|
||||
DEBUG = True
|
||||
DEBUG = os.getenv("DEBUG", "True") == "True"
|
||||
|
||||
ALLOWED_HOSTS = []
|
||||
ALLOWED_HOSTS = os.getenv("ALLOWED_HOSTS", "*").split(",")
|
||||
|
||||
|
||||
# Application definition
|
||||
@ -42,7 +42,7 @@ INSTALLED_APPS = [
|
||||
"django.contrib.staticfiles",
|
||||
"django.contrib.sites",
|
||||
"django.contrib.humanize",
|
||||
"rules",
|
||||
"rules.apps.AutodiscoverRulesConfig",
|
||||
"a17t",
|
||||
"core",
|
||||
"analytics",
|
||||
@ -168,6 +168,24 @@ MESSAGE_TAGS = {
|
||||
messages.SUCCESS: "~positive",
|
||||
}
|
||||
|
||||
# Email
|
||||
|
||||
SERVER_EMAIL = os.getenv("SERVER_EMAIL", "Shynet <noreply@shynet.example.com>")
|
||||
DEFAULT_FROM_EMAIL = SERVER_EMAIL
|
||||
|
||||
if DEBUG:
|
||||
EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"
|
||||
else:
|
||||
EMAIL_HOST = os.environ.get("EMAIL_HOST")
|
||||
EMAIL_PORT = int(os.environ.get("EMAIL_PORT", 465))
|
||||
EMAIL_HOST_USER = os.environ.get("EMAIL_HOST_USER")
|
||||
EMAIL_HOST_PASSWORD = os.environ.get("EMAIL_HOST_PASSWORD")
|
||||
EMAIL_USE_SSL = True
|
||||
|
||||
# Shynet
|
||||
|
||||
ONLY_SUPERUSERS_CREATE = True # Can everyone create services, or only superusers?
|
||||
ONLY_SUPERUSERS_CREATE = True
|
||||
# Can everyone create services, or only superusers?
|
||||
# Note that in the current version of Shynet, being able to edit a service allows
|
||||
# you to see every registered user on the Shynet instance. This will be changed in
|
||||
# a future version.
|
||||
|
Loading…
Reference in New Issue
Block a user