Compare commits
24 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dd6a9d1eaf | ||
|
|
3c74331a74 | ||
|
|
7bfcb1caff | ||
|
|
f7e8580114 | ||
|
|
25b7b1d0e5 | ||
|
|
2223530f51 | ||
|
|
62844db6bf | ||
|
|
3a63f6f850 | ||
|
|
2e386a7e25 | ||
|
|
db8dbb7723 | ||
|
|
1f13408f7f | ||
|
|
5c2838af27 | ||
|
|
20c530f669 | ||
|
|
17cdf052d8 | ||
|
|
e693406114 | ||
|
|
39ef4c9645 | ||
|
|
3f7aaa8f0d | ||
|
|
9881dedac0 | ||
|
|
7ef54175b0 | ||
|
|
96057fe02c | ||
|
|
64a3a1c3ad | ||
|
|
b94330b7c5 | ||
|
|
2879e4626e | ||
|
|
2d9209745e |
199
GUIDE.md
Normal file
199
GUIDE.md
Normal file
@@ -0,0 +1,199 @@
|
||||
# Getting Started
|
||||
|
||||
## Table of Contents
|
||||
|
||||
* [Installation](#installation)
|
||||
* [Basic Installation](#basic-installation)
|
||||
* [Installation with SSL](#installation-with-ssl)
|
||||
<!--
|
||||
* Usage
|
||||
* Adding Shynet tracking to your first website
|
||||
* Adding a new website
|
||||
* Adding a new administrator
|
||||
* Setting up a reverse proxy
|
||||
* Cloudflare
|
||||
* nginx
|
||||
-->
|
||||
|
||||
## Installation
|
||||
|
||||
Installation of Shynet is easy! Follow the [Basic Installation](#basic-installation) guide below if you'd like to run Shynet over HTTP or if you are going to be running it over HTTPS through a reverse proxy. If you'd like to run Shynet over HTTPS without a reverse proxy, skip ahead to [Installation with SSL](#installation-with-ssl) instead.
|
||||
|
||||
> **These commands assume Ubuntu.** If you're installing Shynet on a different platform, the process will be different.
|
||||
|
||||
Before continuing, please be sure to have the latest version of Docker installed.
|
||||
|
||||
### Basic Installation
|
||||
|
||||
1. Pull the latest version of Shynet using `docker pull milesmcc/shynet:latest`. If you don't have Docker installed, [install it](https://docs.docker.com/get-docker/).
|
||||
|
||||
2. Have a PostgreSQL server ready to go. This can be on the same machine as the deployment, or elsewhere. You'll just need a username, password, host, and port (default is `5432`). (For info on how to setup a PostgreSQL server on Ubuntu, follow [this guide](https://www.digitalocean.com/community/tutorials/how-to-install-and-use-postgresql-on-ubuntu-18-04)).
|
||||
|
||||
3. Configure an environment file for Shynet. (For example, create a file called `.env`.) Be sure to swap out the variables below with the correct values for your setup. (The comments refer to the lines that follow. Note that Docker is weird with quotes, so it tends to be better to omit them from your env file.)
|
||||
|
||||
```
|
||||
# Database
|
||||
DB_NAME=<your db name>
|
||||
DB_USER=<your db user>
|
||||
DB_PASSWORD=<your db user password>
|
||||
DB_HOST=<your db host>
|
||||
DB_PORT=<your db port>
|
||||
|
||||
# General Django settings
|
||||
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=*
|
||||
# Set to True (capitalized) if you want people to be able to sign up for your Shynet instance (not recommended)
|
||||
SIGNUPS_ENABLED=False
|
||||
# Change as required
|
||||
TIME_ZONE=America/New_York
|
||||
# Set to "False" if you will not be serving content over HTTPS
|
||||
SCRIPT_USE_HTTPS=True
|
||||
```
|
||||
|
||||
For more advanced deployments, you may consider adding the following settings to your environment file. **The following settings are optional, and not required for simple deployments.**
|
||||
|
||||
```env
|
||||
# Email settings
|
||||
EMAIL_HOST_USER=<your SMTP email user>
|
||||
EMAIL_HOST_PASSWORD=<your SMTP email password>
|
||||
EMAIL_HOST=<your SMTP email hostname>
|
||||
SERVER_EMAIL=Shynet <noreply@shynet.example.com>
|
||||
|
||||
# Redis and queue settings; not necessary for single-instance deployments
|
||||
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`.
|
||||
|
||||
5. Create your admin account by running `docker run --env-file=<your env file> milesmcc/shynet:latest python manage.py registeradmin <your email>`. The command will print a temporary password that you'll be able to use to log in.
|
||||
|
||||
6. Configure Shynet's hostname (e.g. `shynet.example.com` or `localhost:8000`) by running `docker run --env-file=<your env file> milesmcc/shynet:latest python manage.py hostname "<your hostname>"`. This doesn't affect Shynet's bind port; instead, it determines what hostname to inject into the tracking script. (So you'll want to use the "user-facing" hostname here.)
|
||||
|
||||
7. Name your Shynet instance by running `docker run --env-file=<your env file> milesmcc/shynet:latest python manage.py whitelabel "<your instance name>"`. This could be something like "My Shynet Server" or "Acme Analytics"—whatever suits you.
|
||||
|
||||
8. Launch the Shynet server by running `docker run --env-file=<your env file> milesmcc/shynet:latest`. You may need to bind Docker's port 8080 (where Shynet runs) to your local port 80 (http); this can be done using the flag `-p 80:8080` after `run`.
|
||||
|
||||
9. Visit your service's homepage, and verify everything looks right! You should see a login prompt. Log in with the credentials from step 5. You'll probably be prompted to "confirm your email"—if you haven't set up an email server, the confirmation email will be printed to the console instead.
|
||||
|
||||
10. Create a service by clicking "+ Create Service" in the top right hand corner. Fill out the options as appropriate. Once you're done, press "create" and you'll be redirected to your new service's analytics page.
|
||||
|
||||
11. Finally, click on "Manage" in the top right of the service's page to get the tracking script code. Inject this script on all pages you'd like the service to track.
|
||||
|
||||
### Installation with SSL
|
||||
|
||||
If you are going to be running Shynet through a reverse proxy, please use the [Basic Installation](#basic-installation) guide instead.
|
||||
|
||||
0. We'll be cloning this into the home directory to make this installation easier, so run `cd ~/` if you need to.
|
||||
|
||||
1. Instead of pulling from Docker, we will be pulling from GitHub and building in Docker so that we may easily add SSL certificates. You will want to run `git clone https://github.com/milesmcc/shynet.git` to clone the GitHub repo to your computer.
|
||||
|
||||
2. To install `certbot` follow [the guide here](https://certbot.eff.org/instructions) or follow along below
|
||||
* Ubuntu 18.04
|
||||
* `sudo apt-get update`
|
||||
* `sudo apt-get install software-properties-common`
|
||||
* `sudo add-apt-repository universe`
|
||||
* `sudo add-apt-repository ppa:certbot/certbot`
|
||||
* `sudo apt-get update`
|
||||
* `sudo apt-get install certbot`
|
||||
|
||||
3. Run `sudo certbot certonly --standalone` and follow the instructions to generate your SSL certificate.
|
||||
* If you registering it to a domain name like `example.com`, please be sure to point your DNS records to your server before running `certbot`.
|
||||
|
||||
4. We are going to move the SSL certificates to the Shynet repo with with command below. Replace `<domain>` with the domain name you used in step 3.
|
||||
* `cp /etc/letsencrypt/live/<domain>/{cert,privkey}.pem ~/shynet/shynet/`
|
||||
|
||||
5. With that, we are going to replace the `webserver.sh` with `ssl.webserver.sh` to enable the use of SSL certificates. The original `webserver.sh` will be backed up to `backup.webserver.sh`
|
||||
* `mv ~/shynet/shynet/webserver.sh ~/shynet/shynet/backup.webserver.sh`
|
||||
* `mv ~/shynet/shynet/ssl.webserver.sh ~/shynet/shynet/webserver.sh`
|
||||
|
||||
6. Now we build the image!
|
||||
* `docker image build shynet -t milesmcc/shynet:latest-ssl`
|
||||
|
||||
7. Have a PostgreSQL server ready to go. This can be on the same machine as the deployment, or elsewhere. You'll just need a username, password, host, and port (default is `5432`). (For info on how to setup a PostgreSQL server on Ubuntu, follow [this guide](https://www.digitalocean.com/community/tutorials/how-to-install-and-use-postgresql-on-ubuntu-18-04)).
|
||||
|
||||
8. Configure an environment file for Shynet. (For example, create a file called `.env`.) Be sure to swap out the variables below with the correct values for your setup. (The comments refer to the lines that follow. Note that Docker is weird with quotes, so it tends to be better to omit them from your env file.)
|
||||
|
||||
```
|
||||
# Database
|
||||
DB_NAME=<your db name>
|
||||
DB_USER=<your db user>
|
||||
DB_PASSWORD=<your db user password>
|
||||
DB_HOST=<your db host>
|
||||
DB_PORT=<your db port>
|
||||
|
||||
# General Django settings
|
||||
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=*
|
||||
# Set to True (capitalized) if you want people to be able to sign up for your Shynet instance (not recommended)
|
||||
SIGNUPS_ENABLED=False
|
||||
# Change as required
|
||||
TIME_ZONE=America/New_York
|
||||
# Set to "False" if you will not be serving content over HTTPS
|
||||
SCRIPT_USE_HTTPS=True
|
||||
```
|
||||
|
||||
For more advanced deployments, you may consider adding the following settings to your environment file. **The following settings are optional, and not required for simple deployments.**
|
||||
|
||||
```env
|
||||
# Email settings
|
||||
EMAIL_HOST_USER=<your SMTP email user>
|
||||
EMAIL_HOST_PASSWORD=<your SMTP email password>
|
||||
EMAIL_HOST=<your SMTP email hostname>
|
||||
SERVER_EMAIL=Shynet <noreply@shynet.example.com>
|
||||
|
||||
# Redis and queue settings; not necessary for single-instance deployments
|
||||
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`.
|
||||
|
||||
10. Create your admin account by running `docker run --env-file=<your env file> milesmcc/shynet:latest-ssl python manage.py registeradmin <your email>`. The command will print a temporary password that you'll be able to use to log in.
|
||||
|
||||
11. Configure Shynet's hostname (e.g. `shynet.example.com` or `localhost:8000`) by running `docker run --env-file=<your env file> milesmcc/shynet:latest-ssl python manage.py hostname "<your hostname>"`. This doesn't affect Shynet's bind port; instead, it determines what hostname to inject into the tracking script. (So you'll want to use the "user-facing" hostname here.)
|
||||
|
||||
12. Name your Shynet instance by running `docker run --env-file=<your env file> milesmcc/shynet:latest-ssl python manage.py whitelabel "<your instance name>"`. This could be something like "My Shynet Server" or "Acme Analytics"—whatever suits you.
|
||||
|
||||
13. Launch the Shynet server by running `docker run --env-file=<your env file> milesmcc/shynet:latest-ssl`. You may need to bind Docker's port 8080 (where Shynet runs) to your local port 443 (https); this can be done using the flag `-p 443:8080` after `run`.
|
||||
|
||||
14. Visit your service's homepage using `https://`, and verify everything looks right! You should see a login prompt. Log in with the credentials from step 10. You'll probably be prompted to "confirm your email"—if you haven't set up an email server, the confirmation email will be printed to the console instead.
|
||||
|
||||
15. Create a service by clicking "+ Create Service" in the top right hand corner. Fill out the options as appropriate. Once you're done, press "create" and you'll be redirected to your new service's analytics page.
|
||||
|
||||
16. Finally, click on "Manage" in the top right of the service's page to get the tracking script code. Inject this script on all pages you'd like the service to track.
|
||||
|
||||
---
|
||||
|
||||
**Next steps:** while out of the scope of this short guide, next steps include setting up Shynet behind a reverse proxy (be it your own [Nginx server](https://docs.nginx.com/nginx/admin-guide/web-server/reverse-proxy/) or [Cloudflare](https://cloudflare.com)), making it run in the background, and integrating it on your sites. Integration instructions are available on each service's management page.
|
||||
5
Pipfile
5
Pipfile
@@ -19,13 +19,10 @@ user-agents = "*"
|
||||
emoji-country-flag = "*"
|
||||
rules = "*"
|
||||
gunicorn = "*"
|
||||
psycopg2 = "*"
|
||||
psycopg2-binary = "*"
|
||||
redis = "*"
|
||||
django-redis-cache = "*"
|
||||
pycountry = "*"
|
||||
|
||||
[requires]
|
||||
python_version = "3.6"
|
||||
|
||||
[pipenv]
|
||||
allow_prereleases = true
|
||||
|
||||
66
Pipfile.lock
generated
66
Pipfile.lock
generated
@@ -1,12 +1,10 @@
|
||||
{
|
||||
"_meta": {
|
||||
"hash": {
|
||||
"sha256": "c7c9248fad70f86dda59b61f823f0992276c6cb9ee7ead05ae5719077c0c0c1f"
|
||||
"sha256": "2cd3e33ea333a40476d2030b6be66826e93c3a4de67032655061725835c92f09"
|
||||
},
|
||||
"pipfile-spec": 6,
|
||||
"requires": {
|
||||
"python_version": "3.6"
|
||||
},
|
||||
"requires": {},
|
||||
"sources": [
|
||||
{
|
||||
"name": "pypi",
|
||||
@@ -127,14 +125,6 @@
|
||||
],
|
||||
"version": "==2.9"
|
||||
},
|
||||
"importlib-metadata": {
|
||||
"hashes": [
|
||||
"sha256:2a688cbaa90e0cc587f1df48bdc97a6eadccdcd9c35fb3f976a09e3b5016d90f",
|
||||
"sha256:34513a8a0c4962bc66d35b359558fd8a5e10cd472d37aec5f66858addef32c1e"
|
||||
],
|
||||
"markers": "python_version < '3.8'",
|
||||
"version": "==1.6.0"
|
||||
},
|
||||
"kombu": {
|
||||
"hashes": [
|
||||
"sha256:2d1cda774126a044d91a7ff5fa6d09edf99f46924ab332a810760fe6740e9b76",
|
||||
@@ -155,21 +145,38 @@
|
||||
],
|
||||
"version": "==3.1.0"
|
||||
},
|
||||
"psycopg2": {
|
||||
"psycopg2-binary": {
|
||||
"hashes": [
|
||||
"sha256:132efc7ee46a763e68a815f4d26223d9c679953cd190f1f218187cb60decf535",
|
||||
"sha256:2327bf42c1744a434ed8ed0bbaa9168cac7ee5a22a9001f6fc85c33b8a4a14b7",
|
||||
"sha256:27c633f2d5db0fc27b51f1b08f410715b59fa3802987aec91aeb8f562724e95c",
|
||||
"sha256:2c0afb40cfb4d53487ee2ebe128649028c9a78d2476d14a67781e45dc287f080",
|
||||
"sha256:2df2bf1b87305bd95eb3ac666ee1f00a9c83d10927b8144e8e39644218f4cf81",
|
||||
"sha256:440a3ea2c955e89321a138eb7582aa1d22fe286c7d65e26a2c5411af0a88ae72",
|
||||
"sha256:6a471d4d2a6f14c97a882e8d3124869bc623f3df6177eefe02994ea41fd45b52",
|
||||
"sha256:6b306dae53ec7f4f67a10942cf8ac85de930ea90e9903e2df4001f69b7833f7e",
|
||||
"sha256:a0984ff49e176062fcdc8a5a2a670c9bb1704a2f69548bce8f8a7bad41c661bf",
|
||||
"sha256:ac5b23d0199c012ad91ed1bbb971b7666da651c6371529b1be8cbe2a7bf3c3a9",
|
||||
"sha256:acf56d564e443e3dea152efe972b1434058244298a94348fc518d6dd6a9fb0bb",
|
||||
"sha256:d3b29d717d39d3580efd760a9a46a7418408acebbb784717c90d708c9ed5f055",
|
||||
"sha256:f7d46240f7a1ae1dd95aab38bd74f7428d46531f69219954266d669da60c0818"
|
||||
"sha256:008da3ab51adc70a5f1cfbbe5db3a22607ab030eb44bcecf517ad11a0c2b3cac",
|
||||
"sha256:07cf82c870ec2d2ce94d18e70c13323c89f2f2a2628cbf1feee700630be2519a",
|
||||
"sha256:08507efbe532029adee21b8d4c999170a83760d38249936038bd0602327029b5",
|
||||
"sha256:107d9be3b614e52a192719c6bf32e8813030020ea1d1215daa86ded9a24d8b04",
|
||||
"sha256:17a0ea0b0eabf07035e5e0d520dabc7950aeb15a17c6d36128ba99b2721b25b1",
|
||||
"sha256:3286541b9d85a340ee4ed42732d15fc1bb441dc500c97243a768154ab8505bb5",
|
||||
"sha256:3939cf75fc89c5e9ed836e228c4a63604dff95ad19aed2bbf71d5d04c15ed5ce",
|
||||
"sha256:40abc319f7f26c042a11658bf3dd3b0b3bceccf883ec1c565d5c909a90204434",
|
||||
"sha256:51f7823f1b087d2020d8e8c9e6687473d3d239ba9afc162d9b2ab6e80b53f9f9",
|
||||
"sha256:6bb2dd006a46a4a4ce95201f836194eb6a1e863f69ee5bab506673e0ca767057",
|
||||
"sha256:702f09d8f77dc4794651f650828791af82f7c2efd8c91ae79e3d9fe4bb7d4c98",
|
||||
"sha256:7036ccf715925251fac969f4da9ad37e4b7e211b1e920860148a10c0de963522",
|
||||
"sha256:7b832d76cc65c092abd9505cc670c4e3421fd136fb6ea5b94efbe4c146572505",
|
||||
"sha256:8f74e631b67482d504d7e9cf364071fc5d54c28e79a093ff402d5f8f81e23bfa",
|
||||
"sha256:930315ac53dc65cbf52ab6b6d27422611f5fb461d763c531db229c7e1af6c0b3",
|
||||
"sha256:96d3038f5bd061401996614f65d27a4ecb62d843eb4f48e212e6d129171a721f",
|
||||
"sha256:a20299ee0ea2f9cca494396ac472d6e636745652a64a418b39522c120fd0a0a4",
|
||||
"sha256:a34826d6465c2e2bbe9d0605f944f19d2480589f89863ed5f091943be27c9de4",
|
||||
"sha256:a69970ee896e21db4c57e398646af9edc71c003bc52a3cc77fb150240fefd266",
|
||||
"sha256:b9a8b391c2b0321e0cd7ec6b4cfcc3dd6349347bd1207d48bcb752aa6c553a66",
|
||||
"sha256:ba13346ff6d3eb2dca0b6fa0d8a9d999eff3dcd9b55f3a890f12b0b6362b2b38",
|
||||
"sha256:bb0608694a91db1e230b4a314e8ed00ad07ed0c518f9a69b83af2717e31291a3",
|
||||
"sha256:c8830b7d5f16fd79d39b21e3d94f247219036b29b30c8270314c46bf8b732389",
|
||||
"sha256:cac918cd7c4c498a60f5d2a61d4f0a6091c2c9490d81bc805c963444032d0dab",
|
||||
"sha256:cc30cb900f42c8a246e2cb76539d9726f407330bc244ca7729c41a44e8d807fb",
|
||||
"sha256:ccdc6a87f32b491129ada4b87a43b1895cf2c20fdb7f98ad979647506ffc41b6",
|
||||
"sha256:d1a8b01f6a964fec702d6b6dac1f91f2b9f9fe41b310cbb16c7ef1fac82df06d",
|
||||
"sha256:e004db88e5a75e5fdab1620fb9f90c9598c2a195a594225ac4ed2a6f1c23e162",
|
||||
"sha256:eb2f43ae3037f1ef5e19339c41cf56947021ac892f668765cd65f8ab9814192e",
|
||||
"sha256:fa466306fcf6b39b8a61d003123d442b23707d635a5cb05ac4e1b62cc79105cd"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==2.8.5"
|
||||
@@ -292,13 +299,6 @@
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==5.0.1"
|
||||
},
|
||||
"zipp": {
|
||||
"hashes": [
|
||||
"sha256:aa36550ff0c0b7ef7fa639055d797116ee891440eac1a56f378e2d3179e0320b",
|
||||
"sha256:c599e4d75c98f6798c509911d08a22e6c021d074469042177c8c86fb92eefd96"
|
||||
],
|
||||
"version": "==3.1.0"
|
||||
}
|
||||
},
|
||||
"develop": {
|
||||
|
||||
78
README.md
78
README.md
@@ -1,15 +1,18 @@
|
||||
|
||||
<p align="center">
|
||||
<h3 align="center">🔭 Shynet 🔭</h3>
|
||||
<img align="center" src="images/logo.png" height="50" alt="Shynet logo">
|
||||
<br>
|
||||
|
||||
<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>
|
||||
Modern, privacy-friendly, and cookie-free web analytics.
|
||||
<br>
|
||||
<strong><a href="#installation">Getting started »</a></strong>
|
||||
</p>
|
||||
<p align="center"><a href="#screenshots">Screenshots</a> • <a href="#features">Features</a> • <a href="https://github.com/milesmcc/a17t">Design</a></p>
|
||||
</p>
|
||||
|
||||
<br>
|
||||
|
||||
## Motivation
|
||||
|
||||
There are a _lot_ of web analytics tools. Unfortunately, most of them come with the following caveats:
|
||||
@@ -31,16 +34,21 @@ _Note: These screenshots have been edited to hide sensitive data. The "real" Shy
|
||||

|
||||
_Shynet's homepage, where you can see all of your services at a glance._
|
||||
|
||||
Not shown: service view, management view, session view, full service view. (You'll need to install Shynet to see those!)
|
||||

|
||||
_A real service page, where you can see higher-level details about a site._
|
||||
|
||||
Not shown: management view, session view, full service view. (You'll need to install Shynet for yourself to see those!)
|
||||
|
||||
> **Shynet is built using [a17t](https://github.com/milesmcc/a17t),** an atomic design library. Customization and extension is simple; [learn more about a17t](https://github.com/milesmcc/a17t).
|
||||
|
||||
## Features
|
||||
|
||||
#### Architecture
|
||||
|
||||
* **Runs on a single machine** — Because it's so small, Shynet can easily run as a single docker container on a single small VPS.
|
||||
* **...or across a giant Kubernetes cluster** — For higher traffic installations, Shynet can be deployed with as many parallelized ingress nodes as needed, with Redis caching and separate backend workers for database IO.
|
||||
* **Built using Django** — Shynet is built using Django, so deploying, updating, and migrating can be done without headaches.
|
||||
* **Multiple users and sites** — A single Shynet instance can support multiple users, each tracking multiple different sites.
|
||||
* **Runs on a single machine** — Because it's so small, Shynet can easily run as a single docker container on a single small VPS
|
||||
* **...or across a giant Kubernetes cluster** — For higher traffic installations, Shynet can be deployed with as many parallelized ingress nodes as needed, with Redis caching and separate backend workers for database IO
|
||||
* **Built using Django** — Shynet is built using Django, so deploying, updating, and migrating can be done without headaches
|
||||
* **Multiple users and sites** — A single Shynet instance can support multiple users, each tracking multiple different sites
|
||||
|
||||
#### Tracking
|
||||
|
||||
@@ -67,7 +75,7 @@ Here's the information Shynet can give you about your visitors:
|
||||
|
||||
#### Workflow
|
||||
* **Collaboration built-in** — Administrators can easily share services with other users, as well
|
||||
* **Accounts (or not)** — Shynet has a fully featured account management workflow (powered by [Django Allauth](https://github.com/pennersr/django-allauth/)).
|
||||
* **Accounts (or not)** — Shynet has a fully featured account management workflow (powered by [Django Allauth](https://github.com/pennersr/django-allauth/))
|
||||
|
||||
## Recommendations
|
||||
|
||||
@@ -85,55 +93,13 @@ Shynet is pretty simple, but there are a few key terms you need to know in order
|
||||
|
||||
## Installation
|
||||
|
||||
To install Shynet using the simplest possible setup, follow these instructions. Instructions for multi-machine deployments will be available soon.
|
||||
|
||||
> **These commands assume Ubuntu.** If you're installing Shynet on a different platform, the process will be different.
|
||||
|
||||
1. Pull the latest version of Shynet using `docker pull milesmcc/shynet:latest`. If you don't have Docker installed, [install it](https://docs.docker.com/get-docker/).
|
||||
|
||||
2. Have a PostgreSQL server ready to go. This can be on the same machine as the deployment, or elsewhere. You'll just need a username, password, and host. (For info on how to setup a PostgreSQL server on Ubuntu, follow [this guide](https://www.digitalocean.com/community/tutorials/how-to-install-and-use-postgresql-on-ubuntu-18-04)).
|
||||
|
||||
3. Configure an environment file for Shynet. Be sure to swap out the variables below with the correct values for your setup.
|
||||
|
||||
```env
|
||||
DEBUG=False # Don't leak error details to visitors
|
||||
DB_NAME=<your db name>
|
||||
DB_USER=<your db user>
|
||||
DB_PASSWORD=<your db user password>
|
||||
DB_HOST=<your db host>
|
||||
DJANGO_SECRET_KEY=<your Django secret key; just a random string>
|
||||
CELERY_TASK_ALWAYS_EAGER=True # Perform background processes on the main machine; this is what you want for now
|
||||
ALLOWED_HOSTS="*" # For better security, set this to your deployment's domain. Comma separated.
|
||||
SIGNUPS_ENABLED=False # Set to True (capitalized) if you want people to be able to sign up for your Shynet instance (not recommended)
|
||||
TIME_ZONE="America/New_York" # Change as required
|
||||
|
||||
# The following settings are OPTIONAL and not necessary for most basic deployments
|
||||
REDIS_CACHE_LOCATION="redis://redis.default.svc.cluster.local/0"
|
||||
CELERY_BROKER_URL="redis://redis.default.svc.cluster.local/1" # If set, make sure CELERY_TASK_ALWAYS_EAGER is False
|
||||
EMAIL_HOST_USER=""
|
||||
EMAIL_HOST_PASSWORD=""
|
||||
EMAIL_HOST=""
|
||||
SERVER_EMAIL="Shynet <noreply@shynet.example.com>"
|
||||
```
|
||||
|
||||
4. Setup the Shynet database by running `docker run --env-file=<your env file> milesmcc/shynet:latest python manage.py migrate`.
|
||||
|
||||
5. Create your admin account by running `docker run --env-file=<your env file> milesmcc/shynet:latest python manage.py createsuperuser`.
|
||||
|
||||
6. Launch the Shynet server by running `docker run --env-file=<your env file> milesmcc/shynet:latest`.
|
||||
|
||||
7. With the server still running, visit it in a web browser. Go to `http://<your server's location>/admin` and log in with the credentials you setup in step 5. Click on "Sites", then "example.com". Update the values to match your deployment (the domain will be the domain where you'll _eventually_ host Shynet from, and the display name will be used to whitelabel Shynet throughout the management interface). When you're ready, click "Save".
|
||||
|
||||
8. Visit your service's homepage, and verify everything looks right!
|
||||
|
||||
**Next steps:** while out of the scope of this short guide, next steps include setting up Shynet behind a reverse proxy (be it your own [Nginx server](https://docs.nginx.com/nginx/admin-guide/web-server/reverse-proxy/) or [Cloudflare](https://cloudflare.com)), making it run in the background, and integrating it on your sites. Integration instructions are available on each service's management page.
|
||||
|
||||
You can find installation instructions in the [Getting Started Guide](GUIDE.md#installation).
|
||||
|
||||
## FAQ
|
||||
|
||||
**Does Shynet respond to Do Not Track (DNT) signals?** Yes. While there isn't any standardized way to handle DNT requests, Shynet allows you to specify whether you want to collect any data from users with DNT enabled on a per-service basis. (By default, Shynet will _not_ collect any data from users who specify DNT.)
|
||||
|
||||
**Is this GDPR compliant?** I think so, but it also depends on how you use it. If you're worried about GDPR, you should talk to a lawyer about your particular data collection practices. I'm not a lawyer. (And this isn't legal advice.)
|
||||
**Is this GDPR compliant?** It depends on how you use it. If you're worried about GDPR, you should talk to a lawyer about your particular data collection practices. I'm not a lawyer. (And this isn't legal advice.)
|
||||
|
||||
## Roadmap
|
||||
|
||||
@@ -160,4 +126,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 |
BIN
images/service.png
Normal file
BIN
images/service.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 589 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>
|
||||
@@ -42,7 +42,15 @@ def _geoip2_lookup(ip):
|
||||
|
||||
@shared_task
|
||||
def ingress_request(
|
||||
service_uuid, tracker, time, payload, ip, location, user_agent, dnt=False, identifier=""
|
||||
service_uuid,
|
||||
tracker,
|
||||
time,
|
||||
payload,
|
||||
ip,
|
||||
location,
|
||||
user_agent,
|
||||
dnt=False,
|
||||
identifier="",
|
||||
):
|
||||
try:
|
||||
service = Service.objects.get(pk=service_uuid, status=Service.ACTIVE)
|
||||
@@ -78,7 +86,8 @@ def ingress_request(
|
||||
if (
|
||||
ua.is_bot
|
||||
or (ua.browser.family or "").strip().lower() == "googlebot"
|
||||
or (ua.device.family or ua.device.model or "").strip().lower() == "spider"
|
||||
or (ua.device.family or ua.device.model or "").strip().lower()
|
||||
== "spider"
|
||||
):
|
||||
device_type = "ROBOT"
|
||||
elif ua.is_mobile:
|
||||
|
||||
@@ -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();
|
||||
};
|
||||
|
||||
@@ -2,6 +2,7 @@ import base64
|
||||
import json
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.cache import cache
|
||||
from django.http import HttpResponse
|
||||
from django.shortcuts import render, reverse
|
||||
from django.utils import timezone
|
||||
@@ -10,6 +11,8 @@ from django.views.decorators.csrf import csrf_exempt
|
||||
from django.views.generic import TemplateView, View
|
||||
from ipware import get_client_ip
|
||||
|
||||
from core.models import Service
|
||||
|
||||
from ..tasks import ingress_request
|
||||
|
||||
|
||||
@@ -58,8 +61,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 +92,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",
|
||||
)
|
||||
|
||||
|
||||
0
shynet/core/management/commands/__init__.py
Normal file
0
shynet/core/management/commands/__init__.py
Normal file
28
shynet/core/management/commands/hostname.py
Normal file
28
shynet/core/management/commands/hostname.py
Normal file
@@ -0,0 +1,28 @@
|
||||
import traceback
|
||||
import uuid
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.sites.models import Site
|
||||
from django.core.management.base import BaseCommand, CommandError
|
||||
from django.utils.crypto import get_random_string
|
||||
|
||||
from core.models import User
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = "Configures the Shynet hostname"
|
||||
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument(
|
||||
"hostname", type=str,
|
||||
)
|
||||
|
||||
def handle(self, *args, **options):
|
||||
site = Site.objects.get(pk=settings.SITE_ID)
|
||||
site.domain = options.get("hostname")
|
||||
site.save()
|
||||
self.stdout.write(
|
||||
self.style.SUCCESS(
|
||||
f"Successfully set the hostname to '{options.get('hostname')}'"
|
||||
)
|
||||
)
|
||||
26
shynet/core/management/commands/registeradmin.py
Normal file
26
shynet/core/management/commands/registeradmin.py
Normal file
@@ -0,0 +1,26 @@
|
||||
import traceback
|
||||
import uuid
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.sites.models import Site
|
||||
from django.core.management.base import BaseCommand, CommandError
|
||||
from django.utils.crypto import get_random_string
|
||||
|
||||
from core.models import User
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = "Creates an admin user with an auto-generated password"
|
||||
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument(
|
||||
"email", type=str,
|
||||
)
|
||||
|
||||
def handle(self, *args, **options):
|
||||
email = options.get("email")
|
||||
password = get_random_string()
|
||||
User.objects.create_superuser(str(uuid.uuid4()), email=email, password=password)
|
||||
self.stdout.write(self.style.SUCCESS("Successfully created a Shynet superuser"))
|
||||
self.stdout.write(f"Email address: {email}")
|
||||
self.stdout.write(f"Password: {password}")
|
||||
28
shynet/core/management/commands/whitelabel.py
Normal file
28
shynet/core/management/commands/whitelabel.py
Normal file
@@ -0,0 +1,28 @@
|
||||
import traceback
|
||||
import uuid
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.sites.models import Site
|
||||
from django.core.management.base import BaseCommand, CommandError
|
||||
from django.utils.crypto import get_random_string
|
||||
|
||||
from core.models import User
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = "Configures a Shynet whitelabel"
|
||||
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument(
|
||||
"name", type=str,
|
||||
)
|
||||
|
||||
def handle(self, *args, **options):
|
||||
site = Site.objects.get(pk=settings.SITE_ID)
|
||||
site.name = options.get("name")
|
||||
site.save()
|
||||
self.stdout.write(
|
||||
self.style.SUCCESS(
|
||||
f"Successfully set the whitelabel to '{options.get('name')}'"
|
||||
)
|
||||
)
|
||||
@@ -6,13 +6,13 @@ from django.db import migrations, models
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('core', '0002_auto_20200415_1742'),
|
||||
("core", "0002_auto_20200415_1742"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='service',
|
||||
name='respect_dnt',
|
||||
model_name="service",
|
||||
name="respect_dnt",
|
||||
field=models.BooleanField(default=True),
|
||||
),
|
||||
]
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
from allauth.account.admin import EmailAddress
|
||||
from django import forms
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from core.models import Service, User
|
||||
from allauth.account.admin import EmailAddress
|
||||
|
||||
|
||||
class ServiceForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = Service
|
||||
fields = ["name", "link", "origins", "respect_dnt", "collaborators"]
|
||||
fields = ["name", "link", "respect_dnt", "origins", "collaborators"]
|
||||
widgets = {
|
||||
"name": forms.TextInput(),
|
||||
"origins": forms.TextInput(),
|
||||
@@ -27,8 +27,10 @@ class ServiceForm(forms.ModelForm):
|
||||
"respect_dnt": "Should visitors who have enabled <a href='https://en.wikipedia.org/wiki/Do_Not_Track'>Do Not Track</a> be excluded from all data?",
|
||||
}
|
||||
|
||||
collaborators = forms.CharField(help_text="Which users should have read-only access to this service? (Comma separated list of emails.)", required=False)
|
||||
|
||||
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 = []
|
||||
@@ -36,7 +38,9 @@ class ServiceForm(forms.ModelForm):
|
||||
email = collaborator_email.strip()
|
||||
if email == "":
|
||||
continue
|
||||
collaborator_email_linked = EmailAddress.objects.filter(email__iexact=email).first()
|
||||
collaborator_email_linked = EmailAddress.objects.filter(
|
||||
email__iexact=email
|
||||
).first()
|
||||
if collaborator_email_linked is None:
|
||||
raise forms.ValidationError(f"Email '{email}' is not registered")
|
||||
collaborators.append(collaborator_email_linked.user)
|
||||
@@ -45,5 +49,5 @@ class ServiceForm(forms.ModelForm):
|
||||
def get_initial_for_field(self, field, field_name):
|
||||
initial = super(ServiceForm, self).get_initial_for_field(field, field_name)
|
||||
if field_name == "collaborators":
|
||||
return ", ".join([user.email for user in initial])
|
||||
return ", ".join([user.email for user in (initial or [])])
|
||||
return initial
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from datetime import datetime, time
|
||||
from urllib.parse import urlparse
|
||||
|
||||
from datetime import time, datetime
|
||||
from django.utils import timezone
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{% load static rules %}
|
||||
{% load static rules helpers %}
|
||||
<!DOCTYPE html>
|
||||
|
||||
<html>
|
||||
@@ -20,8 +20,8 @@
|
||||
<body class="bg-gray-200 min-h-full">
|
||||
{% block body %}
|
||||
|
||||
<section class="max-w-screen-xl mx-auto px-4 md:px-0 py-4 md:py-12 md:flex">
|
||||
<aside class="mb-8 md:w-2/12 md:pr-6 relative flex flex-wrap md:block justify-between items-center">
|
||||
<section class="max-w-screen-xl mx-auto px-4 py-4 md:py-12 md:flex">
|
||||
<aside class="mb-8 md:w-2/12 md:pr-6 relative flex flex-wrap md:block justify-between items-center overflow-x-hidden">
|
||||
<a class="icon ~urge ml-2 md:ml-6 md:mb-8 md:mt-3" href="{% url 'dashboard:dashboard' %}">
|
||||
<i class="fas fa-binoculars fa-3x text-purple-600 hidden md:block"></i>
|
||||
<i class="fas fa-binoculars fa-2x text-purple-600 md:hidden"></i>
|
||||
@@ -93,6 +93,10 @@
|
||||
{% include 'dashboard/includes/sidebar_portal.html' with label="Sign Up" url=url %}
|
||||
|
||||
{% endif %}
|
||||
|
||||
<hr class="sep h-8">
|
||||
|
||||
{% sidebar_footer %}
|
||||
</div>
|
||||
</aside>
|
||||
<div class="md:w-10/12">
|
||||
|
||||
@@ -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>
|
||||
@@ -0,0 +1,5 @@
|
||||
<div class="pl-2">
|
||||
<p class="support text-gray-600">
|
||||
<a href="https://github.com/milesmcc/shynet">Shynet {{version}}</a>
|
||||
</p>
|
||||
</div>
|
||||
@@ -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>
|
||||
|
||||
@@ -59,8 +59,8 @@
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<p>Time Zone</p>
|
||||
<p class="label">{{session.time_zone|default:"Unknown"}}</p>
|
||||
<p>IP</p>
|
||||
<p class="label" title="{{session.ip}}">{{session.ip|truncatechars:"16"}}</p>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
from urllib.parse import urlparse
|
||||
import pycountry
|
||||
|
||||
import flag
|
||||
import pycountry
|
||||
from django import template
|
||||
from django.conf import settings
|
||||
from django.utils import timezone
|
||||
from django.utils.html import escape
|
||||
from django.utils.safestring import SafeString
|
||||
@@ -41,17 +43,12 @@ def country_name(isocode):
|
||||
|
||||
@register.simple_tag
|
||||
def relative_stat_tone(
|
||||
start,
|
||||
end,
|
||||
good="UP",
|
||||
good_classes=None,
|
||||
bad_classes=None,
|
||||
neutral_classes=None,
|
||||
start, end, good="UP", good_classes=None, bad_classes=None, neutral_classes=None,
|
||||
):
|
||||
good_classes = good_classes or "~positive"
|
||||
bad_classes = bad_classes or "~critical"
|
||||
neutral_classes = neutral_classes or "~neutral"
|
||||
|
||||
|
||||
if start == None or end == None or start == end:
|
||||
return neutral_classes
|
||||
if good == "UP":
|
||||
@@ -84,6 +81,11 @@ def percent_change_display(start, end):
|
||||
|
||||
return SafeString(direction + pct_change)
|
||||
|
||||
@register.inclusion_tag("dashboard/includes/sidebar_footer.html")
|
||||
def sidebar_footer():
|
||||
return {
|
||||
"version": settings.VERSION
|
||||
}
|
||||
|
||||
@register.inclusion_tag("dashboard/includes/stat_comparison.html")
|
||||
def compare(
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.contrib.messages.views import SuccessMessageMixin
|
||||
from django.core.cache import cache
|
||||
from django.db.models import Q
|
||||
from django.shortcuts import get_object_or_404, reverse
|
||||
from django.utils import timezone
|
||||
@@ -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
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
"""
|
||||
Django settings for shynet project.
|
||||
|
||||
Generated by 'django-admin startproject' using Django 2.2.5.
|
||||
Django settings for Shynet.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/2.2/topics/settings/
|
||||
@@ -15,6 +13,9 @@ import os
|
||||
# Messages
|
||||
from django.contrib.messages import constants as messages
|
||||
|
||||
# Increment on new releases
|
||||
VERSION = "v0.2.2"
|
||||
|
||||
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
|
||||
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
|
||||
@@ -256,3 +257,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"))
|
||||
|
||||
10
shynet/ssl.webserver.sh
Normal file
10
shynet/ssl.webserver.sh
Normal file
@@ -0,0 +1,10 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Start Gunicorn processes
|
||||
echo Launching Shynet web server...
|
||||
exec gunicorn shynet.wsgi:application \
|
||||
--bind 0.0.0.0:8080 \
|
||||
--workers 3 \
|
||||
--timeout 100 \
|
||||
--certfile=cert.pem \
|
||||
--keyfile=privkey.pem
|
||||
@@ -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>
|
||||
Reference in New Issue
Block a user