merge experimental #1

Merged
jlobbes merged 25 commits from experimental into master 2023-08-17 15:16:51 +01:00
10 changed files with 153 additions and 14 deletions
Showing only changes of commit b43a2ffbdc - Show all commits

2
.dockerignore Normal file
View File

@ -0,0 +1,2 @@
**/.venv/
**/__pycache__/

View File

@ -108,6 +108,7 @@ PG_HOST=medwings-postgres
PG_PORT=5432
GOTIFY_USER=<secret>
GOTIFY_PASSWORD=<secret>
GOTIFY_HOST=medwings-gotify
WITHINGS_CLIENT_ID=<secret>
WITHINGS_CLIENT_SECRET=<secret>
```

View File

@ -7,6 +7,11 @@
{% block content %}
<div class="flex flex-col justify-center items-center gap-2 py-4 mx-4 max-w-4xl">
<h2>Register</h2>
<p>{{ auth_code }}</p>
<form method="post">
{% csrf_token %}
{{ user_form }}
{{ profile_form }}
<button type="submit">Register</button>
</form>
</div>
{% endblock content %}

View File

@ -1,13 +1,19 @@
from urllib.parse import urlencode
from uuid import uuid4
from django.shortcuts import render
from django.shortcuts import redirect, render
from django.conf import settings
from django.urls import reverse
from django.core.exceptions import PermissionDenied
from django.http import HttpResponseBadRequest
from django.contrib.auth.forms import UserCreationForm
from django.utils.dateparse import parse_datetime
import withings.api
import withings.models
import gotify.api
import gotify.models
from medwings.forms import ProfileForm
def register_init(request):
@ -36,15 +42,15 @@ def register_init(request):
def register_continue(request):
# Parse GET request parameters
authorization_code = request.GET.get('code')
authorization_state = request.GET.get('state')
if not authorization_code:
return HttpResponseBadRequest()
if not authorization_state:
return HttpResponseBadRequest()
if not request.session.get('spoof_protection_token', None) == authorization_state:
return HttpResponseBadRequest()
# TODO enable this when not mocking
# if not request.session.get('spoof_protection_token', None) == authorization_state:
# return HttpResponseBadRequest()
# Fetch access and refresh tokens and save them to session storage
redirect_uri = request.build_absolute_uri(reverse('register-continue'))
@ -54,13 +60,62 @@ def register_continue(request):
return HttpResponseBadRequest()
withings.api.save_tokens_to_session(request, response_data)
# TODO add user registration form
if request.method == 'POST':
user_form = UserCreationForm(request.POST)
profile_form = ProfileForm(request.POST)
# TODO once user registration form is valid, make gotify API calls
if user_form.is_valid() and profile_form.is_valid():
user = user_form.save(commit=False)
profile = profile_form.save(commit=False)
profile.user = user
# TODO once gotify is set up, create and save database objects
user_password = request.POST.get('password1')
gotify_user_info = gotify.api.create_user(user.username, user_password)
gotify_app_info = gotify.api.create_application(user.username, user_password)
gotify_user = gotify.models.GotifyUser(
user=user,
id=gotify_user_info['id']
)
gotify_app = gotify.models.GotifyApplication(
user=gotify_user,
id=gotify_app_info['id'],
token=gotify_app_info['token']
)
context = {}
withings_api_account = withings.models.ApiAccount(
user=user,
userid=request.session.get('withings_userid')
)
withings_access_token = withings.models.AccessToken(
account=withings_api_account,
value=request.session.get('withings_access_token'),
expires=parse_datetime(request.session.get('withings_access_token_expiry'))
)
withings_refresh_token = withings.models.RefreshToken(
account=withings_api_account,
value=request.session.get('withings_refresh_token'),
expires=parse_datetime(request.session.get('withings_refresh_token_expiry'))
)
for instance in [
user, profile,
gotify_user, gotify_app,
withings_api_account, withings_access_token, withings_refresh_token
]:
instance.save()
# TODO sync withings health data
# TODO redirect user to some other page and ask them to log in
return redirect('dashboard')
else:
user_form = UserCreationForm()
profile_form = ProfileForm()
context = {
'user_form': user_form,
'profile_form': profile_form,
}
return render(request, 'authentication/register-continue.html', context)

View File

@ -127,9 +127,10 @@ DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
WITHINGS_CONFIG = {
'CLIENT_ID': getenv('WITHINGS_CLIENT_ID'),
'CLIENT_SECRET': getenv('WITHINGS_CLIENT_SECRET'),
'CLIENT_SECRET': getenv('WITHINGS_CLIENT_SECRET')
}
GOTIFY_CONFIG = {
'USERNAME': getenv('GOTIFY_USER'),
'PASSWORD': getenv('GOTIFY_PASSWORD'),
'HOST': getenv('GOTIFY_HOST')
}

58
app/gotify/api.py Normal file
View File

@ -0,0 +1,58 @@
import requests
from requests.auth import HTTPBasicAuth
from django.conf import settings
from .models import GotifyUser
def create_user(username: str, password: str) -> dict:
"""Creates a user on the Gotify instance.
:param username: The name of the user to be created.
:param password: The password of the user to be created.
"""
data = {
'admin': False,
'name': username,
'pass': password
}
response = requests.post(
url=f"http://{settings.GOTIFY_CONFIG['HOST']}/user",
auth=HTTPBasicAuth(
settings.GOTIFY_CONFIG['USERNAME'],
settings.GOTIFY_CONFIG['PASSWORD'],
),
json=data
)
if response is not None:
response.raise_for_status()
return response.json()
def create_application(username: str, password: str) -> dict:
"""Creates an application on the Gotify instance.
:param username: The user for whom an application will be created.
:param password: The user's password.
"""
data = {
'defaultPriority': 6,
'description': 'A remote patient health monitoring system.',
'name': 'Medwings'
}
response = requests.post(
url=f"http://{settings.GOTIFY_CONFIG['HOST']}/application",
auth=HTTPBasicAuth(username, password),
json=data
)
if response is not None:
response.raise_for_status()
return response.json()

12
app/medwings/forms.py Normal file
View File

@ -0,0 +1,12 @@
from django import forms
from medwings.models import Profile
class ProfileForm(forms.ModelForm):
class Meta:
model = Profile
fields = ['date_of_birth', 'sex']
widgets = {
'date_of_birth': forms.DateInput(attrs={'type': 'date'}),
}

View File

@ -1,8 +1,9 @@
from datetime import datetime, timedelta
from datetime import timedelta
from random import randint
import requests
from django.conf import settings
from django.utils import timezone
from urllib.parse import urlencode
def fetch_withings_tokens(authorization_code, redirect_uri):
@ -43,6 +44,6 @@ def save_tokens_to_session(request, response_data):
request.session['withings_access_token'] = response_data['body']['access_token']
request.session['withings_refresh_token'] = response_data['body']['refresh_token']
now = datetime.now()
request.session['withings_access_token_expiry'] = now + timedelta(seconds=response_data['body']['expires_in'])
request.session['withings_refresh_token_expiry'] = now + timedelta(days=365)
now = timezone.now()
request.session['withings_access_token_expiry'] = (now + timedelta(seconds=response_data['body']['expires_in'])).isoformat()
request.session['withings_refresh_token_expiry'] = (now + timedelta(days=365)).isoformat()

View File

@ -52,6 +52,7 @@ services:
WITHINGS_CLIENT_SECRET: ${WITHINGS_CLIENT_SECRET}
GOTIFY_USER: ${GOTIFY_USER}
GOTIFY_PASSWORD: ${GOTIFY_PASSWORD}
GOTIFY_HOST: ${GOTIFY_HOST}
medwings-postgres:
image: postgres:alpine
container_name: ${PG_HOST}
@ -80,6 +81,9 @@ services:
GOTIFY_SERVER_SSL_REDIRECTTOHTTPS: false
GOTIFY_DEFAULTUSER_NAME: ${GOTIFY_USER}
GOTIFY_DEFAULTUSER_PASS: ${GOTIFY_PASSWORD}
GOTIFY_SERVER_CORS_ALLOWORIGINS: "- \"localhost:8000\"\n- \"medwings.lobbes.dev\""
GOTIFY_SERVER_CORS_ALLOWMETHODS: "- \"GET\"\n- \"POST\""
GOTIFY_SERVER_CORS_ALLOWHEADERS: "- \"Authorization\"\n- \"content-type\""
medwings-pgweb:
image: sosedoff/pgweb
container_name: medwings-pgweb