mirror of
https://github.com/seanmorley15/AdventureLog.git
synced 2025-07-21 05:49:37 +02:00
Initial migration to new session based auth system with AllAuth
This commit is contained in:
parent
7defdac3a8
commit
9bc20be70e
24 changed files with 313 additions and 773 deletions
|
@ -11,3 +11,13 @@ class AppVersionMiddleware:
|
||||||
response['X-AdventureLog-Version'] = '1.0.0'
|
response['X-AdventureLog-Version'] = '1.0.0'
|
||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
# make a middlewra that prints all of the request cookies
|
||||||
|
class PrintCookiesMiddleware:
|
||||||
|
def __init__(self, get_response):
|
||||||
|
self.get_response = get_response
|
||||||
|
|
||||||
|
def __call__(self, request):
|
||||||
|
print(request.COOKIES)
|
||||||
|
response = self.get_response(request)
|
||||||
|
return response
|
|
@ -73,6 +73,7 @@ class AdventureViewSet(viewsets.ModelViewSet):
|
||||||
return queryset.order_by(ordering)
|
return queryset.order_by(ordering)
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
|
print(self.request.user)
|
||||||
# if the user is not authenticated return only public adventures for retrieve action
|
# if the user is not authenticated return only public adventures for retrieve action
|
||||||
if not self.request.user.is_authenticated:
|
if not self.request.user.is_authenticated:
|
||||||
if self.action == 'retrieve':
|
if self.action == 'retrieve':
|
||||||
|
|
|
@ -47,10 +47,11 @@ INSTALLED_APPS = (
|
||||||
'django.contrib.sites',
|
'django.contrib.sites',
|
||||||
'rest_framework',
|
'rest_framework',
|
||||||
'rest_framework.authtoken',
|
'rest_framework.authtoken',
|
||||||
'dj_rest_auth',
|
# 'dj_rest_auth',
|
||||||
'allauth',
|
'allauth',
|
||||||
'allauth.account',
|
'allauth.account',
|
||||||
'dj_rest_auth.registration',
|
'allauth.headless',
|
||||||
|
# 'dj_rest_auth.registration',
|
||||||
'allauth.socialaccount',
|
'allauth.socialaccount',
|
||||||
'allauth.socialaccount.providers.facebook',
|
'allauth.socialaccount.providers.facebook',
|
||||||
'drf_yasg',
|
'drf_yasg',
|
||||||
|
@ -113,6 +114,7 @@ DATABASES = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ACCOUNT_SIGNUP_FORM_CLASS = 'users.form_overrides.CustomSignupForm'
|
||||||
|
|
||||||
# Internationalization
|
# Internationalization
|
||||||
# https://docs.djangoproject.com/en/1.7/topics/i18n/
|
# https://docs.djangoproject.com/en/1.7/topics/i18n/
|
||||||
|
@ -157,16 +159,6 @@ TEMPLATES = [
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
REST_AUTH = {
|
|
||||||
'SESSION_LOGIN': True,
|
|
||||||
'USE_JWT': True,
|
|
||||||
'JWT_AUTH_COOKIE': 'auth',
|
|
||||||
'JWT_AUTH_HTTPONLY': False,
|
|
||||||
'REGISTER_SERIALIZER': 'users.serializers.RegisterSerializer',
|
|
||||||
'USER_DETAILS_SERIALIZER': 'users.serializers.CustomUserDetailsSerializer',
|
|
||||||
'PASSWORD_RESET_SERIALIZER': 'users.serializers.MyPasswordResetSerializer'
|
|
||||||
}
|
|
||||||
|
|
||||||
DISABLE_REGISTRATION = getenv('DISABLE_REGISTRATION', 'False') == 'True'
|
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.')
|
DISABLE_REGISTRATION_MESSAGE = getenv('DISABLE_REGISTRATION_MESSAGE', 'Registration is disabled. Please contact the administrator if you need an account.')
|
||||||
|
|
||||||
|
@ -181,8 +173,16 @@ STORAGES = {
|
||||||
|
|
||||||
AUTH_USER_MODEL = 'users.CustomUser'
|
AUTH_USER_MODEL = 'users.CustomUser'
|
||||||
|
|
||||||
|
ACCOUNT_ADAPTER = 'users.adapters.NoNewUsersAccountAdapter'
|
||||||
|
|
||||||
FRONTEND_URL = getenv('FRONTEND_URL', 'http://localhost:3000')
|
FRONTEND_URL = getenv('FRONTEND_URL', 'http://localhost:3000')
|
||||||
|
|
||||||
|
# HEADLESS_FRONTEND_URLS = {
|
||||||
|
# "account_confirm_email": "https://app.project.org/account/verify-email/{key}",
|
||||||
|
# "account_reset_password_from_key": "https://app.org/account/password/reset/key/{key}",
|
||||||
|
# "account_signup": "https://app.org/account/signup",
|
||||||
|
# }
|
||||||
|
|
||||||
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
|
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
|
||||||
SITE_ID = 1
|
SITE_ID = 1
|
||||||
ACCOUNT_EMAIL_REQUIRED = True
|
ACCOUNT_EMAIL_REQUIRED = True
|
||||||
|
@ -228,12 +228,14 @@ SWAGGER_SETTINGS = {
|
||||||
'LOGOUT_URL': 'logout',
|
'LOGOUT_URL': 'logout',
|
||||||
}
|
}
|
||||||
|
|
||||||
# For demo purposes only. Use a white list in the real world.
|
|
||||||
CORS_ORIGIN_ALLOW_ALL = True
|
|
||||||
|
|
||||||
from os import getenv
|
from os import getenv
|
||||||
|
|
||||||
|
|
||||||
|
CORS_ALLOWED_ORIGINS = [origin.strip() for origin in getenv('CSRF_TRUSTED_ORIGINS', 'http://localhost').split(',') if origin.strip()]
|
||||||
|
|
||||||
|
|
||||||
CSRF_TRUSTED_ORIGINS = [origin.strip() for origin in getenv('CSRF_TRUSTED_ORIGINS', 'http://localhost').split(',') if origin.strip()]
|
CSRF_TRUSTED_ORIGINS = [origin.strip() for origin in getenv('CSRF_TRUSTED_ORIGINS', 'http://localhost').split(',') if origin.strip()]
|
||||||
|
|
||||||
DEFAULT_AUTO_FIELD = 'django.db.models.AutoField'
|
DEFAULT_AUTO_FIELD = 'django.db.models.AutoField'
|
||||||
|
|
||||||
LOGGING = {
|
LOGGING = {
|
||||||
|
@ -260,6 +262,7 @@ LOGGING = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
# https://github.com/dr5hn/countries-states-cities-database/tags
|
# https://github.com/dr5hn/countries-states-cities-database/tags
|
||||||
COUNTRY_REGION_JSON_VERSION = 'v2.4'
|
COUNTRY_REGION_JSON_VERSION = 'v2.4'
|
||||||
|
|
||||||
|
SESSION_SAVE_EVERY_REQUEST = True
|
|
@ -4,7 +4,7 @@ from django.views.generic import RedirectView, TemplateView
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.conf.urls.static import static
|
from django.conf.urls.static import static
|
||||||
from adventures import urls as adventures
|
from adventures import urls as adventures
|
||||||
from users.views import ChangeEmailView, IsRegistrationDisabled, PublicUserListView, PublicUserDetailView
|
from users.views import ChangeEmailView, IsRegistrationDisabled, PublicUserListView, PublicUserDetailView, UserMetadataView
|
||||||
from .views import get_csrf_token
|
from .views import get_csrf_token
|
||||||
from drf_yasg.views import get_schema_view
|
from drf_yasg.views import get_schema_view
|
||||||
|
|
||||||
|
@ -25,6 +25,10 @@ urlpatterns = [
|
||||||
path('auth/users/', PublicUserListView.as_view(), name='public-user-list'),
|
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/user/<uuid:user_id>/', PublicUserDetailView.as_view(), name='public-user-detail'),
|
||||||
|
|
||||||
|
path('auth/user-metadata/', UserMetadataView.as_view(), name='user-metadata'),
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
path('csrf/', get_csrf_token, name='get_csrf_token'),
|
path('csrf/', get_csrf_token, name='get_csrf_token'),
|
||||||
re_path(r'^$', TemplateView.as_view(
|
re_path(r'^$', TemplateView.as_view(
|
||||||
template_name="home.html"), name='home'),
|
template_name="home.html"), name='home'),
|
||||||
|
@ -64,11 +68,15 @@ urlpatterns = [
|
||||||
re_path(r'^auth/', include('dj_rest_auth.urls')),
|
re_path(r'^auth/', include('dj_rest_auth.urls')),
|
||||||
re_path(r'^auth/registration/',
|
re_path(r'^auth/registration/',
|
||||||
include('dj_rest_auth.registration.urls')),
|
include('dj_rest_auth.registration.urls')),
|
||||||
re_path(r'^account/', include('allauth.urls')),
|
# re_path(r'^account/', include('allauth.urls')),
|
||||||
re_path(r'^admin/', admin.site.urls),
|
re_path(r'^admin/', admin.site.urls),
|
||||||
re_path(r'^accounts/profile/$', RedirectView.as_view(url='/',
|
re_path(r'^accounts/profile/$', RedirectView.as_view(url='/',
|
||||||
permanent=True), name='profile-redirect'),
|
permanent=True), name='profile-redirect'),
|
||||||
re_path(r'^docs/$', schema_view.with_ui('swagger',
|
re_path(r'^docs/$', schema_view.with_ui('swagger',
|
||||||
cache_timeout=0), name='api_docs'),
|
cache_timeout=0), name='api_docs'),
|
||||||
# path('auth/account-confirm-email/', VerifyEmailView.as_view(), name='account_email_verification_sent'),
|
# path('auth/account-confirm-email/', VerifyEmailView.as_view(), name='account_email_verification_sent'),
|
||||||
|
path("accounts/", include("allauth.urls")),
|
||||||
|
|
||||||
|
# Include the API endpoints:
|
||||||
|
path("_allauth/", include("allauth.headless.urls")),
|
||||||
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
||||||
|
|
|
@ -1,13 +0,0 @@
|
||||||
{% extends "account/email/base_message.txt" %}
|
|
||||||
{% load i18n %}
|
|
||||||
|
|
||||||
{% block content %}{% autoescape off %}{% blocktrans %}You're receiving this email because you or someone else has requested a password reset for your user account.
|
|
||||||
|
|
||||||
It can be safely ignored if you did not request a password reset. Click the link below to reset your password.{% endblocktrans %}
|
|
||||||
|
|
||||||
{{ frontend_url }}/settings/forgot-password/confirm?token={{ temp_key }}&uid={{ user_pk }}
|
|
||||||
|
|
||||||
{% if username %}
|
|
||||||
|
|
||||||
|
|
||||||
{% blocktrans %}In case you forgot, your username is {{ username }}.{% endblocktrans %}{% endif %}{% endautoescape %}{% endblock content %}
|
|
10
backend/server/users/adapters.py
Normal file
10
backend/server/users/adapters.py
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
from allauth.account.adapter import DefaultAccountAdapter
|
||||||
|
from django.conf import settings
|
||||||
|
|
||||||
|
class NoNewUsersAccountAdapter(DefaultAccountAdapter):
|
||||||
|
"""
|
||||||
|
Disable new user registration.
|
||||||
|
"""
|
||||||
|
def is_open_for_signup(self, request):
|
||||||
|
is_disabled = getattr(settings, 'DISABLE_REGISTRATION', False)
|
||||||
|
return not is_disabled
|
|
@ -1,3 +1,10 @@
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
|
||||||
# Register your models here
|
from django.contrib.sessions.models import Session
|
||||||
|
|
||||||
|
class SessionAdmin(admin.ModelAdmin):
|
||||||
|
def _session_data(self, obj):
|
||||||
|
return obj.get_decoded()
|
||||||
|
list_display = ['session_key', '_session_data', 'expire_date']
|
||||||
|
|
||||||
|
admin.site.register(Session, SessionAdmin)
|
17
backend/server/users/form_overrides.py
Normal file
17
backend/server/users/form_overrides.py
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
from django import forms
|
||||||
|
|
||||||
|
class CustomSignupForm(forms.Form):
|
||||||
|
first_name = forms.CharField(max_length=30, required=True)
|
||||||
|
last_name = forms.CharField(max_length=30, required=True)
|
||||||
|
|
||||||
|
def signup(self, request, user):
|
||||||
|
# Delay the import to avoid circular import
|
||||||
|
from allauth.account.forms import SignupForm
|
||||||
|
|
||||||
|
# No need to call super() from CustomSignupForm; use the SignupForm directly if needed
|
||||||
|
user.first_name = self.cleaned_data['first_name']
|
||||||
|
user.last_name = self.cleaned_data['last_name']
|
||||||
|
|
||||||
|
# Save the user instance
|
||||||
|
user.save()
|
||||||
|
return user
|
|
@ -1,55 +1,17 @@
|
||||||
from allauth.account.utils import (filter_users_by_email, user_pk_to_url_str, user_username)
|
from django import forms
|
||||||
from allauth.utils import build_absolute_uri
|
|
||||||
from allauth.account.adapter import get_adapter
|
|
||||||
from allauth.account.forms import default_token_generator
|
|
||||||
from allauth.account import app_settings
|
|
||||||
from django.conf import settings
|
|
||||||
|
|
||||||
from allauth.account.forms import ResetPasswordForm as AllAuthPasswordResetForm
|
class CustomSignupForm(forms.Form):
|
||||||
|
first_name = forms.CharField(max_length=30, required=True)
|
||||||
|
last_name = forms.CharField(max_length=30, required=True)
|
||||||
|
|
||||||
class CustomAllAuthPasswordResetForm(AllAuthPasswordResetForm):
|
def signup(self, request, user):
|
||||||
|
# Delay the import to avoid circular import
|
||||||
def clean_email(self):
|
from allauth.account.forms import SignupForm
|
||||||
"""
|
|
||||||
Invalid email should not raise error, as this would leak users
|
|
||||||
for unit test: test_password_reset_with_invalid_email
|
|
||||||
"""
|
|
||||||
email = self.cleaned_data["email"]
|
|
||||||
email = get_adapter().clean_email(email)
|
|
||||||
self.users = filter_users_by_email(email, is_active=True)
|
|
||||||
return self.cleaned_data["email"]
|
|
||||||
|
|
||||||
def save(self, request, **kwargs):
|
|
||||||
email = self.cleaned_data['email']
|
|
||||||
token_generator = kwargs.get('token_generator', default_token_generator)
|
|
||||||
|
|
||||||
for user in self.users:
|
|
||||||
temp_key = token_generator.make_token(user)
|
|
||||||
|
|
||||||
path = f"custom_password_reset_url/{user_pk_to_url_str(user)}/{temp_key}/"
|
|
||||||
url = build_absolute_uri(request, path)
|
|
||||||
|
|
||||||
frontend_url = settings.FRONTEND_URL
|
|
||||||
# remove ' from frontend_url
|
|
||||||
frontend_url = frontend_url.replace("'", "")
|
|
||||||
|
|
||||||
#Values which are passed to password_reset_key_message.txt
|
|
||||||
context = {
|
|
||||||
"frontend_url": frontend_url,
|
|
||||||
"user": user,
|
|
||||||
"password_reset_url": url,
|
|
||||||
"request": request,
|
|
||||||
"path": path,
|
|
||||||
"temp_key": temp_key,
|
|
||||||
'user_pk': user_pk_to_url_str(user),
|
|
||||||
}
|
|
||||||
|
|
||||||
if app_settings.AUTHENTICATION_METHOD != app_settings.AuthenticationMethod.EMAIL:
|
|
||||||
context['username'] = user_username(user)
|
|
||||||
get_adapter(request).send_mail(
|
|
||||||
'account/email/password_reset_key', email, context
|
|
||||||
)
|
|
||||||
|
|
||||||
return self.cleaned_data['email']
|
|
||||||
|
|
||||||
|
# No need to call super() from CustomSignupForm; use the SignupForm directly if needed
|
||||||
|
user.first_name = self.cleaned_data['first_name']
|
||||||
|
user.last_name = self.cleaned_data['last_name']
|
||||||
|
|
||||||
|
# Save the user instance
|
||||||
|
user.save()
|
||||||
|
return user
|
|
@ -1,10 +1,8 @@
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
from django.contrib.auth import get_user_model
|
from django.contrib.auth import get_user_model
|
||||||
|
|
||||||
from adventures.models import Adventure, Collection
|
from adventures.models import Collection
|
||||||
from users.forms import CustomAllAuthPasswordResetForm
|
|
||||||
from dj_rest_auth.serializers import PasswordResetSerializer
|
from dj_rest_auth.serializers import PasswordResetSerializer
|
||||||
from rest_framework.exceptions import PermissionDenied
|
|
||||||
|
|
||||||
User = get_user_model()
|
User = get_user_model()
|
||||||
|
|
||||||
|
@ -32,77 +30,7 @@ class ChangeEmailSerializer(serializers.Serializer):
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
class RegisterSerializer(serializers.Serializer):
|
|
||||||
username = serializers.CharField(
|
|
||||||
max_length=get_username_max_length(),
|
|
||||||
min_length=allauth_account_settings.USERNAME_MIN_LENGTH,
|
|
||||||
required=allauth_account_settings.USERNAME_REQUIRED,
|
|
||||||
)
|
|
||||||
email = serializers.EmailField(required=allauth_account_settings.EMAIL_REQUIRED)
|
|
||||||
password1 = serializers.CharField(write_only=True)
|
|
||||||
password2 = serializers.CharField(write_only=True)
|
|
||||||
first_name = serializers.CharField(required=False)
|
|
||||||
last_name = serializers.CharField(required=False)
|
|
||||||
|
|
||||||
def validate_username(self, username):
|
|
||||||
username = get_adapter().clean_username(username)
|
|
||||||
return username
|
|
||||||
|
|
||||||
def validate_email(self, email):
|
|
||||||
email = get_adapter().clean_email(email)
|
|
||||||
if allauth_account_settings.UNIQUE_EMAIL:
|
|
||||||
if email and EmailAddress.objects.is_verified(email):
|
|
||||||
raise serializers.ValidationError(
|
|
||||||
_('A user is already registered with this e-mail address.'),
|
|
||||||
)
|
|
||||||
return email
|
|
||||||
|
|
||||||
def validate_password1(self, password):
|
|
||||||
return get_adapter().clean_password(password)
|
|
||||||
|
|
||||||
def validate(self, data):
|
|
||||||
if data['password1'] != data['password2']:
|
|
||||||
raise serializers.ValidationError(_("The two password fields didn't match."))
|
|
||||||
|
|
||||||
# check if a user with the same email already exists
|
|
||||||
if User.objects.filter(email=data['email']).exists():
|
|
||||||
raise serializers.ValidationError("This email is already in use.")
|
|
||||||
|
|
||||||
return data
|
|
||||||
|
|
||||||
def custom_signup(self, request, user):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def get_cleaned_data(self):
|
|
||||||
return {
|
|
||||||
'username': self.validated_data.get('username', ''),
|
|
||||||
'password1': self.validated_data.get('password1', ''),
|
|
||||||
'email': self.validated_data.get('email', ''),
|
|
||||||
'first_name': self.validated_data.get('first_name', ''),
|
|
||||||
'last_name': self.validated_data.get('last_name', ''),
|
|
||||||
}
|
|
||||||
|
|
||||||
def save(self, request):
|
|
||||||
# Check if registration is disabled
|
|
||||||
if getattr(settings, 'DISABLE_REGISTRATION', False):
|
|
||||||
raise PermissionDenied("Registration is currently disabled.")
|
|
||||||
|
|
||||||
# If registration is not disabled, proceed with the original logic
|
|
||||||
adapter = get_adapter()
|
|
||||||
user = adapter.new_user(request)
|
|
||||||
self.cleaned_data = self.get_cleaned_data()
|
|
||||||
user = adapter.save_user(request, user, self, commit=False)
|
|
||||||
if "password1" in self.cleaned_data:
|
|
||||||
try:
|
|
||||||
adapter.clean_password(self.cleaned_data['password1'], user=user)
|
|
||||||
except DjangoValidationError as exc:
|
|
||||||
raise serializers.ValidationError(
|
|
||||||
detail=serializers.as_serializer_error(exc)
|
|
||||||
)
|
|
||||||
user.save()
|
|
||||||
self.custom_signup(request, user)
|
|
||||||
setup_user_email(request, user, [])
|
|
||||||
return user
|
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.auth import get_user_model
|
from django.contrib.auth import get_user_model
|
||||||
|
@ -116,20 +44,6 @@ from rest_framework import serializers
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
import os
|
import os
|
||||||
|
|
||||||
# class AdventureSerializer(serializers.ModelSerializer):
|
|
||||||
# image = serializers.SerializerMethodField()
|
|
||||||
|
|
||||||
# class Meta:
|
|
||||||
# model = Adventure
|
|
||||||
# fields = ['id', 'user_id', 'type', 'name', 'location', 'activity_types', 'description',
|
|
||||||
# 'rating', 'link', 'image', 'date', 'trip_id', 'is_public', 'longitude', 'latitude']
|
|
||||||
|
|
||||||
# def get_image(self, obj):
|
|
||||||
# if obj.image:
|
|
||||||
# public_url = os.environ.get('PUBLIC_URL', '')
|
|
||||||
# return f'{public_url}/media/{obj.image.name}'
|
|
||||||
# return None
|
|
||||||
|
|
||||||
class UserDetailsSerializer(serializers.ModelSerializer):
|
class UserDetailsSerializer(serializers.ModelSerializer):
|
||||||
"""
|
"""
|
||||||
User model w/o password
|
User model w/o password
|
||||||
|
@ -203,13 +117,3 @@ class CustomUserDetailsSerializer(UserDetailsSerializer):
|
||||||
representation['profile_pic'] = f"{public_url}/media/{instance.profile_pic.name}"
|
representation['profile_pic'] = f"{public_url}/media/{instance.profile_pic.name}"
|
||||||
del representation['pk'] # remove the pk field from the response
|
del representation['pk'] # remove the pk field from the response
|
||||||
return representation
|
return representation
|
||||||
|
|
||||||
class MyPasswordResetSerializer(PasswordResetSerializer):
|
|
||||||
|
|
||||||
def validate_email(self, value):
|
|
||||||
# use the custom reset form
|
|
||||||
self.reset_form = CustomAllAuthPasswordResetForm(data=self.initial_data)
|
|
||||||
if not self.reset_form.is_valid():
|
|
||||||
raise serializers.ValidationError(self.reset_form.errors)
|
|
||||||
|
|
||||||
return value
|
|
|
@ -83,3 +83,18 @@ class PublicUserDetailView(APIView):
|
||||||
user.email = None
|
user.email = None
|
||||||
serializer = PublicUserSerializer(user)
|
serializer = PublicUserSerializer(user)
|
||||||
return Response(serializer.data, status=status.HTTP_200_OK)
|
return Response(serializer.data, status=status.HTTP_200_OK)
|
||||||
|
|
||||||
|
class UserMetadataView(APIView):
|
||||||
|
permission_classes = [IsAuthenticated]
|
||||||
|
|
||||||
|
@swagger_auto_schema(
|
||||||
|
responses={
|
||||||
|
200: openapi.Response('User metadata'),
|
||||||
|
400: 'Bad Request'
|
||||||
|
},
|
||||||
|
operation_description="Get user metadata."
|
||||||
|
)
|
||||||
|
def get(self, request):
|
||||||
|
user = request.user
|
||||||
|
serializer = PublicUserSerializer(user)
|
||||||
|
return Response(serializer.data, status=status.HTTP_200_OK)
|
|
@ -1,95 +1,75 @@
|
||||||
import type { Handle } from '@sveltejs/kit';
|
import type { Handle } from '@sveltejs/kit';
|
||||||
import { sequence } from '@sveltejs/kit/hooks';
|
import { sequence } from '@sveltejs/kit/hooks';
|
||||||
const PUBLIC_SERVER_URL = process.env['PUBLIC_SERVER_URL'];
|
const PUBLIC_SERVER_URL = process.env['PUBLIC_SERVER_URL'];
|
||||||
import { fetchCSRFToken, tryRefreshToken } from '$lib/index.server';
|
|
||||||
|
|
||||||
export const authHook: Handle = async ({ event, resolve }) => {
|
export const authHook: Handle = async ({ event, resolve }) => {
|
||||||
try {
|
try {
|
||||||
let authCookie = event.cookies.get('auth');
|
let sessionid = event.cookies.get('sessionid');
|
||||||
let refreshCookie = event.cookies.get('refresh');
|
console.log('sessionid:', sessionid);
|
||||||
|
|
||||||
if (!authCookie && !refreshCookie) {
|
if (!sessionid) {
|
||||||
|
console.log('No sessionid cookie');
|
||||||
event.locals.user = null;
|
event.locals.user = null;
|
||||||
return await resolve(event);
|
return await resolve(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!authCookie && refreshCookie) {
|
// print all cookies in the request
|
||||||
event.locals.user = null;
|
console.log('Cookies:', event.request.headers.get('cookie'));
|
||||||
const token = await tryRefreshToken(event.cookies.get('refresh') || '');
|
|
||||||
if (token) {
|
|
||||||
authCookie = token;
|
|
||||||
event.cookies.set('auth', authCookie, {
|
|
||||||
httpOnly: true,
|
|
||||||
sameSite: 'lax',
|
|
||||||
expires: new Date(Date.now() + 60 * 60 * 1000), // 60 minutes
|
|
||||||
path: '/'
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
return await resolve(event);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const serverEndpoint = PUBLIC_SERVER_URL || 'http://localhost:8000';
|
const serverEndpoint = PUBLIC_SERVER_URL || 'http://localhost:8000';
|
||||||
|
|
||||||
let userFetch = await event.fetch(`${serverEndpoint}/auth/user/`, {
|
const cookie = event.request.headers.get('cookie') || '';
|
||||||
|
|
||||||
|
let userFetch = await event.fetch(`${serverEndpoint}/auth/user-metadata/`, {
|
||||||
headers: {
|
headers: {
|
||||||
Cookie: `${authCookie}`
|
cookie
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!userFetch.ok) {
|
if (!userFetch.ok) {
|
||||||
console.log('Refreshing token');
|
event.locals.user = null;
|
||||||
const refreshCookie = event.cookies.get('refresh');
|
event.cookies.delete('sessionid', { path: '/' });
|
||||||
|
return await resolve(event);
|
||||||
if (refreshCookie) {
|
|
||||||
const csrfToken = await fetchCSRFToken();
|
|
||||||
if (!csrfToken) {
|
|
||||||
console.error('Failed to fetch CSRF token');
|
|
||||||
event.locals.user = null;
|
|
||||||
return await resolve(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
const refreshFetch = await event.fetch(`${serverEndpoint}/auth/token/refresh/`, {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'X-CSRFToken': csrfToken,
|
|
||||||
'Content-Type': 'application/json'
|
|
||||||
},
|
|
||||||
body: JSON.stringify({ refresh: refreshCookie })
|
|
||||||
});
|
|
||||||
|
|
||||||
if (refreshFetch.ok) {
|
|
||||||
const refresh = await refreshFetch.json();
|
|
||||||
event.cookies.set('auth', 'auth=' + refresh.access, {
|
|
||||||
httpOnly: true,
|
|
||||||
sameSite: 'lax',
|
|
||||||
expires: new Date(Date.now() + 60 * 60 * 1000), // 60 minutes
|
|
||||||
path: '/'
|
|
||||||
});
|
|
||||||
|
|
||||||
userFetch = await event.fetch(`${serverEndpoint}/auth/user/`, {
|
|
||||||
headers: {
|
|
||||||
'X-CSRFToken': csrfToken,
|
|
||||||
Cookie: `auth=${refresh.access}`
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (userFetch.ok) {
|
if (userFetch.ok) {
|
||||||
const user = await userFetch.json();
|
const user = await userFetch.json();
|
||||||
event.locals.user = user;
|
event.locals.user = user;
|
||||||
|
const setCookieHeader = userFetch.headers.get('Set-Cookie');
|
||||||
|
|
||||||
|
console.log('setCookieHeader:', setCookieHeader);
|
||||||
|
|
||||||
|
if (setCookieHeader) {
|
||||||
|
// Regular expression to match sessionid cookie and its expiry
|
||||||
|
const sessionIdRegex = /sessionid=([^;]+).*?expires=([^;]+)/;
|
||||||
|
const match = setCookieHeader.match(sessionIdRegex);
|
||||||
|
|
||||||
|
if (match) {
|
||||||
|
const sessionId = match[1];
|
||||||
|
const expiryString = match[2];
|
||||||
|
const expiryDate = new Date(expiryString);
|
||||||
|
|
||||||
|
console.log('Session ID:', sessionId);
|
||||||
|
console.log('Expiry Date:', expiryDate);
|
||||||
|
|
||||||
|
// Set the sessionid cookie
|
||||||
|
event.cookies.set('sessionid', sessionId, {
|
||||||
|
path: '/',
|
||||||
|
httpOnly: true,
|
||||||
|
sameSite: 'lax',
|
||||||
|
secure: true,
|
||||||
|
expires: expiryDate
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
event.locals.user = null;
|
event.locals.user = null;
|
||||||
event.cookies.delete('auth', { path: '/' });
|
event.cookies.delete('sessionid', { path: '/' });
|
||||||
event.cookies.delete('refresh', { path: '/' });
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error in authHook:', error);
|
console.error('Error in authHook:', error);
|
||||||
event.locals.user = null;
|
event.locals.user = null;
|
||||||
event.cookies.delete('auth', { path: '/' });
|
event.cookies.delete('sessionid', { path: '/' });
|
||||||
event.cookies.delete('refresh', { path: '/' });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return await resolve(event);
|
return await resolve(event);
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
console.log(res);
|
||||||
let data = await res.json();
|
let data = await res.json();
|
||||||
console.log('ACTIVITIES' + data.activities);
|
console.log('ACTIVITIES' + data.activities);
|
||||||
if (data && data.activities) {
|
if (data && data.activities) {
|
||||||
|
|
|
@ -444,6 +444,7 @@
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
},
|
},
|
||||||
|
credentials: 'include',
|
||||||
body: JSON.stringify(adventure)
|
body: JSON.stringify(adventure)
|
||||||
});
|
});
|
||||||
let data = await res.json();
|
let data = await res.json();
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { redirect, type Actions } from '@sveltejs/kit';
|
import { redirect, type Actions } from '@sveltejs/kit';
|
||||||
import type { PageServerLoad } from './$types';
|
import type { PageServerLoad } from './$types';
|
||||||
|
import { fetchCSRFToken } from '$lib/index.server';
|
||||||
const PUBLIC_SERVER_URL = process.env['PUBLIC_SERVER_URL'];
|
const PUBLIC_SERVER_URL = process.env['PUBLIC_SERVER_URL'];
|
||||||
const endpoint = PUBLIC_SERVER_URL || 'http://localhost:8000';
|
const endpoint = PUBLIC_SERVER_URL || 'http://localhost:8000';
|
||||||
|
|
||||||
|
@ -7,13 +8,16 @@ export const load = (async (event) => {
|
||||||
if (!event.locals.user) {
|
if (!event.locals.user) {
|
||||||
return redirect(302, '/login');
|
return redirect(302, '/login');
|
||||||
}
|
}
|
||||||
|
let csrfToken = await fetchCSRFToken();
|
||||||
let allActivities: string[] = [];
|
let allActivities: string[] = [];
|
||||||
let res = await fetch(`${endpoint}/api/activity-types/types/`, {
|
let res = await event.fetch(`${endpoint}/api/activity-types/types/`, {
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'X-CSRFToken': csrfToken,
|
||||||
Cookie: `${event.cookies.get('auth')}`
|
Cookie: `csrftoken=${csrfToken}`
|
||||||
}
|
},
|
||||||
|
credentials: 'include'
|
||||||
});
|
});
|
||||||
|
console.log(res);
|
||||||
let data = await res.json();
|
let data = await res.json();
|
||||||
if (data) {
|
if (data) {
|
||||||
allActivities = data;
|
allActivities = data;
|
||||||
|
@ -27,13 +31,16 @@ export const load = (async (event) => {
|
||||||
|
|
||||||
export const actions: Actions = {
|
export const actions: Actions = {
|
||||||
getActivities: async (event) => {
|
getActivities: async (event) => {
|
||||||
|
let csrfToken = await fetchCSRFToken();
|
||||||
let allActivities: string[] = [];
|
let allActivities: string[] = [];
|
||||||
let res = await fetch(`${endpoint}/api/activity-types/types/`, {
|
let res = await fetch(`${endpoint}/api/activity-types/types/`, {
|
||||||
headers: {
|
headers: {
|
||||||
|
'X-CSRFToken': csrfToken,
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
Cookie: `${event.cookies.get('auth')}`
|
Cookie: `csrftoken=${csrfToken}`
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
console.log(res);
|
||||||
let data = await res.json();
|
let data = await res.json();
|
||||||
if (data) {
|
if (data) {
|
||||||
allActivities = data;
|
allActivities = data;
|
||||||
|
|
|
@ -1,15 +1,19 @@
|
||||||
import { json } from '@sveltejs/kit';
|
import { json } from '@sveltejs/kit';
|
||||||
import type { RequestHandler } from '../data/$types';
|
import type { RequestHandler } from '@sveltejs/kit';
|
||||||
|
import { fetchCSRFToken } from '$lib/index.server';
|
||||||
const PUBLIC_SERVER_URL = process.env['PUBLIC_SERVER_URL'];
|
const PUBLIC_SERVER_URL = process.env['PUBLIC_SERVER_URL'];
|
||||||
const endpoint = PUBLIC_SERVER_URL || 'http://localhost:8000';
|
const endpoint = PUBLIC_SERVER_URL || 'http://localhost:8000';
|
||||||
|
|
||||||
export const POST: RequestHandler = async (event) => {
|
export const POST: RequestHandler = async (event) => {
|
||||||
let allActivities: string[] = [];
|
let allActivities: string[] = [];
|
||||||
let res = await fetch(`${endpoint}/api/activity-types/types/`, {
|
let csrfToken = await fetchCSRFToken();
|
||||||
|
let sessionId = event.cookies.get('sessionid');
|
||||||
|
let res = await event.fetch(`${endpoint}/api/activity-types/types/`, {
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'X-CSRFToken': csrfToken,
|
||||||
Cookie: `${event.cookies.get('auth')}`
|
Cookie: `csrftoken=${csrfToken}; sessionid=${sessionId}`
|
||||||
}
|
},
|
||||||
|
credentials: 'include'
|
||||||
});
|
});
|
||||||
let data = await res.json();
|
let data = await res.json();
|
||||||
if (data) {
|
if (data) {
|
||||||
|
|
|
@ -4,8 +4,7 @@ const PUBLIC_SERVER_URL = process.env['PUBLIC_SERVER_URL'];
|
||||||
import type { Adventure } from '$lib/types';
|
import type { Adventure } from '$lib/types';
|
||||||
|
|
||||||
import type { Actions } from '@sveltejs/kit';
|
import type { Actions } from '@sveltejs/kit';
|
||||||
import { fetchCSRFToken, tryRefreshToken } from '$lib/index.server';
|
import { fetchCSRFToken } from '$lib/index.server';
|
||||||
import { checkLink } from '$lib';
|
|
||||||
|
|
||||||
const serverEndpoint = PUBLIC_SERVER_URL || 'http://localhost:8000';
|
const serverEndpoint = PUBLIC_SERVER_URL || 'http://localhost:8000';
|
||||||
|
|
||||||
|
@ -29,12 +28,13 @@ export const load = (async (event) => {
|
||||||
const page = event.url.searchParams.get('page') || '1';
|
const page = event.url.searchParams.get('page') || '1';
|
||||||
const is_visited = event.url.searchParams.get('is_visited') || 'all';
|
const is_visited = event.url.searchParams.get('is_visited') || 'all';
|
||||||
|
|
||||||
let initialFetch = await fetch(
|
let initialFetch = await event.fetch(
|
||||||
`${serverEndpoint}/api/adventures/filtered?types=${typeString}&order_by=${order_by}&order_direction=${order_direction}&include_collections=${include_collections}&page=${page}&is_visited=${is_visited}`,
|
`${serverEndpoint}/api/adventures/filtered?types=${typeString}&order_by=${order_by}&order_direction=${order_direction}&include_collections=${include_collections}&page=${page}&is_visited=${is_visited}`,
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
Cookie: `${event.cookies.get('auth')}`
|
Cookie: `sessionid=${event.cookies.get('sessionid')}`
|
||||||
}
|
},
|
||||||
|
credentials: 'include'
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -61,371 +61,15 @@ export const load = (async (event) => {
|
||||||
}) satisfies PageServerLoad;
|
}) satisfies PageServerLoad;
|
||||||
|
|
||||||
export const actions: Actions = {
|
export const actions: Actions = {
|
||||||
create: async (event) => {
|
|
||||||
const formData = await event.request.formData();
|
|
||||||
|
|
||||||
const type = formData.get('type') as string;
|
|
||||||
const name = formData.get('name') as string;
|
|
||||||
const location = formData.get('location') as string | null;
|
|
||||||
let date = (formData.get('date') as string | null) ?? null;
|
|
||||||
const description = formData.get('description') as string | null;
|
|
||||||
const activity_types = formData.get('activity_types')
|
|
||||||
? (formData.get('activity_types') as string).split(',')
|
|
||||||
: null;
|
|
||||||
const rating = formData.get('rating') ? Number(formData.get('rating')) : null;
|
|
||||||
let link = formData.get('link') as string | null;
|
|
||||||
let latitude = formData.get('latitude') as string | null;
|
|
||||||
let longitude = formData.get('longitude') as string | null;
|
|
||||||
let collection = formData.get('collection') as string | null;
|
|
||||||
let is_public = formData.get('is_public') as string | null | boolean;
|
|
||||||
|
|
||||||
if (is_public) {
|
|
||||||
is_public = true;
|
|
||||||
} else {
|
|
||||||
is_public = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if latitude and longitude are valid
|
|
||||||
if (latitude && longitude) {
|
|
||||||
if (isNaN(Number(latitude)) || isNaN(Number(longitude))) {
|
|
||||||
return {
|
|
||||||
status: 400,
|
|
||||||
body: { error: 'Invalid latitude or longitude' }
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// round latitude and longitude to 6 decimal places
|
|
||||||
if (latitude) {
|
|
||||||
latitude = Number(latitude).toFixed(6);
|
|
||||||
}
|
|
||||||
if (longitude) {
|
|
||||||
longitude = Number(longitude).toFixed(6);
|
|
||||||
}
|
|
||||||
|
|
||||||
const image = formData.get('image') as File;
|
|
||||||
|
|
||||||
if (!type || !name) {
|
|
||||||
return {
|
|
||||||
status: 400,
|
|
||||||
body: { error: 'Missing required fields' }
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (date == null || date == '') {
|
|
||||||
date = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (link) {
|
|
||||||
link = checkLink(link);
|
|
||||||
}
|
|
||||||
|
|
||||||
const formDataToSend = new FormData();
|
|
||||||
formDataToSend.append('type', type);
|
|
||||||
formDataToSend.append('name', name);
|
|
||||||
formDataToSend.append('location', location || '');
|
|
||||||
formDataToSend.append('date', date || '');
|
|
||||||
formDataToSend.append('description', description || '');
|
|
||||||
formDataToSend.append('latitude', latitude || '');
|
|
||||||
formDataToSend.append('longitude', longitude || '');
|
|
||||||
formDataToSend.append('is_public', is_public.toString());
|
|
||||||
|
|
||||||
if (!isNaN(Number(collection))) {
|
|
||||||
if (collection !== null) {
|
|
||||||
formDataToSend.append('collection', collection);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (activity_types) {
|
|
||||||
// Filter out empty and duplicate activity types, then trim each activity type
|
|
||||||
const cleanedActivityTypes = Array.from(
|
|
||||||
new Set(
|
|
||||||
activity_types
|
|
||||||
.map((activity_type) => activity_type.trim())
|
|
||||||
.filter((activity_type) => activity_type !== '' && activity_type !== ',')
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
// Append each cleaned activity type to formDataToSend
|
|
||||||
cleanedActivityTypes.forEach((activity_type) => {
|
|
||||||
formDataToSend.append('activity_types', activity_type);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
formDataToSend.append('rating', rating ? rating.toString() : '');
|
|
||||||
formDataToSend.append('link', link || '');
|
|
||||||
// formDataToSend.append('image', image);
|
|
||||||
|
|
||||||
// log each key-value pair in the FormData
|
|
||||||
for (let pair of formDataToSend.entries()) {
|
|
||||||
console.log(pair[0] + ', ' + pair[1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
let auth = event.cookies.get('auth');
|
|
||||||
|
|
||||||
if (!auth) {
|
|
||||||
const refresh = event.cookies.get('refresh');
|
|
||||||
if (!refresh) {
|
|
||||||
return {
|
|
||||||
status: 401,
|
|
||||||
body: { message: 'Unauthorized' }
|
|
||||||
};
|
|
||||||
}
|
|
||||||
let res = await tryRefreshToken(refresh);
|
|
||||||
if (res) {
|
|
||||||
auth = res;
|
|
||||||
event.cookies.set('auth', auth, {
|
|
||||||
httpOnly: true,
|
|
||||||
sameSite: 'lax',
|
|
||||||
expires: new Date(Date.now() + 60 * 60 * 1000), // 60 minutes
|
|
||||||
path: '/'
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
return {
|
|
||||||
status: 401,
|
|
||||||
body: { message: 'Unauthorized' }
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!auth) {
|
|
||||||
return {
|
|
||||||
status: 401,
|
|
||||||
body: { message: 'Unauthorized' }
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const csrfToken = await fetchCSRFToken();
|
|
||||||
|
|
||||||
if (!csrfToken) {
|
|
||||||
return {
|
|
||||||
status: 500,
|
|
||||||
body: { message: 'Failed to fetch CSRF token' }
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const res = await fetch(`${serverEndpoint}/api/adventures/`, {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'X-CSRFToken': csrfToken,
|
|
||||||
Cookie: auth
|
|
||||||
},
|
|
||||||
body: formDataToSend
|
|
||||||
});
|
|
||||||
|
|
||||||
let new_id = await res.json();
|
|
||||||
|
|
||||||
if (!res.ok) {
|
|
||||||
const errorBody = await res.json();
|
|
||||||
return {
|
|
||||||
status: res.status,
|
|
||||||
body: { error: errorBody }
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
let id = new_id.id;
|
|
||||||
let user_id = new_id.user_id;
|
|
||||||
let image_url = new_id.image;
|
|
||||||
let link_url = new_id.link;
|
|
||||||
|
|
||||||
if (image && image.size > 0) {
|
|
||||||
let imageForm = new FormData();
|
|
||||||
imageForm.append('image', image);
|
|
||||||
imageForm.append('adventure', id);
|
|
||||||
let imageRes = await fetch(`${serverEndpoint}/api/images/`, {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
Cookie: `${event.cookies.get('auth')}`
|
|
||||||
},
|
|
||||||
body: imageForm
|
|
||||||
});
|
|
||||||
let data = await imageRes.json();
|
|
||||||
console.log(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
return { id, user_id, image_url, link };
|
|
||||||
},
|
|
||||||
edit: async (event) => {
|
|
||||||
const formData = await event.request.formData();
|
|
||||||
|
|
||||||
const adventureId = formData.get('adventureId') as string;
|
|
||||||
const type = formData.get('type') as string;
|
|
||||||
const name = formData.get('name') as string;
|
|
||||||
const location = formData.get('location') as string | null;
|
|
||||||
let date = (formData.get('date') as string | null) ?? null;
|
|
||||||
const description = formData.get('description') as string | null;
|
|
||||||
let activity_types = formData.get('activity_types')
|
|
||||||
? (formData.get('activity_types') as string).split(',')
|
|
||||||
: null;
|
|
||||||
const rating = formData.get('rating') ? Number(formData.get('rating')) : null;
|
|
||||||
let link = formData.get('link') as string | null;
|
|
||||||
let latitude = formData.get('latitude') as string | null;
|
|
||||||
let longitude = formData.get('longitude') as string | null;
|
|
||||||
let is_public = formData.get('is_public') as string | null | boolean;
|
|
||||||
|
|
||||||
if (is_public) {
|
|
||||||
is_public = true;
|
|
||||||
} else {
|
|
||||||
is_public = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if latitude and longitude are valid
|
|
||||||
if (latitude && longitude) {
|
|
||||||
if (isNaN(Number(latitude)) || isNaN(Number(longitude))) {
|
|
||||||
return {
|
|
||||||
status: 400,
|
|
||||||
body: { error: 'Invalid latitude or longitude' }
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// round latitude and longitude to 6 decimal places
|
|
||||||
if (latitude) {
|
|
||||||
latitude = Number(latitude).toFixed(6);
|
|
||||||
}
|
|
||||||
if (longitude) {
|
|
||||||
longitude = Number(longitude).toFixed(6);
|
|
||||||
}
|
|
||||||
|
|
||||||
const image = formData.get('image') as File;
|
|
||||||
|
|
||||||
// console.log(activity_types);
|
|
||||||
|
|
||||||
if (!type || !name) {
|
|
||||||
return {
|
|
||||||
status: 400,
|
|
||||||
body: { error: 'Missing required fields' }
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (date == null || date == '') {
|
|
||||||
date = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (link) {
|
|
||||||
link = checkLink(link);
|
|
||||||
}
|
|
||||||
|
|
||||||
const formDataToSend = new FormData();
|
|
||||||
formDataToSend.append('type', type);
|
|
||||||
formDataToSend.append('name', name);
|
|
||||||
formDataToSend.append('location', location || '');
|
|
||||||
formDataToSend.append('date', date || '');
|
|
||||||
formDataToSend.append('description', description || '');
|
|
||||||
formDataToSend.append('latitude', latitude || '');
|
|
||||||
formDataToSend.append('longitude', longitude || '');
|
|
||||||
formDataToSend.append('is_public', is_public.toString());
|
|
||||||
|
|
||||||
let csrfToken = await fetchCSRFToken();
|
|
||||||
|
|
||||||
if (activity_types) {
|
|
||||||
// Filter out empty and duplicate activity types, then trim each activity type
|
|
||||||
const cleanedActivityTypes = Array.from(
|
|
||||||
new Set(
|
|
||||||
activity_types
|
|
||||||
.map((activity_type) => activity_type.trim())
|
|
||||||
.filter((activity_type) => activity_type !== '' && activity_type !== ',')
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
// Append each cleaned activity type to formDataToSend
|
|
||||||
cleanedActivityTypes.forEach((activity_type) => {
|
|
||||||
formDataToSend.append('activity_types', activity_type);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
let res = await fetch(`${serverEndpoint}/api/adventures/${adventureId}/`, {
|
|
||||||
method: 'PATCH',
|
|
||||||
headers: {
|
|
||||||
Cookie: `${event.cookies.get('auth')}`,
|
|
||||||
'X-CSRFToken': csrfToken,
|
|
||||||
'Content-Type': 'application/json'
|
|
||||||
},
|
|
||||||
body: JSON.stringify({ activity_types: [] })
|
|
||||||
});
|
|
||||||
if (!res.ok) {
|
|
||||||
const errorBody = await res.json();
|
|
||||||
return {
|
|
||||||
status: res.status,
|
|
||||||
body: { error: errorBody }
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
formDataToSend.append('rating', rating ? rating.toString() : '');
|
|
||||||
formDataToSend.append('link', link || '');
|
|
||||||
|
|
||||||
if (image && image.size > 0) {
|
|
||||||
formDataToSend.append('image', image);
|
|
||||||
}
|
|
||||||
|
|
||||||
let auth = event.cookies.get('auth');
|
|
||||||
|
|
||||||
if (!auth) {
|
|
||||||
const refresh = event.cookies.get('refresh');
|
|
||||||
if (!refresh) {
|
|
||||||
return {
|
|
||||||
status: 401,
|
|
||||||
body: { message: 'Unauthorized' }
|
|
||||||
};
|
|
||||||
}
|
|
||||||
let res = await tryRefreshToken(refresh);
|
|
||||||
if (res) {
|
|
||||||
auth = res;
|
|
||||||
event.cookies.set('auth', auth, {
|
|
||||||
httpOnly: true,
|
|
||||||
sameSite: 'lax',
|
|
||||||
expires: new Date(Date.now() + 60 * 60 * 1000), // 60 minutes
|
|
||||||
path: '/'
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
return {
|
|
||||||
status: 401,
|
|
||||||
body: { message: 'Unauthorized' }
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!auth) {
|
|
||||||
return {
|
|
||||||
status: 401,
|
|
||||||
body: { message: 'Unauthorized' }
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!csrfToken) {
|
|
||||||
return {
|
|
||||||
status: 500,
|
|
||||||
body: { message: 'Failed to fetch CSRF token' }
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const res = await fetch(`${serverEndpoint}/api/adventures/${adventureId}/`, {
|
|
||||||
method: 'PATCH',
|
|
||||||
headers: {
|
|
||||||
'X-CSRFToken': csrfToken,
|
|
||||||
Cookie: auth
|
|
||||||
},
|
|
||||||
body: formDataToSend
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!res.ok) {
|
|
||||||
const errorBody = await res.json();
|
|
||||||
return {
|
|
||||||
status: res.status,
|
|
||||||
body: { error: errorBody }
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
let adventure = await res.json();
|
|
||||||
|
|
||||||
let image_url = adventure.image;
|
|
||||||
let link_url = adventure.link;
|
|
||||||
return { image_url, link_url };
|
|
||||||
},
|
|
||||||
image: async (event) => {
|
image: async (event) => {
|
||||||
let formData = await event.request.formData();
|
let formData = await event.request.formData();
|
||||||
|
let csrfToken = await fetchCSRFToken();
|
||||||
|
let sessionId = event.cookies.get('sessionid');
|
||||||
let res = await fetch(`${serverEndpoint}/api/images/`, {
|
let res = await fetch(`${serverEndpoint}/api/images/`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
Cookie: `${event.cookies.get('auth')}`
|
Cookie: `csrftoken=${csrfToken}; sessionid=${sessionId}`,
|
||||||
|
'X-CSRFToken': csrfToken
|
||||||
},
|
},
|
||||||
body: formData
|
body: formData
|
||||||
});
|
});
|
||||||
|
|
|
@ -7,8 +7,9 @@ export const load = (async (event) => {
|
||||||
const id = event.params as { id: string };
|
const id = event.params as { id: string };
|
||||||
let request = await fetch(`${endpoint}/api/adventures/${id.id}/`, {
|
let request = await fetch(`${endpoint}/api/adventures/${id.id}/`, {
|
||||||
headers: {
|
headers: {
|
||||||
Cookie: `${event.cookies.get('auth')}`
|
Cookie: `sessionid=${event.cookies.get('sessionid')}`
|
||||||
}
|
},
|
||||||
|
credentials: 'include'
|
||||||
});
|
});
|
||||||
if (!request.ok) {
|
if (!request.ok) {
|
||||||
console.error('Failed to fetch adventure ' + id.id);
|
console.error('Failed to fetch adventure ' + id.id);
|
||||||
|
@ -24,8 +25,9 @@ export const load = (async (event) => {
|
||||||
if (adventure.collection) {
|
if (adventure.collection) {
|
||||||
let res2 = await fetch(`${endpoint}/api/collections/${adventure.collection}/`, {
|
let res2 = await fetch(`${endpoint}/api/collections/${adventure.collection}/`, {
|
||||||
headers: {
|
headers: {
|
||||||
Cookie: `${event.cookies.get('auth')}`
|
Cookie: `sessionid=${event.cookies.get('sessionid')}`
|
||||||
}
|
},
|
||||||
|
credentials: 'include'
|
||||||
});
|
});
|
||||||
collection = await res2.json();
|
collection = await res2.json();
|
||||||
}
|
}
|
||||||
|
@ -39,8 +41,8 @@ export const load = (async (event) => {
|
||||||
}
|
}
|
||||||
}) satisfies PageServerLoad;
|
}) satisfies PageServerLoad;
|
||||||
|
|
||||||
import type { Actions } from '@sveltejs/kit';
|
import { redirect, type Actions } from '@sveltejs/kit';
|
||||||
import { tryRefreshToken } from '$lib/index.server';
|
import { fetchCSRFToken } from '$lib/index.server';
|
||||||
|
|
||||||
const serverEndpoint = PUBLIC_SERVER_URL || 'http://localhost:8000';
|
const serverEndpoint = PUBLIC_SERVER_URL || 'http://localhost:8000';
|
||||||
|
|
||||||
|
@ -50,29 +52,7 @@ export const actions: Actions = {
|
||||||
const adventureId = id.id;
|
const adventureId = id.id;
|
||||||
|
|
||||||
if (!event.locals.user) {
|
if (!event.locals.user) {
|
||||||
const refresh = event.cookies.get('refresh');
|
return redirect(302, '/login');
|
||||||
let auth = event.cookies.get('auth');
|
|
||||||
if (!refresh) {
|
|
||||||
return {
|
|
||||||
status: 401,
|
|
||||||
body: { message: 'Unauthorized' }
|
|
||||||
};
|
|
||||||
}
|
|
||||||
let res = await tryRefreshToken(refresh);
|
|
||||||
if (res) {
|
|
||||||
auth = res;
|
|
||||||
event.cookies.set('auth', auth, {
|
|
||||||
httpOnly: true,
|
|
||||||
sameSite: 'lax',
|
|
||||||
expires: new Date(Date.now() + 60 * 60 * 1000), // 60 minutes
|
|
||||||
path: '/'
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
return {
|
|
||||||
status: 401,
|
|
||||||
body: { message: 'Unauthorized' }
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (!adventureId) {
|
if (!adventureId) {
|
||||||
return {
|
return {
|
||||||
|
@ -81,12 +61,15 @@ export const actions: Actions = {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let csrfToken = await fetchCSRFToken();
|
||||||
|
|
||||||
let res = await fetch(`${serverEndpoint}/api/adventures/${event.params.id}`, {
|
let res = await fetch(`${serverEndpoint}/api/adventures/${event.params.id}`, {
|
||||||
method: 'DELETE',
|
method: 'DELETE',
|
||||||
headers: {
|
headers: {
|
||||||
Cookie: `${event.cookies.get('auth')}`,
|
Cookie: `sessionid=${event.cookies.get('sessionid')}; csrftoken=${csrfToken}`,
|
||||||
'Content-Type': 'application/json'
|
'X-CSRFToken': csrfToken
|
||||||
}
|
},
|
||||||
|
credentials: 'include'
|
||||||
});
|
});
|
||||||
console.log(res);
|
console.log(res);
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
|
|
|
@ -1,69 +1,77 @@
|
||||||
const PUBLIC_SERVER_URL = process.env['PUBLIC_SERVER_URL'];
|
const PUBLIC_SERVER_URL = process.env['PUBLIC_SERVER_URL'];
|
||||||
const endpoint = PUBLIC_SERVER_URL || 'http://localhost:8000';
|
const endpoint = PUBLIC_SERVER_URL || 'http://localhost:8000';
|
||||||
|
import { fetchCSRFToken } from '$lib/index.server';
|
||||||
import { json } from '@sveltejs/kit';
|
import { json } from '@sveltejs/kit';
|
||||||
|
|
||||||
/** @type {import('./$types').RequestHandler} */
|
/** @type {import('./$types').RequestHandler} */
|
||||||
export async function GET({ url, params, request, fetch, cookies }) {
|
export async function GET(event) {
|
||||||
// add the param format = json to the url or add additional if anothre param is already present
|
const { url, params, request, fetch, cookies } = event;
|
||||||
if (url.search) {
|
const searchParam = url.search ? `${url.search}&format=json` : '?format=json';
|
||||||
url.search = url.search + '&format=json';
|
return handleRequest(url, params, request, fetch, cookies, searchParam);
|
||||||
} else {
|
|
||||||
url.search = '?format=json';
|
|
||||||
}
|
|
||||||
return handleRequest(url, params, request, fetch, cookies);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @type {import('./$types').RequestHandler} */
|
/** @type {import('./$types').RequestHandler} */
|
||||||
export async function POST({ url, params, request, fetch, cookies }) {
|
export async function POST({ url, params, request, fetch, cookies }) {
|
||||||
return handleRequest(url, params, request, fetch, cookies, true);
|
const searchParam = url.search ? `${url.search}&format=json` : '?format=json';
|
||||||
|
return handleRequest(url, params, request, fetch, cookies, searchParam, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function PATCH({ url, params, request, fetch, cookies }) {
|
export async function PATCH({ url, params, request, fetch, cookies }) {
|
||||||
return handleRequest(url, params, request, fetch, cookies, true);
|
const searchParam = url.search ? `${url.search}&format=json` : '?format=json';
|
||||||
|
return handleRequest(url, params, request, fetch, cookies, searchParam, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function PUT({ url, params, request, fetch, cookies }) {
|
export async function PUT({ url, params, request, fetch, cookies }) {
|
||||||
return handleRequest(url, params, request, fetch, cookies, true);
|
const searchParam = url.search ? `${url.search}&format=json` : '?format=json';
|
||||||
|
return handleRequest(url, params, request, fetch, cookies, searchParam, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function DELETE({ url, params, request, fetch, cookies }) {
|
export async function DELETE({ url, params, request, fetch, cookies }) {
|
||||||
return handleRequest(url, params, request, fetch, cookies, true);
|
const searchParam = url.search ? `${url.search}&format=json` : '?format=json';
|
||||||
|
return handleRequest(url, params, request, fetch, cookies, searchParam, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implement other HTTP methods as needed (PUT, DELETE, etc.)
|
|
||||||
|
|
||||||
async function handleRequest(
|
async function handleRequest(
|
||||||
url: any,
|
url: any,
|
||||||
params: any,
|
params: any,
|
||||||
request: any,
|
request: any,
|
||||||
fetch: any,
|
fetch: any,
|
||||||
cookies: any,
|
cookies: any,
|
||||||
|
searchParam: string,
|
||||||
requreTrailingSlash: boolean | undefined = false
|
requreTrailingSlash: boolean | undefined = false
|
||||||
) {
|
) {
|
||||||
const path = params.path;
|
const path = params.path;
|
||||||
let targetUrl = `${endpoint}/api/${path}${url.search}`;
|
let targetUrl = `${endpoint}/api/${path}`;
|
||||||
|
|
||||||
|
// Ensure the path ends with a trailing slash
|
||||||
if (requreTrailingSlash && !targetUrl.endsWith('/')) {
|
if (requreTrailingSlash && !targetUrl.endsWith('/')) {
|
||||||
targetUrl += '/';
|
targetUrl += '/';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Append query parameters to the path correctly
|
||||||
|
targetUrl += searchParam; // This will add ?format=json or &format=json to the URL
|
||||||
|
|
||||||
const headers = new Headers(request.headers);
|
const headers = new Headers(request.headers);
|
||||||
|
|
||||||
const authCookie = cookies.get('auth');
|
const csrfToken = await fetchCSRFToken();
|
||||||
|
if (!csrfToken) {
|
||||||
if (authCookie) {
|
return json({ error: 'CSRF token is missing or invalid' }, { status: 400 });
|
||||||
headers.set('Cookie', `${authCookie}`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch(targetUrl, {
|
const response = await fetch(targetUrl, {
|
||||||
method: request.method,
|
method: request.method,
|
||||||
headers: headers,
|
headers: {
|
||||||
body: request.method !== 'GET' && request.method !== 'HEAD' ? await request.text() : undefined
|
...Object.fromEntries(headers),
|
||||||
|
'X-CSRFToken': csrfToken,
|
||||||
|
Cookie: `csrftoken=${csrfToken}`
|
||||||
|
},
|
||||||
|
body:
|
||||||
|
request.method !== 'GET' && request.method !== 'HEAD' ? await request.text() : undefined,
|
||||||
|
credentials: 'include' // This line ensures cookies are sent with the request
|
||||||
});
|
});
|
||||||
|
|
||||||
if (response.status === 204) {
|
if (response.status === 204) {
|
||||||
// For 204 No Content, return a response with no body
|
|
||||||
return new Response(null, {
|
return new Response(null, {
|
||||||
status: 204,
|
status: 204,
|
||||||
headers: response.headers
|
headers: response.headers
|
||||||
|
|
|
@ -17,10 +17,12 @@ export const load = (async (event) => {
|
||||||
let previous = null;
|
let previous = null;
|
||||||
let count = 0;
|
let count = 0;
|
||||||
let adventures: Adventure[] = [];
|
let adventures: Adventure[] = [];
|
||||||
|
let sessionId = event.cookies.get('sessionid');
|
||||||
let initialFetch = await fetch(`${serverEndpoint}/api/collections/?order_by=updated_at`, {
|
let initialFetch = await fetch(`${serverEndpoint}/api/collections/?order_by=updated_at`, {
|
||||||
headers: {
|
headers: {
|
||||||
Cookie: `${event.cookies.get('auth')}`
|
Cookie: `sessionid=${sessionId}`
|
||||||
}
|
},
|
||||||
|
credentials: 'include'
|
||||||
});
|
});
|
||||||
if (!initialFetch.ok) {
|
if (!initialFetch.ok) {
|
||||||
console.error('Failed to fetch visited adventures');
|
console.error('Failed to fetch visited adventures');
|
||||||
|
@ -72,34 +74,9 @@ export const actions: Actions = {
|
||||||
formDataToSend.append('start_date', start_date || '');
|
formDataToSend.append('start_date', start_date || '');
|
||||||
formDataToSend.append('end_date', end_date || '');
|
formDataToSend.append('end_date', end_date || '');
|
||||||
formDataToSend.append('link', link || '');
|
formDataToSend.append('link', link || '');
|
||||||
let auth = event.cookies.get('auth');
|
let sessionid = event.cookies.get('sessionid');
|
||||||
|
|
||||||
if (!auth) {
|
if (!sessionid) {
|
||||||
const refresh = event.cookies.get('refresh');
|
|
||||||
if (!refresh) {
|
|
||||||
return {
|
|
||||||
status: 401,
|
|
||||||
body: { message: 'Unauthorized' }
|
|
||||||
};
|
|
||||||
}
|
|
||||||
let res = await tryRefreshToken(refresh);
|
|
||||||
if (res) {
|
|
||||||
auth = res;
|
|
||||||
event.cookies.set('auth', auth, {
|
|
||||||
httpOnly: true,
|
|
||||||
sameSite: 'lax',
|
|
||||||
expires: new Date(Date.now() + 60 * 60 * 1000), // 60 minutes
|
|
||||||
path: '/'
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
return {
|
|
||||||
status: 401,
|
|
||||||
body: { message: 'Unauthorized' }
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!auth) {
|
|
||||||
return {
|
return {
|
||||||
status: 401,
|
status: 401,
|
||||||
body: { message: 'Unauthorized' }
|
body: { message: 'Unauthorized' }
|
||||||
|
@ -119,7 +96,7 @@ export const actions: Actions = {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'X-CSRFToken': csrfToken,
|
'X-CSRFToken': csrfToken,
|
||||||
Cookie: auth
|
Cookie: `sessionid=${sessionid}; csrftoken=${csrfToken}`
|
||||||
},
|
},
|
||||||
body: formDataToSend
|
body: formDataToSend
|
||||||
});
|
});
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { fail, redirect } from '@sveltejs/kit';
|
||||||
|
|
||||||
import type { Actions, PageServerLoad } from './$types';
|
import type { Actions, PageServerLoad } from './$types';
|
||||||
import { getRandomBackground, getRandomQuote } from '$lib';
|
import { getRandomBackground, getRandomQuote } from '$lib';
|
||||||
|
import { fetchCSRFToken } from '$lib/index.server';
|
||||||
const PUBLIC_SERVER_URL = process.env['PUBLIC_SERVER_URL'];
|
const PUBLIC_SERVER_URL = process.env['PUBLIC_SERVER_URL'];
|
||||||
|
|
||||||
export const load: PageServerLoad = async (event) => {
|
export const load: PageServerLoad = async (event) => {
|
||||||
|
@ -24,37 +25,29 @@ export const actions: Actions = {
|
||||||
default: async (event) => {
|
default: async (event) => {
|
||||||
const formData = await event.request.formData();
|
const formData = await event.request.formData();
|
||||||
const formUsername = formData.get('username');
|
const formUsername = formData.get('username');
|
||||||
const formPassword = formData.get('password');
|
|
||||||
|
|
||||||
let username = formUsername?.toString().toLocaleLowerCase();
|
let username = formUsername?.toString().toLocaleLowerCase();
|
||||||
|
|
||||||
const password = formData.get('password');
|
const password = formData.get('password');
|
||||||
|
|
||||||
const serverEndpoint = PUBLIC_SERVER_URL || 'http://localhost:8000';
|
const serverEndpoint = PUBLIC_SERVER_URL || 'http://localhost:8000';
|
||||||
const csrfTokenFetch = await event.fetch(`${serverEndpoint}/csrf/`);
|
|
||||||
|
|
||||||
if (!csrfTokenFetch.ok) {
|
const csrfToken = await fetchCSRFToken();
|
||||||
console.error('Failed to fetch CSRF token');
|
|
||||||
event.locals.user = null;
|
|
||||||
return fail(500, {
|
|
||||||
message: 'Failed to fetch CSRF token'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const tokenPromise = await csrfTokenFetch.json();
|
const loginFetch = await event.fetch(`${serverEndpoint}/_allauth/browser/v1/auth/login`, {
|
||||||
const csrfToken = tokenPromise.csrfToken;
|
|
||||||
|
|
||||||
const loginFetch = await event.fetch(`${serverEndpoint}/auth/login/`, {
|
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'X-CSRFToken': csrfToken,
|
'X-CSRFToken': csrfToken,
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json',
|
||||||
|
Cookie: `csrftoken=${csrfToken}`
|
||||||
},
|
},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
username,
|
username,
|
||||||
password
|
password
|
||||||
})
|
}),
|
||||||
|
credentials: 'include'
|
||||||
});
|
});
|
||||||
|
|
||||||
const loginResponse = await loginFetch.json();
|
const loginResponse = await loginFetch.json();
|
||||||
if (!loginFetch.ok) {
|
if (!loginFetch.ok) {
|
||||||
// get the value of the first key in the object
|
// get the value of the first key in the object
|
||||||
|
@ -64,25 +57,34 @@ export const actions: Actions = {
|
||||||
message: error
|
message: error
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
const token = loginResponse.access;
|
const setCookieHeader = loginFetch.headers.get('Set-Cookie');
|
||||||
const tokenFormatted = `auth=${token}`;
|
|
||||||
const refreshToken = `${loginResponse.refresh}`;
|
|
||||||
event.cookies.set('auth', tokenFormatted, {
|
|
||||||
httpOnly: true,
|
|
||||||
sameSite: 'lax',
|
|
||||||
expires: new Date(Date.now() + 60 * 60 * 1000), // 60 minutes
|
|
||||||
path: '/',
|
|
||||||
secure: false
|
|
||||||
});
|
|
||||||
event.cookies.set('refresh', refreshToken, {
|
|
||||||
httpOnly: true,
|
|
||||||
sameSite: 'lax',
|
|
||||||
expires: new Date(Date.now() + 365 * 24 * 60 * 60 * 1000), // 1 year
|
|
||||||
path: '/',
|
|
||||||
secure: false
|
|
||||||
});
|
|
||||||
|
|
||||||
return redirect(302, '/');
|
console.log('setCookieHeader:', setCookieHeader);
|
||||||
|
|
||||||
|
if (setCookieHeader) {
|
||||||
|
// Regular expression to match sessionid cookie and its expiry
|
||||||
|
const sessionIdRegex = /sessionid=([^;]+).*?expires=([^;]+)/;
|
||||||
|
const match = setCookieHeader.match(sessionIdRegex);
|
||||||
|
|
||||||
|
if (match) {
|
||||||
|
const sessionId = match[1];
|
||||||
|
const expiryString = match[2];
|
||||||
|
const expiryDate = new Date(expiryString);
|
||||||
|
|
||||||
|
console.log('Session ID:', sessionId);
|
||||||
|
console.log('Expiry Date:', expiryDate);
|
||||||
|
|
||||||
|
// Set the sessionid cookie
|
||||||
|
event.cookies.set('sessionid', sessionId, {
|
||||||
|
path: '/',
|
||||||
|
httpOnly: true,
|
||||||
|
sameSite: 'lax',
|
||||||
|
secure: true,
|
||||||
|
expires: expiryDate
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
redirect(302, '/');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { fetchCSRFToken } from '$lib/index.server';
|
||||||
import { fail, type Actions } from '@sveltejs/kit';
|
import { fail, type Actions } from '@sveltejs/kit';
|
||||||
|
|
||||||
const PUBLIC_SERVER_URL = process.env['PUBLIC_SERVER_URL'];
|
const PUBLIC_SERVER_URL = process.env['PUBLIC_SERVER_URL'];
|
||||||
|
@ -13,10 +14,14 @@ export const actions: Actions = {
|
||||||
return fail(400, { message: 'missing_email' });
|
return fail(400, { message: 'missing_email' });
|
||||||
}
|
}
|
||||||
|
|
||||||
let res = await fetch(`${endpoint}/auth/password/reset/`, {
|
let csrfToken = await fetchCSRFToken();
|
||||||
|
|
||||||
|
let res = await fetch(`${endpoint}/_allauth/browser/v1/auth/password/request`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json',
|
||||||
|
'X-CSRFToken': csrfToken,
|
||||||
|
Cookie: `csrftoken=${csrfToken}`
|
||||||
},
|
},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
email
|
email
|
||||||
|
@ -25,10 +30,7 @@ export const actions: Actions = {
|
||||||
|
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
let message = await res.json();
|
let message = await res.json();
|
||||||
|
return fail(res.status, message);
|
||||||
const key = Object.keys(message)[0];
|
|
||||||
|
|
||||||
return fail(res.status, { message: message[key] });
|
|
||||||
}
|
}
|
||||||
return { success: true };
|
return { success: true };
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,19 +44,23 @@ export const actions: Actions = {
|
||||||
return fail(500, { message: 'Failed to fetch CSRF token' });
|
return fail(500, { message: 'Failed to fetch CSRF token' });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (password1 !== password2) {
|
||||||
|
return fail(400, { message: 'Passwords do not match' });
|
||||||
|
}
|
||||||
|
|
||||||
const tokenPromise = await csrfTokenFetch.json();
|
const tokenPromise = await csrfTokenFetch.json();
|
||||||
const csrfToken = tokenPromise.csrfToken;
|
const csrfToken = tokenPromise.csrfToken;
|
||||||
|
|
||||||
const loginFetch = await event.fetch(`${serverEndpoint}/auth/registration/`, {
|
const loginFetch = await event.fetch(`${serverEndpoint}/_allauth/browser/v1/auth/signup`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'X-CSRFToken': csrfToken,
|
'X-CSRFToken': csrfToken,
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json',
|
||||||
|
Cookie: `csrftoken=${csrfToken}`
|
||||||
},
|
},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
username: username,
|
username: username,
|
||||||
password1: password1,
|
password: password1,
|
||||||
password2: password2,
|
|
||||||
email: email,
|
email: email,
|
||||||
first_name,
|
first_name,
|
||||||
last_name
|
last_name
|
||||||
|
@ -65,31 +69,36 @@ export const actions: Actions = {
|
||||||
const loginResponse = await loginFetch.json();
|
const loginResponse = await loginFetch.json();
|
||||||
|
|
||||||
if (!loginFetch.ok) {
|
if (!loginFetch.ok) {
|
||||||
// get the value of the first key in the object
|
return fail(loginFetch.status, loginResponse);
|
||||||
const firstKey = Object.keys(loginResponse)[0] || 'error';
|
|
||||||
const error =
|
|
||||||
loginResponse[firstKey][0] || 'Failed to register user. Check your inputs and try again.';
|
|
||||||
return fail(400, {
|
|
||||||
message: error
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
const token = loginResponse.access;
|
const setCookieHeader = loginFetch.headers.get('Set-Cookie');
|
||||||
const tokenFormatted = `auth=${token}`;
|
|
||||||
const refreshToken = `${loginResponse.refresh}`;
|
|
||||||
event.cookies.set('auth', tokenFormatted, {
|
|
||||||
httpOnly: true,
|
|
||||||
sameSite: 'lax',
|
|
||||||
expires: new Date(Date.now() + 60 * 60 * 1000), // 60 minutes
|
|
||||||
path: '/'
|
|
||||||
});
|
|
||||||
event.cookies.set('refresh', refreshToken, {
|
|
||||||
httpOnly: true,
|
|
||||||
sameSite: 'lax',
|
|
||||||
expires: new Date(Date.now() + 365 * 24 * 60 * 60 * 1000), // 1 year
|
|
||||||
path: '/'
|
|
||||||
});
|
|
||||||
|
|
||||||
return redirect(302, '/');
|
console.log('setCookieHeader:', setCookieHeader);
|
||||||
|
|
||||||
|
if (setCookieHeader) {
|
||||||
|
// Regular expression to match sessionid cookie and its expiry
|
||||||
|
const sessionIdRegex = /sessionid=([^;]+).*?expires=([^;]+)/;
|
||||||
|
const match = setCookieHeader.match(sessionIdRegex);
|
||||||
|
|
||||||
|
if (match) {
|
||||||
|
const sessionId = match[1];
|
||||||
|
const expiryString = match[2];
|
||||||
|
const expiryDate = new Date(expiryString);
|
||||||
|
|
||||||
|
console.log('Session ID:', sessionId);
|
||||||
|
console.log('Expiry Date:', expiryDate);
|
||||||
|
|
||||||
|
// Set the sessionid cookie
|
||||||
|
event.cookies.set('sessionid', sessionId, {
|
||||||
|
path: '/',
|
||||||
|
httpOnly: true,
|
||||||
|
sameSite: 'lax',
|
||||||
|
secure: true,
|
||||||
|
expires: expiryDate
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
redirect(302, '/');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -9,11 +9,9 @@ export const load = (async (event) => {
|
||||||
if (!event.locals.user) {
|
if (!event.locals.user) {
|
||||||
return redirect(302, '/login');
|
return redirect(302, '/login');
|
||||||
} else {
|
} else {
|
||||||
const res = await fetch(`${endpoint}/api/countries/`, {
|
const res = await event.fetch(`${endpoint}/api/countries/`, {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
headers: {
|
credentials: 'include'
|
||||||
Cookie: `${event.cookies.get('auth')}`
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
console.error('Failed to fetch countries');
|
console.error('Failed to fetch countries');
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue