mirror of
https://github.com/seanmorley15/AdventureLog.git
synced 2025-08-05 13:15:18 +02:00
User profile settings API and remove old Dj-Rest-Auth code
This commit is contained in:
parent
c65fcc2558
commit
84566b8ec1
22 changed files with 514 additions and 791 deletions
|
@ -11,7 +11,6 @@ https://docs.djangoproject.com/en/1.7/ref/settings/
|
|||
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
|
||||
import os
|
||||
from dotenv import load_dotenv
|
||||
from datetime import timedelta
|
||||
from os import getenv
|
||||
from pathlib import Path
|
||||
# Load environment variables from .env file
|
||||
|
@ -35,8 +34,6 @@ DEBUG = getenv('DEBUG', 'True') == 'True'
|
|||
# ]
|
||||
ALLOWED_HOSTS = ['*']
|
||||
|
||||
# Application definition
|
||||
|
||||
INSTALLED_APPS = (
|
||||
'django.contrib.admin',
|
||||
'django.contrib.auth',
|
||||
|
@ -50,7 +47,7 @@ INSTALLED_APPS = (
|
|||
"allauth_ui",
|
||||
'allauth',
|
||||
'allauth.account',
|
||||
'allauth.mfa',
|
||||
# 'allauth.mfa',
|
||||
'allauth.headless',
|
||||
'allauth.socialaccount',
|
||||
"widget_tweaks",
|
||||
|
@ -108,7 +105,7 @@ DATABASES = {
|
|||
}
|
||||
}
|
||||
|
||||
ACCOUNT_SIGNUP_FORM_CLASS = 'users.form_overrides.CustomSignupForm'
|
||||
|
||||
|
||||
# Internationalization
|
||||
# https://docs.djangoproject.com/en/1.7/topics/i18n/
|
||||
|
@ -123,8 +120,7 @@ USE_L10N = True
|
|||
|
||||
USE_TZ = True
|
||||
|
||||
ALLAUTH_UI_THEME = "dark"
|
||||
SILENCED_SYSTEM_CHECKS = ["slippers.E001"]
|
||||
|
||||
|
||||
# Static files (CSS, JavaScript, Images)
|
||||
# https://docs.djangoproject.com/en/1.7/howto/static-files/
|
||||
|
@ -138,6 +134,16 @@ MEDIA_URL = '/media/'
|
|||
MEDIA_ROOT = BASE_DIR / 'media'
|
||||
STATICFILES_DIRS = [BASE_DIR / 'static']
|
||||
|
||||
STORAGES = {
|
||||
"staticfiles": {
|
||||
"BACKEND": "whitenoise.storage.CompressedManifestStaticFilesStorage",
|
||||
},
|
||||
"default": {
|
||||
"BACKEND": "django.core.files.storage.FileSystemStorage",
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEMPLATES = [
|
||||
{
|
||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||
|
@ -154,22 +160,22 @@ TEMPLATES = [
|
|||
},
|
||||
]
|
||||
|
||||
# Authentication settings
|
||||
|
||||
DISABLE_REGISTRATION = getenv('DISABLE_REGISTRATION', 'False') == 'True'
|
||||
DISABLE_REGISTRATION_MESSAGE = getenv('DISABLE_REGISTRATION_MESSAGE', 'Registration is disabled. Please contact the administrator if you need an account.')
|
||||
|
||||
STORAGES = {
|
||||
"staticfiles": {
|
||||
"BACKEND": "whitenoise.storage.CompressedManifestStaticFilesStorage",
|
||||
},
|
||||
"default": {
|
||||
"BACKEND": "django.core.files.storage.FileSystemStorage",
|
||||
}
|
||||
}
|
||||
ALLAUTH_UI_THEME = "dark"
|
||||
SILENCED_SYSTEM_CHECKS = ["slippers.E001"]
|
||||
|
||||
AUTH_USER_MODEL = 'users.CustomUser'
|
||||
|
||||
ACCOUNT_ADAPTER = 'users.adapters.NoNewUsersAccountAdapter'
|
||||
|
||||
ACCOUNT_SIGNUP_FORM_CLASS = 'users.form_overrides.CustomSignupForm'
|
||||
|
||||
SESSION_SAVE_EVERY_REQUEST = True
|
||||
|
||||
FRONTEND_URL = getenv('FRONTEND_URL', 'http://localhost:3000')
|
||||
|
||||
# HEADLESS_FRONTEND_URLS = {
|
||||
|
@ -218,9 +224,6 @@ SWAGGER_SETTINGS = {
|
|||
'LOGOUT_URL': 'logout',
|
||||
}
|
||||
|
||||
from os import getenv
|
||||
|
||||
|
||||
CORS_ALLOWED_ORIGINS = [origin.strip() for origin in getenv('CSRF_TRUSTED_ORIGINS', 'http://localhost').split(',') if origin.strip()]
|
||||
|
||||
|
||||
|
@ -253,6 +256,4 @@ LOGGING = {
|
|||
},
|
||||
}
|
||||
# https://github.com/dr5hn/countries-states-cities-database/tags
|
||||
COUNTRY_REGION_JSON_VERSION = 'v2.4'
|
||||
|
||||
SESSION_SAVE_EVERY_REQUEST = True
|
||||
COUNTRY_REGION_JSON_VERSION = 'v2.4'
|
|
@ -3,8 +3,7 @@ from django.contrib import admin
|
|||
from django.views.generic import RedirectView, TemplateView
|
||||
from django.conf import settings
|
||||
from django.conf.urls.static import static
|
||||
from adventures import urls as adventures
|
||||
from users.views import ChangeEmailView, IsRegistrationDisabled, PublicUserListView, PublicUserDetailView, UserMetadataView
|
||||
from users.views import IsRegistrationDisabled, PublicUserListView, PublicUserDetailView, UserMetadataView, UpdateUserMetadataView
|
||||
from .views import get_csrf_token
|
||||
from drf_yasg.views import get_schema_view
|
||||
|
||||
|
@ -19,56 +18,19 @@ schema_view = get_schema_view(
|
|||
urlpatterns = [
|
||||
path('api/', include('adventures.urls')),
|
||||
path('api/', include('worldtravel.urls')),
|
||||
path("_allauth/", include("allauth.headless.urls")),
|
||||
|
||||
path('auth/change-email/', ChangeEmailView.as_view(), name='change_email'),
|
||||
path('auth/is-registration-disabled/', IsRegistrationDisabled.as_view(), name='is_registration_disabled'),
|
||||
path('auth/users/', PublicUserListView.as_view(), name='public-user-list'),
|
||||
path('auth/user/<uuid:user_id>/', PublicUserDetailView.as_view(), name='public-user-detail'),
|
||||
path('auth/update-user/', UpdateUserMetadataView.as_view(), name='update-user-metadata'),
|
||||
|
||||
path('auth/user-metadata/', UserMetadataView.as_view(), name='user-metadata'),
|
||||
|
||||
|
||||
path('auth/user-metadata/', UserMetadataView.as_view(), name='user-metadata'),
|
||||
|
||||
path('csrf/', get_csrf_token, name='get_csrf_token'),
|
||||
re_path(r'^$', TemplateView.as_view(
|
||||
template_name="home.html"), name='home'),
|
||||
re_path(r'^signup/$', TemplateView.as_view(template_name="signup.html"),
|
||||
name='signup'),
|
||||
re_path(r'^email-verification/$',
|
||||
TemplateView.as_view(template_name="email_verification.html"),
|
||||
name='email-verification'),
|
||||
re_path(r'^login/$', TemplateView.as_view(template_name="login.html"),
|
||||
name='login'),
|
||||
re_path(r'^logout/$', TemplateView.as_view(template_name="logout.html"),
|
||||
name='logout'),
|
||||
re_path(r'^password-reset/$',
|
||||
TemplateView.as_view(template_name="password_reset.html"),
|
||||
name='password-reset'),
|
||||
re_path(r'^password-reset/confirm/$',
|
||||
TemplateView.as_view(template_name="password_reset_confirm.html"),
|
||||
name='password-reset-confirm'),
|
||||
|
||||
re_path(r'^user-details/$',
|
||||
TemplateView.as_view(template_name="user_details.html"),
|
||||
name='user-details'),
|
||||
re_path(r'^password-change/$',
|
||||
TemplateView.as_view(template_name="password_change.html"),
|
||||
name='password-change'),
|
||||
re_path(r'^resend-email-verification/$',
|
||||
TemplateView.as_view(
|
||||
template_name="resend_email_verification.html"),
|
||||
name='resend-email-verification'),
|
||||
|
||||
|
||||
# this url is used to generate email content
|
||||
re_path(r'^password-reset/confirm/(?P<uidb64>[0-9A-Za-z_\-]+)/(?P<token>[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,32})/$',
|
||||
TemplateView.as_view(template_name="password_reset_confirm.html"),
|
||||
name='password_reset_confirm'),
|
||||
|
||||
re_path(r'^auth/', include('dj_rest_auth.urls')),
|
||||
re_path(r'^auth/registration/',
|
||||
include('dj_rest_auth.registration.urls')),
|
||||
# re_path(r'^account/', include('allauth.urls')),
|
||||
|
||||
path('', TemplateView.as_view(template_name='home.html')),
|
||||
|
||||
re_path(r'^admin/', admin.site.urls),
|
||||
re_path(r'^accounts/profile/$', RedirectView.as_view(url='/',
|
||||
permanent=True), name='profile-redirect'),
|
||||
|
@ -78,5 +40,5 @@ urlpatterns = [
|
|||
path("accounts/", include("allauth.urls")),
|
||||
|
||||
# Include the API endpoints:
|
||||
path("_allauth/", include("allauth.headless.urls")),
|
||||
|
||||
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
Django==5.0.8
|
||||
dj-rest-auth @ git+https://github.com/iMerica/dj-rest-auth.git@master
|
||||
djangorestframework>=3.15.2
|
||||
djangorestframework-simplejwt==5.3.1
|
||||
django-allauth==0.63.3
|
||||
drf-yasg==1.21.4
|
||||
django-cors-headers==4.4.0
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
<meta charset="utf-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="description" content="Django-dj-rest-auth demo" />
|
||||
<meta name="author" content="iMerica, Inc." />
|
||||
<meta name="description" content="AdventureLog Server" />
|
||||
<meta name="author" content="Sean Morley" />
|
||||
|
||||
<title>AdventureLog API Server</title>
|
||||
|
||||
|
@ -31,39 +31,6 @@
|
|||
<body role="document">
|
||||
<div class="navbar navbar-inverse" role="navigation">
|
||||
<div class="container">
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li class="dropdown">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown"
|
||||
>API endpoints <span class="caret"></span
|
||||
></a>
|
||||
|
||||
<ul class="dropdown-menu" role="menu">
|
||||
<!-- these pages don't require user token -->
|
||||
<li><a href="{% url 'signup' %}">Signup</a></li>
|
||||
<li>
|
||||
<a href="{% url 'email-verification' %}">E-mail verification</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{% url 'resend-email-verification' %}"
|
||||
>Resend E-mail verification</a
|
||||
>
|
||||
</li>
|
||||
<li><a href="{% url 'login' %}">Login</a></li>
|
||||
<li><a href="{% url 'password-reset' %}">Password Reset</a></li>
|
||||
<li>
|
||||
<a href="{% url 'password-reset-confirm' %}"
|
||||
>Password Reset Confirm</a
|
||||
>
|
||||
</li>
|
||||
<li class="divider"></li>
|
||||
<!-- these pages require user token -->
|
||||
<li><a href="{% url 'user-details' %}">User details</a></li>
|
||||
<li><a href="{% url 'logout' %}">Logout</a></li>
|
||||
<li><a href="{% url 'password-change' %}">Password change</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="navbar-header">
|
||||
<button
|
||||
type="button"
|
||||
|
@ -80,20 +47,19 @@
|
|||
</div>
|
||||
<div class="collapse navbar-collapse">
|
||||
<ul class="nav navbar-nav">
|
||||
<li class="active"><a href="/">Demo</a></li>
|
||||
<li class="active"><a href="/">Server Home</a></li>
|
||||
<li>
|
||||
<a
|
||||
target="_blank"
|
||||
href="http://dj-rest-auth.readthedocs.org/en/latest/"
|
||||
<a target="_blank" href="http://adventurelog.app"
|
||||
>Documentation</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a target="_blank" href="https://github.com/iMerica/dj-rest-auth"
|
||||
>Source code</a
|
||||
<a
|
||||
target="_blank"
|
||||
href="https://github.com/seanmorley15/AdventureLog"
|
||||
>Source Code</a
|
||||
>
|
||||
</li>
|
||||
<li><a target="_blank" href="{% url 'api_docs' %}">API Docs</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<!--/.nav-collapse -->
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<h3>E-mail verification</h3><hr/>
|
||||
{% include "fragments/email_verification_form.html" %}
|
||||
</div>
|
||||
{% endblock %}
|
|
@ -4,7 +4,12 @@
|
|||
<h1>AdventureLog API Server</h1>
|
||||
<p>
|
||||
<a class="btn btn-primary btn-lg" href="/admin" role="button">Admin Site</a>
|
||||
<a class="btn btn-secondary btn-lg" href="/docs" role="button">API Docs</a>
|
||||
<a
|
||||
class="btn btn-secondary btn-lg"
|
||||
href="/accounts/password/change"
|
||||
role="button"
|
||||
>Account Managment</a
|
||||
>
|
||||
</p>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<h3>Login</h3><hr/>
|
||||
{% include "fragments/login_form.html" %}
|
||||
</div>
|
||||
{% endblock %}
|
|
@ -1,8 +0,0 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<h3>Logout</h3><hr/>
|
||||
{% include "fragments/logout_form.html" %}
|
||||
</div>
|
||||
{% endblock %}
|
|
@ -1,39 +0,0 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="row">
|
||||
<div class="form-group">
|
||||
<label for="token" class="col-sm-2 control-label">User Token</label>
|
||||
<div class="col-sm-4">
|
||||
<input name="token" type="text" class="form-control" id="token" placeholder="Token">
|
||||
<p class="help-block">Token received after login</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<h3>Update User Details</h3><hr/>
|
||||
{% include "fragments/password_change_form.html" %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block script %}
|
||||
<script type="text/javascript">
|
||||
$().ready(function(){
|
||||
$('form button[type=submit]').click(function(){
|
||||
var token = $('input[name=token]').val();
|
||||
var form = $('form');
|
||||
$.ajax({
|
||||
url: form.attr('action'),
|
||||
data: $('form').serialize(),
|
||||
type: "POST",
|
||||
beforeSend: function(xhr){xhr.setRequestHeader('Authorization', 'Token '+token);}
|
||||
}).fail(function(data){error_response(data);})
|
||||
.done(function(data){susccess_response(data);});
|
||||
return false;
|
||||
});
|
||||
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
|
@ -1,8 +0,0 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<h3>Password reset</h3><hr/>
|
||||
{% include "fragments/password_reset_form.html" %}
|
||||
</div>
|
||||
{% endblock %}
|
|
@ -1,26 +0,0 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<h3>Password reset confirmation</h3><hr/>
|
||||
{% include "fragments/password_reset_confirm_form.html" %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
|
||||
|
||||
{% block script %}
|
||||
<script type="text/javascript">
|
||||
var url_elements = window.location.pathname.split('/');
|
||||
if (url_elements.length == 6){
|
||||
var uid = url_elements[url_elements.length - 3];
|
||||
if (uid !== undefined){
|
||||
$('input[name=uid]').val(uid);
|
||||
}
|
||||
var token = url_elements[url_elements.length - 2];
|
||||
if (token !== undefined){
|
||||
$('input[name=token]').val(token);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
|
@ -1,8 +0,0 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<h3>Resend E-mail verification</h3><hr/>
|
||||
{% include "fragments/resend_email_verification_form.html" %}
|
||||
</div>
|
||||
{% endblock %}
|
|
@ -1,8 +0,0 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<h3>Signup</h3><hr/>
|
||||
{% include "fragments/signup_form.html" %}
|
||||
</div>
|
||||
{% endblock %}
|
|
@ -1,58 +0,0 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="row">
|
||||
<h3>Retrieve User Details</h3><hr/>
|
||||
<div class="form-group">
|
||||
<label for="token" class="col-sm-2 control-label">User Token</label>
|
||||
<div class="col-sm-4">
|
||||
<input name="token" type="text" class="form-control" id="token" placeholder="Token">
|
||||
<p class="help-block">Token received after login</p>
|
||||
</div>
|
||||
<button id="get-user-details" class="btn btn-primary">GET user details</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<h3>Update User Details</h3><hr/>
|
||||
{% include "fragments/user_details_form.html" %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block script %}
|
||||
<script type="text/javascript">
|
||||
$().ready(function(){
|
||||
$('#get-user-details').click(function(){
|
||||
var token = $('input[name=token]').val();
|
||||
|
||||
$.ajax({
|
||||
url: "{% url 'rest_user_details' %}",
|
||||
beforeSend: function(xhr){xhr.setRequestHeader('Authorization', 'Token '+token);},
|
||||
type: "GET",
|
||||
success: function(data) {
|
||||
$('input[name=username]').val(data.username);
|
||||
$('input[name=email]').val(data.email);
|
||||
$('input[name=first_name]').val(data.first_name);
|
||||
$('input[name=last_name]').val(data.last_name);
|
||||
}
|
||||
});
|
||||
return false;
|
||||
});
|
||||
|
||||
$('form button[type=submit]').click(function(){
|
||||
var token = $('input[name=token]').val();
|
||||
var form = $('form');
|
||||
$.ajax({
|
||||
url: form.attr('action'),
|
||||
data: $('form').serialize(),
|
||||
type: "PUT",
|
||||
beforeSend: function(xhr){xhr.setRequestHeader('Authorization', 'Token '+token);}
|
||||
}).fail(function(data){error_response(data);})
|
||||
.done(function(data){susccess_response(data);});
|
||||
return false;
|
||||
});
|
||||
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
|
@ -106,6 +106,7 @@ class CustomUserDetailsSerializer(UserDetailsSerializer):
|
|||
class Meta(UserDetailsSerializer.Meta):
|
||||
model = CustomUser
|
||||
fields = UserDetailsSerializer.Meta.fields + ('profile_pic', 'uuid', 'public_profile')
|
||||
read_only_fields = UserDetailsSerializer.Meta.read_only_fields + ('uuid',)
|
||||
|
||||
def to_representation(self, instance):
|
||||
representation = super().to_representation(instance)
|
||||
|
|
|
@ -97,4 +97,27 @@ class UserMetadataView(APIView):
|
|||
def get(self, request):
|
||||
user = request.user
|
||||
serializer = PublicUserSerializer(user)
|
||||
return Response(serializer.data, status=status.HTTP_200_OK)
|
||||
return Response(serializer.data, status=status.HTTP_200_OK)
|
||||
|
||||
class UpdateUserMetadataView(APIView):
|
||||
"""
|
||||
Update user metadata using fields from the PublicUserSerializer.
|
||||
Using patch opposed to put allows for partial updates, covers the case where it checks the username and says it's already taken. Duplicate uesrname values should not be included in the request to avoid this.
|
||||
"""
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
@swagger_auto_schema(
|
||||
request_body=PublicUserSerializer,
|
||||
responses={
|
||||
200: openapi.Response('User metadata updated'),
|
||||
400: 'Bad Request'
|
||||
},
|
||||
operation_description="Update user metadata."
|
||||
)
|
||||
def patch(self, request):
|
||||
user = request.user
|
||||
serializer = PublicUserSerializer(user, data=request.data, partial=True, context={'request': request})
|
||||
if serializer.is_valid():
|
||||
serializer.save()
|
||||
return Response(serializer.data, status=status.HTTP_200_OK)
|
||||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
Loading…
Add table
Add a link
Reference in a new issue