Skip to content

Commit

Permalink
Auth Migration, Calendar and Other Misc. Fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
seanmorley15 authored Dec 15, 2024
2 parents f7742ca + 10dbafd commit 148568f
Show file tree
Hide file tree
Showing 95 changed files with 3,266 additions and 2,378 deletions.
31 changes: 15 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,9 @@

[!["Buy Me A Coffee"](https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png)](https://www.buymeacoffee.com/seanmorley15)

- **[Documentation](https://adventurelog.app)**
- **[Demo](https://demo.adventurelog.app)**
- **[Join the AdventureLog Community Discord Server](https://discord.gg/wRbQ9Egr8C)**

- **[Documentation](https://adventurelog.app)**
- **[Demo](https://demo.adventurelog.app)**
- **[Join the AdventureLog Community Discord Server](https://discord.gg/wRbQ9Egr8C)**

# Table of Contents

Expand Down Expand Up @@ -59,18 +58,18 @@ Here is a summary of the configuration options available in the `docker-compose.

### Backend Container (server)

| Name | Required | Description | Default Value |
| ----------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------- |
| `PGHOST` | Yes | Databse host. | db |
| `PGDATABASE` | Yes | Database. | database |
| `PGUSER` | Yes | Database user. | adventure |
| `PGPASSWORD` | Yes | Database password. | changeme123 |
| `DJANGO_ADMIN_USERNAME` | Yes | Default username. | admin |
| `DJANGO_ADMIN_PASSWORD` | Yes | Default password, change after inital login. | admin |
| `DJANGO_ADMIN_EMAIL` | Yes | Default user's email. | [email protected] |
| `PUBLIC_URL` | Yes | This needs to match the outward port of the server and be accessible from where the app is used. It is used for the creation of image urls. | 'http://localhost:8016' |
| `CSRF_TRUSTED_ORIGINS` | Yes | Need to be changed to the orgins where you use your backend server and frontend. These values are comma seperated. | http://localhost:8016 |
| `FRONTEND_URL` | Yes | This is the publicly accessible url to the **frontend** container. This link should be accessible for all users. Used for email generation. | 'http://localhost:8015' |
| Name | Required | Description | Default Value |
| ----------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------- | --------------------- |
| `PGHOST` | Yes | Databse host. | db |
| `PGDATABASE` | Yes | Database. | database |
| `PGUSER` | Yes | Database user. | adventure |
| `PGPASSWORD` | Yes | Database password. | changeme123 |
| `DJANGO_ADMIN_USERNAME` | Yes | Default username. | admin |
| `DJANGO_ADMIN_PASSWORD` | Yes | Default password, change after inital login. | admin |
| `DJANGO_ADMIN_EMAIL` | Yes | Default user's email. | [email protected] |
| `PUBLIC_URL` | Yes | This needs to match the outward port of the server and be accessible from where the app is used. It is used for the creation of image urls. | http://localhost:8016 |
| `CSRF_TRUSTED_ORIGINS` | Yes | Need to be changed to the orgins where you use your backend server and frontend. These values are comma seperated. | http://localhost:8016 |
| `FRONTEND_URL` | Yes | This is the publicly accessible url to the **frontend** container. This link should be accessible for all users. Used for email generation. | http://localhost:8015 |

## Running the Containers

Expand Down
27 changes: 0 additions & 27 deletions backend/LICENSE

This file was deleted.

4 changes: 0 additions & 4 deletions backend/README.md

This file was deleted.

9 changes: 7 additions & 2 deletions backend/server/adventures/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@
from django.utils.html import mark_safe
from .models import Adventure, Checklist, ChecklistItem, Collection, Transportation, Note, AdventureImage, Visit, Category
from worldtravel.models import Country, Region, VisitedRegion
from allauth.account.decorators import secure_admin_login

admin.autodiscover()
admin.site.login = secure_admin_login(admin.site.login)



class AdventureAdmin(admin.ModelAdmin):
Expand Down Expand Up @@ -54,9 +59,9 @@ def number_of_visits(self, obj):

class CustomUserAdmin(UserAdmin):
model = CustomUser
list_display = ['username', 'email', 'is_staff', 'is_active', 'image_display']
list_display = ['username', 'is_staff', 'is_active', 'image_display']
readonly_fields = ('uuid',)
search_fields = ('username', 'email')
search_fields = ('username',)
fieldsets = UserAdmin.fieldsets + (
(None, {'fields': ('profile_pic', 'uuid', 'public_profile')}),
)
Expand Down
10 changes: 10 additions & 0 deletions backend/server/adventures/middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,13 @@ def __call__(self, request):
response['X-AdventureLog-Version'] = '1.0.0'

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
3 changes: 2 additions & 1 deletion backend/server/adventures/urls.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from django.urls import include, path
from rest_framework.routers import DefaultRouter
from .views import AdventureViewSet, ChecklistViewSet, CollectionViewSet, NoteViewSet, StatsViewSet, GenerateDescription, ActivityTypesView, TransportationViewSet, AdventureImageViewSet, ReverseGeocodeViewSet, CategoryViewSet
from .views import AdventureViewSet, ChecklistViewSet, CollectionViewSet, NoteViewSet, StatsViewSet, GenerateDescription, ActivityTypesView, TransportationViewSet, AdventureImageViewSet, ReverseGeocodeViewSet, CategoryViewSet, IcsCalendarGeneratorViewSet

router = DefaultRouter()
router.register(r'adventures', AdventureViewSet, basename='adventures')
Expand All @@ -14,6 +14,7 @@
router.register(r'images', AdventureImageViewSet, basename='images')
router.register(r'reverse-geocode', ReverseGeocodeViewSet, basename='reverse-geocode')
router.register(r'categories', CategoryViewSet, basename='categories')
router.register(r'ics-calendar', IcsCalendarGeneratorViewSet, basename='ics-calendar')


urlpatterns = [
Expand Down
59 changes: 58 additions & 1 deletion backend/server/adventures/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@
from django.shortcuts import get_object_or_404
from rest_framework import status
from django.contrib.auth import get_user_model
from icalendar import Calendar, Event, vText, vCalAddress
from django.http import HttpResponse
from datetime import datetime

User = get_user_model()

Expand Down Expand Up @@ -73,6 +76,7 @@ def apply_sorting(self, queryset):
return queryset.order_by(ordering)

def get_queryset(self):
print(self.request.user)
# if the user is not authenticated return only public adventures for retrieve action
if not self.request.user.is_authenticated:
if self.action == 'retrieve':
Expand Down Expand Up @@ -1201,4 +1205,57 @@ def mark_visited_region(self, request):
visited_region.save()
new_region_count += 1
new_regions[region.id] = region.name
return Response({"new_regions": new_region_count, "regions": new_regions})
return Response({"new_regions": new_region_count, "regions": new_regions})


from django.http import HttpResponse
from rest_framework import viewsets
from rest_framework.decorators import action
from rest_framework.permissions import IsAuthenticated
from icalendar import Calendar, Event, vText, vCalAddress
from datetime import datetime, timedelta

class IcsCalendarGeneratorViewSet(viewsets.ViewSet):
permission_classes = [IsAuthenticated]

@action(detail=False, methods=['get'])
def generate(self, request):
adventures = Adventure.objects.filter(user_id=request.user)
serializer = AdventureSerializer(adventures, many=True)
user = request.user
name = f"{user.first_name} {user.last_name}"
print(serializer.data)

cal = Calendar()
cal.add('prodid', '-//My Adventure Calendar//example.com//')
cal.add('version', '2.0')

for adventure in serializer.data:
if adventure['visits']:
for visit in adventure['visits']:
event = Event()
event.add('summary', adventure['name'])
start_date = datetime.strptime(visit['start_date'], '%Y-%m-%d').date()
end_date = datetime.strptime(visit['end_date'], '%Y-%m-%d').date() + timedelta(days=1) if visit['end_date'] else start_date + timedelta(days=1)
event.add('dtstart', start_date)
event.add('dtend', end_date)
event.add('dtstamp', datetime.now())
event.add('transp', 'TRANSPARENT')
event.add('class', 'PUBLIC')
event.add('created', datetime.now())
event.add('last-modified', datetime.now())
event.add('description', adventure['description'])
if adventure.get('location'):
event.add('location', adventure['location'])
if adventure.get('link'):
event.add('url', adventure['link'])

organizer = vCalAddress(f'MAILTO:{user.email}')
organizer.params['cn'] = vText(name)
event.add('organizer', organizer)

cal.add_component(event)

response = HttpResponse(cal.to_ical(), content_type='text/calendar')
response['Content-Disposition'] = 'attachment; filename=adventures.ics'
return response
77 changes: 37 additions & 40 deletions backend/server/main/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
# 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
Expand All @@ -35,8 +34,6 @@
# ]
ALLOWED_HOSTS = ['*']

# Application definition

INSTALLED_APPS = (
'django.contrib.admin',
'django.contrib.auth',
Expand All @@ -47,19 +44,19 @@
'django.contrib.sites',
'rest_framework',
'rest_framework.authtoken',
'dj_rest_auth',
'allauth',
'allauth.account',
'dj_rest_auth.registration',
'allauth.mfa',
'allauth.headless',
'allauth.socialaccount',
'allauth.socialaccount.providers.facebook',
# "widget_tweaks",
# "slippers",
'drf_yasg',
'corsheaders',
'adventures',
'worldtravel',
'users',
'django.contrib.gis',

)

MIDDLEWARE = (
Expand All @@ -83,19 +80,13 @@
}
}


# For backwards compatibility for Django 1.8
MIDDLEWARE_CLASSES = MIDDLEWARE

ROOT_URLCONF = 'main.urls'

# WSGI_APPLICATION = 'demo.wsgi.application'

SIMPLE_JWT = {
"ACCESS_TOKEN_LIFETIME": timedelta(minutes=60),
"REFRESH_TOKEN_LIFETIME": timedelta(days=365),
}

# Database
# https://docs.djangoproject.com/en/1.7/ref/settings/#databases

Expand All @@ -114,6 +105,7 @@
}



# Internationalization
# https://docs.djangoproject.com/en/1.7/topics/i18n/

Expand All @@ -127,6 +119,8 @@

USE_TZ = True



# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.7/howto/static-files/

Expand All @@ -139,7 +133,15 @@
MEDIA_ROOT = BASE_DIR / 'media'
STATICFILES_DIRS = [BASE_DIR / 'static']

# TEMPLATE_DIRS = [os.path.join(BASE_DIR, 'templates')]
STORAGES = {
"staticfiles": {
"BACKEND": "whitenoise.storage.CompressedManifestStaticFilesStorage",
},
"default": {
"BACKEND": "django.core.files.storage.FileSystemStorage",
}
}


TEMPLATES = [
{
Expand All @@ -157,32 +159,34 @@
},
]

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'
}
# 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 = {
"account_confirm_email": f"{FRONTEND_URL}/user/verify-email/{{key}}",
"account_reset_password": f"{FRONTEND_URL}/user/reset-password",
"account_reset_password_from_key": f"{FRONTEND_URL}/user/reset-password/{{key}}",
"account_signup": f"{FRONTEND_URL}/signup",
# Fallback in case the state containing the `next` URL is lost and the handshake
# with the third-party provider fails.
"socialaccount_login_error": f"{FRONTEND_URL}/account/provider/callback",
}

EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
SITE_ID = 1
ACCOUNT_EMAIL_REQUIRED = True
Expand Down Expand Up @@ -214,26 +218,20 @@
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.TokenAuthentication',
'dj_rest_auth.jwt_auth.JWTCookieAuthentication'
),
'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.coreapi.AutoSchema',
# 'DEFAULT_PERMISSION_CLASSES': [
# 'rest_framework.permissions.IsAuthenticated',
# ],
}

SWAGGER_SETTINGS = {
'LOGIN_URL': 'login',
'LOGOUT_URL': 'logout',
}

# For demo purposes only. Use a white list in the real world.
CORS_ORIGIN_ALLOW_ALL = True
CORS_ALLOWED_ORIGINS = [origin.strip() for origin in getenv('CSRF_TRUSTED_ORIGINS', 'http://localhost').split(',') if origin.strip()]

from os import getenv

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'

LOGGING = {
Expand All @@ -260,6 +258,5 @@
},
},
}

# https://github.com/dr5hn/countries-states-cities-database/tags
COUNTRY_REGION_JSON_VERSION = 'v2.4'
Loading

0 comments on commit 148568f

Please sign in to comment.