diff --git a/.dockerignore b/.dockerignore
index b9cfaf9..c0e5dd2 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -10,3 +10,4 @@ venv
data/**/*
!data/**/.gitignore
fly.toml
+.env*
diff --git a/.gitignore b/.gitignore
index b2b4b82..70a06dd 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,3 +7,4 @@ __pycache__/
*.log
.DS_Store
node_modules/
+.env*
diff --git a/README.md b/README.md
index 1a375c5..ed93c00 100644
--- a/README.md
+++ b/README.md
@@ -10,13 +10,44 @@
```bash
python -m venv venv && \
. venv/bin/activate && \
- pip install pip-tools --upgrade && \
+ pip install pip pip-tools --upgrade && \
pip-sync requirements/dev.txt && \
+ npm install && \
python manage.py makemigrations && \
python manage.py migrate && \
python manage.py createsuperuser
+```
+
+## Run development server
+
+```bash
+npm run dev
+```
+
+## Deploy
+
+### Launch fly.io app
+
+```bash
+fly launch --ha=false --volume-initial-size=1
+```
-python manage.py runserver
+### Set fly.io secrets
+
+```bash
+cat .env.prod | xargs fly secrets set
+```
+
+### Build bundles
+
+```bash
+npm run build
+```
+
+### Deploy changes when needed
+
+```bash
+fly deploy
```
diff --git a/apps/site/templates/site/base.html b/apps/site/templates/site/base.html
index e525f77..d363329 100644
--- a/apps/site/templates/site/base.html
+++ b/apps/site/templates/site/base.html
@@ -1,16 +1,51 @@
-
-
- {% block html %}
-
- {% block head %}
- {% block title %}{% endblock title %}
- {% block meta %}
- {% endblock meta %}
- {% endblock head %}
-
-
- {% block body %}
- {% endblock body %}
-
- {% endblock html %}
-
+{% extends 'site/layouts/stacked.html' %}
+{% load static %}
+{% block title %}Example{% endblock title %}
+{% block meta %}
+{% include 'site/partials/meta.html' %}
+{% endblock meta %}
+{% block nav %}
+
+{% endblock nav %}
+{% block header %}
+
+{% endblock header %}
+{% block footer %}
+
+
+ © {% now 'Y' %} Example. All rights reserved.
+
+
+{% endblock footer %}
diff --git a/apps/site/templates/site/home.html b/apps/site/templates/site/home.html
index 01c64c8..92c7a2e 100644
--- a/apps/site/templates/site/home.html
+++ b/apps/site/templates/site/home.html
@@ -1,33 +1,9 @@
-{% extends 'site/layouts/stacked.html' %}
+{% extends 'site/base.html' %}
{% load static %}
-{% block title %}Example{% endblock title %}
-{% block meta %}
-{% include 'site/partials/meta.html' %}
-{% endblock meta %}
{% block body %}
{% include 'site/components/faq.html' %}
{{ block.super }}
{% endblock body %}
-{% block nav %}
-
-{% endblock nav %}
{% block header %}
{% endblock main %}
-{% block footer %}
-
-
- © {% now 'Y' %} Example. All rights reserved.
-
-
-{% endblock footer %}
diff --git a/apps/site/templates/site/layouts/base.html b/apps/site/templates/site/layouts/base.html
new file mode 100644
index 0000000..e525f77
--- /dev/null
+++ b/apps/site/templates/site/layouts/base.html
@@ -0,0 +1,16 @@
+
+
+ {% block html %}
+
+ {% block head %}
+ {% block title %}{% endblock title %}
+ {% block meta %}
+ {% endblock meta %}
+ {% endblock head %}
+
+
+ {% block body %}
+ {% endblock body %}
+
+ {% endblock html %}
+
diff --git a/apps/site/templates/site/layouts/stacked.html b/apps/site/templates/site/layouts/stacked.html
index 13699d3..e846684 100644
--- a/apps/site/templates/site/layouts/stacked.html
+++ b/apps/site/templates/site/layouts/stacked.html
@@ -1,4 +1,4 @@
-{% extends 'site/base.html' %}
+{% extends 'site/layouts/base.html' %}
{% load static %}
{% block head %}
{{ block.super }}
diff --git a/example/settings/base.py b/example/settings/base.py
index 5dd9b73..9833f7b 100644
--- a/example/settings/base.py
+++ b/example/settings/base.py
@@ -176,6 +176,7 @@
"level": "ERROR",
"filters": ["require_debug_false"],
"class": "django.utils.log.AdminEmailHandler",
+ "include_html": True,
},
"file": {
"level": "DEBUG",
diff --git a/example/settings/prod.py b/example/settings/prod.py
index bb01c11..ca51cd6 100644
--- a/example/settings/prod.py
+++ b/example/settings/prod.py
@@ -6,4 +6,16 @@
ALLOWED_HOSTS = [
"example.fly.dev",
+ "example.com",
]
+
+EMAIL_BACKEND = "django_ses.SESBackend"
+
+AWS_SES_ACCESS_KEY_ID = os.environ.get("AWS_SES_ACCESS_KEY_ID", "")
+AWS_SES_SECRET_ACCESS_KEY = os.environ.get("AWS_SES_SECRET_ACCESS_KEY", "")
+AWS_SES_REGION_NAME = os.environ.get("AWS_SES_REGION_NAME", "")
+AWS_SES_REGION_ENDPOINT = os.environ.get("AWS_SES_REGION_ENDPOINT", "")
+
+SERVER_EMAIL = "Example Server "
+DEFAULT_FROM_EMAIL = "Admin from Example "
+ADMINS = [("Admin", "admin@example.com")]
diff --git a/fly.toml b/fly.toml
index 9664e4a..a6ae806 100644
--- a/fly.toml
+++ b/fly.toml
@@ -1,10 +1,17 @@
app = 'example'
+[build]
+
[env]
DJANGO_SETTINGS_MODULE = 'example.settings.dev'
PYTHONDONTWRITEBYTECODE = '1'
PYTHONUNBUFFERED = '1'
+[[mounts]]
+ source = 'example_data'
+ destination = '/app/data'
+ processes = ['app']
+
[http_service]
internal_port = 8000
force_https = true
@@ -13,9 +20,7 @@ app = 'example'
min_machines_running = 1
processes = ['app']
-[[http_service.checks]]
- interval = '30s'
- timeout = '5s'
- grace_period = '5s'
- method = 'GET'
- path = '/'
+[[vm]]
+ memory = '1gb'
+ cpu_kind = 'shared'
+ cpus = 1
diff --git a/requirements/base.ini b/requirements/base.ini
index 84be6ac..8545879 100644
--- a/requirements/base.ini
+++ b/requirements/base.ini
@@ -3,3 +3,4 @@ celery
ipython
gunicorn
whitenoise
+django-ses
diff --git a/requirements/base.txt b/requirements/base.txt
index 3160371..5a8f2b8 100644
--- a/requirements/base.txt
+++ b/requirements/base.txt
@@ -12,6 +12,12 @@ asttokens==2.4.1
# via stack-data
billiard==4.2.0
# via celery
+boto3==1.34.61
+ # via django-ses
+botocore==1.34.61
+ # via
+ # boto3
+ # s3transfer
celery==5.3.6
# via -r requirements/base.ini
click==8.1.7
@@ -29,6 +35,10 @@ click-repl==0.3.0
decorator==5.1.1
# via ipython
django==5.0.1
+ # via
+ # -r requirements/base.ini
+ # django-ses
+django-ses==3.5.2
# via -r requirements/base.ini
executing==2.0.1
# via stack-data
@@ -38,6 +48,10 @@ ipython==8.20.0
# via -r requirements/base.ini
jedi==0.19.1
# via ipython
+jmespath==1.0.1
+ # via
+ # boto3
+ # botocore
kombu==5.3.5
# via celery
matplotlib-inline==0.1.6
@@ -59,7 +73,13 @@ pure-eval==0.2.2
pygments==2.17.2
# via ipython
python-dateutil==2.8.2
- # via celery
+ # via
+ # botocore
+ # celery
+pytz==2024.1
+ # via django-ses
+s3transfer==0.10.0
+ # via boto3
six==1.16.0
# via
# asttokens
@@ -74,6 +94,8 @@ traitlets==5.14.1
# matplotlib-inline
tzdata==2023.4
# via celery
+urllib3==2.0.7
+ # via botocore
vine==5.1.0
# via
# amqp
diff --git a/requirements/dev.txt b/requirements/dev.txt
index 1b64f6e..735a548 100644
--- a/requirements/dev.txt
+++ b/requirements/dev.txt
@@ -22,6 +22,15 @@ billiard==4.2.0
# celery
black==24.1.0
# via -r requirements/dev.ini
+boto3==1.34.61
+ # via
+ # -r requirements/base.txt
+ # django-ses
+botocore==1.34.61
+ # via
+ # -r requirements/base.txt
+ # boto3
+ # s3transfer
celery==5.3.6
# via -r requirements/base.txt
click==8.1.7
@@ -51,7 +60,10 @@ decorator==5.1.1
django==5.0.1
# via
# -r requirements/base.txt
+ # django-ses
# model-bakery
+django-ses==3.5.2
+ # via -r requirements/base.txt
executing==2.0.1
# via
# -r requirements/base.txt
@@ -64,6 +76,11 @@ jedi==0.19.1
# via
# -r requirements/base.txt
# ipython
+jmespath==1.0.1
+ # via
+ # -r requirements/base.txt
+ # boto3
+ # botocore
kombu==5.3.5
# via
# -r requirements/base.txt
@@ -113,7 +130,16 @@ pygments==2.17.2
python-dateutil==2.8.2
# via
# -r requirements/base.txt
+ # botocore
# celery
+pytz==2024.1
+ # via
+ # -r requirements/base.txt
+ # django-ses
+s3transfer==0.10.0
+ # via
+ # -r requirements/base.txt
+ # boto3
six==1.16.0
# via
# -r requirements/base.txt
@@ -136,6 +162,10 @@ tzdata==2023.4
# via
# -r requirements/base.txt
# celery
+urllib3==2.0.7
+ # via
+ # -r requirements/base.txt
+ # botocore
vine==5.1.0
# via
# -r requirements/base.txt