├── config ├── settings │ ├── __init__.py │ ├── test.py │ ├── local.py │ ├── production.py │ └── base.py ├── __init__.py ├── celery_app.py ├── wsgi.py └── urls.py ├── .gitattributes ├── aws_ecs_deploy ├── users │ ├── __init__.py │ ├── tests │ │ ├── __init__.py │ │ ├── test_models.py │ │ ├── test_tasks.py │ │ ├── test_urls.py │ │ ├── factories.py │ │ ├── test_forms.py │ │ └── test_views.py │ ├── migrations │ │ ├── __init__.py │ │ └── 0001_initial.py │ ├── tasks.py │ ├── apps.py │ ├── urls.py │ ├── models.py │ ├── admin.py │ ├── adapters.py │ ├── forms.py │ └── views.py ├── utils │ ├── __init__.py │ ├── context_processors.py │ └── storages.py ├── static │ ├── fonts │ │ └── .gitkeep │ ├── sass │ │ ├── custom_bootstrap_vars.scss │ │ └── project.scss │ ├── js │ │ └── project.js │ ├── images │ │ └── favicons │ │ │ └── favicon.ico │ └── css │ │ └── project.css ├── templates │ ├── pages │ │ ├── about.html │ │ └── home.html │ ├── 403.html │ ├── 404.html │ ├── account │ │ ├── base.html │ │ ├── account_inactive.html │ │ ├── password_reset_from_key_done.html │ │ ├── signup_closed.html │ │ ├── verification_sent.html │ │ ├── password_set.html │ │ ├── password_reset_done.html │ │ ├── password_change.html │ │ ├── logout.html │ │ ├── signup.html │ │ ├── verified_email_required.html │ │ ├── password_reset.html │ │ ├── email_confirm.html │ │ ├── password_reset_from_key.html │ │ ├── login.html │ │ └── email.html │ ├── 500.html │ ├── users │ │ ├── user_form.html │ │ └── user_detail.html │ └── base.html ├── __init__.py ├── contrib │ ├── __init__.py │ └── sites │ │ ├── __init__.py │ │ └── migrations │ │ ├── __init__.py │ │ ├── 0002_alter_domain_unique.py │ │ ├── 0003_set_site_domain_and_name.py │ │ └── 0001_initial.py └── conftest.py ├── .dockerignore ├── docs ├── __init__.py ├── index.rst ├── Makefile ├── make.bat └── conf.py ├── pytest.ini ├── compose ├── production │ ├── ecs │ │ ├── nginx │ │ │ ├── Dockerfile │ │ │ └── nginx.conf │ │ └── django │ │ │ ├── start │ │ │ ├── entrypoint │ │ │ └── Dockerfile │ └── standalone │ │ ├── postgres │ │ ├── maintenance │ │ │ ├── _sourced │ │ │ │ ├── constants.sh │ │ │ │ ├── yes_no.sh │ │ │ │ ├── countdown.sh │ │ │ │ └── messages.sh │ │ │ ├── backups │ │ │ ├── backup │ │ │ └── restore │ │ └── Dockerfile │ │ ├── django │ │ ├── celery │ │ │ ├── worker │ │ │ │ └── start │ │ │ ├── flower │ │ │ │ └── start │ │ │ └── beat │ │ │ │ └── start │ │ ├── start │ │ ├── entrypoint │ │ └── Dockerfile │ │ ├── traefik │ │ ├── Dockerfile │ │ └── traefik.yml │ │ └── aws │ │ ├── Dockerfile │ │ └── maintenance │ │ ├── download │ │ └── upload └── local │ └── django │ ├── celery │ ├── worker │ │ └── start │ ├── flower │ │ └── start │ └── beat │ │ └── start │ ├── start │ └── Dockerfile ├── locale └── README.rst ├── .pylintrc ├── .envs ├── .local │ ├── .postgres │ └── .django └── .production │ ├── .postgres │ ├── template.env │ └── .django ├── .travis.yml ├── appspec.yaml ├── .pre-commit-config.yaml ├── requirements ├── production.txt ├── base.txt └── local.txt ├── .editorconfig ├── setup.cfg ├── manage.py ├── Dockerfile ├── local.yml ├── .github └── workflows │ ├── nginx.yml │ └── aws.yml ├── production.yml ├── merge_production_dotenvs_in_dotenv.py ├── aws-task-definition.json ├── .gitignore ├── LICENSE └── README.rst /config/settings/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | -------------------------------------------------------------------------------- /aws_ecs_deploy/users/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /aws_ecs_deploy/utils/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /aws_ecs_deploy/static/fonts/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /aws_ecs_deploy/users/tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /aws_ecs_deploy/users/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | .* 2 | !.coveragerc 3 | !.pylintrc 4 | -------------------------------------------------------------------------------- /aws_ecs_deploy/static/sass/custom_bootstrap_vars.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /aws_ecs_deploy/templates/pages/about.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} -------------------------------------------------------------------------------- /aws_ecs_deploy/templates/pages/home.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} -------------------------------------------------------------------------------- /aws_ecs_deploy/static/js/project.js: -------------------------------------------------------------------------------- 1 | /* Project specific Javascript goes here. */ 2 | -------------------------------------------------------------------------------- /docs/__init__.py: -------------------------------------------------------------------------------- 1 | # Included so that Django's startproject comment runs against the docs directory 2 | -------------------------------------------------------------------------------- /pytest.ini: -------------------------------------------------------------------------------- 1 | [pytest] 2 | addopts = --ds=config.settings.test --reuse-db 3 | python_files = tests.py test_*.py 4 | -------------------------------------------------------------------------------- /compose/production/ecs/nginx/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM nginx 2 | COPY ./compose/production/ecs/nginx/nginx.conf /etc/nginx/nginx.conf 3 | -------------------------------------------------------------------------------- /compose/local/django/celery/worker/start: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -o errexit 4 | set -o nounset 5 | 6 | 7 | celery -A config.celery_app worker -l INFO 8 | -------------------------------------------------------------------------------- /aws_ecs_deploy/utils/context_processors.py: -------------------------------------------------------------------------------- 1 | from django.conf import settings 2 | 3 | 4 | def settings_context(_request): 5 | return {"settings": settings} 6 | -------------------------------------------------------------------------------- /locale/README.rst: -------------------------------------------------------------------------------- 1 | Translations 2 | ============ 3 | 4 | Translations will be placed in this folder when running:: 5 | 6 | python manage.py makemessages 7 | -------------------------------------------------------------------------------- /compose/production/standalone/postgres/maintenance/_sourced/constants.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | 4 | BACKUP_DIR_PATH='/backups' 5 | BACKUP_FILE_PREFIX='backup' 6 | -------------------------------------------------------------------------------- /aws_ecs_deploy/static/images/favicons/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Andrew-Chen-Wang/cookiecutter-django-ecs-github/HEAD/aws_ecs_deploy/static/images/favicons/favicon.ico -------------------------------------------------------------------------------- /compose/production/standalone/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 config.celery_app worker -l INFO 9 | -------------------------------------------------------------------------------- /compose/local/django/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 | python manage.py runserver_plus 0.0.0.0:8000 10 | -------------------------------------------------------------------------------- /config/__init__.py: -------------------------------------------------------------------------------- 1 | # This will make sure the app is always imported when 2 | # Django starts so that shared_task will use this app. 3 | from .celery_app import app as celery_app 4 | 5 | __all__ = ("celery_app",) 6 | -------------------------------------------------------------------------------- /aws_ecs_deploy/__init__.py: -------------------------------------------------------------------------------- 1 | __version__ = "0.1.0" 2 | __version_info__ = tuple( 3 | [ 4 | int(num) if num.isdigit() else num 5 | for num in __version__.replace("-", ".", 1).split(".") 6 | ] 7 | ) 8 | -------------------------------------------------------------------------------- /aws_ecs_deploy/contrib/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | To understand why this file is here, please read: 3 | 4 | http://cookiecutter-django.readthedocs.io/en/latest/faq.html#why-is-there-a-django-contrib-sites-directory-in-cookiecutter-django 5 | """ 6 | -------------------------------------------------------------------------------- /aws_ecs_deploy/contrib/sites/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | To understand why this file is here, please read: 3 | 4 | http://cookiecutter-django.readthedocs.io/en/latest/faq.html#why-is-there-a-django-contrib-sites-directory-in-cookiecutter-django 5 | """ 6 | -------------------------------------------------------------------------------- /aws_ecs_deploy/contrib/sites/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | To understand why this file is here, please read: 3 | 4 | http://cookiecutter-django.readthedocs.io/en/latest/faq.html#why-is-there-a-django-contrib-sites-directory-in-cookiecutter-django 5 | """ 6 | -------------------------------------------------------------------------------- /aws_ecs_deploy/templates/403.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}Forbidden (403){% endblock %} 4 | 5 | {% block content %} 6 |
CSRF verification failed. Request aborted.
9 | {% endblock content %} 10 | -------------------------------------------------------------------------------- /aws_ecs_deploy/templates/404.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}Page not found{% endblock %} 4 | 5 | {% block content %} 6 |This is not the page you were looking for.
9 | {% endblock content %} 10 | -------------------------------------------------------------------------------- /compose/production/standalone/django/start: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -o errexit 4 | set -o pipefail 5 | set -o nounset 6 | 7 | 8 | python /app/manage.py collectstatic --noinput 9 | 10 | /usr/local/bin/gunicorn config.wsgi --bind 0.0.0.0:5000 --chdir=/app 11 | -------------------------------------------------------------------------------- /compose/local/django/celery/flower/start: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -o errexit 4 | set -o nounset 5 | 6 | 7 | celery flower \ 8 | --app=config.celery_app \ 9 | --broker="${CELERY_BROKER_URL}" \ 10 | --basic_auth="${CELERY_FLOWER_USER}:${CELERY_FLOWER_PASSWORD}" 11 | -------------------------------------------------------------------------------- /aws_ecs_deploy/users/tests/test_models.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from aws_ecs_deploy.users.models import User 4 | 5 | pytestmark = pytest.mark.django_db 6 | 7 | 8 | def test_user_get_absolute_url(user: User): 9 | assert user.get_absolute_url() == f"/users/{user.username}/" 10 | -------------------------------------------------------------------------------- /compose/production/standalone/django/celery/flower/start: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -o errexit 4 | set -o nounset 5 | 6 | 7 | celery flower \ 8 | --app=config.celery_app \ 9 | --broker="${CELERY_BROKER_URL}" \ 10 | --basic_auth="${CELERY_FLOWER_USER}:${CELERY_FLOWER_PASSWORD}" 11 | -------------------------------------------------------------------------------- /compose/production/standalone/postgres/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM postgres:11.3 2 | 3 | COPY ./compose/production/standalone/postgres/maintenance /usr/local/bin/maintenance 4 | RUN chmod +x /usr/local/bin/maintenance/* 5 | RUN mv /usr/local/bin/maintenance/* /usr/local/bin \ 6 | && rmdir /usr/local/bin/maintenance 7 | -------------------------------------------------------------------------------- /compose/production/standalone/traefik/Dockerfile: -------------------------------------------------------------------------------- 1 | # Use this if you do this with plain EC2 2 | 3 | FROM traefik:v2.0 4 | RUN mkdir -p /etc/traefik/acme 5 | RUN touch /etc/traefik/acme/acme.json 6 | RUN chmod 600 /etc/traefik/acme/acme.json 7 | COPY ./compose/production/standalone/traefik/traefik.yml /etc/traefik 8 | -------------------------------------------------------------------------------- /aws_ecs_deploy/users/tasks.py: -------------------------------------------------------------------------------- 1 | from django.contrib.auth import get_user_model 2 | 3 | from config import celery_app 4 | 5 | User = get_user_model() 6 | 7 | 8 | @celery_app.task() 9 | def get_users_count(): 10 | """A pointless Celery task to demonstrate usage.""" 11 | return User.objects.count() 12 | -------------------------------------------------------------------------------- /compose/production/ecs/django/start: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -o errexit 4 | set -o pipefail 5 | set -o nounset 6 | 7 | export DJANGO_SETTINGS_MODULE="config.settings.production" 8 | python /app/manage.py collectstatic --noinput 9 | 10 | /usr/local/bin/gunicorn config.wsgi --bind 0.0.0.0:5000 --chdir=/app 11 | -------------------------------------------------------------------------------- /aws_ecs_deploy/static/css/project.css: -------------------------------------------------------------------------------- 1 | /* These styles are generated from project.scss. */ 2 | 3 | .alert-debug { 4 | color: black; 5 | background-color: white; 6 | border-color: #d6e9c6; 7 | } 8 | 9 | .alert-error { 10 | color: #b94a48; 11 | background-color: #f2dede; 12 | border-color: #eed3d7; 13 | } 14 | -------------------------------------------------------------------------------- /.pylintrc: -------------------------------------------------------------------------------- 1 | [MASTER] 2 | load-plugins=pylint_django, pylint_celery 3 | 4 | [FORMAT] 5 | max-line-length=120 6 | 7 | [MESSAGES CONTROL] 8 | disable=missing-docstring,invalid-name 9 | 10 | [DESIGN] 11 | max-parents=13 12 | 13 | [TYPECHECK] 14 | generated-members=REQUEST,acl_users,aq_parent,"[a-zA-Z]+_set{1,2}",save,delete 15 | -------------------------------------------------------------------------------- /.envs/.local/.postgres: -------------------------------------------------------------------------------- 1 | # PostgreSQL 2 | # ------------------------------------------------------------------------------ 3 | POSTGRES_HOST=postgres 4 | POSTGRES_PORT=5432 5 | POSTGRES_DB=aws_ecs_deploy 6 | POSTGRES_USER=ijqMHHwxondYmdEMKIhDpTzNBkuONwKx 7 | POSTGRES_PASSWORD=98Y3d9pH3HTvOCHZ8lQmmOgNuOWWyPxvkYc6HfmfyNGTyOKY6svciDHD5KcGRF6x 8 | -------------------------------------------------------------------------------- /aws_ecs_deploy/templates/account/base.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% block title %}{% block head_title %}{% endblock head_title %}{% endblock title %} 3 | 4 | {% block content %} 5 |{% trans "This account is inactive." %}
11 | {% endblock %} 12 | 13 | -------------------------------------------------------------------------------- /aws_ecs_deploy/templates/account/password_reset_from_key_done.html: -------------------------------------------------------------------------------- 1 | {% extends "account/base.html" %} 2 | 3 | {% load i18n %} 4 | {% block head_title %}{% trans "Change Password" %}{% endblock %} 5 | 6 | {% block inner %} 7 |{% trans 'Your password is now changed.' %}
9 | {% endblock %} 10 | 11 | -------------------------------------------------------------------------------- /aws_ecs_deploy/templates/account/signup_closed.html: -------------------------------------------------------------------------------- 1 | {% extends "account/base.html" %} 2 | 3 | {% load i18n %} 4 | 5 | {% block head_title %}{% trans "Sign Up Closed" %}{% endblock %} 6 | 7 | {% block inner %} 8 |{% trans "We are sorry, but the sign up is currently closed." %}
11 | {% endblock %} 12 | 13 | -------------------------------------------------------------------------------- /aws_ecs_deploy/conftest.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from aws_ecs_deploy.users.models import User 4 | from aws_ecs_deploy.users.tests.factories import UserFactory 5 | 6 | 7 | @pytest.fixture(autouse=True) 8 | def media_storage(settings, tmpdir): 9 | settings.MEDIA_ROOT = tmpdir.strpath 10 | 11 | 12 | @pytest.fixture 13 | def user() -> User: 14 | return UserFactory() 15 | -------------------------------------------------------------------------------- /aws_ecs_deploy/templates/500.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}Server Error{% endblock %} 4 | 5 | {% block content %} 6 |We track these errors automatically, but if the problem persists feel free to contact us. In the meantime, try refreshing.
11 | {% endblock content %} 12 | 13 | 14 | -------------------------------------------------------------------------------- /aws_ecs_deploy/users/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | from django.utils.translation import gettext_lazy as _ 3 | 4 | 5 | class UsersConfig(AppConfig): 6 | name = "aws_ecs_deploy.users" 7 | verbose_name = _("Users") 8 | 9 | def ready(self): 10 | try: 11 | import aws_ecs_deploy.users.signals # noqa F401 12 | except ImportError: 13 | pass 14 | -------------------------------------------------------------------------------- /compose/production/standalone/aws/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM garland/aws-cli-docker:1.15.47 2 | 3 | COPY ./compose/production/aws/maintenance /usr/local/bin/maintenance 4 | COPY ./compose/production/postgres/maintenance/_sourced /usr/local/bin/maintenance/_sourced 5 | 6 | RUN chmod +x /usr/local/bin/maintenance/* 7 | 8 | RUN mv /usr/local/bin/maintenance/* /usr/local/bin \ 9 | && rmdir /usr/local/bin/maintenance 10 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | os: linux 2 | dist: xenial 3 | 4 | services: 5 | - docker 6 | 7 | language: python 8 | python: 9 | - "3.7" 10 | 11 | jobs: 12 | include: 13 | - name: "Django Test" 14 | script: 15 | - docker-compose -f local.yml build 16 | - docker-compose -f local.yml up -d 17 | - docker-compose -f local.yml run --rm django pytest 18 | - docker-compose -f local.yml logs 19 | -------------------------------------------------------------------------------- /compose/production/standalone/postgres/maintenance/_sourced/yes_no.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | 4 | yes_no() { 5 | declare desc="Prompt for confirmation. \$\"\{1\}\": confirmation message." 6 | local arg1="${1}" 7 | 8 | local response= 9 | read -r -p "${arg1} (y/[n])? " response 10 | if [[ "${response}" =~ ^[Yy]$ ]] 11 | then 12 | exit 0 13 | else 14 | exit 1 15 | fi 16 | } 17 | -------------------------------------------------------------------------------- /compose/production/standalone/postgres/maintenance/_sourced/countdown.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | 4 | countdown() { 5 | declare desc="A simple countdown. Source: https://superuser.com/a/611582" 6 | local seconds="${1}" 7 | local d=$(($(date +%s) + "${seconds}")) 8 | while [ "$d" -ge `date +%s` ]; do 9 | echo -ne "$(date -u --date @$(($d - `date +%s`)) +%H:%M:%S)\r"; 10 | sleep 0.1 11 | done 12 | } 13 | -------------------------------------------------------------------------------- /aws_ecs_deploy/users/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | 3 | from aws_ecs_deploy.users.views import ( 4 | user_detail_view, 5 | user_redirect_view, 6 | user_update_view, 7 | ) 8 | 9 | app_name = "users" 10 | urlpatterns = [ 11 | path("~redirect/", view=user_redirect_view, name="redirect"), 12 | path("~update/", view=user_update_view, name="update"), 13 | path("{% blocktrans %}We have sent an e-mail to you for verification. Follow the link provided to finalize the signup process. Please contact us if you do not receive it within a few minutes.{% endblocktrans %}
11 | 12 | {% endblock %} 13 | 14 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | exclude: 'docs|node_modules|migrations|.git|.tox' 2 | default_stages: [commit] 3 | fail_fast: true 4 | 5 | repos: 6 | - repo: https://github.com/pre-commit/pre-commit-hooks 7 | rev: master 8 | hooks: 9 | - id: trailing-whitespace 10 | files: (^|/).+\.(py|html|sh|css|js)$ 11 | 12 | - repo: local 13 | hooks: 14 | - id: flake8 15 | name: flake8 16 | entry: flake8 17 | language: python 18 | types: [python] 19 | args: ['--config=setup.cfg'] 20 | 21 | -------------------------------------------------------------------------------- /compose/production/standalone/postgres/maintenance/backups: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | 4 | ### View backups. 5 | ### 6 | ### Usage: 7 | ### $ docker-compose -f{% blocktrans %}We have sent you an e-mail. Please contact us if you do not receive it within a few minutes.{% endblocktrans %}
16 | {% endblock %} 17 | 18 | -------------------------------------------------------------------------------- /aws_ecs_deploy/users/tests/test_tasks.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from celery.result import EagerResult 3 | 4 | from aws_ecs_deploy.users.tasks import get_users_count 5 | from aws_ecs_deploy.users.tests.factories import UserFactory 6 | 7 | pytestmark = pytest.mark.django_db 8 | 9 | 10 | def test_user_count(settings): 11 | """A basic test to execute the get_users_count Celery task.""" 12 | UserFactory.create_batch(3) 13 | settings.CELERY_TASK_ALWAYS_EAGER = True 14 | task_result = get_users_count.delay() 15 | assert isinstance(task_result, EagerResult) 16 | assert task_result.result == 3 17 | -------------------------------------------------------------------------------- /aws_ecs_deploy/templates/account/password_change.html: -------------------------------------------------------------------------------- 1 | {% extends "account/base.html" %} 2 | 3 | {% load i18n %} 4 | {% load crispy_forms_tags %} 5 | 6 | {% block head_title %}{% trans "Change Password" %}{% endblock %} 7 | 8 | {% block inner %} 9 |{% trans 'Are you sure you want to sign out?' %}
11 | 12 | 19 | 20 | 21 | {% endblock %} 22 | 23 | -------------------------------------------------------------------------------- /aws_ecs_deploy/contrib/sites/migrations/0002_alter_domain_unique.py: -------------------------------------------------------------------------------- 1 | import django.contrib.sites.models 2 | from django.db import migrations, models 3 | 4 | 5 | class Migration(migrations.Migration): 6 | 7 | dependencies = [("sites", "0001_initial")] 8 | 9 | operations = [ 10 | migrations.AlterField( 11 | model_name="site", 12 | name="domain", 13 | field=models.CharField( 14 | max_length=100, 15 | unique=True, 16 | validators=[django.contrib.sites.models._simple_domain_name_validator], 17 | verbose_name="domain name", 18 | ), 19 | ) 20 | ] 21 | -------------------------------------------------------------------------------- /aws_ecs_deploy/users/adapters.py: -------------------------------------------------------------------------------- 1 | from typing import Any 2 | 3 | from allauth.account.adapter import DefaultAccountAdapter 4 | from allauth.socialaccount.adapter import DefaultSocialAccountAdapter 5 | from django.conf import settings 6 | from django.http import HttpRequest 7 | 8 | 9 | class AccountAdapter(DefaultAccountAdapter): 10 | def is_open_for_signup(self, request: HttpRequest): 11 | return getattr(settings, "ACCOUNT_ALLOW_REGISTRATION", True) 12 | 13 | 14 | class SocialAccountAdapter(DefaultSocialAccountAdapter): 15 | def is_open_for_signup(self, request: HttpRequest, sociallogin: Any): 16 | return getattr(settings, "ACCOUNT_ALLOW_REGISTRATION", True) 17 | -------------------------------------------------------------------------------- /compose/production/standalone/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 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line, and also 5 | # from the environment for the first two. 6 | SPHINXOPTS ?= 7 | SPHINXBUILD ?= sphinx-build 8 | SOURCEDIR = . 9 | BUILDDIR = _build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 21 | -------------------------------------------------------------------------------- /aws_ecs_deploy/users/tests/test_urls.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from django.urls import resolve, reverse 3 | 4 | from aws_ecs_deploy.users.models import User 5 | 6 | pytestmark = pytest.mark.django_db 7 | 8 | 9 | def test_detail(user: User): 10 | assert ( 11 | reverse("users:detail", kwargs={"username": user.username}) 12 | == f"/users/{user.username}/" 13 | ) 14 | assert resolve(f"/users/{user.username}/").view_name == "users:detail" 15 | 16 | 17 | def test_update(): 18 | assert reverse("users:update") == "/users/~update/" 19 | assert resolve("/users/~update/").view_name == "users:update" 20 | 21 | 22 | def test_redirect(): 23 | assert reverse("users:redirect") == "/users/~redirect/" 24 | assert resolve("/users/~redirect/").view_name == "users:redirect" 25 | -------------------------------------------------------------------------------- /aws_ecs_deploy/static/sass/project.scss: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | // project specific CSS goes here 6 | 7 | //////////////////////////////// 8 | //Variables// 9 | //////////////////////////////// 10 | 11 | // Alert colors 12 | 13 | $white: #fff; 14 | $mint-green: #d6e9c6; 15 | $black: #000; 16 | $pink: #f2dede; 17 | $dark-pink: #eed3d7; 18 | $red: #b94a48; 19 | 20 | //////////////////////////////// 21 | //Alerts// 22 | //////////////////////////////// 23 | 24 | // bootstrap alert CSS, translated to the django-standard levels of 25 | // debug, info, success, warning, error 26 | 27 | .alert-debug { 28 | background-color: $white; 29 | border-color: $mint-green; 30 | color: $black; 31 | } 32 | 33 | .alert-error { 34 | background-color: $pink; 35 | border-color: $dark-pink; 36 | color: $red; 37 | } 38 | -------------------------------------------------------------------------------- /compose/production/standalone/aws/maintenance/download: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | ### Download a file from your Amazon S3 bucket to the postgres /backups folder 4 | ### 5 | ### Usage: 6 | ### $ docker-compose -f production.yml run --rm awscli <1> 7 | 8 | set -o errexit 9 | set -o pipefail 10 | set -o nounset 11 | 12 | working_dir="$(dirname ${0})" 13 | source "${working_dir}/_sourced/constants.sh" 14 | source "${working_dir}/_sourced/messages.sh" 15 | 16 | export AWS_ACCESS_KEY_ID="${DJANGO_AWS_ACCESS_KEY_ID}" 17 | export AWS_SECRET_ACCESS_KEY="${DJANGO_AWS_SECRET_ACCESS_KEY}" 18 | export AWS_STORAGE_BUCKET_NAME="${DJANGO_AWS_STORAGE_BUCKET_NAME}" 19 | 20 | 21 | aws s3 cp s3://${AWS_STORAGE_BUCKET_NAME}${BACKUP_DIR_PATH}/${1} ${BACKUP_DIR_PATH}/${1} 22 | 23 | message_success "Finished downloading ${1}." 24 | 25 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | charset = utf-8 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.{py,rst,ini}] 12 | indent_style = space 13 | indent_size = 4 14 | 15 | [*.py] 16 | line_length = 88 17 | known_first_party = aws_ecs_deploy,config 18 | multi_line_output = 3 19 | default_section = THIRDPARTY 20 | recursive = true 21 | skip = venv/ 22 | skip_glob = **/migrations/*.py 23 | include_trailing_comma = true 24 | force_grid_wrap = 0 25 | use_parentheses = true 26 | 27 | [*.{html,css,scss,json,yml}] 28 | indent_style = space 29 | indent_size = 2 30 | 31 | [*.md] 32 | trim_trailing_whitespace = false 33 | 34 | [Makefile] 35 | indent_style = tab 36 | 37 | [nginx.conf] 38 | indent_style = space 39 | indent_size = 2 40 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [flake8] 2 | max-line-length = 120 3 | exclude = .tox,.git,*/migrations/*,*/static/CACHE/*,docs,node_modules 4 | 5 | [pycodestyle] 6 | max-line-length = 120 7 | exclude = .tox,.git,*/migrations/*,*/static/CACHE/*,docs,node_modules 8 | 9 | [mypy] 10 | python_version = 3.8 11 | check_untyped_defs = True 12 | ignore_missing_imports = True 13 | warn_unused_ignores = True 14 | warn_redundant_casts = True 15 | warn_unused_configs = True 16 | plugins = mypy_django_plugin.main 17 | 18 | [mypy.plugins.django-stubs] 19 | django_settings_module = config.settings.test 20 | 21 | [mypy-*.migrations.*] 22 | # Django migrations should not produce any errors: 23 | ignore_errors = True 24 | 25 | [coverage:run] 26 | include = aws_ecs_deploy/* 27 | omit = *migrations*, *tests* 28 | plugins = 29 | django_coverage_plugin 30 | -------------------------------------------------------------------------------- /aws_ecs_deploy/templates/account/signup.html: -------------------------------------------------------------------------------- 1 | {% extends "account/base.html" %} 2 | 3 | {% load i18n %} 4 | {% load crispy_forms_tags %} 5 | 6 | {% block head_title %}{% trans "Signup" %}{% endblock %} 7 | 8 | {% block inner %} 9 |{% blocktrans %}Already have an account? Then please sign in.{% endblocktrans %}
12 | 13 | 21 | 22 | {% endblock %} 23 | 24 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=. 11 | set BUILDDIR=_build 12 | 13 | if "%1" == "" goto help 14 | 15 | %SPHINXBUILD% >NUL 2>NUL 16 | if errorlevel 9009 ( 17 | echo. 18 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 19 | echo.installed, then set the SPHINXBUILD environment variable to point 20 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 21 | echo.may add the Sphinx directory to PATH. 22 | echo. 23 | echo.If you don't have Sphinx installed, grab it from 24 | echo.http://sphinx-doc.org/ 25 | exit /b 1 26 | ) 27 | 28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 29 | goto end 30 | 31 | :help 32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 33 | 34 | :end 35 | popd 36 | -------------------------------------------------------------------------------- /aws_ecs_deploy/templates/account/verified_email_required.html: -------------------------------------------------------------------------------- 1 | {% extends "account/base.html" %} 2 | 3 | {% load i18n %} 4 | 5 | {% block head_title %}{% trans "Verify Your E-mail Address" %}{% endblock %} 6 | 7 | {% block inner %} 8 |{% blocktrans %}This part of the site requires us to verify that 13 | you are who you claim to be. For this purpose, we require that you 14 | verify ownership of your e-mail address. {% endblocktrans %}
15 | 16 |{% blocktrans %}We have sent an e-mail to you for 17 | verification. Please click on the link inside this e-mail. Please 18 | contact us if you do not receive it within a few minutes.{% endblocktrans %}
19 | 20 |{% blocktrans %}Note: you can still change your e-mail address.{% endblocktrans %}
21 | 22 | 23 | {% endblock %} 24 | 25 | -------------------------------------------------------------------------------- /aws_ecs_deploy/templates/users/user_detail.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% load static %} 3 | 4 | {% block title %}User: {{ object.username }}{% endblock %} 5 | 6 | {% block content %} 7 |{{ object.name }}
15 | {% endif %} 16 |{% trans "Forgotten your password? Enter your e-mail address below, and we'll send you an e-mail allowing you to reset it." %}
17 | 18 | 23 | 24 |{% blocktrans %}Please contact us if you have any trouble resetting your password.{% endblocktrans %}
25 | {% endblock %} 26 | 27 | -------------------------------------------------------------------------------- /requirements/base.txt: -------------------------------------------------------------------------------- 1 | pytz==2019.3 # https://github.com/stub42/pytz 2 | python-slugify==4.0.0 # https://github.com/un33k/python-slugify 3 | Pillow==7.1.1 # https://github.com/python-pillow/Pillow 4 | argon2-cffi==19.2.0 # https://github.com/hynek/argon2_cffi 5 | redis==3.4.1 # https://github.com/andymccurdy/redis-py 6 | celery==4.4.2 # pyup: < 5.0 # https://github.com/celery/celery 7 | django-celery-beat==2.0.0 # https://github.com/celery/django-celery-beat 8 | flower==0.9.4 # https://github.com/mher/flower 9 | 10 | # Django 11 | # ------------------------------------------------------------------------------ 12 | django==3.0.5 # pyup: < 3.1 # https://www.djangoproject.com/ 13 | django-environ==0.4.5 # https://github.com/joke2k/django-environ 14 | django-model-utils==4.0.0 # https://github.com/jazzband/django-model-utils 15 | django-allauth==0.41.0 # https://github.com/pennersr/django-allauth 16 | django-crispy-forms==1.9.0 # https://github.com/django-crispy-forms/django-crispy-forms 17 | django-redis==4.11.0 # https://github.com/niwinz/django-redis 18 | -------------------------------------------------------------------------------- /aws_ecs_deploy/templates/account/email_confirm.html: -------------------------------------------------------------------------------- 1 | {% extends "account/base.html" %} 2 | 3 | {% load i18n %} 4 | {% load account %} 5 | 6 | {% block head_title %}{% trans "Confirm E-mail Address" %}{% endblock %} 7 | 8 | 9 | {% block inner %} 10 |{% blocktrans with confirmation.email_address.email as email %}Please confirm that {{ email }} is an e-mail address for user {{ user_display }}.{% endblocktrans %}
17 | 18 | 22 | 23 | {% else %} 24 | 25 | {% url 'account_email' as email_url %} 26 | 27 |{% blocktrans %}This e-mail confirmation link expired or is invalid. Please issue a new e-mail confirmation request.{% endblocktrans %}
28 | 29 | {% endif %} 30 | 31 | {% endblock %} 32 | 33 | -------------------------------------------------------------------------------- /aws_ecs_deploy/templates/account/password_reset_from_key.html: -------------------------------------------------------------------------------- 1 | {% extends "account/base.html" %} 2 | 3 | {% load i18n %} 4 | {% load crispy_forms_tags %} 5 | {% block head_title %}{% trans "Change Password" %}{% endblock %} 6 | 7 | {% block inner %} 8 |{% blocktrans %}The password reset link was invalid, possibly because it has already been used. Please request a new password reset.{% endblocktrans %}
13 | {% else %} 14 | {% if form %} 15 | 20 | {% else %} 21 |{% trans 'Your password is now changed.' %}
22 | {% endif %} 23 | {% endif %} 24 | {% endblock %} 25 | 26 | -------------------------------------------------------------------------------- /compose/production/standalone/django/entrypoint: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -o errexit 4 | set -o pipefail 5 | set -o nounset 6 | 7 | 8 | 9 | # N.B. If only .env files supported variable expansion... 10 | export CELERY_BROKER_URL="${REDIS_URL}" 11 | 12 | 13 | if [ -z "${POSTGRES_USER}" ]; then 14 | base_postgres_image_default_user='postgres' 15 | export POSTGRES_USER="${base_postgres_image_default_user}" 16 | fi 17 | export DATABASE_URL="postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB}" 18 | 19 | postgres_ready() { 20 | python << END 21 | import sys 22 | 23 | import psycopg2 24 | 25 | try: 26 | psycopg2.connect( 27 | dbname="${POSTGRES_DB}", 28 | user="${POSTGRES_USER}", 29 | password="${POSTGRES_PASSWORD}", 30 | host="${POSTGRES_HOST}", 31 | port="${POSTGRES_PORT}", 32 | ) 33 | except psycopg2.OperationalError: 34 | sys.exit(-1) 35 | sys.exit(0) 36 | 37 | END 38 | } 39 | until postgres_ready; do 40 | >&2 echo 'Waiting for PostgreSQL to become available...' 41 | sleep 1 42 | done 43 | >&2 echo 'PostgreSQL is available' 44 | 45 | exec "$@" 46 | -------------------------------------------------------------------------------- /compose/local/django/celery/beat/start: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -o errexit 4 | set -o nounset 5 | 6 | 7 | rm -f './celerybeat.pid' 8 | 9 | postgres_ready() { 10 | python << END 11 | import sys 12 | from time import sleep 13 | 14 | import psycopg2 15 | 16 | try: 17 | conn = psycopg2.connect( 18 | dbname="${POSTGRES_DB}", 19 | user="${POSTGRES_USER}", 20 | password="${POSTGRES_PASSWORD}", 21 | host="${POSTGRES_HOST}", 22 | port="${POSTGRES_PORT}", 23 | ) 24 | 25 | # Check if table exists yet. 26 | # If not, wait for docker-compose up to migrate all tables. 27 | cur = conn.cursor() 28 | cur.execute( 29 | "select exists(select * from ${POSTGRES_DB}.tables where table_name=%s)", 30 | ('django_celery_beat_periodictask',) 31 | ) 32 | 33 | except (psycopg2.OperationalError, psycopg2.errors.UndefinedTable): 34 | sys.exit(-1) 35 | 36 | sys.exit(0) 37 | 38 | END 39 | } 40 | until postgres_ready; do 41 | >&2 echo 'Waiting for celerybeat models to be migrated...' 42 | sleep 1 43 | done 44 | >&2 echo 'PostgreSQL is ready' 45 | 46 | celery -A config.celery_app beat -l INFO 47 | -------------------------------------------------------------------------------- /compose/production/standalone/django/celery/beat/start: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -o errexit 4 | set -o pipefail 5 | set -o nounset 6 | 7 | 8 | postgres_ready() { 9 | python << END 10 | import sys 11 | from time import sleep 12 | 13 | import psycopg2 14 | 15 | try: 16 | conn = psycopg2.connect( 17 | dbname="${POSTGRES_DB}", 18 | user="${POSTGRES_USER}", 19 | password="${POSTGRES_PASSWORD}", 20 | host="${POSTGRES_HOST}", 21 | port="${POSTGRES_PORT}", 22 | ) 23 | 24 | # Check if table exists yet. 25 | # If not, wait for docker-compose up to migrate all tables. 26 | cur = conn.cursor() 27 | cur.execute( 28 | "select exists(select * from ${POSTGRES_DB}.tables where table_name=%s)", 29 | ('django_celery_beat_periodictask',) 30 | ) 31 | 32 | except (psycopg2.OperationalError, psycopg2.errors.UndefinedTable): 33 | sys.exit(-1) 34 | 35 | sys.exit(0) 36 | 37 | END 38 | } 39 | until postgres_ready; do 40 | >&2 echo 'Waiting for celerybeat models to be migrated...' 41 | sleep 1 42 | done 43 | >&2 echo 'PostgreSQL is ready' 44 | 45 | 46 | celery -A config.celery_app beat -l INFO 47 | -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | from pathlib import Path 5 | 6 | if __name__ == "__main__": 7 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings.local") 8 | 9 | try: 10 | from django.core.management import execute_from_command_line 11 | except ImportError: 12 | # The above import may fail for some other reason. Ensure that the 13 | # issue is really that Django is missing to avoid masking other 14 | # exceptions on Python 2. 15 | try: 16 | import django # noqa 17 | except ImportError: 18 | raise ImportError( 19 | "Couldn't import Django. Are you sure it's installed and " 20 | "available on your PYTHONPATH environment variable? Did you " 21 | "forget to activate a virtual environment?" 22 | ) 23 | 24 | raise 25 | 26 | # This allows easy placement of apps within the interior 27 | # aws_ecs_deploy directory. 28 | current_path = Path(__file__).parent.resolve() 29 | sys.path.append(str(current_path / "aws_ecs_deploy")) 30 | 31 | execute_from_command_line(sys.argv) 32 | -------------------------------------------------------------------------------- /compose/production/ecs/django/entrypoint: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -o errexit 4 | set -o pipefail 5 | set -o nounset 6 | 7 | 8 | 9 | # N.B. If only .env files supported variable expansion... 10 | # export CELERY_BROKER_URL="${REDIS_URL}" 11 | # 12 | # 13 | # if [ -z "${POSTGRES_USER}" ]; then 14 | # base_postgres_image_default_user='postgres' 15 | # export POSTGRES_USER="${base_postgres_image_default_user}" 16 | # fi 17 | # export DATABASE_URL="postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB}" 18 | # 19 | # postgres_ready() { 20 | # python << END 21 | # import sys 22 | # 23 | # import psycopg2 24 | # 25 | # try: 26 | # psycopg2.connect( 27 | # dbname="${POSTGRES_DB}", 28 | # user="${POSTGRES_USER}", 29 | # password="${POSTGRES_PASSWORD}", 30 | # host="${POSTGRES_HOST}", 31 | # port="${POSTGRES_PORT}", 32 | # ) 33 | # except psycopg2.OperationalError: 34 | # sys.exit(-1) 35 | # sys.exit(0) 36 | # 37 | # END 38 | # } 39 | # until postgres_ready; do 40 | # >&2 echo 'Waiting for PostgreSQL to become available...' 41 | # sleep 1 42 | # done 43 | # >&2 echo 'PostgreSQL is available' 44 | # 45 | exec "$@" 46 | -------------------------------------------------------------------------------- /aws_ecs_deploy/contrib/sites/migrations/0003_set_site_domain_and_name.py: -------------------------------------------------------------------------------- 1 | """ 2 | To understand why this file is here, please read: 3 | 4 | http://cookiecutter-django.readthedocs.io/en/latest/faq.html#why-is-there-a-django-contrib-sites-directory-in-cookiecutter-django 5 | """ 6 | from django.conf import settings 7 | from django.db import migrations 8 | 9 | 10 | def update_site_forward(apps, schema_editor): 11 | """Set site domain and name.""" 12 | Site = apps.get_model("sites", "Site") 13 | Site.objects.update_or_create( 14 | id=settings.SITE_ID, 15 | defaults={ 16 | "domain": "example.com", 17 | "name": "AWS ECS Deploy", 18 | }, 19 | ) 20 | 21 | 22 | def update_site_backward(apps, schema_editor): 23 | """Revert site domain and name to default.""" 24 | Site = apps.get_model("sites", "Site") 25 | Site.objects.update_or_create( 26 | id=settings.SITE_ID, defaults={"domain": "example.com", "name": "example.com"} 27 | ) 28 | 29 | 30 | class Migration(migrations.Migration): 31 | 32 | dependencies = [("sites", "0002_alter_domain_unique")] 33 | 34 | operations = [migrations.RunPython(update_site_forward, update_site_backward)] 35 | -------------------------------------------------------------------------------- /compose/production/standalone/postgres/maintenance/backup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | 4 | ### Create a database backup. 5 | ### 6 | ### Usage: 7 | ### $ docker-compose -f{% blocktrans with site.name as site_name %}Please sign in with one 17 | of your existing third party accounts. Or, sign up 18 | for a {{ site_name }} account and sign in below:{% endblocktrans %}
19 | 20 | 29 | 30 | {% include "socialaccount/snippets/login_extra.html" %} 31 | 32 | {% else %} 33 |{% blocktrans %}If you have not created an account yet, then please 34 | sign up first.{% endblocktrans %}
35 | {% endif %} 36 | 37 | 46 | 47 | {% endblock %} 48 | 49 | -------------------------------------------------------------------------------- /config/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for AWS ECS Deploy project. 3 | 4 | This module contains the WSGI application used by Django's development server 5 | and any production WSGI deployments. It should expose a module-level variable 6 | named ``application``. Django's ``runserver`` and ``runfcgi`` commands discover 7 | this application via the ``WSGI_APPLICATION`` setting. 8 | 9 | Usually you will have the standard Django WSGI application here, but it also 10 | might make sense to replace the whole Django WSGI application with a custom one 11 | that later delegates to the Django one. For example, you could introduce WSGI 12 | middleware here, or combine a Django application with an application of another 13 | framework. 14 | 15 | """ 16 | import os 17 | import sys 18 | from pathlib import Path 19 | 20 | from django.core.wsgi import get_wsgi_application 21 | 22 | # This allows easy placement of apps within the interior 23 | # aws_ecs_deploy directory. 24 | app_path = Path(__file__).parents[1].resolve() 25 | sys.path.append(str(app_path / "aws_ecs_deploy")) 26 | # We defer to a DJANGO_SETTINGS_MODULE already in the environment. This breaks 27 | # if running multiple sites in the same mod_wsgi process. To fix this, use 28 | # mod_wsgi daemon mode with each site in its own daemon process, or use 29 | # os.environ["DJANGO_SETTINGS_MODULE"] = "config.settings.production" 30 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings.production") 31 | 32 | # This application object is used by any WSGI server configured to use this 33 | # file. This includes Django's development server, if the WSGI_APPLICATION 34 | # setting points here. 35 | application = get_wsgi_application() 36 | # Apply WSGI middleware here. 37 | # from helloworld.wsgi import HelloWorldApplication 38 | # application = HelloWorldApplication(application) 39 | -------------------------------------------------------------------------------- /config/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf import settings 2 | from django.conf.urls.static import static 3 | from django.contrib import admin 4 | from django.urls import include, path 5 | from django.views import defaults as default_views 6 | from django.views.generic import TemplateView 7 | 8 | urlpatterns = [ 9 | path("", TemplateView.as_view(template_name="pages/home.html"), name="home"), 10 | path( 11 | "about/", TemplateView.as_view(template_name="pages/about.html"), name="about" 12 | ), 13 | # Django Admin, use {% url 'admin:index' %} 14 | path(settings.ADMIN_URL, admin.site.urls), 15 | # User management 16 | path("users/", include("aws_ecs_deploy.users.urls", namespace="users")), 17 | path("accounts/", include("allauth.urls")), 18 | # Your stuff: custom urls includes go here 19 | ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) 20 | 21 | 22 | if settings.DEBUG: 23 | # This allows the error pages to be debugged during development, just visit 24 | # these url in browser to see how these error pages look like. 25 | urlpatterns += [ 26 | path( 27 | "400/", 28 | default_views.bad_request, 29 | kwargs={"exception": Exception("Bad Request!")}, 30 | ), 31 | path( 32 | "403/", 33 | default_views.permission_denied, 34 | kwargs={"exception": Exception("Permission Denied")}, 35 | ), 36 | path( 37 | "404/", 38 | default_views.page_not_found, 39 | kwargs={"exception": Exception("Page not Found")}, 40 | ), 41 | path("500/", default_views.server_error), 42 | ] 43 | if "debug_toolbar" in settings.INSTALLED_APPS: 44 | import debug_toolbar 45 | 46 | urlpatterns = [path("__debug__/", include(debug_toolbar.urls))] + urlpatterns 47 | -------------------------------------------------------------------------------- /.github/workflows/nginx.yml: -------------------------------------------------------------------------------- 1 | # Build and push the nginx image 2 | # 3 | # Since my ECR free tier was at stake, and since 4 | # nginx takes a relatively short amount of time to 5 | # build anyways, I decided to move the build and 6 | # push to separate file since nginx configuration 7 | # doesn't change too often. 8 | 9 | name: Build and Push NGINX Image 10 | 11 | on: 12 | push: 13 | branches: 14 | - master 15 | paths: 16 | # Trigger this workflow on master push 17 | - 'compose/production/ecs/nginx/**' 18 | - '.github/workflows/nginx.yml' 19 | 20 | jobs: 21 | build-nginx: 22 | name: Build NGINX Image 23 | runs-on: ubuntu-latest 24 | 25 | steps: 26 | - name: Checkout 27 | uses: actions/checkout@v2 28 | 29 | - name: Configure AWS credentials 30 | uses: aws-actions/configure-aws-credentials@v1 31 | with: 32 | aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} 33 | aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} 34 | # TODO Change your AWS region here! 35 | aws-region: us-east-2 36 | 37 | - name: Login to Amazon ECR 38 | id: login-ecr 39 | uses: aws-actions/amazon-ecr-login@v1 40 | 41 | # Build nginx 42 | # ------------------------------------------------------- 43 | - name: Build, tag, and push nginx image to Amazon ECR 44 | id: build-nginx-image 45 | env: 46 | ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }} 47 | ECR_REPOSITORY: nginx-reverse-proxy 48 | IMAGE_TAG: latest 49 | run: | 50 | # Build a docker container and 51 | # push it to ECR so that it can 52 | # be deployed to ECS. 53 | docker build -f compose/production/ecs/nginx/Dockerfile -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG . 54 | docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG 55 | -------------------------------------------------------------------------------- /compose/production/standalone/django/Dockerfile: -------------------------------------------------------------------------------- 1 | 2 | FROM python:3.8-slim-buster 3 | 4 | ENV PYTHONUNBUFFERED 1 5 | 6 | RUN apt-get update \ 7 | # dependencies for building Python packages 8 | && apt-get install -y build-essential \ 9 | # psycopg2 dependencies 10 | && apt-get install -y libpq-dev \ 11 | # Translations dependencies 12 | && apt-get install -y gettext \ 13 | # cleaning up unused files 14 | && apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false \ 15 | && rm -rf /var/lib/apt/lists/* 16 | 17 | RUN addgroup --system django \ 18 | && adduser --system --ingroup django django 19 | 20 | # Requirements are installed here to ensure they will be cached. 21 | COPY ./requirements /requirements 22 | RUN pip install --no-cache-dir -r /requirements/production.txt \ 23 | && rm -rf /requirements 24 | 25 | COPY ./compose/production/standalone/django/entrypoint /entrypoint 26 | RUN sed -i 's/\r$//g' /entrypoint 27 | RUN chmod +x /entrypoint 28 | RUN chown django /entrypoint 29 | 30 | COPY ./compose/production/standalone/django/start /start 31 | RUN sed -i 's/\r$//g' /start 32 | RUN chmod +x /start 33 | RUN chown django /start 34 | COPY ./compose/production/standalone/django/celery/worker/start /start-celeryworker 35 | RUN sed -i 's/\r$//g' /start-celeryworker 36 | RUN chmod +x /start-celeryworker 37 | RUN chown django /start-celeryworker 38 | 39 | COPY ./compose/production/standalone/django/celery/beat/start /start-celerybeat 40 | RUN sed -i 's/\r$//g' /start-celerybeat 41 | RUN chmod +x /start-celerybeat 42 | RUN chown django /start-celerybeat 43 | 44 | COPY ./compose/production/standalone/django/celery/flower/start /start-flower 45 | RUN sed -i 's/\r$//g' /start-flower 46 | RUN chmod +x /start-flower 47 | COPY --chown=django:django . /app 48 | 49 | USER django 50 | 51 | WORKDIR /app 52 | 53 | ENTRYPOINT ["/entrypoint"] 54 | -------------------------------------------------------------------------------- /compose/production/standalone/postgres/maintenance/restore: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | 4 | ### Restore database from a backup. 5 | ### 6 | ### Parameters: 7 | ### <1> filename of an existing backup. 8 | ### 9 | ### Usage: 10 | ### $ docker-compose -f{% trans 'The following e-mail addresses are associated with your account:' %}
14 | 15 | 44 | 45 | {% else %} 46 |{% trans 'Warning:'%} {% trans "You currently do not have any e-mail address set up. You should really add an e-mail address so you can receive notifications, reset your password, etc." %}
47 | 48 | {% endif %} 49 | 50 | 51 |Use this document as a way to quick start any new project.
87 | {% endblock content %} 88 | 89 |