Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
71ec196ec4 | ||
|
|
4834c5722f | ||
|
|
4b4c8f207e | ||
|
|
aed88b7c9a | ||
|
|
1fd46b019c | ||
|
|
e534269c77 | ||
|
|
0d64ef33b0 | ||
|
|
56c82e7d23 | ||
|
|
c71d934c67 | ||
|
|
85ae56fcdb | ||
|
|
cd422ffd71 | ||
|
|
060a9b2a96 | ||
|
|
8d13ccd0fd | ||
|
|
0d46e6d1f4 | ||
|
|
81ae84efb3 | ||
|
|
ea5f58fbd3 | ||
|
|
4079a8494a | ||
|
|
780b71083a | ||
|
|
62fbb014e7 | ||
|
|
53bc690435 | ||
|
|
04120323a6 |
7
.github/workflows/build-docker-edge.yml
vendored
7
.github/workflows/build-docker-edge.yml
vendored
@@ -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 }}
|
||||
|
||||
7
.github/workflows/build-docker-manual.yml
vendored
7
.github/workflows/build-docker-manual.yml
vendored
@@ -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 }}
|
||||
|
||||
7
.github/workflows/build-docker-release.yml
vendored
7
.github/workflows/build-docker-release.yml
vendored
@@ -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
37
.github/workflows/run-tests.yml
vendored
Normal 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
15
CONTRIBUTING.md
Normal 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!_
|
||||
@@ -47,4 +47,5 @@ RUN python manage.py collectstatic --noinput && \
|
||||
# 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" ]
|
||||
|
||||
41
GUIDE.md
41
GUIDE.md
@@ -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/)!
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<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 align="center"><a href="#screenshots">Screenshots</a> • <a href="#features">Features</a> • <a href="https://miles.land/officehours/">Office Hours</a></p>
|
||||
</p>
|
||||
|
||||
<br>
|
||||
|
||||
@@ -17,7 +17,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: "shynet-webserver"
|
||||
image: "milesmcc/shynet:latest"
|
||||
image: "milesmcc/shynet:edge" # Change to the version appropriate for you (e.g., :latest)
|
||||
imagePullPolicy: Always
|
||||
envFrom:
|
||||
- secretRef:
|
||||
@@ -42,7 +42,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: "shynet-celeryworker"
|
||||
image: "milesmcc/shynet:latest"
|
||||
image: "milesmcc/shynet:edge" # Change to the version appropriate for you (e.g., :latest)
|
||||
command: ["./celeryworker.sh"]
|
||||
imagePullPolicy: Always
|
||||
envFrom:
|
||||
@@ -95,7 +95,7 @@ spec:
|
||||
selector:
|
||||
app: shynet-webserver
|
||||
---
|
||||
apiVersion: networking.k8s.io/v1beta1
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: shynet-webserver-ingress
|
||||
|
||||
19
package-lock.json
generated
19
package-lock.json
generated
@@ -4,6 +4,7 @@
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "shynet",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-free": "^5.15.1",
|
||||
@@ -160,8 +161,7 @@
|
||||
"esprima": "^3.1.3",
|
||||
"estraverse": "^4.2.0",
|
||||
"esutils": "^2.0.2",
|
||||
"optionator": "^0.8.1",
|
||||
"source-map": "~0.6.1"
|
||||
"optionator": "^0.8.1"
|
||||
},
|
||||
"bin": {
|
||||
"escodegen": "bin/escodegen.js",
|
||||
@@ -367,9 +367,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/path-parse": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
|
||||
"integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw=="
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
|
||||
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="
|
||||
},
|
||||
"node_modules/prelude-ls": {
|
||||
"version": "1.1.2",
|
||||
@@ -480,8 +480,7 @@
|
||||
"esprima": "^4.0.1",
|
||||
"estraverse": "^4.2.0",
|
||||
"esutils": "^2.0.2",
|
||||
"optionator": "^0.8.1",
|
||||
"source-map": "~0.6.1"
|
||||
"optionator": "^0.8.1"
|
||||
},
|
||||
"bin": {
|
||||
"escodegen": "bin/escodegen.js",
|
||||
@@ -993,9 +992,9 @@
|
||||
}
|
||||
},
|
||||
"path-parse": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
|
||||
"integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw=="
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
|
||||
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="
|
||||
},
|
||||
"prelude-ls": {
|
||||
"version": "1.1.2",
|
||||
|
||||
1288
poetry.lock
generated
1288
poetry.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -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"
|
||||
|
||||
@@ -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 field !low bg-neutral-000 w-auto mr-1">Previous</a>
|
||||
{% else %}
|
||||
<a class="button field bg-neutral-000 w-auto mr-1" disabled>Previous</a>
|
||||
<a class="button field !low bg-neutral-000 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 field !low bg-neutral-000 w-auto">Next</a>
|
||||
{% else %}
|
||||
<a class="button field bg-neutral-000 w-auto" disabled>Next</a>
|
||||
<a class="button field !low bg-neutral-000 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 field !low w-auto mx-1 text-white bg-neutral-700">{{ 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 field !low bg-neutral-000 w-auto mx-1" href="?page={{ pnum }}&{{url_parameters}}">{{ pnum }}</a></li>
|
||||
{% endifequal %}
|
||||
{% endfor %}
|
||||
|
||||
@@ -25,9 +25,9 @@
|
||||
<li><span class="pagination-ellipsis">…</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 field !low w-auto mx-1 text-white bg-neutral-700">{{ 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 field !low bg-neutral-000 w-auto mx-1" href="?page={{ pnum }}&{{url_parameters}}">{{ pnum }}</a></li>
|
||||
{% endifequal %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
@@ -36,9 +36,9 @@
|
||||
<li><span class="pagination-ellipsis">…</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 field !low w-auto mx-1 text-white bg-neutral-700">{{ 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 field !low bg-neutral-000 w-auto mx-1" href="?page={{ pnum }}&{{url_parameters}}">{{ pnum }}</a></li>
|
||||
{% endifequal %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
37
shynet/core/factories.py
Normal file
37
shynet/core/factories.py
Normal file
@@ -0,0 +1,37 @@
|
||||
from django.contrib.auth import get_user_model
|
||||
import factory
|
||||
from factory.django import DjangoModelFactory
|
||||
from .models import Service
|
||||
|
||||
|
||||
class UserFactory(DjangoModelFactory):
|
||||
username = factory.Faker("user_name")
|
||||
email = factory.Faker("email")
|
||||
name = factory.Faker("name")
|
||||
|
||||
@post_generation
|
||||
def password(self, create, extracted, **kwargs):
|
||||
password = (
|
||||
extracted
|
||||
if extracted
|
||||
else factory.Faker(
|
||||
"password",
|
||||
length=42,
|
||||
special_chars=True,
|
||||
digits=True,
|
||||
upper_case=True,
|
||||
lower_case=True,
|
||||
).evaluate(None, None, extra={"locale": None})
|
||||
)
|
||||
self.set_password(password)
|
||||
|
||||
class Meta:
|
||||
model = get_user_model()
|
||||
django_get_or_create = ["username"]
|
||||
|
||||
|
||||
class ServiceFactory(DjangoModelFactory):
|
||||
class Meta:
|
||||
model = Service
|
||||
|
||||
name = factory.Faker("company")
|
||||
1
shynet/core/tests/__init__.py
Normal file
1
shynet/core/tests/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
@@ -45,11 +45,6 @@ class DateRangeMixin:
|
||||
"start": now.replace(day=1),
|
||||
"end": now,
|
||||
},
|
||||
{
|
||||
"name": "Last month",
|
||||
"start": now.replace(day=1, month=now.month - 1),
|
||||
"end": now.replace(day=1, month=now.month) - timezone.timedelta(days=1),
|
||||
},
|
||||
{
|
||||
"name": "This year",
|
||||
"start": now.replace(day=1, month=1),
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<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>
|
||||
<input type="input" id="rangePicker" placeholder="Date range" class="input ~neutral !low bg-neutral-000 cursor-pointer" style="max-width: 200px;" readonly>
|
||||
<style>
|
||||
:root {
|
||||
--litepicker-button-prev-month-color-hover: var(--color-urge);
|
||||
|
||||
@@ -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 field !low bg-neutral-000 w-auto">+ New Service</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -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 →</a>
|
||||
<a href="{% contextual_url 'dashboard:service_update' service.uuid %}" class="button field !low bg-neutral-000 w-auto">Manage →</a>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
{% 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 →</a>
|
||||
<a href="{% contextual_url 'dashboard:service' object.uuid %}" class="button field !low bg-neutral-000 w-auto">Analytics →</a>
|
||||
{% endblock %}
|
||||
|
||||
{% block service_content %}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
{% 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 →</a>
|
||||
<a href="{% contextual_url 'dashboard:service' object.uuid %}" class="button field ~neutral !low bg-neutral-000 w-auto">Analytics →</a>
|
||||
{% endblock %}
|
||||
|
||||
{% block service_content %}
|
||||
|
||||
@@ -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 →</a>
|
||||
<a href="{% url 'dashboard:service' object.uuid %}" class="button field !low bg-neutral-000 w-auto">View →</a>
|
||||
{% endblock %}
|
||||
|
||||
{% block service_content %}
|
||||
|
||||
0
shynet/dashboard/tests/__init__.py
Normal file
0
shynet/dashboard/tests/__init__.py
Normal file
43
shynet/dashboard/tests/tests_dashboard_views.py
Normal file
43
shynet/dashboard/tests/tests_dashboard_views.py
Normal file
@@ -0,0 +1,43 @@
|
||||
from django.test import TestCase, RequestFactory
|
||||
from django.conf import settings
|
||||
from django.urls import reverse
|
||||
from core.factories import UserFactory
|
||||
|
||||
from dashboard.views import DashboardView
|
||||
|
||||
|
||||
class QuestionModelTests(TestCase):
|
||||
def setUp(self):
|
||||
# Every test needs access to the request factory.
|
||||
self.factory = RequestFactory()
|
||||
self.user = UserFactory()
|
||||
|
||||
def tearDown(self):
|
||||
pass
|
||||
|
||||
def tests_unauthenticated_dashboard_view(self):
|
||||
"""
|
||||
GIVEN: Unauthenticated user
|
||||
WHEN: Accessing the dashboard view
|
||||
THEN: It's redirected to login page with NEXT url to dashboard
|
||||
"""
|
||||
login_url = settings.LOGIN_URL
|
||||
response = self.client.get(reverse("dashboard:dashboard"))
|
||||
|
||||
self.assertEqual(response.status_code, 302)
|
||||
self.assertEqual(
|
||||
response.url, f"{login_url}?next={reverse('dashboard:dashboard')}"
|
||||
)
|
||||
|
||||
def tests_authenticated_dashboard_view(self):
|
||||
"""
|
||||
GIVEN: Authenticated user
|
||||
WHEN: Accessing the dashboard view
|
||||
THEN: It should respond with 200 and render the view
|
||||
"""
|
||||
request = self.factory.get(reverse("dashboard:dashboard"))
|
||||
request.user = self.user
|
||||
|
||||
# Use this syntax for class-based views.
|
||||
response = DashboardView.as_view()(request)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
@@ -22,7 +22,7 @@ from django.contrib.messages import constants as messages
|
||||
load_dotenv()
|
||||
|
||||
# Increment on new releases
|
||||
VERSION = "0.11.0"
|
||||
VERSION = "0.12.0"
|
||||
|
||||
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
|
||||
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
@@ -299,7 +299,7 @@ else:
|
||||
EMAIL_USE_TLS = os.environ.get("EMAIL_USE_TLS")
|
||||
|
||||
# Auto fields
|
||||
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
|
||||
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
|
||||
|
||||
# NPM
|
||||
|
||||
|
||||
Reference in New Issue
Block a user