Compare commits

..

21 Commits

Author SHA1 Message Date
R. Miles McCain
c718d49c54 Use Alpine 3.14 2021-12-31 13:26:44 -05:00
R. Miles McCain
04f095b4fb Use new a17t and Tailwind 2021-12-31 13:25:10 -05:00
R. Miles McCain
fc38fd88bd Use new a17t/Tailwind 2021-12-31 13:02:16 -05:00
R. Miles McCain
7d92a557f4 Use latest alpine 2021-12-31 12:10:19 -05:00
R. Miles McCain
e12848b094 Lessen priorities on field buttons 2021-12-21 00:58:11 -05:00
R. Miles McCain
e534269c77 Provide initial contributing guide 2021-12-21 00:23:29 -05:00
R. Miles McCain
0d64ef33b0 Remove installation with SSL from guide TOC 2021-12-21 00:15:15 -05:00
R. Miles McCain
56c82e7d23 Remove HTTPS without reverse proxy from the GUIDE 2021-12-21 00:14:18 -05:00
R. Miles McCain
c71d934c67 Remove armv7 support 2021-12-21 00:10:12 -05:00
R. Miles McCain
85ae56fcdb Add swap space to GitHub Actions runners 2021-12-18 15:34:11 -05:00
R. Miles McCain
cd422ffd71 Add note about ALLOWED_HOSTS to GUIDE.md 2021-12-18 15:14:11 -05:00
R. Miles McCain
060a9b2a96 Update Python dependencies 2021-12-18 14:38:31 -05:00
R. Miles McCain
8d13ccd0fd Update dependencies 2021-12-18 13:56:57 -05:00
lionep
0d46e6d1f4 Fix typo in GUIDE.md (#180) 2021-12-04 09:50:26 -08:00
R. Miles McCain
81ae84efb3 Add office hours link 2021-11-21 00:12:24 -08:00
R. Miles McCain
ea5f58fbd3 Update lock 2021-11-13 21:11:52 -08:00
dependabot[bot]
4079a8494a Bump sqlparse from 0.4.1 to 0.4.2 (#178)
Bumps [sqlparse](https://github.com/andialbrecht/sqlparse) from 0.4.1 to 0.4.2.
- [Release notes](https://github.com/andialbrecht/sqlparse/releases)
- [Changelog](https://github.com/andialbrecht/sqlparse/blob/master/CHANGELOG)
- [Commits](https://github.com/andialbrecht/sqlparse/compare/0.4.1...0.4.2)

---
updated-dependencies:
- dependency-name: sqlparse
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-13 21:10:06 -08:00
Sérgio
780b71083a Add first factories and first dashboard tests (#172)
* Add factories and first dashboard tests

* Code cleanup

Co-authored-by: R. Miles McCain <github@sendmiles.email>
2021-11-13 21:09:55 -08:00
Sérgio
62fbb014e7 Testing setup (dependencies and github actions) (#169)
* Add testing dependencies

* Add draft github action

* Fix testing env variables

* Newline lint

* Run tests on every pull request and push
2021-11-13 21:03:06 -08:00
JuniorJPDJ
53bc690435 fix(docker): healthcheck (#175)
fixes healthcheck for $ALLOWED_HOSTS longer than 2 domains
2021-09-30 16:12:57 -07:00
JuniorJPDJ
04120323a6 feat(docker): healthcheck (#166) 2021-09-16 13:35:38 -07:00
47 changed files with 3136 additions and 484 deletions

View File

@@ -9,6 +9,11 @@ jobs:
publish_to_docker_hub:
runs-on: ubuntu-latest
steps:
- name: Set swap space
uses: pierotofy/set-swap-space@master
with:
swap-size-gb: 5
- name: Checkout code
uses: actions/checkout@v2
@@ -38,6 +43,6 @@ jobs:
with:
context: .
file: ./Dockerfile
platforms: linux/amd64,linux/arm64,linux/arm/v7
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.prep.outputs.tags }}

View File

@@ -9,6 +9,11 @@ jobs:
publish_to_docker_hub:
runs-on: ubuntu-latest
steps:
- name: Set swap space
uses: pierotofy/set-swap-space@master
with:
swap-size-gb: 5
- name: Checkout code
uses: actions/checkout@v2
@@ -38,6 +43,6 @@ jobs:
with:
context: .
file: ./Dockerfile
platforms: linux/amd64,linux/arm64,linux/arm/v7
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.prep.outputs.tags }}

View File

@@ -9,6 +9,11 @@ jobs:
publish_to_docker_hub:
runs-on: ubuntu-latest
steps:
- name: Set swap space
uses: pierotofy/set-swap-space@master
with:
swap-size-gb: 5
- name: Checkout code
uses: actions/checkout@v2
@@ -39,6 +44,6 @@ jobs:
with:
context: .
file: ./Dockerfile
platforms: linux/amd64,linux/arm64,linux/arm/v7
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.prep.outputs.tags }}

37
.github/workflows/run-tests.yml vendored Normal file
View File

@@ -0,0 +1,37 @@
name: Run tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
services:
db:
image: postgres:12.3-alpine
env:
POSTGRES_USER: shynet_db_user
POSTGRES_PASSWORD: shynet_db_user_password
POSTGRES_DB: shynet_db
ports:
- 5432:5432
strategy:
max-parallel: 4
matrix:
python-version: [3.9]
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- name: Run image
uses: abatilo/actions-poetry@v2.0.0
with:
poetry-version: 1.1.6
- name: Install dependencies
run: poetry install
- name: Django Testing project
run: |
cp TEMPLATE.env .env
poetry run ./shynet/manage.py test

15
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,15 @@
# Contributing
This document provides an overview of how to contribute to Shynet. Currently, it focuses on the more technical elements of contributing --- for example, setting up your development environment. Eventually, we will expand this guide to cover the social and governance oriented side of contributing as well.
## Setting up your development environment
To contribute to Shynet, you must have a reliable development environment. Because Shynet is intended to be run inside containers, we strongly encourage you to run Shynet in a container in development as well. The development setup described in this guide will use Docker and Docker Compose.
To begin, clone the Shynet repository to your computer, and ensure that you have Docker and Docker Compose installed.
Copy `TEMPLATE.env` to a new file called `.env`. This `.env` file will be used in your development environment. Paste `DEBUG=True` into the end of your new `.env` file so that Shynet will know to run in development mode.
Finally, follow the steps in [GUIDE.md](GUIDE.md) on setting up a Shynet instance with Docker Compose. This is where you'll setup an admin user.
_Did you have to perform additional steps to setup your environment? Document them here and submit a pull request!_

View File

@@ -41,10 +41,15 @@ RUN apk --purge del .build-deps && \
# Install Shynet
COPY shynet .
# Build Tailwind CSS and build static files
COPY tailwind.config.js .
RUN npx tailwindcss -i ./a17t/static/a17t/css/tailwind.css -o ./a17t/static/a17t/dist/tailwind.css --jit --minify
RUN python manage.py collectstatic --noinput && \
python manage.py compilemessages
# Launch
USER appuser
EXPOSE 8080
HEALTHCHECK CMD bash -c 'wget -o /dev/null -O /dev/null --header "Host: ${ALLOWED_HOSTS%%,*}" "http://127.0.0.1:$PORT/healthz/?format=json"'
CMD [ "./entrypoint.sh" ]

View File

@@ -7,7 +7,6 @@
- [Render](#render)
- [Updating Your Configuration](#updating-your-configuration)
- [Advanced Usage](#advanced-usage)
* [Installation with SSL](#installation-with-ssl)
* [Configuring a Reverse Proxy](#configuring-a-reverse-proxy)
+ [Cloudflare](#cloudflare)
+ [Nginx](#nginx)
@@ -23,7 +22,7 @@
## Installation
Installation of Shynet is easy! Follow the [Basic Installation](#basic-installation) guide or the [Basic Installation with Docker Compose](#basic-installation-with-docker-compose) below for a minimal installation, or if you are going to be running Shynet 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.
Installation of Shynet is easy! Follow the [Basic Installation](#basic-installation) guide or the [Basic Installation with Docker Compose](#basic-installation-with-docker-compose) below for a minimal installation, or if you are going to be running Shynet over HTTPS through a reverse proxy.
> **These commands assume Ubuntu.** If you're installing Shynet on a different platform, the process will be different.
@@ -35,7 +34,7 @@ Before continuing, please be sure to have the latest version of Docker installed
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. (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, using [this file](/TEMPLATE.env) as a template. (This file is typically named `.env`.) Make sure you set the database settings, or Shynet won't be able to run.
3. Configure an environment file for Shynet, using [this file](/TEMPLATE.env) as a template. (This file is typically named `.env`.) Make sure you set the database settings, or Shynet won't be able to run. Also consider setting `ALLOWED_HOSTS` inside the environment file to your deployment's domain for better security.
4. Launch the Shynet server for the first time by running `docker run --env-file=<your env file> milesmcc/shynet:latest`. Provided you're using the default environment information (i.e., `PERFORM_CHECKS_AND_SETUP` is `True`), you'll see a few warnings about not having an admin user or host setup; these are normal. Don't worry — we'll do this in the next step. You only need to stop if you see a stacktrace about being unable to connect to the database.
@@ -56,7 +55,7 @@ Before continuing, please be sure to have the latest version of Docker installed
1. Clone the repository.
2. Using [TEMPLATE.env](/TEMPLATE.env) as a template, confiure the environment for your Shynet instance and place the modified config in a file called `.env` in the root of the repository. Do _not_ change the port number at the end; you can set the public facing port in the next step.
2. Using [TEMPLATE.env](/TEMPLATE.env) as a template, configure the environment for your Shynet instance and place the modified config in a file called `.env` in the root of the repository. Do _not_ change the port number at the end; you can set the public facing port in the next step.
3. On line 2 of the `nginx.conf` file located in the root of the repository, replace `example.com` with your hostname. Then, in the `docker-compose.yml` file, set the port number by replacing `8080` in line 38 ( `- 8080:80` ) with whatever local port you want to bind it to. For example, set the port number to `- 80:80` if you want your site will be available via HTTP (port 80) at `http://<your hostname>`.
@@ -96,40 +95,6 @@ See the [Render docs](https://render.com/docs/deploy-shynet) for more informatio
## Advanced Usage
### Installation with SSL
If you are going to be running Shynet through a reverse proxy, please see [Configuring a Reverse Proxy](#configuring-a-reverse-proxy) 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 using Docker in order to easily add SSL certificates. You will want to run `git clone https://github.com/milesmcc/shynet.git` to clone the GitHub repo to your current working directory.
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 the certificate to a domain name like `example.com`, please be sure to point your DNS records to your current 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 shynet-ssl:latest`
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. Follow the [Basic Installation](#basic-installation) guide with just one modification: in step #4, change the local bind port from `80` to `443`, and use `shynet-ssl:latest` as your Docker image instead of `milesmcc/shynet:latest`.
### Configuring a Reverse Proxy
A reverse proxy has many benefits. It can be used for DDoS protection, caching files to reduce server load, routing HTTPS and/or HTTP connections, hosting multiple services on a single server, [and more](https://www.cloudflare.com/learning/cdn/glossary/reverse-proxy/)!

View File

@@ -8,7 +8,7 @@
<br>
<strong><a href="#installation">Getting started »</a></strong>
</p>
<p align="center"><a href="#screenshots">Screenshots</a> &bull; <a href="#features">Features</a> &bull; <a href="https://github.com/milesmcc/a17t">Design</a></p>
<p align="center"><a href="#screenshots">Screenshots</a> &bull; <a href="#features">Features</a> &bull; <a href="https://miles.land/officehours/">Office Hours</a></p>
</p>
<br>

46
docker-compose.dev.yml Normal file
View File

@@ -0,0 +1,46 @@
version: '3'
services:
shynet:
container_name: shynet_main
build: .
restart: unless-stopped
expose:
- 8080
env_file:
# Create a file called '.env' if it doesn't already exist.
# You can use `TEMPLATE.env` as a guide.
- .env
environment:
- DB_HOST=db
networks:
- internal
depends_on:
- db
db:
container_name: shynet_database
image: postgres
restart: always
environment:
- "POSTGRES_USER=${DB_USER}"
- "POSTGRES_PASSWORD=${DB_PASSWORD}"
- "POSTGRES_DB=${DB_NAME}"
volumes:
- shynet_db:/var/lib/postgresql/data
networks:
- internal
webserver:
container_name: shynet_webserver
image: nginx
restart: always
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf
ports:
- 8080:80
depends_on:
- shynet
networks:
- internal
volumes:
shynet_db:
networks:
internal:

1904
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -18,7 +18,8 @@
"homepage": "https://github.com/milesmcc/shynet#readme",
"dependencies": {
"@fortawesome/fontawesome-free": "^5.15.1",
"a17t": "^0.5.1",
"a17t": "^0.10.1",
"tailwindcss": "^3.0.1",
"apexcharts": "^3.24.0",
"datamaps": "^0.5.9",
"flag-icon-css": "^3.5.0",

1288
poetry.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -17,7 +17,7 @@ PyYAML = "^5.4.1"
user-agents = "^2.2.0"
rules = "^3.0"
gunicorn = "^20.1.0"
psycopg2-binary = "^2.9.1"
psycopg2-binary = "^2.9.2"
redis = "^3.5.3"
django-redis-cache = "^3.0.0"
pycountry = "^20.7.3"
@@ -27,6 +27,14 @@ django-npm = "^1.0.0"
python-dotenv = "^0.18.0"
django-debug-toolbar = "^3.2.1"
[tool.poetry.dev-dependencies]
pytest-sugar = "^0.9.4"
factory-boy = "^3.2.0"
pytest-django = "^4.4.0"
django-coverage-plugin = "^2.0.0"
django-stubs = "^1.8.0"
mypy = "^0.910"
[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"

View File

@@ -0,0 +1,9 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer components {
.litepicker .container__main {
@apply card p-2;
}
}

View File

@@ -28,10 +28,10 @@
{% endfor %}
{% elif field|is_input %}
{% include 'a17t/includes/label.html' %}
{{field|add_class:"input my-1"}}
{{field|add_class:"input ~neutral my-1"}}
{% elif field|is_textarea %}
{% include 'a17t/includes/label.html' %}
{{ field|add_class:'textarea my-1' }}
{{ field|add_class:'textarea ~neutral my-1' }}
{% elif field|is_select %}
{% include 'a17t/includes/label.html' %}
<div class="select {% if field.errors|length > 0 %}~critical{% endif %} my-1">
@@ -39,7 +39,7 @@
</div>
{% else %}
{% include 'a17t/includes/label.html' %}
{{field|add_class:"field my-1"}}
{{field|add_class:"field ~neutral my-1"}}
{% endif %}
{% for error in field.errors %}

View File

@@ -1,6 +1,5 @@
{% load static %}
<link rel="stylesheet" href="{% static 'a17t/dist/a17t.css' %}">
<script async src="{% static '@fortawesome/fontawesome-free/js/all.min.js' %}" data-mutate-approach="sync"></script>
<link href="{% static 'a17t/dist/tailwind.css' %}" rel="stylesheet">
<link href="{% static 'inter-ui/Inter (web)/inter.css' %}" rel="stylesheet">

View File

@@ -1,23 +1,23 @@
<nav class="flex w-full flex-wrap items-center justify-between" role="navigation" aria-label="pagination">
<div class="w-full md:w-auto mb-2">
{% if page.has_previous %}
<a href="?page={{ page.previous_page_number }}&{{url_parameters}}" class="button field bg-neutral-000 w-auto mr-1">Previous</a>
<a href="?page={{ page.previous_page_number }}&{{url_parameters}}" class="button ~neutral @low w-auto mr-1">Previous</a>
{% else %}
<a class="button field bg-neutral-000 w-auto mr-1" disabled>Previous</a>
<a class="button ~neutral @low w-auto mr-1" disabled>Previous</a>
{% endif %}
{% if page.has_next %}
<a href="?page={{ page.next_page_number }}&{{url_parameters}}" class="button field bg-neutral-000 w-auto">Next</a>
<a href="?page={{ page.next_page_number }}&{{url_parameters}}" class="button ~neutral @low w-auto">Next</a>
{% else %}
<a class="button field bg-neutral-000 w-auto" disabled>Next</a>
<a class="button ~neutral @low w-auto" disabled>Next</a>
{% endif %}
</div>
<ul class="pagination-list w-full md:w-auto mb-2 flex">
{% for pnum in begin %}
{% ifequal page.number pnum %}
<li><a class="button field w-auto mx-1 text-white bg-neutral-700">{{ pnum }}</a></li>
<li><a class="button ~neutral @high w-auto mx-1">{{ pnum }}</a></li>
{% else %}
<li><a class="button field bg-neutral-000 w-auto mx-1" href="?page={{ pnum }}&{{url_parameters}}">{{ pnum }}</a></li>
<li><a class="button ~neutral @low w-auto mx-1" href="?page={{ pnum }}&{{url_parameters}}">{{ pnum }}</a></li>
{% endifequal %}
{% endfor %}
@@ -25,9 +25,9 @@
<li><span class="pagination-ellipsis">&hellip;</span></li>
{% for pnum in middle %}
{% ifequal page.number pnum %}
<li><a class="button field w-auto mx-1 text-white bg-neutral-700">{{ pnum }}</a></li>
<li><a class="button ~neutral @high w-auto mx-1">{{ pnum }}</a></li>
{% else %}
<li><a class="button field bg-neutral-000 w-auto mx-1" href="?page={{ pnum }}&{{url_parameters}}">{{ pnum }}</a></li>
<li><a class="button ~neutral @low w-auto mx-1" href="?page={{ pnum }}&{{url_parameters}}">{{ pnum }}</a></li>
{% endifequal %}
{% endfor %}
{% endif %}
@@ -36,9 +36,9 @@
<li><span class="pagination-ellipsis">&hellip;</span></li>
{% for pnum in end %}
{% ifequal page.number pnum %}
<li><a class="button field w-auto mx-1 text-white bg-neutral-700">{{ pnum }}</a></li>
<li><a class="button ~neutral @high w-auto mx-1">{{ pnum }}</a></li>
{% else %}
<li><a class="button field bg-neutral-000 w-auto mx-1" href="?page={{ pnum }}&{{url_parameters}}">{{ pnum }}</a></li>
<li><a class="button ~neutral @low w-auto mx-1" href="?page={{ pnum }}&{{url_parameters}}">{{ pnum }}</a></li>
{% endifequal %}
{% endfor %}
{% endif %}

View File

@@ -38,17 +38,3 @@
.geo-card--use-table-view .geo-table {
display: inline-block;
}
:root {
--color-neutral-000: white;
--color-neutral-50: #F8FAFC;
--color-neutral-100: #F1F5F9;
--color-neutral-200: #E2E8F0;
--color-neutral-300: #CBD5E1;
--color-neutral-400: #94A3B8;
--color-neutral-500: #64748B;
--color-neutral-600: #475569;
--color-neutral-700: #334155;
--color-neutral-800: #1E293B;
--color-neutral-900: #0F172A;
}

View File

@@ -6,7 +6,7 @@
</div>
<hr class="sep">
{% block main %}
<div class="card ~neutral !low max-w-lg content">
<div class="card max-w-lg content">
{% block card %}
{% endblock %}
</div>

View File

@@ -6,7 +6,7 @@
{% block page_title %}{% trans "Email Addresses" %}{% endblock %}
{% block main %}
<div class="card ~neutral !low max-w-lg">
<div class="card max-w-lg">
{% if user.emailaddress_set.all %}
<p>{% trans 'These are your known email addresses:' %}</p>
@@ -33,7 +33,7 @@
{% endfor %}
<div class="block mt-4">
<button class="button ~neutral !high mb-1" type="submit" name="action_primary">{% trans 'Make Primary' %}</button>
<button class="button ~neutral @high mb-1" type="submit" name="action_primary">{% trans 'Make Primary' %}</button>
<button class="button ~neutral mb-1" type="submit" name="action_send">{% trans 'Resend Verification' %}</button>
<button class="button ~neutral mb-1" type="submit" name="action_remove">{% trans 'Remove' %}</button>
</div>
@@ -51,10 +51,10 @@
<hr class="sep">
<form method="post" action="{% url 'account_email' %}" class="card ~neutral !low max-w-lg">
<form method="post" action="{% url 'account_email' %}" class="card max-w-lg">
{% csrf_token %}
{{ form|a17t }}
<button name="action_add" class="button ~neutral !high" type="submit">{% trans "Add Address" %}</button>
<button name="action_add" class="button ~neutral @high" type="submit">{% trans "Add Address" %}</button>
</form>
{% endblock %}

View File

@@ -18,7 +18,7 @@
<form method="post" action="{% url 'account_confirm_email' confirmation.key %}">
{% csrf_token %}
<button type="submit" class="button ~positive !high">{% trans 'Confirm' %}</button>
<button type="submit" class="button ~positive @high">{% trans 'Confirm' %}</button>
</form>
{% else %}

View File

@@ -16,7 +16,7 @@
{% if redirect_field_value %}
<input type="hidden" name="{{ redirect_field_name }}" value="{{ redirect_field_value }}" />
{% endif %}
<button class="button ~urge !high mr-2" type="submit">{% trans "Sign In" %}</button>
<button class="button ~urge @high mr-2" type="submit">{% trans "Sign In" %}</button>
<a href="{% url 'account_reset_password' %}" class="button ~neutral mr-2">{% trans "Reset Password" %}</a>
<a href="{{ signup_url }}" class="button ~neutral">Sign Up</a>
</form>

View File

@@ -13,6 +13,6 @@
{% if redirect_field_value %}
<input type="hidden" name="{{ redirect_field_name }}" value="{{ redirect_field_value }}"/>
{% endif %}
<button type="submit" class="button ~neutral !high">{% trans 'Sign Out' %}</button>
<button type="submit" class="button ~neutral @high">{% trans 'Sign Out' %}</button>
</form>
{% endblock %}

View File

@@ -9,6 +9,6 @@
<form method="POST" action="{% url 'account_change_password' %}" class="password_change 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>
{% endblock %}

View File

@@ -18,6 +18,6 @@
<form method="POST" action="{% url 'account_reset_password' %}" class="password_reset max-w-lg">
{% csrf_token %}
{{ form|a17t }}
<button type="submit" class="button ~urge !high">{% trans 'Reset Password' %}</button>
<button type="submit" class="button ~urge @high">{% trans 'Reset Password' %}</button>
</form>
{% endblock %}

View File

@@ -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>

View File

@@ -6,5 +6,5 @@
{% block card %}
<p>{% trans 'Your password is now changed.' %}</p>
<a href="{% url 'account_login' %}" class="button ~urge !high">Log In</a>
<a href="{% url 'account_login' %}" class="button ~urge @high">Log In</a>
{% endblock %}

View File

@@ -9,6 +9,6 @@
<form method="POST" action="{% url 'account_set_password' %}" class="password_set">
{% csrf_token %}
{{ form|a17t }}
<button type="submit" name="action" class="button ~urge !high">{% trans 'Set Password' %}</button>
<button type="submit" name="action" class="button ~urge @high">{% trans 'Set Password' %}</button>
</form>
{% endblock %}

View File

@@ -14,7 +14,7 @@
{% if redirect_field_value %}
<input type="hidden" name="{{ redirect_field_name }}" value="{{ redirect_field_value }}" />
{% endif %}
<button type="submit" class="button ~urge !high">{% trans "Sign Up" %}</button>
<button type="submit" class="button ~urge @high">{% trans "Sign Up" %}</button>
</form>
{% endblock %}

View File

@@ -23,7 +23,7 @@
{% endblock %}
</head>
<body class="bg-neutral-100 min-h-full overflow-x-hidden">
<body class="bg-neutral-50 min-h-full overflow-x-hidden">
{% block body %}
<section class="max-w-screen-xl mx-auto px-4 py-4 md:py-12 md:flex">
@@ -34,7 +34,7 @@
<i class="fas fa-binoculars fa-2x text-urge-600 md:hidden"></i>
</a>
<a tabindex="0" role="button" class="button ~neutral !low md:hidden"
<a tabindex="0" role="button" class="text-neutral-600 md:hidden"
onclick="document.getElementById('navMenuExpanded').classList.toggle('hidden')">
<span class="icon">
<i class="fas fa-bars"></i>
@@ -110,7 +110,7 @@
{% if messages %}
<div>
{% for message in messages %}
<article class="card {{message.tags}} !high mb-2 w-full">{{message}}</article>
<article class="card {{message.tags}} @high mb-2 w-full">{{message}}</article>
{% endfor %}
</ul>
</div>

View File

@@ -1,18 +1,20 @@
<form method="GET" id="datePicker">
<input type="hidden" name="startDate" value="{{start_date.isoformat}}" id="startDate">
<input type="hidden" name="endDate" value="{{end_date.isoformat}}" id="endDate">
</form>
<input type="input" id="rangePicker" placeholder="Date range" class="input ~neutral bg-neutral-000 cursor-pointer" style="max-width: 200px;" readonly>
<div class="~urge">
<form method="GET" id="datePicker" class="~urge">
<input type="hidden" name="startDate" value="{{start_date.isoformat}}" id="startDate">
<input type="hidden" name="endDate" value="{{end_date.isoformat}}" id="endDate">
</form>
<input type="input" id="rangePicker" placeholder="Date range" class="button ~neutral @low cursor-pointer" style="max-width: 200px;" readonly>
</div>
<style>
:root {
--litepicker-button-prev-month-color-hover: var(--color-urge);
--litepicker-button-next-month-color-hover: var(--color-urge);
--litepicker-day-color-hover: var(--color-urge);
--litepicker-is-today-color: var(--color-urge);
--litepicker-is-in-range-color: var(--color-urge-normal-fill);
--litepicker-is-start-color-bg: var(--color-urge);
--litepicker-is-end-color-bg: var(--color-urge);
--litepicker-button-apply-color-bg: var(--color-urge);
--litepicker-button-prev-month-color-hover: #7c3aed;
--litepicker-button-next-month-color-hover: #7c3aed;
--litepicker-day-color-hover: #7c3aed;
--litepicker-is-today-color: #7c3aed;
--litepicker-is-in-range-color: #ddd6fe;
--litepicker-is-start-color-bg: #7c3aed;
--litepicker-is-end-color-bg: #7c3aed;
--litepicker-button-apply-color-bg: #7c3aed;
}
.litepicker .container__predefined-ranges, .litepicker .container__months {

View File

@@ -1,6 +1,6 @@
{% load humanize helpers %}
<a class="card chart-card overflow-visible ~neutral !low service mb-6 p-0" href="{% contextual_url 'dashboard:service' object.uuid %}">
<a class="card chart-card overflow-visible service mb-6 p-0" href="{% contextual_url 'dashboard:service' object.uuid %}">
{% with stats=object.stats %}
<div class="p-4 md:flex justify-between overflow-none">
<div class="flex items-center mb-4 md:mb-0 md:flex-1 md:min-w-0 truncate pr-0 md:pr-2">

View File

@@ -1,4 +1,4 @@
<div class="card ~neutral !high font-mono text-sm whitespace-pre-wrap break-all">{% filter force_escape %}<noscript>
<div class="card ~neutral @high font-mono text-sm whitespace-pre-wrap break-all">{% filter force_escape %}<noscript>
<img src="{{script_protocol}}{{request.get_host}}{% url 'ingress:endpoint_pixel' object.uuid %}">
</noscript>
<script defer src="{{script_protocol}}{{request.get_host}}{% url 'ingress:endpoint_script' object.uuid %}"></script>{% endfilter %}

View File

@@ -1,6 +1,6 @@
{% load helpers %}
<div>
<a class="portal !low {% if request.get_full_path|startswith:url %}~urge active bg-neutral-100{% endif %} flex items-center" title="{{label}}"
{% if disable_turbolinks %}data-turbolinks="false"{% endif %} href="{{url}}">{{icon}} <span class="truncate">{{label}}</span></a>
<a class="portal {% if request.get_full_path|startswith:url %}text-urge-600{% endif %} flex items-center" title="{{label}}"
href="{{url}}">{{icon}} <span class="truncate">{{label}}</span></a>
</div>

View File

@@ -2,7 +2,7 @@
{% with stats=object.get_daily_stats %}
{% if stats.currently_online > 0 %}
<span class="chip ~positive !high whitespace-nowrap">
<span class="chip ~positive @high whitespace-nowrap">
{{stats.currently_online|intcomma}} online
</span>
{% endif %}

View File

@@ -13,7 +13,7 @@
</div>
{% has_perm "core.create_service" user as can_create %}
{% if can_create %}
<a href="{% url 'dashboard:service_create' %}" class="button field bg-neutral-000 w-auto">+ New Service</a>
<a href="{% url 'dashboard:service_create' %}" class="button ~neutral @low w-auto">+ New Service</a>
{% endif %}
</div>
</div>
@@ -21,7 +21,7 @@
{% for object in object_list|dictsortreversed:"stats.session_count" %}
{% include 'dashboard/includes/service_overview.html' %}
{% empty %}
<p class="aside ~urge !high">You don't have any services yet. {% if can_create %}Get started by <a href="{% url 'dashboard:service_create' %}" class="underline">creating one</a>.{% endif %}</p>
<p class="aside ~urge @high">You don't have any services yet. {% if can_create %}Get started by <a href="{% url 'dashboard:service_create' %}" class="underline">creating one</a>.{% endif %}</p>
{% endfor %}
{% if object_list %}

View File

@@ -4,6 +4,6 @@
<section class="content">
<h2>{{request.site.name}} Analytics</h2>
<p>{{request.site.name}} uses Shynet. Eventually, more information about Shynet will be available here.</p>
<a href="{% url 'account_login' %}" class="button ~urge !high">Log In</a>
<a href="{% url 'account_login' %}" class="button ~urge @high">Log In</a>
</section>
{% endblock %}

View File

@@ -6,7 +6,7 @@
<div class="mr-2">{% include 'dashboard/includes/date_range.html' %}</div>
{% has_perm 'core.change_service' user object as can_update %}
{% if can_update %}
<a href="{% contextual_url 'dashboard:service_update' service.uuid %}" class="button field bg-neutral-000 w-auto">Manage &rarr;</a>
<a href="{% contextual_url 'dashboard:service_update' service.uuid %}" class="button ~neutral @low w-auto">Manage &rarr;</a>
{% endif %}
{% endblock %}
@@ -19,7 +19,7 @@
{% include 'dashboard/includes/service_snippet.html' %}
</div>
{% else %}
<div class="grid grid-cols-2 gap-6 md:flex justify-between mb-6 card ~neutral !high px-6" id="stats">
<div class="grid grid-cols-2 gap-6 md:flex justify-between mb-6 card ~neutral @high px-6" id="stats">
{% with classes="text-sm font-semibold" good_classes="text-positive-400" bad_classes="text-critical-400" neutral_classes="text-gray-400" %}
<article class="">
<p class="label text-gray-400">Sessions</p>
@@ -93,12 +93,12 @@
</article>
{% endwith %}
</div>
<div class="card overflow-visible ~neutral !low py-0 mb-6">
<div class="card overflow-visible py-0 mb-6">
{% include 'dashboard/includes/time_chart.html' with data=stats.chart_data tooltip_format=stats.chart_tooltip_format granularity=stats.chart_granularity click_zoom=True %}
</div>
{% endif %}
<div id="card-grid" class="grid grid-cols-1 md:grid-cols-2 gap-6 mb-6">
<div class="card ~neutral !low limited-height py-2">
<div class="card limited-height py-2">
<table class="table">
<thead class="text-sm">
<tr>
@@ -132,22 +132,22 @@
</tbody>
</table>
</div>
<div class="geo-map card ~neutral !low py-2 overflow-y-hidden">
<div class="geo-map card py-2 overflow-y-hidden">
<p class="text-sm font-semibold p-2 border-b mb-2" style="color: var(--color-title)">
Sessions by Geography &nbsp
<button onclick="document.getElementById('card-grid').classList.add('geo-card--use-table-view')" class="text-xs select-none p-0 button ~urge !low">
<button onclick="document.getElementById('card-grid').classList.add('geo-card--use-table-view')" class="text-xs select-none p-0 text-urge-600">
(view table)
</button>
</p>
{% include 'dashboard/includes/map_chart.html' with countries=stats.countries %}
</div>
<div class="geo-table card ~neutral !low limited-height py-2">
<div class="geo-table card limited-height py-2">
<table class="table">
<thead class="text-sm">
<tr>
<th>
Country &nbsp
<button onclick="document.getElementById('card-grid').classList.remove('geo-card--use-table-view'); geoMap.resize()" class="text-xs select-none p-0 button ~urge !low">
<button onclick="document.getElementById('card-grid').classList.remove('geo-card--use-table-view'); geoMap.resize()" class="text-xs select-none p-0 text-urge-600">
(view map)
</button>
</th>
@@ -180,7 +180,7 @@
</tbody>
</table>
</div>
<div class="card ~neutral !low limited-height py-2">
<div class="card limited-height py-2">
<table class="table">
<thead class="text-sm">
<tr>
@@ -214,7 +214,7 @@
</tbody>
</table>
</div>
<div class="card ~neutral !low limited-height py-2">
<div class="card limited-height py-2">
<table class="table">
<thead class="text-sm">
<tr>
@@ -248,7 +248,7 @@
</tbody>
</table>
</div>
<div class="card ~neutral !low limited-height py-2">
<div class="card limited-height py-2">
<table class="table">
<thead class="text-sm">
<tr>
@@ -283,7 +283,7 @@
</tbody>
</table>
</div>
<div class="card ~neutral !low limited-height py-2">
<div class="card limited-height py-2">
<table class="table">
<thead class="text-sm">
<tr>
@@ -318,7 +318,7 @@
</table>
</div>
</div>
<div class="card ~neutral !low py-2 overflow-auto">
<div class="card py-2 overflow-auto">
{% include 'dashboard/includes/session_list.html' %}
<hr class="sep h-8 md:h-12">
<a href="{% contextual_url 'dashboard:service_session_list' service.uuid %}" class="button ~neutral w-auto mb-2">View more

View File

@@ -7,14 +7,14 @@
{% block content %}
<h4 class="heading leading-none">Create Service</h4>
<hr class="sep">
<form class="card ~neutral !low p-0 max-w-xl" method="POST">
<form class="card p-0 max-w-xl" method="POST">
{% csrf_token %}
<div class="p-4">
{% include 'dashboard/includes/service_form.html' %}
</div>
<div class="section ~urge !normal p-4">
<button type="submit" class="button ~urge !high">Create</button>
<a href="{% url 'dashboard:dashboard' %}" class="button ~urge !low">Cancel</a>
<div class="section ~urge @low p-4">
<button type="submit" class="button ~urge @high">Create</button>
<a href="{% url 'dashboard:dashboard' %}" class="ml-4 text-urge-600">Cancel</a>
</div>
</form>
{% endblock %}

View File

@@ -5,16 +5,16 @@
{% block head_title %}Delete {{object.name}}{% endblock %}
{% block service_content %}
<form class="card ~neutral !low p-0 max-w-xl" method="POST">
<form class="card p-0 max-w-xl" method="POST">
{% csrf_token %}
<div class="p-4">
<p>Are you sure you want to delete this service? All of its
analytics and associated data will be permanently deleted.</p>
{{form|a17t}}
</div>
<div class="section ~critical !normal p-4">
<button type="submit" class="button ~critical !high">Delete</button>
<a href="{% url 'dashboard:service' object.uuid %}" class="button ~critical !low">Cancel</a>
<div class="section ~critical @low p-4">
<button type="submit" class="button ~critical @high">Delete</button>
<a href="{% url 'dashboard:service' object.uuid %}" class="ml-4 text-critical-600">Cancel</a>
</div>
</form>
{% endblock %}

View File

@@ -5,11 +5,11 @@
{% block head_title %}{{object.name}} Session{% endblock %}
{% block service_actions %}
<a href="{% contextual_url 'dashboard:service' object.uuid %}" class="button field bg-neutral-000 w-auto">Analytics &rarr;</a>
<a href="{% contextual_url 'dashboard:service' object.uuid %}" class="button ~neutral @low w-auto">Analytics &rarr;</a>
{% endblock %}
{% block service_content %}
<article class="card ~neutral !high">
<article class="card ~neutral @high">
<div class="md:flex items-center justify-between">
<div>
<h3 class="heading text-2xl mr-4">
@@ -18,7 +18,7 @@
</div>
<div>
<p class="font-medium text-lg">{{session.start_time|date:"M j Y, g:i a"}} to
{{session.last_seen|date:"g:i a"}}{% if session.is_currently_active %} <span class="chip ~positive !high text-base">Online</span>{% endif %}</p>
{{session.last_seen|date:"g:i a"}}{% if session.is_currently_active %} <span class="chip ~positive @high text-base">Online</span>{% endif %}</p>
</div>
</div>
<hr class="sep h-8 md:h-12">
@@ -70,7 +70,7 @@
<div class="md:w-2/12 mb-2 md:mr-4 pt-4 md:text-right">
<div class="text-lg font-medium">{{hit.start_time|date:"g:i a"}}</div>
</div>
<div class="md:flex card ~neutral !low flex-grow justify-between">
<div class="md:flex card flex-grow justify-between">
<div class="mb-4 md:mb-0 md:w-1/2">
<p class="label font-medium text-lg truncate">{{hit.location|default:"Unknown"|urlize}}</p>
{% if hit.referrer %}

View File

@@ -6,11 +6,11 @@
{% block service_actions %}
<div class="mr-2">{% include 'dashboard/includes/date_range.html' %}</div>
<a href="{% contextual_url 'dashboard:service' object.uuid %}" class="button field ~neutral bg-neutral-000 w-auto">Analytics &rarr;</a>
<a href="{% contextual_url 'dashboard:service' object.uuid %}" class="button ~neutral @low bg-neutral-000 w-auto">Analytics &rarr;</a>
{% endblock %}
{% block service_content %}
<div class="card ~neutral !low mb-8 pt-2 max-w-full overflow-x-auto">
<div class="card mb-8 pt-2 max-w-full overflow-x-auto">
{% include 'dashboard/includes/session_list.html' %}
</div>
{% pagination page_obj request %}

View File

@@ -5,7 +5,7 @@
{% block head_title %}{{object.name}} Management{% endblock %}
{% block service_actions %}
<a href="{% url 'dashboard:service' object.uuid %}" class="button field bg-neutral-000 w-auto">View &rarr;</a>
<a href="{% url 'dashboard:service' object.uuid %}" class="button ~neutral @low w-auto">View &rarr;</a>
{% endblock %}
{% block service_content %}
@@ -15,18 +15,18 @@
{% include 'dashboard/includes/service_snippet.html' %}
<hr class="sep h-4">
<h5>Settings</h5>
<form class="card ~neutral !low p-0" method="POST">
<form class="card p-0" method="POST">
{% csrf_token %}
<div class="p-4">
{% include 'dashboard/includes/service_form.html' %}
</div>
<div class="section ~neutral !normal p-4 flex justify-between">
<div class="section ~neutral @low p-4 flex justify-between">
<div>
<button type="submit" class="button ~neutral !high">Save</button>
<a href="{% url 'dashboard:service' object.uuid %}" class="button ~neutral !low">Cancel</a>
<button type="submit" class="button ~neutral @high">Save</button>
<a href="{% url 'dashboard:service' object.uuid %}" class="button ~neutral @low">Cancel</a>
</div>
<div>
<a href="{% url 'dashboard:service_delete' object.uuid %}" class="button ~critical !high">Delete</a>
<a href="{% url 'dashboard:service_delete' object.uuid %}" class="button ~critical @high">Delete</a>
</div>
</div>
</form>

View File

@@ -306,15 +306,12 @@ DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
NPM_ROOT_PATH = "../"
NPM_FILE_PATTERNS = {
"a17t": [os.path.join("dist", "a17t.css"), os.path.join("dist", "tailwind.css")],
"apexcharts": [os.path.join("dist", "apexcharts.min.js")],
"litepicker": [
os.path.join("dist", "nocss", "litepicker.js"),
os.path.join("dist", "css", "litepicker.css"),
os.path.join("dist", "plugins", "ranges.js"),
],
"turbolinks": [os.path.join("dist", "turbolinks.js")],
"stimulus": [os.path.join("dist", "stimulus.umd.js")],
"inter-ui": [os.path.join("Inter (web)", "*")],
"@fortawesome": [os.path.join("fontawesome-free", "js", "all.min.js")],
"datamaps": [os.path.join("dist", "datamaps.world.min.js")],

22
tailwind.config.js Normal file
View File

@@ -0,0 +1,22 @@
let colors = require("tailwindcss/colors")
module.exports = {
content: ["./**/*.{html,py}"],
theme: {
extend: {
colors: {
neutral: colors.slate,
positive: colors.green,
urge: colors.violet,
warning: colors.yellow,
info: colors.blue,
critical: colors.red,
inf: "white",
zero: colors.slate[900]
}
},
},
plugins: [
require("a17t")
],
}

16
tests/js.html Normal file
View File

@@ -0,0 +1,16 @@
<!DOCTYPE html>
<html>
<head>
<title>JS test</title>
</head>
<body>
<noscript>
<img src="http://localhost:8000/ingress/0ca733e8-c41f-462b-a11a-4ba0cea29948/pixel.gif">
</noscript>
<script defer src="http://localhost:8000/ingress/0ca733e8-c41f-462b-a11a-4ba0cea29948/script.js"></script>
</body>
</html>

13
tests/pixel.html Normal file
View File

@@ -0,0 +1,13 @@
<!DOCTYPE html>
<html>
<head>
<title>Pixel test</title>
</head>
<body>
<img src="http://localhost:8000/ingress/9b2c4e2f-8d29-4418-82d4-b68e06795025/pixel.gif">
</body>
</html>