merge experimental #1
2
.dockerignore
Normal file
2
.dockerignore
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
**/.venv/
|
||||||
|
**/__pycache__/
|
@ -108,6 +108,7 @@ PG_HOST=medwings-postgres
|
|||||||
PG_PORT=5432
|
PG_PORT=5432
|
||||||
GOTIFY_USER=<secret>
|
GOTIFY_USER=<secret>
|
||||||
GOTIFY_PASSWORD=<secret>
|
GOTIFY_PASSWORD=<secret>
|
||||||
|
GOTIFY_HOST=medwings-gotify
|
||||||
WITHINGS_CLIENT_ID=<secret>
|
WITHINGS_CLIENT_ID=<secret>
|
||||||
WITHINGS_CLIENT_SECRET=<secret>
|
WITHINGS_CLIENT_SECRET=<secret>
|
||||||
```
|
```
|
||||||
|
@ -7,6 +7,11 @@
|
|||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="flex flex-col justify-center items-center gap-2 py-4 mx-4 max-w-4xl">
|
<div class="flex flex-col justify-center items-center gap-2 py-4 mx-4 max-w-4xl">
|
||||||
<h2>Register</h2>
|
<h2>Register</h2>
|
||||||
<p>{{ auth_code }}</p>
|
<form method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
{{ user_form }}
|
||||||
|
{{ profile_form }}
|
||||||
|
<button type="submit">Register</button>
|
||||||
|
</form>
|
||||||
</div>
|
</div>
|
||||||
{% endblock content %}
|
{% endblock content %}
|
||||||
|
@ -1,13 +1,19 @@
|
|||||||
from urllib.parse import urlencode
|
from urllib.parse import urlencode
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
|
|
||||||
from django.shortcuts import render
|
from django.shortcuts import redirect, render
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.core.exceptions import PermissionDenied
|
from django.core.exceptions import PermissionDenied
|
||||||
from django.http import HttpResponseBadRequest
|
from django.http import HttpResponseBadRequest
|
||||||
|
from django.contrib.auth.forms import UserCreationForm
|
||||||
|
from django.utils.dateparse import parse_datetime
|
||||||
|
|
||||||
import withings.api
|
import withings.api
|
||||||
|
import withings.models
|
||||||
|
import gotify.api
|
||||||
|
import gotify.models
|
||||||
|
from medwings.forms import ProfileForm
|
||||||
|
|
||||||
|
|
||||||
def register_init(request):
|
def register_init(request):
|
||||||
@ -36,15 +42,15 @@ def register_init(request):
|
|||||||
|
|
||||||
|
|
||||||
def register_continue(request):
|
def register_continue(request):
|
||||||
# Parse GET request parameters
|
|
||||||
authorization_code = request.GET.get('code')
|
authorization_code = request.GET.get('code')
|
||||||
authorization_state = request.GET.get('state')
|
authorization_state = request.GET.get('state')
|
||||||
if not authorization_code:
|
if not authorization_code:
|
||||||
return HttpResponseBadRequest()
|
return HttpResponseBadRequest()
|
||||||
if not authorization_state:
|
if not authorization_state:
|
||||||
return HttpResponseBadRequest()
|
return HttpResponseBadRequest()
|
||||||
if not request.session.get('spoof_protection_token', None) == authorization_state:
|
# TODO enable this when not mocking
|
||||||
return HttpResponseBadRequest()
|
# if not request.session.get('spoof_protection_token', None) == authorization_state:
|
||||||
|
# return HttpResponseBadRequest()
|
||||||
|
|
||||||
# Fetch access and refresh tokens and save them to session storage
|
# Fetch access and refresh tokens and save them to session storage
|
||||||
redirect_uri = request.build_absolute_uri(reverse('register-continue'))
|
redirect_uri = request.build_absolute_uri(reverse('register-continue'))
|
||||||
@ -54,13 +60,62 @@ def register_continue(request):
|
|||||||
return HttpResponseBadRequest()
|
return HttpResponseBadRequest()
|
||||||
withings.api.save_tokens_to_session(request, response_data)
|
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)
|
return render(request, 'authentication/register-continue.html', context)
|
||||||
|
|
||||||
|
@ -127,9 +127,10 @@ DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
|
|||||||
|
|
||||||
WITHINGS_CONFIG = {
|
WITHINGS_CONFIG = {
|
||||||
'CLIENT_ID': getenv('WITHINGS_CLIENT_ID'),
|
'CLIENT_ID': getenv('WITHINGS_CLIENT_ID'),
|
||||||
'CLIENT_SECRET': getenv('WITHINGS_CLIENT_SECRET'),
|
'CLIENT_SECRET': getenv('WITHINGS_CLIENT_SECRET')
|
||||||
}
|
}
|
||||||
GOTIFY_CONFIG = {
|
GOTIFY_CONFIG = {
|
||||||
'USERNAME': getenv('GOTIFY_USER'),
|
'USERNAME': getenv('GOTIFY_USER'),
|
||||||
'PASSWORD': getenv('GOTIFY_PASSWORD'),
|
'PASSWORD': getenv('GOTIFY_PASSWORD'),
|
||||||
|
'HOST': getenv('GOTIFY_HOST')
|
||||||
}
|
}
|
||||||
|
58
app/gotify/api.py
Normal file
58
app/gotify/api.py
Normal 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
12
app/medwings/forms.py
Normal 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'}),
|
||||||
|
}
|
@ -1,8 +1,9 @@
|
|||||||
from datetime import datetime, timedelta
|
from datetime import timedelta
|
||||||
from random import randint
|
from random import randint
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from django.utils import timezone
|
||||||
from urllib.parse import urlencode
|
from urllib.parse import urlencode
|
||||||
|
|
||||||
def fetch_withings_tokens(authorization_code, redirect_uri):
|
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_access_token'] = response_data['body']['access_token']
|
||||||
request.session['withings_refresh_token'] = response_data['body']['refresh_token']
|
request.session['withings_refresh_token'] = response_data['body']['refresh_token']
|
||||||
|
|
||||||
now = datetime.now()
|
now = timezone.now()
|
||||||
request.session['withings_access_token_expiry'] = now + timedelta(seconds=response_data['body']['expires_in'])
|
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)
|
request.session['withings_refresh_token_expiry'] = (now + timedelta(days=365)).isoformat()
|
||||||
|
@ -52,6 +52,7 @@ services:
|
|||||||
WITHINGS_CLIENT_SECRET: ${WITHINGS_CLIENT_SECRET}
|
WITHINGS_CLIENT_SECRET: ${WITHINGS_CLIENT_SECRET}
|
||||||
GOTIFY_USER: ${GOTIFY_USER}
|
GOTIFY_USER: ${GOTIFY_USER}
|
||||||
GOTIFY_PASSWORD: ${GOTIFY_PASSWORD}
|
GOTIFY_PASSWORD: ${GOTIFY_PASSWORD}
|
||||||
|
GOTIFY_HOST: ${GOTIFY_HOST}
|
||||||
medwings-postgres:
|
medwings-postgres:
|
||||||
image: postgres:alpine
|
image: postgres:alpine
|
||||||
container_name: ${PG_HOST}
|
container_name: ${PG_HOST}
|
||||||
@ -80,6 +81,9 @@ services:
|
|||||||
GOTIFY_SERVER_SSL_REDIRECTTOHTTPS: false
|
GOTIFY_SERVER_SSL_REDIRECTTOHTTPS: false
|
||||||
GOTIFY_DEFAULTUSER_NAME: ${GOTIFY_USER}
|
GOTIFY_DEFAULTUSER_NAME: ${GOTIFY_USER}
|
||||||
GOTIFY_DEFAULTUSER_PASS: ${GOTIFY_PASSWORD}
|
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:
|
medwings-pgweb:
|
||||||
image: sosedoff/pgweb
|
image: sosedoff/pgweb
|
||||||
container_name: medwings-pgweb
|
container_name: medwings-pgweb
|
||||||
|
Loading…
x
Reference in New Issue
Block a user