├── requirements.txt ├── .gitattributes ├── {{cookiecutter.github_repository}} ├── docs │ ├── img │ │ └── .gitkeep │ ├── index.md │ ├── releases.md │ ├── graphql │ │ ├── errors.md │ │ ├── 2-users.md │ │ ├── 0-overview.md │ │ ├── 1-auth.md │ │ └── errors_handling.md │ ├── api │ │ ├── 2-current-user.md │ │ ├── errors.md │ │ ├── 0-overview.md │ │ └── 1-auth.md │ └── backend │ │ ├── api_mixins.md │ │ └── coding_rules.md ├── runtime.txt ├── ansible.cfg ├── tests │ ├── __init__.py │ ├── graphql │ │ ├── __init__.py │ │ ├── test_current_user_api.py │ │ └── test_users_list_api.py │ ├── unit │ │ ├── __init__.py │ │ ├── test_example.py │ │ └── test_api_versioning.py │ ├── integration │ │ ├── __init__.py │ │ ├── test_site_pages.py │ │ └── test_current_user_api.py │ ├── utils.py │ └── factories.py ├── {{cookiecutter.main_module}} │ ├── locale │ │ └── .gitkeep │ ├── static │ │ ├── fonts │ │ │ └── .gitkeep │ │ ├── images │ │ │ ├── .gitkeep │ │ │ └── favicon.png │ │ └── js │ │ │ └── main.js │ ├── users │ │ ├── __init__.py │ │ ├── auth │ │ │ ├── __init__.py │ │ │ ├── services.py │ │ │ ├── utils.py │ │ │ ├── backends.py │ │ │ ├── tokens.py │ │ │ ├── serializers.py │ │ │ └── api.py │ │ ├── migrations │ │ │ ├── __init__.py │ │ │ └── 0002_auto_20171024_1200.py │ │ ├── serializers.py │ │ ├── services.py │ │ ├── tests │ │ │ └── test_models.py │ │ ├── api.py │ │ ├── admin.py │ │ └── models.py │ ├── base │ │ ├── __init__.py │ │ ├── api │ │ │ ├── __init__.py │ │ │ ├── schemas.py │ │ │ ├── routers.py │ │ │ ├── mixins.py │ │ │ └── pagination.py │ │ ├── templatetags │ │ │ ├── __init__.py │ │ │ └── urls_extra.py │ │ ├── utils │ │ │ ├── __init__.py │ │ │ └── urls.py │ │ ├── context_processors.py │ │ ├── renderers.py │ │ ├── models.py │ │ └── views.py │ ├── graphql │ │ ├── __init__.py │ │ ├── users │ │ │ ├── resolvers.py │ │ │ ├── types.py │ │ │ └── schema.py │ │ ├── api.py │ │ ├── middleware.py │ │ ├── decorators.py │ │ └── utils.py │ ├── templates │ │ ├── robots.txt │ │ ├── pages │ │ │ ├── base.html │ │ │ ├── about.html │ │ │ └── home.html │ │ ├── rest_framework │ │ │ ├── login.html │ │ │ └── api.html │ │ ├── humans.txt │ │ ├── 403.html │ │ ├── 400.html │ │ ├── admin │ │ │ └── auth │ │ │ │ └── user │ │ │ │ └── add_form.html │ │ ├── 500.html │ │ ├── 404.html │ │ ├── email │ │ │ └── password_reset_mail.tpl │ │ ├── 403_csrf.html │ │ └── base.html │ ├── __init__.py │ ├── api_urls.py │ ├── celery.py │ └── urls.py ├── provisioner │ ├── roles │ │ ├── celery │ │ │ ├── meta │ │ │ │ └── main.yml │ │ │ ├── templates │ │ │ │ ├── celery.log.j2 │ │ │ │ ├── celerybeat.service.j2 │ │ │ │ └── celery.service.j2 │ │ │ ├── handlers │ │ │ │ └── main.yml │ │ │ ├── defaults │ │ │ │ └── main.yml │ │ │ └── tasks │ │ │ │ └── main.yml │ │ ├── common │ │ │ ├── files │ │ │ │ ├── pydistutils.cfg │ │ │ │ ├── inputrc │ │ │ │ ├── pip.conf │ │ │ │ └── bashrc │ │ │ ├── templates │ │ │ │ └── locale.j2 │ │ │ ├── defaults │ │ │ │ └── main.yml │ │ │ └── tasks │ │ │ │ └── main.yml │ │ ├── postgresql │ │ │ ├── handlers │ │ │ │ └── main.yml │ │ │ ├── defaults │ │ │ │ └── main.yml │ │ │ ├── templates │ │ │ │ └── pg_hba.conf.j2 │ │ │ └── tasks │ │ │ │ └── main.yml │ │ ├── redis │ │ │ ├── handlers │ │ │ │ └── main.yml │ │ │ └── tasks │ │ │ │ └── main.yml │ │ ├── project_data │ │ │ ├── templates │ │ │ │ ├── django.logrotate.j2 │ │ │ │ ├── env.env.j2 │ │ │ │ ├── django.asgi.ini.j2 │ │ │ │ ├── django.uwsgi.ini.j2 │ │ │ │ └── uwsgi-emperor-init.d.j2 │ │ │ ├── tasks │ │ │ │ ├── asgi-setup.yml │ │ │ │ ├── uwsgi-setup.yml │ │ │ │ └── main.yml │ │ │ └── defaults │ │ │ │ └── main.yml │ │ └── nginx │ │ │ ├── tasks │ │ │ ├── htpasswd.yml │ │ │ ├── letsencrypt.yml │ │ │ └── main.yml │ │ │ ├── handlers │ │ │ └── main.yml │ │ │ ├── templates │ │ │ ├── site.80.conf.j2 │ │ │ ├── site.443.conf.j2 │ │ │ └── nginx.conf.j2 │ │ │ └── defaults │ │ │ └── main.yml │ ├── ansible.cfg │ ├── vars.yml │ ├── hosts │ └── site.yml ├── compose │ ├── dev │ │ ├── postgres │ │ │ ├── maintenance │ │ │ │ ├── _sourced │ │ │ │ │ ├── constants.sh │ │ │ │ │ ├── yes_no.sh │ │ │ │ │ ├── countdown.sh │ │ │ │ │ └── messages.sh │ │ │ │ ├── backups │ │ │ │ ├── backup │ │ │ │ └── restore │ │ │ └── Dockerfile │ │ └── django │ │ │ ├── celery │ │ │ ├── beat │ │ │ │ └── start │ │ │ ├── worker │ │ │ │ └── start │ │ │ └── flower │ │ │ │ └── start │ │ │ ├── start │ │ │ ├── entrypoint │ │ │ └── Dockerfile │ ├── local │ │ ├── start │ │ ├── entrypoint │ │ └── Dockerfile │ └── fly │ │ └── django │ │ └── Dockerfile ├── uwsgi.ini ├── .gitattributes ├── settings │ ├── __init__.py │ ├── testing.py │ └── development.py ├── Procfile ├── bin │ ├── post_compile │ └── generate_db_schema.py ├── .github │ └── workflows │ │ ├── fly.yml │ │ └── main.yml ├── .editorconfig ├── asgi.py ├── manage.py ├── .pre-commit-config.yaml ├── setup.cfg ├── dev.yml ├── .gitignore ├── .env.sample ├── wsgi.py ├── CONTRIBUTING.md ├── Vagrantfile ├── local.yml ├── .envs │ ├── .local │ │ └── .env.sample │ └── .dev │ │ └── .env.sample └── mkdocs.yml ├── .github ├── django-init-logo.png ├── PULL_REQUEST_TEMPLATE └── workflows │ └── main.yml ├── hooks └── pre_gen_project.sh ├── renovate.json ├── cookiecutter-test-config.yaml ├── AUTHORS.md ├── .editorconfig ├── generate-history.sh ├── run_test.sh ├── cookiecutter.json ├── .gitignore └── LICENSE /requirements.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | -------------------------------------------------------------------------------- /{{cookiecutter.github_repository}}/docs/img/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /{{cookiecutter.github_repository}}/runtime.txt: -------------------------------------------------------------------------------- 1 | python-3.9.0 2 | -------------------------------------------------------------------------------- /{{cookiecutter.github_repository}}/ansible.cfg: -------------------------------------------------------------------------------- 1 | provisioner/ansible.cfg -------------------------------------------------------------------------------- /{{cookiecutter.github_repository}}/tests/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | -------------------------------------------------------------------------------- /{{cookiecutter.github_repository}}/{{cookiecutter.main_module}}/locale/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /{{cookiecutter.github_repository}}/tests/graphql/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | -------------------------------------------------------------------------------- /{{cookiecutter.github_repository}}/tests/unit/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | -------------------------------------------------------------------------------- /{{cookiecutter.github_repository}}/{{cookiecutter.main_module}}/static/fonts/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /{{cookiecutter.github_repository}}/{{cookiecutter.main_module}}/static/images/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /{{cookiecutter.github_repository}}/tests/integration/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | -------------------------------------------------------------------------------- /{{cookiecutter.github_repository}}/{{cookiecutter.main_module}}/users/__init__.py: -------------------------------------------------------------------------------- 1 | # user app 2 | -------------------------------------------------------------------------------- /{{cookiecutter.github_repository}}/{{cookiecutter.main_module}}/users/auth/__init__.py: -------------------------------------------------------------------------------- 1 | # user auth 2 | -------------------------------------------------------------------------------- /{{cookiecutter.github_repository}}/{{cookiecutter.main_module}}/base/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | -------------------------------------------------------------------------------- /.github/django-init-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fueled/django-init/HEAD/.github/django-init-logo.png -------------------------------------------------------------------------------- /{{cookiecutter.github_repository}}/{{cookiecutter.main_module}}/base/api/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | -------------------------------------------------------------------------------- /{{cookiecutter.github_repository}}/{{cookiecutter.main_module}}/graphql/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | -------------------------------------------------------------------------------- /{{cookiecutter.github_repository}}/{{cookiecutter.main_module}}/base/templatetags/__init__.py: -------------------------------------------------------------------------------- 1 | # base templatetags 2 | -------------------------------------------------------------------------------- /{{cookiecutter.github_repository}}/{{cookiecutter.main_module}}/base/utils/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | -------------------------------------------------------------------------------- /{{cookiecutter.github_repository}}/{{cookiecutter.main_module}}/static/js/main.js: -------------------------------------------------------------------------------- 1 | // Add your javascript code here. 2 | -------------------------------------------------------------------------------- /{{cookiecutter.github_repository}}/{{cookiecutter.main_module}}/users/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | -------------------------------------------------------------------------------- /hooks/pre_gen_project.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo -n "==> Generating project files at ./{{ cookiecutter.github_repository }} " 4 | -------------------------------------------------------------------------------- /{{cookiecutter.github_repository}}/provisioner/roles/celery/meta/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | dependencies: 3 | - { role: project_data } 4 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "config:base" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /{{cookiecutter.github_repository}}/provisioner/roles/common/files/pydistutils.cfg: -------------------------------------------------------------------------------- 1 | [easy_install] 2 | index-url=https://pypi.python.org/simple/ 3 | -------------------------------------------------------------------------------- /cookiecutter-test-config.yaml: -------------------------------------------------------------------------------- 1 | default_context: 2 | enable_whitenoise: "y" 3 | add_celery: "y" 4 | add_graphql: "y" 5 | add_asgi: "y" 6 | -------------------------------------------------------------------------------- /{{cookiecutter.github_repository}}/tests/unit/test_example.py: -------------------------------------------------------------------------------- 1 | def func(x): 2 | return x + 1 3 | 4 | 5 | def test_answer(): 6 | assert func(3) == 4 7 | -------------------------------------------------------------------------------- /{{cookiecutter.github_repository}}/provisioner/roles/common/files/inputrc: -------------------------------------------------------------------------------- 1 | ## arrow up 2 | "\e[A":history-search-backward 3 | ## arrow down 4 | "\e[B":history-search-forward 5 | -------------------------------------------------------------------------------- /{{cookiecutter.github_repository}}/{{cookiecutter.main_module}}/templates/robots.txt: -------------------------------------------------------------------------------- 1 | # http://www.robotstxt.org 2 | 3 | User-agent: * 4 | Disallow: /api/ 5 | Disallow: /admin/ 6 | -------------------------------------------------------------------------------- /{{cookiecutter.github_repository}}/compose/dev/postgres/maintenance/_sourced/constants.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | 4 | BACKUP_DIR_PATH='/backups' 5 | BACKUP_FILE_PREFIX='backup' 6 | -------------------------------------------------------------------------------- /{{cookiecutter.github_repository}}/provisioner/roles/common/files/pip.conf: -------------------------------------------------------------------------------- 1 | [global] 2 | index-url=https://pypi.python.org/simple/ 3 | 4 | [install] 5 | trusted-host=pypi.python.org 6 | -------------------------------------------------------------------------------- /{{cookiecutter.github_repository}}/provisioner/roles/postgresql/handlers/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: restart postgresql 3 | service: name=postgresql state=restarted 4 | become: true 5 | -------------------------------------------------------------------------------- /{{cookiecutter.github_repository}}/provisioner/ansible.cfg: -------------------------------------------------------------------------------- 1 | [defaults] 2 | allow_world_readable_tmpfiles = True 3 | 4 | # human-readable stdout/stderr results display 5 | stdout_callback = debug 6 | -------------------------------------------------------------------------------- /{{cookiecutter.github_repository}}/uwsgi.ini: -------------------------------------------------------------------------------- 1 | [uwsgi] 2 | http-socket = :$(PORT) 3 | master = true 4 | processes = 4 5 | die-on-term = true 6 | module = wsgi:application 7 | memory-report = true 8 | -------------------------------------------------------------------------------- /{{cookiecutter.github_repository}}/.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | # http://davidlaing.com/2012/09/19/customise-your-gitattributes-to-become-a-git-ninja/ 3 | * text=auto 4 | -------------------------------------------------------------------------------- /{{cookiecutter.github_repository}}/compose/dev/django/celery/beat/start: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -o errexit 4 | set -o pipefail 5 | set -o nounset 6 | 7 | 8 | celery -A {{ cookiecutter.main_module }} beat -l INFO 9 | -------------------------------------------------------------------------------- /{{cookiecutter.github_repository}}/compose/dev/django/celery/worker/start: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -o errexit 4 | set -o pipefail 5 | set -o nounset 6 | 7 | 8 | celery -A {{ cookiecutter.main_module }} worker -l INFO 9 | -------------------------------------------------------------------------------- /{{cookiecutter.github_repository}}/{{cookiecutter.main_module}}/templates/pages/base.html: -------------------------------------------------------------------------------- 1 | {% raw %}{% extends 'base.html' %} 2 | {% block body_classes %}{{ block.super}} pages {% endblock body_classes %}{% endraw %} 3 | -------------------------------------------------------------------------------- /{{cookiecutter.github_repository}}/{{cookiecutter.main_module}}/graphql/users/resolvers.py: -------------------------------------------------------------------------------- 1 | from django.contrib.auth import get_user_model 2 | 3 | 4 | def get_all_users(info): 5 | return get_user_model().objects.all() 6 | -------------------------------------------------------------------------------- /{{cookiecutter.github_repository}}/provisioner/roles/postgresql/defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | pg_hstore: False 3 | pg_gis: False 4 | pg_db: '{% raw %}{{ project_namespace }}{% endraw %}' 5 | pg_user: dev 6 | pg_password: password 7 | -------------------------------------------------------------------------------- /{{cookiecutter.github_repository}}/{{cookiecutter.main_module}}/static/images/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fueled/django-init/HEAD/{{cookiecutter.github_repository}}/{{cookiecutter.main_module}}/static/images/favicon.png -------------------------------------------------------------------------------- /{{cookiecutter.github_repository}}/provisioner/roles/redis/handlers/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: reload redis 3 | service: name=redis-server state=reloaded 4 | 5 | - name: restart redis 6 | service: name=redis-server state=restarted 7 | -------------------------------------------------------------------------------- /{{cookiecutter.github_repository}}/settings/__init__.py: -------------------------------------------------------------------------------- 1 | # Standard Library 2 | import sys 3 | 4 | if "test" in sys.argv: 5 | print("\033[1;91mNo django tests.\033[0m") 6 | print("Try: \033[1;33mpytest\033[0m") 7 | sys.exit(0) 8 | -------------------------------------------------------------------------------- /{{cookiecutter.github_repository}}/{{cookiecutter.main_module}}/base/context_processors.py: -------------------------------------------------------------------------------- 1 | # Third Party Stuff 2 | from django.conf import settings 3 | 4 | 5 | def site_settings(context): 6 | return {"site_info": settings.SITE_INFO} 7 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE: -------------------------------------------------------------------------------- 1 | > Why was this change necessary? 2 | 3 | add your text here... 4 | 5 | > How does it address the problem? 6 | 7 | add your text here... 8 | 9 | > Are there any side effects? 10 | 11 | add your text here... 12 | -------------------------------------------------------------------------------- /{{cookiecutter.github_repository}}/provisioner/roles/celery/templates/celery.log.j2: -------------------------------------------------------------------------------- 1 | {% raw %}"{{celery_log_file}}" { 2 | copytruncate 3 | daily 4 | rotate 5 5 | compress 6 | delaycompress 7 | missingok 8 | notifempty 9 | }{% endraw %} 10 | -------------------------------------------------------------------------------- /{{cookiecutter.github_repository}}/tests/unit/test_api_versioning.py: -------------------------------------------------------------------------------- 1 | def test_api_default_and_allowed_versions(settings): 2 | assert settings.REST_FRAMEWORK["DEFAULT_VERSION"] == "1.0" 3 | assert settings.REST_FRAMEWORK["ALLOWED_VERSIONS"] == ["1.0"] 4 | -------------------------------------------------------------------------------- /{{cookiecutter.github_repository}}/provisioner/roles/project_data/templates/django.logrotate.j2: -------------------------------------------------------------------------------- 1 | {% raw %}"{{ uwsgi_log_file }}" { 2 | copytruncate 3 | daily 4 | rotate 5 5 | compress 6 | delaycompress 7 | missingok 8 | notifempty 9 | }{% endraw %} 10 | -------------------------------------------------------------------------------- /{{cookiecutter.github_repository}}/{{cookiecutter.main_module}}/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | __version__ = "{{ cookiecutter.version }}" 3 | {%- if cookiecutter.add_celery.lower() == 'y' %} 4 | 5 | from .celery import app as celery_app # noqa 6 | {%- endif %} 7 | -------------------------------------------------------------------------------- /{{cookiecutter.github_repository}}/Procfile: -------------------------------------------------------------------------------- 1 | web: {% if cookiecutter.add_newrelic == 'y' %}newrelic-admin run-program {% endif %}uwsgi uwsgi.ini 2 | {%- if cookiecutter.add_celery == 'y' %} 3 | worker: celery -A {{ cookiecutter.main_module }} worker -l info --concurrency=2 -B 4 | {%- endif %} 5 | -------------------------------------------------------------------------------- /{{cookiecutter.github_repository}}/provisioner/roles/project_data/templates/env.env.j2: -------------------------------------------------------------------------------- 1 | DATABASE_URL="{% if cookiecutter.add_postgis == 'y' %}postgis{% else %}postgres{% endif %}{%raw%}://{{ pg_user }}:{{ pg_password }}@localhost/{{ pg_db }}" 2 | DJANGO_SETTINGS_MODULE="{{ django_settings }}" 3 | {%endraw%} 4 | -------------------------------------------------------------------------------- /{{cookiecutter.github_repository}}/provisioner/roles/celery/handlers/main.yml: -------------------------------------------------------------------------------- 1 | {% raw %}--- 2 | - name: restart celery 3 | systemd: state=restarted daemon_reload=yes name=celery-{{ project_namespace }} 4 | 5 | - name: reload celery 6 | systemd: state=reloaded name=celery-{{ project_namespace }}{% endraw %} 7 | -------------------------------------------------------------------------------- /{{cookiecutter.github_repository}}/provisioner/roles/redis/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Update apt-cache if not already ran 3 | shell: apt update 4 | become: yes 5 | when: apt_updated is not defined 6 | 7 | - name: install redis server 8 | apt: 9 | pkg: redis-server 10 | state: present 11 | -------------------------------------------------------------------------------- /{{cookiecutter.github_repository}}/compose/dev/django/celery/flower/start: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -o errexit 4 | set -o nounset 5 | 6 | 7 | celery \ 8 | -A {{ cookiecutter.main_module }} \ 9 | -b "${CELERY_BROKER_URL}" \ 10 | flower \ 11 | --basic_auth="${CELERY_FLOWER_USER}:${CELERY_FLOWER_PASSWORD}" 12 | -------------------------------------------------------------------------------- /{{cookiecutter.github_repository}}/{{cookiecutter.main_module}}/users/serializers.py: -------------------------------------------------------------------------------- 1 | # Third Party Stuff 2 | from rest_framework import serializers 3 | 4 | from . import models 5 | 6 | 7 | class UserSerializer(serializers.ModelSerializer): 8 | class Meta: 9 | model = models.User 10 | fields = ["id", "first_name", "last_name", "email"] 11 | -------------------------------------------------------------------------------- /{{cookiecutter.github_repository}}/compose/local/start: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -o errexit 4 | set -o pipefail 5 | set -o nounset 6 | 7 | 8 | python manage.py migrate 9 | 10 | {%- if cookiecutter.add_asgi.lower() == "y" %} 11 | uvicorn config.asgi:application --host 0.0.0.0 --reload 12 | {%- else %} 13 | python manage.py runserver_plus 0.0.0.0:8000 14 | {%- endif %} 15 | -------------------------------------------------------------------------------- /{{cookiecutter.github_repository}}/provisioner/roles/nginx/tasks/htpasswd.yml: -------------------------------------------------------------------------------- 1 | {%raw%}--- 2 | # htpasswd module needs passlib 3 | - name: Install passlib 4 | pip: name=passlib 5 | 6 | - name: Create htpasswd file 7 | htpasswd: 8 | path: '{{ htpasswd_file_path }}' 9 | name: '{{ nginx_docs_username }}' 10 | password: '{{ nginx_docs_password }}'{%endraw%} 11 | -------------------------------------------------------------------------------- /{{cookiecutter.github_repository}}/bin/post_compile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Run migration while answering "yes" to any question prompted. 4 | yes "yes" | python manage.py migrate 5 | 6 | # Run production checks 7 | python manage.py check --deploy 8 | 9 | # Run collectstatic 10 | python manage.py collectstatic --noinput 11 | 12 | # used by heroku after slug compilation 13 | -------------------------------------------------------------------------------- /{{cookiecutter.github_repository}}/{{cookiecutter.main_module}}/templates/rest_framework/login.html: -------------------------------------------------------------------------------- 1 | {% raw %}{% extends "rest_framework/login_base.html" %} 2 | 3 | {# Override this template in your own templates directory to customize #} 4 | {% block branding %}
{% trans 'About page coming soon!' %}
8 | {% endblock content %}{% endraw %} 9 | -------------------------------------------------------------------------------- /{{cookiecutter.github_repository}}/{{cookiecutter.main_module}}/templates/pages/home.html: -------------------------------------------------------------------------------- 1 | {% raw %}{% extends 'pages/base.html' %} 2 | {% load i18n %} 3 | {% block title %}{% trans 'Home' %} • {{ block.super }}{% endblock title %} 4 | {% block body_classes %}{{ block.super}} page-home {% endblock body_classes %} 5 | 6 | {% block content %} 7 |{% trans 'You are not authorized to access this page.' %}
12 | {% endblock content %}{% endraw %} 13 | -------------------------------------------------------------------------------- /{{cookiecutter.github_repository}}/asgi.py: -------------------------------------------------------------------------------- 1 | # Standard Library 2 | import os 3 | 4 | # Third Party Stuff 5 | from django.core.asgi import get_asgi_application 6 | from dotenv import load_dotenv 7 | 8 | # Read .env file and set key/value inside it as environment variables 9 | # see: http://github.com/theskumar/python-dotenv 10 | load_dotenv(os.path.join(os.path.dirname(__file__), ".env")) 11 | 12 | # We defer to a DJANGO_SETTINGS_MODULE already in the environment. 13 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings.production") 14 | 15 | application = get_asgi_application() 16 | -------------------------------------------------------------------------------- /{{cookiecutter.github_repository}}/{{cookiecutter.main_module}}/templates/400.html: -------------------------------------------------------------------------------- 1 | {% raw %}{% extends "base.html" %} 2 | {% load i18n %} 3 | {% block title %}{% trans 'Bad Request (400)' %}{% endblock %} 4 | {% block head_extras %} 5 | 6 | {% endblock head_extras %} 7 | {% block body_classes %}{{ block.super }} page-error page-400 {% endblock body_classes %} 8 | 9 | {% block content %} 10 |{% trans 'The request cannot be fulfilled due to bad syntax.' %}
12 | {% endblock content %}{% endraw %} 13 | -------------------------------------------------------------------------------- /{{cookiecutter.github_repository}}/{{cookiecutter.main_module}}/templates/admin/auth/user/add_form.html: -------------------------------------------------------------------------------- 1 | {% raw %}{% extends "admin/change_form.html" %} 2 | {% load i18n %} 3 | 4 | {% block form_top %} 5 | {% if not is_popup %} 6 |{% trans "First, enter an email and password. Then, you'll be able to edit more user options." %}
7 | {% else %} 8 |{% trans "Enter an email and password." %}
9 | {% endif %} 10 | {% endblock %} 11 | 12 | {% block after_field_sets %} 13 | 14 | {% endblock %} 15 | {% endraw %} 16 | -------------------------------------------------------------------------------- /{{cookiecutter.github_repository}}/{{cookiecutter.main_module}}/users/auth/services.py: -------------------------------------------------------------------------------- 1 | # Third Party Stuff 2 | from django.conf import settings 3 | from mail_templated import send_mail 4 | 5 | from .tokens import get_token_for_password_reset 6 | 7 | 8 | def send_password_reset_mail(user, template_name="email/password_reset_mail.tpl"): 9 | ctx = {"user": user, "token": get_token_for_password_reset(user)} 10 | 11 | return send_mail( 12 | from_email=settings.DEFAULT_FROM_EMAIL, 13 | recipient_list=[user.email], 14 | template_name=template_name, 15 | context=ctx, 16 | ) 17 | -------------------------------------------------------------------------------- /{{cookiecutter.github_repository}}/docs/graphql/errors.md: -------------------------------------------------------------------------------- 1 | # Errors 2 | 3 | ## Generic Errors 4 | 5 | 6 | For `/graphql` requests, the API will return the error in the following format: 7 | 8 | ```json 9 | { 10 | "errors": [ 11 | { 12 | "message": "You do not have permission to perform this action", 13 | "locations": [ 14 | { 15 | "line": 33, 16 | "column": 3 17 | } 18 | ], 19 | "path": [ 20 | "users" 21 | ] 22 | } 23 | ] 24 | } 25 | ``` 26 | 27 | __NOTE__: The copy for most of these error messages can be changed by backend developers. 28 | -------------------------------------------------------------------------------- /{{cookiecutter.github_repository}}/manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Standard Library 4 | import os 5 | import sys 6 | 7 | # Third Party Stuff 8 | from django.core.management import execute_from_command_line 9 | from dotenv import load_dotenv 10 | 11 | if __name__ == "__main__": 12 | 13 | # Read .env file and set key/value inside it as environement variables 14 | # see: http://github.com/theskumar/python-dotenv 15 | load_dotenv(os.path.join(os.path.dirname(__file__), ".env")) 16 | 17 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings.development") 18 | 19 | execute_from_command_line(sys.argv) 20 | -------------------------------------------------------------------------------- /{{cookiecutter.github_repository}}/{{cookiecutter.main_module}}/templates/rest_framework/api.html: -------------------------------------------------------------------------------- 1 | {% raw %}{% extends "rest_framework/base.html" %} 2 | 3 | {# Override this template in your own templates directory to customize #} 4 | {% block title %}{% endraw %}{{ cookiecutter.project_name }}{% raw %} API{% endblock %} 5 | {% block branding %}{% endraw %}{{ cookiecutter.project_name }}{% raw %} API {% endraw %}{{ cookiecutter.version }}{% raw %}{% endblock %}{% endraw %} 6 | -------------------------------------------------------------------------------- /{{cookiecutter.github_repository}}/{{cookiecutter.main_module}}/base/api/schemas.py: -------------------------------------------------------------------------------- 1 | # Third Party Stuff 2 | from rest_framework.permissions import AllowAny 3 | from drf_yasg.views import get_schema_view 4 | from drf_yasg import openapi 5 | 6 | 7 | schema_view = get_schema_view( 8 | openapi.Info( 9 | title="{{ cookiecutter.project_name }} API", 10 | default_version="{{ cookiecutter.version }}", 11 | description="{{ cookiecutter.project_description }}", 12 | ), 13 | public=True, 14 | permission_classes=[AllowAny], 15 | ) 16 | 17 | swagger_schema_view = schema_view.with_ui("swagger", cache_timeout=0) 18 | -------------------------------------------------------------------------------- /generate-history.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Generates changelog day by day 4 | NEXT=$(date +%F) 5 | echo "History" 6 | echo "----------------------" 7 | echo "Note: This file is autogenerated with [generate-history.sh](generate-history.sh)" 8 | git log --no-merges --format="%cd" --date=short | sort -u -r | while read DATE ; do 9 | echo 10 | echo "### $DATE" 11 | GIT_PAGER=cat git log --no-merges --invert-grep --grep="release:" --format=" - %s ([%aN])" --since="$DATE 00:00" --until="$DATE 23:59" 12 | done 13 | 14 | echo "" 15 | echo "----------------------" 16 | echo "" 17 | if test -f "AUTHORS.md"; then 18 | cat AUTHORS.md 19 | fi 20 | 21 | -------------------------------------------------------------------------------- /{{cookiecutter.github_repository}}/provisioner/roles/celery/defaults/main.yml: -------------------------------------------------------------------------------- 1 | {% raw %}--- 2 | celery_user: www-data 3 | celery_group: www-data 4 | celery_log_dir: /var/log/celery 5 | celery_log_file: "{{ celery_log_dir }}/{{ project_namespace }}.log" 6 | celerybeat_log_file: "{{ celery_log_dir }}/{{ project_namespace }}.celerybeat.log" 7 | celery_log_level: "INFO" 8 | celery_runtime_dir: celery 9 | celerybeat_schedule_dir: "/var/run/{{ celery_runtime_dir }}" 10 | celerybeat_schedule_file: "{{ celerybeat_schedule_dir }}/schedule-{{ project_namespace }}.db" 11 | celery_pid_file: "/tmp/celery-{{ project_namespace }}.pid" 12 | celerybeat_pid_file: "/tmp/celerybeat-{{ project_namespace }}.pid" 13 | {% endraw %} 14 | -------------------------------------------------------------------------------- /{{cookiecutter.github_repository}}/{{cookiecutter.main_module}}/templates/500.html: -------------------------------------------------------------------------------- 1 | {% raw %}{% extends "base.html" %} 2 | {% load i18n %} 3 | {% block title %}{% trans "Server Error" %}{% endblock %} 4 | {% block head_extras %} 5 | 6 | {% endblock head_extras %} 7 | {% block body_classes %}{{ block.super }} page-error page-500 {% endblock body_classes %} 8 | 9 | {% block content %} 10 |{% trans "Looks like something went wrong!" %}
13 | 14 |{% trans "We track these errors automatically, but if the problem persists feel free to contact us. In the meantime, try refreshing." %}
15 | {% endblock content %}{% endraw %} 16 | -------------------------------------------------------------------------------- /{{cookiecutter.github_repository}}/provisioner/roles/celery/templates/celerybeat.service.j2: -------------------------------------------------------------------------------- 1 | {%raw%} 2 | [Unit] 3 | Description=Celery Beat {{ project_namespace }} Service 4 | After=network.target 5 | 6 | [Service] 7 | User={{ celery_user }} 8 | RuntimeDirectory={{ celery_runtime_dir }} 9 | Group={{ celery_group }} 10 | Restart=always 11 | WorkingDirectory={{ project_path }} 12 | ExecStart={{ venv_path }}/bin/celery -A {{ project_name }} beat -l {{ celery_log_level }} \ 13 | --logfile={{ celerybeat_log_file }} --pidfile={{ celerybeat_pid_file }} --schedule={{ celerybeat_schedule_file}} 14 | 15 | [Install] 16 | WantedBy=multi-user.target 17 | Alias=celerybeat-{{ project_namespace }}.service 18 | {%endraw%} 19 | -------------------------------------------------------------------------------- /{{cookiecutter.github_repository}}/compose/dev/postgres/maintenance/_sourced/messages.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | 4 | message_newline() { 5 | echo 6 | } 7 | 8 | message_debug() 9 | { 10 | echo -e "DEBUG: ${@}" 11 | } 12 | 13 | message_welcome() 14 | { 15 | echo -e "\e[1m${@}\e[0m" 16 | } 17 | 18 | message_warning() 19 | { 20 | echo -e "\e[33mWARNING\e[0m: ${@}" 21 | } 22 | 23 | message_error() 24 | { 25 | echo -e "\e[31mERROR\e[0m: ${@}" 26 | } 27 | 28 | message_info() 29 | { 30 | echo -e "\e[37mINFO\e[0m: ${@}" 31 | } 32 | 33 | message_suggestion() 34 | { 35 | echo -e "\e[33mSUGGESTION\e[0m: ${@}" 36 | } 37 | 38 | message_success() 39 | { 40 | echo -e "\e[32mSUCCESS\e[0m: ${@}" 41 | } 42 | -------------------------------------------------------------------------------- /{{cookiecutter.github_repository}}/.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: git://github.com/pre-commit/pre-commit-hooks 3 | rev: v1.4.0 4 | hooks: 5 | - id: end-of-file-fixer 6 | - id: trailing-whitespace 7 | - id: check-case-conflict 8 | - id: check-merge-conflict 9 | - id: check-yaml 10 | args: ['--unsafe'] 11 | - id: detect-private-key 12 | - id: forbid-new-submodules 13 | - id: check-json 14 | - id: pretty-format-json 15 | - id: check-added-large-files 16 | - id: flake8 17 | 18 | - repo: https://github.com/pycqa/isort 19 | rev: 5.6.4 20 | hooks: 21 | - id: isort 22 | 23 | - repo: https://github.com/psf/black 24 | rev: stable 25 | hooks: 26 | - id: black 27 | -------------------------------------------------------------------------------- /{{cookiecutter.github_repository}}/provisioner/roles/project_data/templates/django.asgi.ini.j2: -------------------------------------------------------------------------------- 1 | {% raw %}[Unit] 2 | Description={{ project_namespace }} gunicorn daemon 3 | After=network.target 4 | 5 | [Service] 6 | Environment=LC_ALL=en_US.utf-8 7 | Environment=LANG=en_US.utf-8 8 | StandardOutput=syslog 9 | StandardError=syslog 10 | SyslogIdentifier=gunicorn 11 | User={{ asgi_user }} 12 | Group={{ asgi_group }} 13 | WorkingDirectory={{ project_path }} 14 | ExecStart={{ venv_path }}/bin/gunicorn -w {{ asgi_workers }} --bind unix://{{ asgi_socket }} --access-logfile {{project_log_dir}}/asgi.log --capture-output --error-logfile {{project_log_dir}}/asgi-errors.log -k uvicorn.workers.UvicornWorker asgi:application 15 | 16 | [Install] 17 | WantedBy=multi-user.target 18 | {% endraw %} 19 | -------------------------------------------------------------------------------- /{{cookiecutter.github_repository}}/provisioner/roles/nginx/handlers/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: restart nginx 3 | debug: msg="checking config first" 4 | changed_when: True 5 | notify: 6 | - check nginx configuration 7 | - restart nginx - after config check 8 | 9 | - name: reload nginx 10 | debug: msg="checking config first" 11 | changed_when: True 12 | notify: 13 | - check nginx configuration 14 | - reload nginx - after config check 15 | 16 | - name: check nginx configuration 17 | command: "nginx -t" 18 | register: result 19 | changed_when: "result.rc != 0" 20 | check_mode: no 21 | 22 | - name: restart nginx - after config check 23 | service: name=nginx state=restarted 24 | 25 | - name: reload nginx - after config check 26 | service: name=nginx state=reloaded 27 | -------------------------------------------------------------------------------- /{{cookiecutter.github_repository}}/docs/api/2-current-user.md: -------------------------------------------------------------------------------- 1 | # Current User 2 | 3 | ## Get profile 4 | ``` 5 | GET /api/me (requires authentication) 6 | ``` 7 | 8 | __Response__ 9 | 10 | ```json 11 | { 12 | "id": "629b1e03-53f0-43ef-9a03-17164cf782ac", 13 | "first_name": "John", 14 | "last_name": "Hawley", 15 | "email": "john@localhost.com" 16 | } 17 | ``` 18 | 19 | ## Update profile 20 | ``` 21 | PATCH /api/me (requires authentication) 22 | ``` 23 | 24 | __Example__ 25 | ```json 26 | { 27 | "first_name": "James", 28 | "last_name": "Warner" 29 | } 30 | ``` 31 | 32 | __Response__ 33 | 34 | ```json 35 | { 36 | "id": "629b1e03-53f0-43ef-9a03-17164cf782ac", 37 | "first_name": "James", 38 | "last_name": "Warner", 39 | "email": "john@localhost.com", 40 | } 41 | ``` 42 | -------------------------------------------------------------------------------- /{{cookiecutter.github_repository}}/tests/utils.py: -------------------------------------------------------------------------------- 1 | # Third Party Stuff 2 | from django.db.models import signals 3 | 4 | 5 | def signals_switch(): 6 | pre_save = signals.pre_save.receivers 7 | post_save = signals.post_save.receivers 8 | 9 | def disconnect(): 10 | signals.pre_save.receivers = [] 11 | signals.post_save.receivers = [] 12 | 13 | def reconnect(): 14 | signals.pre_save.receivers = pre_save 15 | signals.post_save.receivers = post_save 16 | 17 | return disconnect, reconnect 18 | 19 | 20 | disconnect_signals, reconnect_signals = signals_switch() 21 | 22 | 23 | def get_dict_from_list_where(my_list, key, value): 24 | """see: http://stackoverflow.com/a/7079297/782901""" 25 | return next((item for item in my_list if item[key] == value), None) 26 | -------------------------------------------------------------------------------- /{{cookiecutter.github_repository}}/{{cookiecutter.main_module}}/users/migrations/0002_auto_20171024_1200.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.5 on 2017-10-24 12:00 3 | from __future__ import unicode_literals 4 | 5 | import django.contrib.postgres.fields.citext 6 | from django.contrib.postgres.operations import CITextExtension 7 | from django.db import migrations 8 | 9 | 10 | class Migration(migrations.Migration): 11 | 12 | dependencies = [("users", "0001_initial")] 13 | 14 | operations = [ 15 | CITextExtension(), 16 | migrations.AlterField( 17 | model_name="user", 18 | name="email", 19 | field=django.contrib.postgres.fields.citext.CIEmailField( 20 | db_index=True, max_length=254, unique=True, verbose_name="email address" 21 | ), 22 | ), 23 | ] 24 | -------------------------------------------------------------------------------- /{{cookiecutter.github_repository}}/provisioner/roles/common/defaults/main.yml: -------------------------------------------------------------------------------- 1 | {% raw %}--- 2 | # roles/common/defaults/main.yml 3 | common_timezone: {% endraw %}{{ cookiecutter.timezone }}{% raw %} 4 | 5 | lc_lang: "en_US.UTF-8" 6 | lc_all: "en_US.utf8" 7 | lc_ctype: "en_US.utf8" 8 | lc_collate: "en_US.utf8" 9 | 10 | base_ubuntu: 11 | common: 12 | apt_packages: 13 | - build-essential 14 | - git 15 | - htop 16 | - curl 17 | - python-dev 18 | - python3.9 19 | - python3.9-dev 20 | - python-setuptools 21 | - python3-venv 22 | - python3-pip 23 | - sysstat 24 | - vim 25 | - fail2ban 26 | - libjpeg-dev 27 | - libtiff5-dev 28 | - zlib1g-dev 29 | - libfreetype6-dev 30 | - liblcms2-dev 31 | - postgresql-client 32 | - libpq-dev 33 | - libreadline-dev 34 | - ca-certificates 35 | {% endraw %} 36 | -------------------------------------------------------------------------------- /{{cookiecutter.github_repository}}/tests/factories.py: -------------------------------------------------------------------------------- 1 | """ 2 | Helpers to create dynamic model instances for testing purposes. 3 | 4 | Usages: 5 | >>> from tests import factories as f 6 | >>> 7 | >>> user = f.create_user(first_name="Robert", last_name="Downey") # creates single instance of user 8 | >>> users = f.create_user(n=5, is_active=False) # creates 5 instances of user 9 | 10 | There is a bit of magic going on behind the scenes with `G` method from https://django-dynamic-fixture.readthedocs.io/ 11 | """ 12 | 13 | # Third Party Stuff 14 | from django.apps import apps 15 | from django.conf import settings 16 | from django_dynamic_fixture import G 17 | 18 | 19 | def create_user(**kwargs): 20 | """Create an user along with their dependencies.""" 21 | User = apps.get_model(settings.AUTH_USER_MODEL) 22 | user = G(User, **kwargs) 23 | user.set_password(kwargs.get("password", "test")) 24 | user.save() 25 | return user 26 | -------------------------------------------------------------------------------- /{{cookiecutter.github_repository}}/{{cookiecutter.main_module}}/api_urls.py: -------------------------------------------------------------------------------- 1 | # Third Party Stuff 2 | from rest_framework.routers import DefaultRouter 3 | 4 | # {{ cookiecutter.main_module }} Stuff 5 | from {{cookiecutter.main_module}}.base.api.routers import SingletonRouter 6 | from {{cookiecutter.main_module}}.users.api import CurrentUserViewSet 7 | from {{cookiecutter.main_module}}.users.auth.api import AuthViewSet 8 | 9 | default_router = DefaultRouter(trailing_slash=False) 10 | singleton_router = SingletonRouter(trailing_slash=False) 11 | 12 | # Register all the django rest framework viewsets below. 13 | default_router.register("auth", AuthViewSet, basename="auth") 14 | singleton_router.register("me", CurrentUserViewSet, basename="me") 15 | 16 | # Combine urls from both default and singleton routers and expose as 17 | # 'urlpatterns' which django can pick up from this module. 18 | urlpatterns = default_router.urls + singleton_router.urls 19 | -------------------------------------------------------------------------------- /{{cookiecutter.github_repository}}/tests/integration/test_site_pages.py: -------------------------------------------------------------------------------- 1 | # Third Party Stuff 2 | import pytest 3 | from django.urls import reverse 4 | 5 | pytestmark = pytest.mark.django_db 6 | 7 | 8 | def test_root_txt_files(client): 9 | files = ["robots.txt", "humans.txt"] 10 | for filename in files: 11 | url = reverse("root-txt-files", kwargs={"filename": filename}) 12 | response = client.get(url) 13 | assert response.status_code == 200 14 | assert response["Content-Type"] == "text/plain" 15 | 16 | 17 | def test_landing_pages(client): 18 | # Test that these urls are rendered properly and doesn't required authorization 19 | urls = ["/about/", "/"] 20 | for url in urls: 21 | response = client.get(url) 22 | assert response.status_code == 200 23 | assert response["Content-Type"] == "text/html; charset=utf-8" 24 | assert "