Merge pull request 'staging' (#2) from staging into master
Reviewed-on: #2
This commit is contained in:
commit
53fc63e351
14
README.md
14
README.md
@ -29,6 +29,8 @@ The file contains the following environment variables:
|
||||
|
||||
```conf
|
||||
TIMEZONE=Europe/Berlin
|
||||
DJANGO_DEBUG_MODE=false
|
||||
DJANGO_SECRET_KEY=abc123mySecret
|
||||
PG_NAME=medwings
|
||||
PG_USER=medwings
|
||||
PG_PASSWORD=secret
|
||||
@ -46,9 +48,11 @@ You should set the values of the following variables:
|
||||
|
||||
| variable | description | value |
|
||||
|----------|-------------|-------|
|
||||
| PG_PASSWORD | password for the PostgreSQL admin user | a random string of 32 characters |
|
||||
| GOTIFY_USER | name of the Gotify admin user | a random string of 32 characters |
|
||||
| GOTIFY_PASSWORD | password for the Gotify admin user | a random string of 32 characters |
|
||||
| DJANGO_DEBUG_MODE | whether or not to enable Django's debug mode | 'true' during development and 'false' in production |
|
||||
| DJANGO_SECRET_KEY | private session secret | a random string of 64 characters or more |
|
||||
| PG_PASSWORD | password for the PostgreSQL admin user | a random string of 32 characters or more |
|
||||
| GOTIFY_USER | name of the Gotify admin user | a random string of 32 characters or more |
|
||||
| GOTIFY_PASSWORD | password for the Gotify admin user | a random string of 32 characters or more |
|
||||
| GOTIFY_PUBLIC_URL | URL where your public Gotify server can be reached | this depends on your deployment environment |
|
||||
| WITHINGS_CLIENT_ID | Your Withings API client id | see [Withings API](./app/withings/README.md#api-access) |
|
||||
| WITHINGS_CLIENT_SECRET | Your Withings API client secret | see [Withings API](./app/withings/README.md#api-access) |
|
||||
@ -80,10 +84,10 @@ sudo docker exec -itu django medwings-django <command>
|
||||
Run database migrations inside the running container like so:
|
||||
|
||||
```bash
|
||||
sudo docker exec -itu medwings-django python manage.py migrate
|
||||
sudo docker exec -itu django medwings-django python manage.py migrate
|
||||
```
|
||||
|
||||
To enter django's interactive shell, run:
|
||||
```bash
|
||||
sudo docker exec -itu medwings-django python manage.py shell
|
||||
sudo docker exec -itu django medwings-django python manage.py shell
|
||||
```
|
||||
|
@ -8,14 +8,18 @@
|
||||
<div class="flex flex-col justify-center items-center gap-2 py-4 mx-4 max-w-4xl">
|
||||
<h2>Register</h2>
|
||||
<p>
|
||||
Something something glad you're signing up.
|
||||
We're thrilled that you're taking the next step towards a healthier future by signing up for Medwings!
|
||||
By linking your Withings account, we can seamlessly integrate your health data with Medwings.
|
||||
</p>
|
||||
<div class="flex flex-col gap-2 items-center call-to-action-box">
|
||||
<p class="font-semibold">To get started, please allow us to access your health data</p>
|
||||
<a class="btn text-lg" href="{{ auth_url }}">Link Withings Account</a>
|
||||
</div>
|
||||
<p>
|
||||
Something something why this is necessary.
|
||||
The registration process will continue after you've linked your Withings account.
|
||||
This step is essential as it allows Medwings to securely access your health data from the Withings cloud.
|
||||
We use OAuth2, a standard and secure method, to ensure that your personal data remains private and under your control.+
|
||||
Once your accounts are linked, you'll be all set to start exploring your health data and insights through Medwings.
|
||||
</p>
|
||||
</div>
|
||||
{% endblock content %}
|
||||
|
@ -12,6 +12,7 @@ https://docs.djangoproject.com/en/4.2/ref/settings/
|
||||
|
||||
from pathlib import Path
|
||||
from os import getenv
|
||||
from .utils import parse_string_as_bool
|
||||
|
||||
|
||||
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
||||
@ -21,14 +22,17 @@ BASE_DIR = Path(__file__).resolve().parent.parent
|
||||
# Quick-start development settings - unsuitable for production
|
||||
# See https://docs.djangoproject.com/en/4.2/howto/deployment/checklist/
|
||||
# SECURITY WARNING: keep the secret key used in production secret!
|
||||
SECRET_KEY = 'django-insecure-s^q)z%f-7=1h5b00ctki2*-w=#3!k@p-#sq%=eajw)x2axx-e5'
|
||||
SECRET_KEY = getenv('DJANGO_SECRET_KEY')
|
||||
# SECURITY WARNING: don't run with debug turned on in production!
|
||||
DEBUG = True
|
||||
ALLOWED_HOSTS = [
|
||||
'localhost',
|
||||
'127.0.0.1',
|
||||
'192.168.2.141'
|
||||
]
|
||||
DEBUG = parse_string_as_bool(getenv('DJANGO_DEBUG_MODE', 'false'))
|
||||
ALLOWED_HOSTS = [ '*' ]
|
||||
|
||||
# Force HttpRequest.build_absolute_uri() to generate HTTPS links in production
|
||||
# This is necessary when running behind a reverse proxy
|
||||
#USE_X_FORWARDED_HOST = False if DEBUG else True
|
||||
#SECURE_PROXY_SSL_HEADER = () if DEBUG else ('HTTP_X_FORWARDED_PROTO', 'https')
|
||||
USE_X_FORWARDED_HOST = True
|
||||
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
|
||||
|
||||
|
||||
# Application definition
|
||||
@ -80,8 +84,8 @@ DATABASES = {
|
||||
'ENGINE': 'django.db.backends.postgresql',
|
||||
'NAME': getenv('PG_NAME', 'medwings'),
|
||||
'USER': getenv('PG_USER', 'medwings'),
|
||||
'PASSWORD': getenv('PG_PASSWORD', 'medwings'),
|
||||
'HOST': getenv('PG_HOST', 'medwings-postgres'),
|
||||
'PASSWORD': getenv('PG_PASSWORD'),
|
||||
'HOST': getenv('PG_HOST'),
|
||||
'PORT': getenv('PG_PORT', '5432'),
|
||||
}
|
||||
}
|
||||
@ -122,6 +126,7 @@ STATIC_URL = 'static/'
|
||||
STATICFILES_DIRS = [
|
||||
BASE_DIR / 'static',
|
||||
]
|
||||
STATIC_ROOT = '/srv/static'
|
||||
|
||||
|
||||
# Default primary key field type
|
||||
|
27
app/core/utils.py
Normal file
27
app/core/utils.py
Normal file
@ -0,0 +1,27 @@
|
||||
"""Miscellaneous utility functions."""
|
||||
|
||||
import re
|
||||
|
||||
|
||||
def parse_string_as_bool(value: str) -> bool:
|
||||
"""Parses the given string into a boolean based on its content.
|
||||
|
||||
This is used to parse environment variables as boolean values.
|
||||
|
||||
The following strings are parsed as `True`: "yes", "Yes", "YES", "true", "True", "TRUE", "1"
|
||||
The following strings are parsed as `False`: "no", "No", "NO", "false", "False", "FALSE", "0"
|
||||
|
||||
In any other case, a `ValueError` is raised.
|
||||
"""
|
||||
|
||||
if not isinstance(value, str):
|
||||
raise TypeError("Expected a string argument.")
|
||||
|
||||
regex_true = re.compile(r"^(YES)|(Yes)|(yes)|(TRUE)|(True)|(true)|(1)$")
|
||||
regex_false = re.compile(r"^(NO)|(No)|(no)|(FALSE)|(False)|(false)|(0)$")
|
||||
|
||||
if regex_true.fullmatch(value):
|
||||
return True
|
||||
if regex_false.fullmatch(value):
|
||||
return False
|
||||
raise ValueError(f"Failed to parse the supplied value as a boolean: {value!r}")
|
@ -1,9 +1,11 @@
|
||||
asgiref==3.7.2
|
||||
certifi==2023.7.22
|
||||
charset-normalizer==3.2.0
|
||||
click==8.1.6
|
||||
Django==4.2.3
|
||||
django-widget-tweaks==1.4.12
|
||||
djangorestframework==3.14.0
|
||||
h11==0.14.0
|
||||
idna==3.4
|
||||
psycopg==3.1.9
|
||||
psycopg-binary==3.1.9
|
||||
@ -12,3 +14,4 @@ requests==2.31.0
|
||||
sqlparse==0.4.4
|
||||
typing_extensions==4.7.1
|
||||
urllib3==2.0.4
|
||||
uvicorn==0.23.2
|
||||
|
10
development.Caddyfile
Normal file
10
development.Caddyfile
Normal file
@ -0,0 +1,10 @@
|
||||
:8000 {
|
||||
handle * {
|
||||
reverse_proxy * medwings-django:8000
|
||||
}
|
||||
|
||||
log {
|
||||
output stderr
|
||||
format console
|
||||
}
|
||||
}
|
@ -16,7 +16,7 @@ RUN addgroup --gid ${CUSTOM_GID:-1000} ${CUSTOM_GROUPNAME} && \
|
||||
|
||||
# Copy caddy config
|
||||
WORKDIR /app
|
||||
COPY --chown=${CUSTOM_USERNAME}:${CUSTOM_GROUPNAME} Caddyfile /app/
|
||||
COPY --chown=${CUSTOM_USERNAME}:${CUSTOM_GROUPNAME} development.Caddyfile /app/Caddyfile
|
||||
|
||||
# Run Caddy in development mode
|
||||
USER ${CUSTOM_UID:-1000}:${CUSTOM_GID:-1000}
|
||||
|
@ -26,7 +26,7 @@ services:
|
||||
- ${PG_HOST}
|
||||
build:
|
||||
context: .
|
||||
dockerfile: ./development.django.Dockerfile
|
||||
dockerfile: development.django.Dockerfile
|
||||
args:
|
||||
CUSTOM_UID: 1000
|
||||
CUSTOM_GID: 1000
|
||||
|
21
production.Caddyfile
Normal file
21
production.Caddyfile
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
servers {
|
||||
trusted_proxies static private_ranges
|
||||
}
|
||||
}
|
||||
|
||||
:8000 {
|
||||
handle_path /static/* {
|
||||
root * /srv/static
|
||||
file_server
|
||||
}
|
||||
|
||||
handle * {
|
||||
reverse_proxy * medwings-django:8000
|
||||
}
|
||||
|
||||
log {
|
||||
output stderr
|
||||
format console
|
||||
}
|
||||
}
|
25
production.caddy.Dockerfile
Normal file
25
production.caddy.Dockerfile
Normal file
@ -0,0 +1,25 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
FROM alpine:latest
|
||||
|
||||
# Install caddy
|
||||
RUN apk add --no-cache caddy
|
||||
|
||||
# Create non-root user
|
||||
ARG CUSTOM_UID
|
||||
ARG CUSTOM_GID
|
||||
ENV CUSTOM_USERNAME=webserver
|
||||
ENV CUSTOM_GROUPNAME=webserver
|
||||
RUN addgroup --gid ${CUSTOM_GID:-1000} ${CUSTOM_GROUPNAME} && \
|
||||
adduser --uid ${CUSTOM_UID:-1000} --shell /bin/ash ${CUSTOM_USERNAME} --ingroup ${CUSTOM_GROUPNAME} --disabled-password && \
|
||||
mkdir /app && chown ${CUSTOM_UID:-1000}:${CUSTOM_GID:-1000} /app && chmod 700 /app && \
|
||||
mkdir -p /srv/static && chown ${CUSTOM_UID:-1000}:${CUSTOM_GID:-1000} /srv/static && chmod 700 /srv/static
|
||||
|
||||
# Copy caddy config
|
||||
WORKDIR /app
|
||||
COPY --chown=${CUSTOM_USERNAME}:${CUSTOM_GROUPNAME} production.Caddyfile /app/Caddyfile
|
||||
|
||||
# Run Caddy in development mode
|
||||
USER ${CUSTOM_UID:-1000}:${CUSTOM_GID:-1000}
|
||||
EXPOSE 8000
|
||||
ENTRYPOINT ["caddy", "run", "--config", "/app/Caddyfile", "--adapter", "caddyfile"]
|
35
production.django.Dockerfile
Normal file
35
production.django.Dockerfile
Normal file
@ -0,0 +1,35 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
FROM python:alpine
|
||||
|
||||
# Install cron daemon and supervisord
|
||||
RUN apk add --no-cache dcron supervisor
|
||||
|
||||
# Create non-root user
|
||||
ARG CUSTOM_UID
|
||||
ARG CUSTOM_GID
|
||||
ENV CUSTOM_USERNAME=django
|
||||
ENV CUSTOM_GROUPNAME=django
|
||||
RUN addgroup --gid ${CUSTOM_GID:-1000} ${CUSTOM_GROUPNAME} && \
|
||||
adduser --uid ${CUSTOM_UID:-1000} --shell /bin/ash ${CUSTOM_USERNAME} --ingroup ${CUSTOM_GROUPNAME} --disabled-password && \
|
||||
mkdir /app && chown ${CUSTOM_UID:-1000}:${CUSTOM_GID:-1000} /app && chmod 700 /app && \
|
||||
mkdir -p /srv/static && chown ${CUSTOM_UID:-1000}:${CUSTOM_GID:-1000} /srv/static && chmod 700 /srv/static
|
||||
ENV PATH "$PATH:/home/${CUSTOM_GROUPNAME}/.local/bin"
|
||||
|
||||
# Add supervisord conf
|
||||
COPY production.supervisord.conf /etc/supervisord.conf
|
||||
|
||||
# Add cron job
|
||||
COPY --chmod=600 django.crontab /etc/crontabs/django
|
||||
|
||||
# Copy source files
|
||||
WORKDIR /app
|
||||
COPY --chown=${CUSTOM_USERNAME}:${CUSTOM_GROUPNAME} app/ /app/
|
||||
|
||||
# Install dependencies
|
||||
RUN pip install -r requirements.txt
|
||||
|
||||
# Run supervisord
|
||||
EXPOSE 8000/tcp
|
||||
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisord.conf"]
|
||||
#CMD ["uvicorn", "core.asgi:application", "--host", "0.0.0.0", "--port", "8000", "--access-log"]
|
114
production.docker-compose.yml
Normal file
114
production.docker-compose.yml
Normal file
@ -0,0 +1,114 @@
|
||||
---
|
||||
|
||||
version: "3"
|
||||
|
||||
services:
|
||||
medwings-caddy:
|
||||
container_name: medwings-caddy
|
||||
restart: unless-stopped
|
||||
build:
|
||||
context: .
|
||||
dockerfile: production.caddy.Dockerfile
|
||||
args:
|
||||
CUSTOM_UID: 1000
|
||||
CUSTOM_GID: 1000
|
||||
expose:
|
||||
- "8000"
|
||||
networks:
|
||||
- medwings
|
||||
- proxy
|
||||
environment:
|
||||
TZ: ${TIMEZONE}
|
||||
volumes:
|
||||
- /srv/medwings/static:/srv/static:ro
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.medwings.entrypoints=https"
|
||||
- "traefik.http.routers.medwings.rule=Host(`medwings.lobbes.dev`)"
|
||||
- "traefik.http.routers.medwings.middlewares=default@file"
|
||||
- "traefik.http.routers.medwings.tls=true"
|
||||
- "traefik.http.services.medwings.loadbalancer.server.port=8000"
|
||||
- "traefik.docker.network=proxy"
|
||||
medwings-django:
|
||||
container_name: medwings-django
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- medwings-caddy
|
||||
- ${PG_HOST}
|
||||
build:
|
||||
context: .
|
||||
dockerfile: production.django.Dockerfile
|
||||
args:
|
||||
CUSTOM_UID: 1000
|
||||
CUSTOM_GID: 1000
|
||||
expose:
|
||||
- "8000"
|
||||
networks:
|
||||
- medwings
|
||||
environment:
|
||||
TZ: ${TIMEZONE}
|
||||
DJANGO_DEBUG_MODE: ${DJANGO_DEBUG_MODE}
|
||||
DJANGO_SECRET_KEY: ${DJANGO_SECRET_KEY}
|
||||
PG_NAME: ${PG_NAME}
|
||||
PG_USER: ${PG_USER}
|
||||
PG_PASSWORD: ${PG_PASSWORD}
|
||||
PG_HOST: ${PG_HOST}
|
||||
PG_PORT: ${PG_PORT}
|
||||
WITHINGS_CLIENT_ID: ${WITHINGS_CLIENT_ID}
|
||||
WITHINGS_CLIENT_SECRET: ${WITHINGS_CLIENT_SECRET}
|
||||
GOTIFY_USER: ${GOTIFY_USER}
|
||||
GOTIFY_PASSWORD: ${GOTIFY_PASSWORD}
|
||||
GOTIFY_HOST: ${GOTIFY_HOST}
|
||||
GOTIFY_PUBLIC_URL: ${GOTIFY_PUBLIC_URL}
|
||||
volumes:
|
||||
- /srv/medwings/static:/srv/static
|
||||
medwings-postgres:
|
||||
image: postgres:alpine
|
||||
container_name: ${PG_HOST}
|
||||
restart: unless-stopped
|
||||
expose:
|
||||
- ${PG_PORT}
|
||||
networks:
|
||||
- medwings
|
||||
volumes:
|
||||
- /srv/medwings/db:/var/lib/postgresql/data
|
||||
environment:
|
||||
POSTGRES_DB: ${PG_NAME}
|
||||
POSTGRES_USER: ${PG_USER}
|
||||
POSTGRES_PASSWORD: ${PG_PASSWORD}
|
||||
TZ: ${TIMEZONE}
|
||||
medwings-gotify:
|
||||
image: gotify/server
|
||||
container_name: medwings-gotify
|
||||
restart: unless-stopped
|
||||
expose:
|
||||
- "80"
|
||||
networks:
|
||||
- medwings
|
||||
- proxy
|
||||
volumes:
|
||||
- /srv/medwings/gotify:/app/data
|
||||
environment:
|
||||
TZ: ${TIMEZONE}
|
||||
GOTIFY_SERVER_SSL_REDIRECTTOHTTPS: false
|
||||
GOTIFY_DEFAULTUSER_NAME: ${GOTIFY_USER}
|
||||
GOTIFY_DEFAULTUSER_PASS: ${GOTIFY_PASSWORD}
|
||||
GOTIFY_SERVER_CORS_ALLOWORIGINS: "- \"medwings-django:8000\"\n- \"medwings-notifications.lobbes.dev\"\n- \"medwings.lobbes.dev\""
|
||||
GOTIFY_SERVER_CORS_ALLOWMETHODS: "- \"GET\"\n- \"POST\""
|
||||
GOTIFY_SERVER_CORS_ALLOWHEADERS: "- \"Authorization\"\n- \"content-type\""
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.medwings-notifications.entrypoints=https"
|
||||
- "traefik.http.routers.medwings-notifications.rule=Host(`medwings-notifications.lobbes.dev`)"
|
||||
- "traefik.http.routers.medwings-notifications.middlewares=default@file"
|
||||
- "traefik.http.routers.medwings-notifications.tls=true"
|
||||
- "traefik.http.services.medwings-notifications.loadbalancer.server.port=80"
|
||||
- "traefik.docker.network=proxy"
|
||||
|
||||
networks:
|
||||
medwings:
|
||||
external: false
|
||||
proxy:
|
||||
external: true
|
||||
|
||||
...
|
17
production.supervisord.conf
Normal file
17
production.supervisord.conf
Normal file
@ -0,0 +1,17 @@
|
||||
[supervisord]
|
||||
nodaemon=true
|
||||
user=root
|
||||
|
||||
[program:django]
|
||||
command=sh -c 'uvicorn core.asgi:application --host 0.0.0.0 --port 8000 --access-log'
|
||||
directory=/app
|
||||
user=django
|
||||
autostart=true
|
||||
autorestart=true
|
||||
redirect_stderr=true
|
||||
|
||||
[program:crond]
|
||||
command=crond -f
|
||||
autostart=true
|
||||
autorestart=true
|
||||
redirect_stderr=true
|
Loading…
x
Reference in New Issue
Block a user