From b5ac66a1ccd864c075b1bab6200cc01006f7d68d Mon Sep 17 00:00:00 2001 From: Sean Morley Date: Sun, 29 Dec 2024 12:38:54 -0500 Subject: [PATCH 001/209] Update secure cookie setting to conditionally use HTTPS protocol in authentication flows --- frontend/src/hooks.server.ts | 2 +- frontend/src/routes/login/+page.server.ts | 2 +- frontend/src/routes/signup/+page.server.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/src/hooks.server.ts b/frontend/src/hooks.server.ts index 98830a82..679a59df 100644 --- a/frontend/src/hooks.server.ts +++ b/frontend/src/hooks.server.ts @@ -47,7 +47,7 @@ export const authHook: Handle = async ({ event, resolve }) => { path: '/', httpOnly: true, sameSite: 'lax', - secure: true, + secure: event.url.protocol === 'https:', expires: expiryDate }); } diff --git a/frontend/src/routes/login/+page.server.ts b/frontend/src/routes/login/+page.server.ts index 1b505182..adb0905a 100644 --- a/frontend/src/routes/login/+page.server.ts +++ b/frontend/src/routes/login/+page.server.ts @@ -106,7 +106,7 @@ function handleSuccessfulLogin(event: RequestEvent, respo path: '/', httpOnly: true, sameSite: 'lax', - secure: true, + secure: event.url.protocol === 'https:', expires: new Date(expiryString) }); } diff --git a/frontend/src/routes/signup/+page.server.ts b/frontend/src/routes/signup/+page.server.ts index 4a1d8576..813b4710 100644 --- a/frontend/src/routes/signup/+page.server.ts +++ b/frontend/src/routes/signup/+page.server.ts @@ -93,7 +93,7 @@ export const actions: Actions = { path: '/', httpOnly: true, sameSite: 'lax', - secure: true, + secure: event.url.protocol === 'https:', expires: expiryDate }); } From fd7f285c57c9992c20d762771e4906943672908c Mon Sep 17 00:00:00 2001 From: Sean Morley Date: Sun, 29 Dec 2024 12:55:45 -0500 Subject: [PATCH 002/209] Update session cookie deletion to conditionally use secure flag based on HTTPS protocol --- frontend/src/hooks.server.ts | 6 +++--- frontend/src/routes/+page.server.ts | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/frontend/src/hooks.server.ts b/frontend/src/hooks.server.ts index 679a59df..91e1b607 100644 --- a/frontend/src/hooks.server.ts +++ b/frontend/src/hooks.server.ts @@ -23,7 +23,7 @@ export const authHook: Handle = async ({ event, resolve }) => { if (!userFetch.ok) { event.locals.user = null; - event.cookies.delete('sessionid', { path: '/' }); + event.cookies.delete('sessionid', { path: '/', secure: event.url.protocol === 'https:' }); return await resolve(event); } @@ -54,12 +54,12 @@ export const authHook: Handle = async ({ event, resolve }) => { } } else { event.locals.user = null; - event.cookies.delete('sessionid', { path: '/' }); + event.cookies.delete('sessionid', { path: '/', secure: event.url.protocol === 'https:' }); } } catch (error) { console.error('Error in authHook:', error); event.locals.user = null; - event.cookies.delete('sessionid', { path: '/' }); + event.cookies.delete('sessionid', { path: '/', secure: event.url.protocol === 'https:' }); } return await resolve(event); diff --git a/frontend/src/routes/+page.server.ts b/frontend/src/routes/+page.server.ts index 8d0446a4..b379a8c2 100644 --- a/frontend/src/routes/+page.server.ts +++ b/frontend/src/routes/+page.server.ts @@ -42,6 +42,7 @@ export const actions: Actions = { credentials: 'include' }); if (res.status == 401) { + event.cookies.delete('sessionid', { path: '/', secure: event.url.protocol === 'https:' }); return redirect(302, '/login'); } else { return redirect(302, '/'); From d5ff2fc6b7452c33f367d1e55ae3c0b72729cd8b Mon Sep 17 00:00:00 2001 From: RedTechTiger <32938014+redtechtiger@users.noreply.github.com> Date: Sun, 29 Dec 2024 15:50:21 -0500 Subject: [PATCH 003/209] fix: slightly improve swedish locale --- frontend/src/locales/sv.json | 85 ++++++++++++++++++------------------ 1 file changed, 43 insertions(+), 42 deletions(-) diff --git a/frontend/src/locales/sv.json b/frontend/src/locales/sv.json index 495b0bad..7beccea6 100644 --- a/frontend/src/locales/sv.json +++ b/frontend/src/locales/sv.json @@ -1,9 +1,10 @@ { + "about": { "about": "Om", - "close": "Nära", + "close": "Stäng", "license": "Licensierad under GPL-3.0-licensen.", - "message": "Tillverkad med ❤️ i USA.", + "message": "Skapat med ❤️ i USA.", "nominatim_1": "Platssökning och geokodning tillhandahålls av", "nominatim_2": "Deras data är licensierad under ODbL-licensen.", "oss_attributions": "Tillskrivningar med öppen källkod", @@ -80,12 +81,12 @@ "not_found_desc": "Äventyret du letade efter kunde inte hittas. \nProva ett annat äventyr eller kom tillbaka senare.", "open_details": "Öppna Detaljer", "open_filters": "Öppna filter", - "order_by": "Beställ efter", - "order_direction": "Beställ riktning", - "planned": "Planerad", + "order_by": "Sortera efter", + "order_direction": "Sorteringsriktning", + "planned": "Planerade", "private": "Privat", "public": "Offentlig", - "rating": "Gradering", + "rating": "Betyg", "remove_from_collection": "Ta bort från samlingen", "share": "Dela", "sort": "Sortera", @@ -93,7 +94,7 @@ "unarchive": "Avarkivera", "unarchived_collection_message": "Samlingen har tagits bort från arkivet!", "visit": "Besök", - "visited": "Besökte", + "visited": "Besökta", "visits": "Besök", "image_removed_error": "Det gick inte att ta bort bilden", "image_removed_success": "Bilden har tagits bort!", @@ -153,12 +154,12 @@ "all": "Alla", "error_updating_regions": "Fel vid uppdatering av regioner", "mark_region_as_visited": "Markera region {region}, {country} som besökt?", - "mark_visited": "Mark besökte", + "mark_visited": "Markera som besökt", "my_adventures": "Mina äventyr", "no_adventures_found": "Inga äventyr hittades", "no_collections_found": "Inga samlingar hittades att lägga till detta äventyr till.", "no_linkable_adventures": "Inga äventyr hittades som kan kopplas till denna samling.", - "not_visited": "Ej besökt", + "not_visited": "Ej besökta", "regions_updated": "regioner uppdaterade", "update_visited_regions": "Uppdatera besökta regioner", "update_visited_regions_disclaimer": "Detta kan ta ett tag beroende på antalet äventyr du har besökt.", @@ -223,29 +224,29 @@ "feature_1": "Reselogg", "feature_1_desc": "Håll koll på dina äventyr med en personlig reselogg och dela dina upplevelser med vänner och familj.", "feature_2": "Reseplanering", - "feature_2_desc": "Skapa enkelt anpassade resplaner och få en uppdelning av din resa dag för dag.", + "feature_2_desc": "Skapa enkelt skräddarsydda resplaner och få en översikt över din resa, dag för dag.", "feature_3": "Resekarta", "feature_3_desc": "Se dina resor över hela världen med en interaktiv karta och utforska nya destinationer.", "go_to": "Gå till AdventureLog", "hero_1": "Upptäck världens mest spännande äventyr", "hero_2": "Upptäck och planera ditt nästa äventyr med AdventureLog. \nUtforska hisnande destinationer, skapa anpassade resplaner och håll kontakten när du är på språng.", - "key_features": "Nyckelfunktioner" + "key_features": "Viktiga funktioner" }, "navbar": { "about": "Om AdventureLog", "adventures": "Äventyr", "collections": "Samlingar", - "discord": "Disharmoni", + "discord": "Discord", "documentation": "Dokumentation", "greeting": "Hej", - "logout": "Utloggning", + "logout": "Logga ut", "map": "Karta", "my_adventures": "Mina äventyr", "profile": "Profil", - "search": "Söka", + "search": "Sök", "settings": "Inställningar", "shared_with_me": "Delade med mig", - "theme_selection": "Temaval", + "theme_selection": "Tema", "themes": { "aqua": "Aqua", "dark": "Mörk", @@ -259,21 +260,21 @@ "users": "Användare", "worldtravel": "Världsresor", "my_tags": "Mina taggar", - "tag": "Märka", + "tag": "Tagg", "language_selection": "Språk", - "support": "Stöd", + "support": "Support", "calendar": "Kalender" }, "worldtravel": { "all": "Alla", "all_subregions": "Alla underregioner", "clear_search": "Rensa sökning", - "completely_visited": "Helt besökt", + "completely_visited": "Fullständigt besökta", "country_list": "Lista över länder", "no_countries_found": "Inga länder hittades", - "not_visited": "Ej besökt", - "num_countries": "hittade länder", - "partially_visited": "Delvis besökt" + "not_visited": "Ej besökta", + "num_countries": "länder hittades", + "partially_visited": "Delvis besökta" }, "auth": { "confirm_password": "Bekräfta lösenord", @@ -281,7 +282,7 @@ "first_name": "Förnamn", "forgot_password": "Glömt lösenordet?", "last_name": "Efternamn", - "login": "Inloggning", + "login": "Logga in", "login_error": "Det går inte att logga in med de angivna uppgifterna.", "password": "Lösenord", "registration_disabled": "Registreringen är för närvarande inaktiverad.", @@ -307,25 +308,25 @@ "new_password": "Nytt lösenord", "no_email_set": "Ingen e-post inställd", "password_change": "Ändra lösenord", - "settings_page": "Inställningssida", + "settings_page": "Inställningar", "update": "Uppdatera", - "update_error": "Fel vid uppdatering av inställningar", + "update_error": "Ett fel uppstod vid uppdatering av inställningar", "update_success": "Inställningarna har uppdaterats!", "change_password": "Ändra lösenord", "invalid_token": "Token är ogiltig eller har gått ut", "login_redir": "Du kommer då att omdirigeras till inloggningssidan.", "missing_email": "Vänligen ange en e-postadress", - "password_does_not_match": "Lösenord stämmer inte överens", + "password_does_not_match": "Lösenorden stämmer inte överens", "password_is_required": "Lösenord krävs", "possible_reset": "Om e-postadressen du angav är kopplad till ett konto kommer du att få ett e-postmeddelande med instruktioner för att återställa ditt lösenord!", "reset_password": "Återställ lösenord", - "submit": "Överlämna", + "submit": "Skicka", "token_required": "Token och UID krävs för lösenordsåterställning.", "about_this_background": "Om denna bakgrund", "join_discord": "Gå med i Discord", "join_discord_desc": "för att dela dina egna foton. \nLägg upp dem i", "photo_by": "Foto av", - "change_password_error": "Det går inte att ändra lösenord. \nOgiltigt nuvarande lösenord eller ogiltigt nytt lösenord.", + "change_password_error": "Det gick inte att ändra lösenord. \nDet nuvarande eller det nya lösenordet är ogiltigt.", "current_password": "Aktuellt lösenord", "password_change_lopout_warning": "Du kommer att loggas ut efter att du har ändrat ditt lösenord.", "authenticator_code": "Autentiseringskod", @@ -371,19 +372,19 @@ }, "checklist": { "add_item": "Lägg till objekt", - "checklist_delete_error": "Fel vid borttagning av checklista", + "checklist_delete_error": "Ett fel uppstod vid borttagning av checklista", "checklist_deleted": "Checklistan har raderats!", - "checklist_editor": "Checklista Editor", + "checklist_editor": "Redigerare för checklistor", "checklist_public": "Den här checklistan är offentlig eftersom den finns i en offentlig samling.", - "editing_checklist": "Redigeringschecklista", + "editing_checklist": "Redigerar checklista", "failed_to_save": "Det gick inte att spara checklistan", "item": "Punkt", - "item_already_exists": "Objektet finns redan", - "item_cannot_be_empty": "Objektet får inte vara tomt", - "items": "Föremål", + "item_already_exists": "Listobjektet finns redan", + "item_cannot_be_empty": "Listobjektet får inte vara tomt", + "items": "Punkter", "new_item": "Nytt föremål", "save": "Spara", - "checklist_viewer": "Checklista Viewer", + "checklist_viewer": "Se Checklista", "new_checklist": "Ny checklista" }, "collection": { @@ -392,18 +393,18 @@ "create": "Skapa", "edit_collection": "Redigera samling", "error_creating_collection": "Det gick inte att skapa samlingen", - "error_editing_collection": "Fel vid redigering av samling", + "error_editing_collection": "Ett fel uppstod vid redigering av samling", "new_collection": "Ny samling", "public_collection": "Offentlig samling" }, "notes": { "add_a_link": "Lägg till en länk", "content": "Innehåll", - "editing_note": "Redigeringsanteckning", + "editing_note": "Redigerar anteckning", "failed_to_save": "Det gick inte att spara anteckningen", "note_delete_error": "Det gick inte att ta bort anteckningen", "note_deleted": "Anteckningen har raderats!", - "note_editor": "Note Editor", + "note_editor": "Redigerare för anteckningar", "note_public": "Den här anteckningen är offentlig eftersom den finns i en offentlig samling.", "open": "Öppna", "save": "Spara", @@ -425,7 +426,7 @@ "bus": "Buss", "car": "Bil", "other": "Andra", - "plane": "Plan", + "plane": "Flygplan", "train": "Tåg", "walking": "Gående" }, @@ -463,7 +464,7 @@ "nl": "holländska", "sv": "svenska", "zh": "kinesiska", - "pl": "polsk" + "pl": "polska" }, "share": { "no_users_shared": "Inga användare delas med", @@ -475,13 +476,13 @@ "with": "med", "go_to_settings": "Gå till inställningar", "no_shared_found": "Inga samlingar hittades som delas med dig.", - "set_public": "För att tillåta användare att dela med dig måste du ha din profil inställd på offentlig." + "set_public": "För att tillåta användare att dela med dig måste du ha en offentlig profil." }, "profile": { - "member_since": "Medlem sedan dess", + "member_since": "Medlem sedan", "user_stats": "Användarstatistik", "visited_countries": "Besökta länder", - "visited_regions": "Besökte regioner" + "visited_regions": "Besökta regioner" }, "categories": { "category_name": "Kategorinamn", From dc897435693ad55fac0218e34777e24b6747045f Mon Sep 17 00:00:00 2001 From: Sean Morley Date: Tue, 31 Dec 2024 10:38:15 -0500 Subject: [PATCH 004/209] feat: add Immich integration module with API endpoints and admin interface --- backend/server/adventures/admin.py | 2 - backend/server/integrations/__init__.py | 0 backend/server/integrations/admin.py | 9 +++ backend/server/integrations/apps.py | 6 ++ .../integrations/migrations/0001_initial.py | 26 +++++++ .../integrations/migrations/__init__.py | 0 backend/server/integrations/models.py | 12 +++ backend/server/integrations/tests.py | 3 + backend/server/integrations/urls.py | 12 +++ backend/server/integrations/views.py | 77 +++++++++++++++++++ backend/server/main/settings.py | 1 + backend/server/main/urls.py | 2 + 12 files changed, 148 insertions(+), 2 deletions(-) create mode 100644 backend/server/integrations/__init__.py create mode 100644 backend/server/integrations/admin.py create mode 100644 backend/server/integrations/apps.py create mode 100644 backend/server/integrations/migrations/0001_initial.py create mode 100644 backend/server/integrations/migrations/__init__.py create mode 100644 backend/server/integrations/models.py create mode 100644 backend/server/integrations/tests.py create mode 100644 backend/server/integrations/urls.py create mode 100644 backend/server/integrations/views.py diff --git a/backend/server/adventures/admin.py b/backend/server/adventures/admin.py index 1beac0fb..be1793b1 100644 --- a/backend/server/adventures/admin.py +++ b/backend/server/adventures/admin.py @@ -8,8 +8,6 @@ from allauth.account.decorators import secure_admin_login admin.autodiscover() admin.site.login = secure_admin_login(admin.site.login) - - class AdventureAdmin(admin.ModelAdmin): list_display = ('name', 'get_category', 'get_visit_count', 'user_id', 'is_public') list_filter = ( 'user_id', 'is_public') diff --git a/backend/server/integrations/__init__.py b/backend/server/integrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/backend/server/integrations/admin.py b/backend/server/integrations/admin.py new file mode 100644 index 00000000..d561cf40 --- /dev/null +++ b/backend/server/integrations/admin.py @@ -0,0 +1,9 @@ +from django.contrib import admin +from allauth.account.decorators import secure_admin_login + +from .models import ImmichIntegration + +admin.autodiscover() +admin.site.login = secure_admin_login(admin.site.login) + +admin.site.register(ImmichIntegration) \ No newline at end of file diff --git a/backend/server/integrations/apps.py b/backend/server/integrations/apps.py new file mode 100644 index 00000000..73adb7a5 --- /dev/null +++ b/backend/server/integrations/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class IntegrationsConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'integrations' diff --git a/backend/server/integrations/migrations/0001_initial.py b/backend/server/integrations/migrations/0001_initial.py new file mode 100644 index 00000000..73015d16 --- /dev/null +++ b/backend/server/integrations/migrations/0001_initial.py @@ -0,0 +1,26 @@ +# Generated by Django 5.0.8 on 2024-12-31 15:02 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='ImmichIntegration', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('server_url', models.CharField(max_length=255)), + ('api_key', models.CharField(max_length=255)), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + ), + ] diff --git a/backend/server/integrations/migrations/__init__.py b/backend/server/integrations/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/backend/server/integrations/models.py b/backend/server/integrations/models.py new file mode 100644 index 00000000..d3394cc3 --- /dev/null +++ b/backend/server/integrations/models.py @@ -0,0 +1,12 @@ +from django.db import models +from django.contrib.auth import get_user_model + +User = get_user_model() + +class ImmichIntegration(models.Model): + server_url = models.CharField(max_length=255) + api_key = models.CharField(max_length=255) + user = models.ForeignKey(User, on_delete=models.CASCADE) + + def __str__(self): + return self.user.username + ' - ' + self.server_url \ No newline at end of file diff --git a/backend/server/integrations/tests.py b/backend/server/integrations/tests.py new file mode 100644 index 00000000..7ce503c2 --- /dev/null +++ b/backend/server/integrations/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/backend/server/integrations/urls.py b/backend/server/integrations/urls.py new file mode 100644 index 00000000..cd1cbfa4 --- /dev/null +++ b/backend/server/integrations/urls.py @@ -0,0 +1,12 @@ +from django.urls import path, include +from rest_framework.routers import DefaultRouter +from integrations.views import ImmichIntegrationView + +# Create the router and register the ViewSet +router = DefaultRouter() +router.register(r'immich', ImmichIntegrationView, basename='immich') + +# Include the router URLs +urlpatterns = [ + path("", include(router.urls)), # Includes /immich/ routes +] diff --git a/backend/server/integrations/views.py b/backend/server/integrations/views.py new file mode 100644 index 00000000..2622df92 --- /dev/null +++ b/backend/server/integrations/views.py @@ -0,0 +1,77 @@ +from rest_framework.response import Response +from rest_framework import viewsets, status +from .models import ImmichIntegration +from rest_framework.decorators import action +import requests + +class ImmichIntegrationView(viewsets.ViewSet): + def check_integration(self, request): + """ + Checks if the user has an active Immich integration. + Returns: + - None if the integration exists. + - A Response with an error message if the integration is missing. + """ + user_integrations = ImmichIntegration.objects.filter(user=request.user) + if not user_integrations.exists(): + return Response( + { + 'message': 'You need to have an active Immich integration to use this feature.', + 'error': True, + 'code': 'immich.integration_missing' + }, + status=status.HTTP_403_FORBIDDEN + ) + return ImmichIntegration.objects.first() + + @action(detail=False, methods=['get'], url_path='search') + def search(self, request): + """ + Handles the logic for searching Immich images. + """ + # Check for integration before proceeding + integration = self.check_integration(request) + if isinstance(integration, Response): + return integration + + query = request.query_params.get('query', '') + + if not query: + return Response( + { + 'message': 'Query is required.', + 'error': True, + 'code': 'immich.query_required' + }, + status=status.HTTP_400_BAD_REQUEST + ) + + + immich_fetch = requests.post(f'{integration.server_url}/search/smart', headers={ + 'x-api-key': integration.api_key + }, + json = { + 'query': query + } + ) + res = immich_fetch.json() + + if 'assets' in res and 'items' in res['assets']: + return Response(res['assets']['items'], status=status.HTTP_200_OK) + else: + return Response( + { + 'message': 'No items found.', + 'error': True, + 'code': 'immich.no_items_found' + }, + status=status.HTTP_404_NOT_FOUND + ) + + + + def get(self, request): + """ + RESTful GET method for searching Immich images. + """ + return self.search(request) diff --git a/backend/server/main/settings.py b/backend/server/main/settings.py index 7e4973b3..3882c471 100644 --- a/backend/server/main/settings.py +++ b/backend/server/main/settings.py @@ -56,6 +56,7 @@ INSTALLED_APPS = ( 'adventures', 'worldtravel', 'users', + 'integrations', 'django.contrib.gis', ) diff --git a/backend/server/main/urls.py b/backend/server/main/urls.py index 3e3c53f8..ab1e0844 100644 --- a/backend/server/main/urls.py +++ b/backend/server/main/urls.py @@ -39,6 +39,8 @@ urlpatterns = [ # path('auth/account-confirm-email/', VerifyEmailView.as_view(), name='account_email_verification_sent'), path("accounts/", include("allauth.urls")), + path("api/integrations/", include("integrations.urls")), + # Include the API endpoints: ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) From 15fd15ba40cf20adde92a6c2f1f98a7ed369bc56 Mon Sep 17 00:00:00 2001 From: Sean Morley Date: Wed, 1 Jan 2025 11:00:11 -0500 Subject: [PATCH 005/209] feat: update Immich integration to use OneToOneField for user and enhance image retrieval functionality --- .../0002_alter_immichintegration_user.py | 21 +++ backend/server/integrations/models.py | 2 +- backend/server/integrations/views.py | 87 +++++++++--- .../src/lib/components/AdventureModal.svelte | 125 ++++++++++-------- 4 files changed, 167 insertions(+), 68 deletions(-) create mode 100644 backend/server/integrations/migrations/0002_alter_immichintegration_user.py diff --git a/backend/server/integrations/migrations/0002_alter_immichintegration_user.py b/backend/server/integrations/migrations/0002_alter_immichintegration_user.py new file mode 100644 index 00000000..6f6912b6 --- /dev/null +++ b/backend/server/integrations/migrations/0002_alter_immichintegration_user.py @@ -0,0 +1,21 @@ +# Generated by Django 5.0.8 on 2024-12-31 18:29 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('integrations', '0001_initial'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.AlterField( + model_name='immichintegration', + name='user', + field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), + ), + ] diff --git a/backend/server/integrations/models.py b/backend/server/integrations/models.py index d3394cc3..0d7a0a4a 100644 --- a/backend/server/integrations/models.py +++ b/backend/server/integrations/models.py @@ -6,7 +6,7 @@ User = get_user_model() class ImmichIntegration(models.Model): server_url = models.CharField(max_length=255) api_key = models.CharField(max_length=255) - user = models.ForeignKey(User, on_delete=models.CASCADE) + user = models.OneToOneField(User, on_delete=models.CASCADE) def __str__(self): return self.user.username + ' - ' + self.server_url \ No newline at end of file diff --git a/backend/server/integrations/views.py b/backend/server/integrations/views.py index 2622df92..72c9c4ab 100644 --- a/backend/server/integrations/views.py +++ b/backend/server/integrations/views.py @@ -1,10 +1,20 @@ +import os from rest_framework.response import Response from rest_framework import viewsets, status from .models import ImmichIntegration from rest_framework.decorators import action +from rest_framework.permissions import IsAuthenticated import requests +from rest_framework.pagination import PageNumberPagination + +class StandardResultsSetPagination(PageNumberPagination): + page_size = 25 + page_size_query_param = 'page_size' + max_page_size = 1000 class ImmichIntegrationView(viewsets.ViewSet): + permission_classes = [IsAuthenticated] + pagination_class = StandardResultsSetPagination def check_integration(self, request): """ Checks if the user has an active Immich integration. @@ -46,18 +56,35 @@ class ImmichIntegrationView(viewsets.ViewSet): status=status.HTTP_400_BAD_REQUEST ) + # check so if the server is down, it does not tweak out like a madman and crash the server with a 500 error code + try: + immich_fetch = requests.post(f'{integration.server_url}/search/smart', headers={ + 'x-api-key': integration.api_key + }, + json = { + 'query': query + } + ) + res = immich_fetch.json() + except requests.exceptions.ConnectionError: + return Response( + { + 'message': 'The Immich server is currently down or unreachable.', + 'error': True, + 'code': 'immich.server_down' + }, + status=status.HTTP_503_SERVICE_UNAVAILABLE + ) - immich_fetch = requests.post(f'{integration.server_url}/search/smart', headers={ - 'x-api-key': integration.api_key - }, - json = { - 'query': query - } - ) - res = immich_fetch.json() - if 'assets' in res and 'items' in res['assets']: - return Response(res['assets']['items'], status=status.HTTP_200_OK) + paginator = self.pagination_class() + # for each item in the items, we need to add the image url to the item so we can display it in the frontend + public_url = os.environ.get('PUBLIC_URL', 'http://127.0.0.1:8000').rstrip('/') + public_url = public_url.replace("'", "") + for item in res['assets']['items']: + item['image_url'] = f'{public_url}/api/integrations/immich/get/{item["id"]}' + result_page = paginator.paginate_queryset(res['assets']['items'], request) + return paginator.get_paginated_response(result_page) else: return Response( { @@ -68,10 +95,40 @@ class ImmichIntegrationView(viewsets.ViewSet): status=status.HTTP_404_NOT_FOUND ) - - - def get(self, request): + @action(detail=False, methods=['get'], url_path='get/(?P[^/.]+)') + def get(self, request, imageid=None): """ - RESTful GET method for searching Immich images. + RESTful GET method for retrieving a specific Immich image by ID. """ - return self.search(request) + # Check for integration before proceeding + integration = self.check_integration(request) + if isinstance(integration, Response): + return integration + + if not imageid: + return Response( + { + 'message': 'Image ID is required.', + 'error': True, + 'code': 'immich.imageid_required' + }, + status=status.HTTP_400_BAD_REQUEST + ) + + # check so if the server is down, it does not tweak out like a madman and crash the server with a 500 error code + try: + immich_fetch = requests.get(f'{integration.server_url}/assets/{imageid}/thumbnail?size=preview', headers={ + 'x-api-key': integration.api_key + }) + # should return the image file + from django.http import HttpResponse + return HttpResponse(immich_fetch.content, content_type='image/jpeg', status=status.HTTP_200_OK) + except requests.exceptions.ConnectionError: + return Response( + { + 'message': 'The Immich server is currently down or unreachable.', + 'error': True, + 'code': 'immich.server_down' + }, + status=status.HTTP_503_SERVICE_UNAVAILABLE + ) diff --git a/frontend/src/lib/components/AdventureModal.svelte b/frontend/src/lib/components/AdventureModal.svelte index ff9770d5..cc081a0b 100644 --- a/frontend/src/lib/components/AdventureModal.svelte +++ b/frontend/src/lib/components/AdventureModal.svelte @@ -915,84 +915,105 @@ it would also work to just use on:click on the MapLibre component itself. --> {:else} -

{$t('adventures.upload_images_here')}

- -
-
-
-
- - - -
-
-
-
+

{$t('adventures.upload_images_here')}

+ +
+ +
+ + + +
+
+ +
+ +
- +
-
-
+
+ +
+ +
- +
-
- {#if images.length > 0} -

{$t('adventures.my_images')}

- {:else} -

{$t('adventures.no_images')}

- {/if} -
+
+ +
+ + {#if images.length > 0} +

{$t('adventures.my_images')}

+
{#each images as image}
- {image.id} + {image.id}
{/each}
-
-
- + {:else} +

{$t('adventures.no_images')}

+ {/if} + +
+
{/if} + {#if adventure.is_public && adventure.id}

{$t('adventures.share_adventure')}

From 67f6af8ca36de1006e78f4efdc0ea4178c45f9dc Mon Sep 17 00:00:00 2001 From: Sean Morley Date: Wed, 1 Jan 2025 16:24:44 -0500 Subject: [PATCH 006/209] feat: add Immich integration view and API documentation, enhance error handling, and include SVG asset --- backend/server/integrations/urls.py | 3 +- backend/server/integrations/views.py | 16 ++ backend/server/main/settings.py | 8 - backend/server/templates/base.html | 2 + frontend/src/lib/assets/immich.svg | 1 + .../src/lib/components/AdventureModal.svelte | 151 +++++++++++++++--- frontend/src/locales/en.json | 9 ++ frontend/src/routes/immich/[key]/+server.ts | 54 +++++++ 8 files changed, 209 insertions(+), 35 deletions(-) create mode 100644 frontend/src/lib/assets/immich.svg create mode 100644 frontend/src/routes/immich/[key]/+server.ts diff --git a/backend/server/integrations/urls.py b/backend/server/integrations/urls.py index cd1cbfa4..df405f18 100644 --- a/backend/server/integrations/urls.py +++ b/backend/server/integrations/urls.py @@ -1,10 +1,11 @@ from django.urls import path, include from rest_framework.routers import DefaultRouter -from integrations.views import ImmichIntegrationView +from integrations.views import ImmichIntegrationView, IntegrationView # Create the router and register the ViewSet router = DefaultRouter() router.register(r'immich', ImmichIntegrationView, basename='immich') +router.register(r'', IntegrationView, basename='integrations') # Include the router URLs urlpatterns = [ diff --git a/backend/server/integrations/views.py b/backend/server/integrations/views.py index 72c9c4ab..7a05fa34 100644 --- a/backend/server/integrations/views.py +++ b/backend/server/integrations/views.py @@ -7,6 +7,22 @@ from rest_framework.permissions import IsAuthenticated import requests from rest_framework.pagination import PageNumberPagination +class IntegrationView(viewsets.ViewSet): + permission_classes = [IsAuthenticated] + def list(self, request): + """ + RESTful GET method for listing all integrations. + """ + immich_integrations = ImmichIntegration.objects.filter(user=request.user) + + return Response( + { + 'immich': immich_integrations.exists() + }, + status=status.HTTP_200_OK + ) + + class StandardResultsSetPagination(PageNumberPagination): page_size = 25 page_size_query_param = 'page_size' diff --git a/backend/server/main/settings.py b/backend/server/main/settings.py index 3882c471..b934b990 100644 --- a/backend/server/main/settings.py +++ b/backend/server/main/settings.py @@ -165,9 +165,6 @@ TEMPLATES = [ 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.') -ALLAUTH_UI_THEME = "dark" -SILENCED_SYSTEM_CHECKS = ["slippers.E001"] - AUTH_USER_MODEL = 'users.CustomUser' ACCOUNT_ADAPTER = 'users.adapters.NoNewUsersAccountAdapter' @@ -223,11 +220,6 @@ REST_FRAMEWORK = { 'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.coreapi.AutoSchema', } -SWAGGER_SETTINGS = { - 'LOGIN_URL': 'login', - 'LOGOUT_URL': 'logout', -} - CORS_ALLOWED_ORIGINS = [origin.strip() for origin in getenv('CSRF_TRUSTED_ORIGINS', 'http://localhost').split(',') if origin.strip()] diff --git a/backend/server/templates/base.html b/backend/server/templates/base.html index be712b76..9e1d48cf 100644 --- a/backend/server/templates/base.html +++ b/backend/server/templates/base.html @@ -53,6 +53,7 @@ >Documentation +
  • Source Code
  • +
  • API Docs
  • diff --git a/frontend/src/lib/assets/immich.svg b/frontend/src/lib/assets/immich.svg new file mode 100644 index 00000000..70aa6727 --- /dev/null +++ b/frontend/src/lib/assets/immich.svg @@ -0,0 +1 @@ + diff --git a/frontend/src/lib/components/AdventureModal.svelte b/frontend/src/lib/components/AdventureModal.svelte index cc081a0b..f405b5b0 100644 --- a/frontend/src/lib/components/AdventureModal.svelte +++ b/frontend/src/lib/components/AdventureModal.svelte @@ -13,7 +13,7 @@ import { addToast } from '$lib/toasts'; import { deserialize } from '$app/forms'; import { t } from 'svelte-i18n'; - + import ImmichLogo from '$lib/assets/immich.svg'; export let longitude: number | null = null; export let latitude: number | null = null; export let collection: Collection | null = null; @@ -180,31 +180,74 @@ } async function fetchImage() { - let res = await fetch(url); - let data = await res.blob(); - if (!data) { - imageError = $t('adventures.no_image_url'); - return; - } - let file = new File([data], 'image.jpg', { type: 'image/jpeg' }); - let formData = new FormData(); - formData.append('image', file); - formData.append('adventure', adventure.id); - let res2 = await fetch(`/adventures?/image`, { - method: 'POST', - body: formData - }); - let data2 = await res2.json(); - console.log(data2); - if (data2.type === 'success') { - images = [...images, data2]; - adventure.images = images; - addToast('success', $t('adventures.image_upload_success')); - } else { + try { + let res = await fetch(url); + let data = await res.blob(); + if (!data) { + imageError = $t('adventures.no_image_url'); + return; + } + let file = new File([data], 'image.jpg', { type: 'image/jpeg' }); + let formData = new FormData(); + formData.append('image', file); + formData.append('adventure', adventure.id); + + let res2 = await fetch(`/adventures?/image`, { + method: 'POST', + body: formData + }); + let data2 = await res2.json(); + + if (data2.type === 'success') { + console.log('Response Data:', data2); + + // Deserialize the nested data + let rawData = JSON.parse(data2.data); // Parse the data field + console.log('Deserialized Data:', rawData); + + // Assuming the first object in the array is the new image + let newImage = { + id: rawData[0].id, + image: rawData[2] // This is the URL for the image + }; + console.log('New Image:', newImage); + + // Update images and adventure + images = [...images, newImage]; + adventure.images = images; + + addToast('success', $t('adventures.image_upload_success')); + } else { + addToast('error', $t('adventures.image_upload_error')); + } + } catch (error) { + console.error('Error in fetchImage:', error); addToast('error', $t('adventures.image_upload_error')); } } + let immichSearchValue: string = ''; + let immichError: string = ''; + + async function searchImmich() { + let res = await fetch(`/api/integrations/immich/search/?query=${immichSearchValue}`); + if (!res.ok) { + let data = await res.json(); + let errorMessage = data.message; + console.log(errorMessage); + immichError = $t(data.code); + } else { + let data = await res.json(); + console.log(data); + immichError = ''; + if (data.results && data.results.length > 0) { + immichImages = data.results; + } else { + immichError = $t('immich.no_items_found'); + } + } + } + async function fetchWikiImage() { let res = await fetch(`/api/generate/img/?name=${imageSearch}`); let data = await res.json(); @@ -337,6 +380,9 @@ const dispatch = createEventDispatcher(); let modal: HTMLDialogElement; + let immichIntegration: boolean = false; + let immichImages: any[] = []; + onMount(async () => { modal = document.getElementById('my_modal_1') as HTMLDialogElement; modal.showModal(); @@ -347,6 +393,16 @@ } else { addToast('error', $t('adventures.category_fetch_error')); } + // Check for Immich Integration + let res = await fetch('/api/integrations'); + if (!res.ok) { + addToast('error', $t('immich.integration_fetch_error')); + } else { + let data = await res.json(); + if (data.immich) { + immichIntegration = true; + } + } }); function close() { @@ -915,10 +971,10 @@ it would also work to just use on:click on the MapLibre component itself. -->
    {:else} -

    {$t('adventures.upload_images_here')}

    +

    {$t('adventures.upload_images_here')}

    -
    -
    + +
    +

    + Immich Integration Immich Logo +

    +
    +

    + Integrate your Immich account with AdventureLog to allow you to search your photos library + and import photos for your adventures. +

    + {#if immichIntegration} +
    +
    Integration Enabled
    +
    + + +
    +
    + {/if} + {#if !immichIntegration || newImmichIntegration.id} +
    +
    + + + {#if newImmichIntegration.server_url && !newImmichIntegration.server_url.endsWith('api')} +

    + Note: this must be the URL to the Immich API server so it likely ends with /api + unless you have a custom config. +

    + {/if} +
    +
    + + +
    + +
    + {/if} +
    +
    +

    {$t('adventures.visited_region_check')}

    From 81b60d60212d71b2fb97abfc652eaac7e38205cc Mon Sep 17 00:00:00 2001 From: Sean Morley Date: Thu, 2 Jan 2025 13:40:02 -0500 Subject: [PATCH 009/209] feat: enhance settings page with text-neutral styling for labels and messages --- frontend/src/routes/settings/+page.svelte | 44 +++++++++++++++-------- 1 file changed, 30 insertions(+), 14 deletions(-) diff --git a/frontend/src/routes/settings/+page.svelte b/frontend/src/routes/settings/+page.svelte index aa3bc08c..800084a2 100644 --- a/frontend/src/routes/settings/+page.svelte +++ b/frontend/src/routes/settings/+page.svelte @@ -232,7 +232,9 @@ class="space-y-6" >
    - +
    - +
    - +
    - + - +
    @@ -298,7 +308,7 @@
    -
    - +
    - {/each} {#if emails.length === 0} -

    {$t('settings.no_email_set')}

    +

    {$t('settings.no_email_set')}

    {/if}
    @@ -400,7 +412,7 @@
    {#if !data.props.authenticators} -

    {$t('settings.mfa_not_enabled')}

    +

    {$t('settings.mfa_not_enabled')}

    @@ -422,7 +434,7 @@ />
    -

    +

    Integrate your Immich account with AdventureLog to allow you to search your photos library and import photos for your adventures.

    @@ -443,7 +455,9 @@ {#if !immichIntegration || newImmichIntegration.id}
    - + {#if newImmichIntegration.server_url && !newImmichIntegration.server_url.endsWith('api')} -

    +

    Note: this must be the URL to the Immich API server so it likely ends with /api unless you have a custom config.

    {/if}
    - + Date: Thu, 2 Jan 2025 17:56:47 -0500 Subject: [PATCH 010/209] feat: add Immich album retrieval functionality and implement album selection component --- backend/server/integrations/views.py | 82 +++++++++ .../src/lib/components/AdventureModal.svelte | 120 +------------ .../src/lib/components/ImmichSelect.svelte | 159 ++++++++++++++++++ frontend/src/lib/types.ts | 28 +++ 4 files changed, 278 insertions(+), 111 deletions(-) create mode 100644 frontend/src/lib/components/ImmichSelect.svelte diff --git a/backend/server/integrations/views.py b/backend/server/integrations/views.py index 673fad5a..935e28c2 100644 --- a/backend/server/integrations/views.py +++ b/backend/server/integrations/views.py @@ -150,6 +150,88 @@ class ImmichIntegrationView(viewsets.ViewSet): }, status=status.HTTP_503_SERVICE_UNAVAILABLE ) + + @action(detail=False, methods=['get']) + def albums(self, request): + """ + RESTful GET method for retrieving all Immich albums. + """ + # Check for integration before proceeding + integration = self.check_integration(request) + if isinstance(integration, Response): + return integration + + # check so if the server is down, it does not tweak out like a madman and crash the server with a 500 error code + try: + immich_fetch = requests.get(f'{integration.server_url}/albums', headers={ + 'x-api-key': integration.api_key + }) + res = immich_fetch.json() + except requests.exceptions.ConnectionError: + return Response( + { + 'message': 'The Immich server is currently down or unreachable.', + 'error': True, + 'code': 'immich.server_down' + }, + status=status.HTTP_503_SERVICE_UNAVAILABLE + ) + + return Response( + res, + status=status.HTTP_200_OK + ) + + @action(detail=False, methods=['get'], url_path='albums/(?P[^/.]+)') + def album(self, request, albumid=None): + """ + RESTful GET method for retrieving a specific Immich album by ID. + """ + # Check for integration before proceeding + integration = self.check_integration(request) + if isinstance(integration, Response): + return integration + + if not albumid: + return Response( + { + 'message': 'Album ID is required.', + 'error': True, + 'code': 'immich.albumid_required' + }, + status=status.HTTP_400_BAD_REQUEST + ) + + # check so if the server is down, it does not tweak out like a madman and crash the server with a 500 error code + try: + immich_fetch = requests.get(f'{integration.server_url}/albums/{albumid}', headers={ + 'x-api-key': integration.api_key + }) + res = immich_fetch.json() + except requests.exceptions.ConnectionError: + return Response( + { + 'message': 'The Immich server is currently down or unreachable.', + 'error': True, + 'code': 'immich.server_down' + }, + status=status.HTTP_503_SERVICE_UNAVAILABLE + ) + + if 'assets' in res: + return Response( + res['assets'], + status=status.HTTP_200_OK + ) + else: + return Response( + { + 'message': 'No assets found in this album.', + 'error': True, + 'code': 'immich.no_assets_found' + }, + status=status.HTTP_404_NOT_FOUND + ) class ImmichIntegrationViewSet(viewsets.ModelViewSet): permission_classes = [IsAuthenticated] diff --git a/frontend/src/lib/components/AdventureModal.svelte b/frontend/src/lib/components/AdventureModal.svelte index d5e4d890..550c4194 100644 --- a/frontend/src/lib/components/AdventureModal.svelte +++ b/frontend/src/lib/components/AdventureModal.svelte @@ -13,7 +13,6 @@ import { addToast } from '$lib/toasts'; import { deserialize } from '$app/forms'; import { t } from 'svelte-i18n'; - import ImmichLogo from '$lib/assets/immich.svg'; export let longitude: number | null = null; export let latitude: number | null = null; export let collection: Collection | null = null; @@ -33,6 +32,7 @@ import CategoryDropdown from './CategoryDropdown.svelte'; import { findFirstValue } from '$lib'; import MarkdownEditor from './MarkdownEditor.svelte'; + import ImmichSelect from './ImmichSelect.svelte'; let wikiError: string = ''; @@ -207,7 +207,7 @@ // Assuming the first object in the array is the new image let newImage = { - id: rawData[0].id, + id: rawData[1], image: rawData[2] // This is the URL for the image }; console.log('New Image:', newImage); @@ -217,6 +217,7 @@ adventure.images = images; addToast('success', $t('adventures.image_upload_success')); + url = ''; } else { addToast('error', $t('adventures.image_upload_error')); } @@ -226,68 +227,6 @@ } } - let immichSearchValue: string = ''; - let immichError: string = ''; - let immichNext: string = ''; - let immichPage: number = 1; - - async function searchImmich() { - let res = await fetch(`/api/integrations/immich/search/?query=${immichSearchValue}`); - if (!res.ok) { - let data = await res.json(); - let errorMessage = data.message; - console.log(errorMessage); - immichError = $t(data.code); - } else { - let data = await res.json(); - console.log(data); - immichError = ''; - if (data.results && data.results.length > 0) { - immichImages = data.results; - } else { - immichError = $t('immich.no_items_found'); - } - if (data.next) { - immichNext = - '/api/integrations/immich/search?query=' + - immichSearchValue + - '&page=' + - (immichPage + 1); - } else { - immichNext = ''; - } - } - } - - async function loadMoreImmich() { - let res = await fetch(immichNext); - if (!res.ok) { - let data = await res.json(); - let errorMessage = data.message; - console.log(errorMessage); - immichError = $t(data.code); - } else { - let data = await res.json(); - console.log(data); - immichError = ''; - if (data.results && data.results.length > 0) { - immichImages = [...immichImages, ...data.results]; - } else { - immichError = $t('immich.no_items_found'); - } - if (data.next) { - immichNext = - '/api/integrations/immich/search?query=' + - immichSearchValue + - '&page=' + - (immichPage + 1); - immichPage++; - } else { - immichNext = ''; - } - } - } - async function fetchWikiImage() { let res = await fetch(`/api/generate/img/?name=${imageSearch}`); let data = await res.json(); @@ -421,7 +360,6 @@ let modal: HTMLDialogElement; let immichIntegration: boolean = false; - let immichImages: any[] = []; onMount(async () => { modal = document.getElementById('my_modal_1') as HTMLDialogElement; @@ -1078,52 +1016,12 @@ it would also work to just use on:click on the MapLibre component itself. -->
    {#if immichIntegration} -
    - - -
    - - -
    -

    {immichError}

    -
    - {#each immichImages as image} -
    - - Image from Immich - -
    - {/each} - {#if immichNext} - - {/if} -
    -
    + { + url = e.detail; + fetchImage(); + }} + /> {/if}
    diff --git a/frontend/src/lib/components/ImmichSelect.svelte b/frontend/src/lib/components/ImmichSelect.svelte new file mode 100644 index 00000000..d5602b9d --- /dev/null +++ b/frontend/src/lib/components/ImmichSelect.svelte @@ -0,0 +1,159 @@ + + +
    + +
    + + + {#if searchOrSelect === 'search'} + + + {:else} + + {/if} +
    + +

    {immichError}

    +
    + {#each immichImages as image} +
    + + Image from Immich + +
    + {/each} + {#if immichNext} + + {/if} +
    +
    diff --git a/frontend/src/lib/types.ts b/frontend/src/lib/types.ts index 75cee6e4..a2e6d3ca 100644 --- a/frontend/src/lib/types.ts +++ b/frontend/src/lib/types.ts @@ -202,3 +202,31 @@ export type ImmichIntegration = { server_url: string; api_key: string; }; + +export type ImmichAlbum = { + albumName: string; + description: string; + albumThumbnailAssetId: string; + createdAt: string; + updatedAt: string; + id: string; + ownerId: string; + owner: { + id: string; + email: string; + name: string; + profileImagePath: string; + avatarColor: string; + profileChangedAt: string; + }; + albumUsers: any[]; + shared: boolean; + hasSharedLink: boolean; + startDate: string; + endDate: string; + assets: any[]; + assetCount: number; + isActivityEnabled: boolean; + order: string; + lastModifiedAssetTimestamp: string; +}; From eea87e59a5abafb95585584d1ff79913c4e6d158 Mon Sep 17 00:00:00 2001 From: Sean Morley Date: Thu, 2 Jan 2025 18:34:13 -0500 Subject: [PATCH 011/209] feat: update Immich integration migrations and enhance localization strings for user feedback --- .../integrations/migrations/0001_initial.py | 7 +-- .../0002_alter_immichintegration_user.py | 21 ------- .../0003_alter_immichintegration_user.py | 21 ------- .../src/lib/components/ImmichSelect.svelte | 58 ++++++++++++------- frontend/src/locales/de.json | 26 ++++++++- frontend/src/locales/en.json | 15 ++++- frontend/src/locales/es.json | 26 ++++++++- frontend/src/locales/fr.json | 26 ++++++++- frontend/src/locales/it.json | 26 ++++++++- frontend/src/locales/nl.json | 26 ++++++++- frontend/src/locales/pl.json | 26 ++++++++- frontend/src/locales/sv.json | 27 ++++++++- frontend/src/locales/zh.json | 26 ++++++++- frontend/src/routes/settings/+page.svelte | 43 +++++++------- 14 files changed, 275 insertions(+), 99 deletions(-) delete mode 100644 backend/server/integrations/migrations/0002_alter_immichintegration_user.py delete mode 100644 backend/server/integrations/migrations/0003_alter_immichintegration_user.py diff --git a/backend/server/integrations/migrations/0001_initial.py b/backend/server/integrations/migrations/0001_initial.py index 0b05b2a5..1bf029b3 100644 --- a/backend/server/integrations/migrations/0001_initial.py +++ b/backend/server/integrations/migrations/0001_initial.py @@ -1,7 +1,7 @@ -# Generated by Django 5.0.8 on 2024-12-31 15:02 +# Generated by Django 5.0.8 on 2025-01-02 23:16 -import uuid import django.db.models.deletion +import uuid from django.conf import settings from django.db import migrations, models @@ -18,11 +18,10 @@ class Migration(migrations.Migration): migrations.CreateModel( name='ImmichIntegration', fields=[ - ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)), ('server_url', models.CharField(max_length=255)), ('api_key', models.CharField(max_length=255)), + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)), ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), ], ), ] - diff --git a/backend/server/integrations/migrations/0002_alter_immichintegration_user.py b/backend/server/integrations/migrations/0002_alter_immichintegration_user.py deleted file mode 100644 index 6f6912b6..00000000 --- a/backend/server/integrations/migrations/0002_alter_immichintegration_user.py +++ /dev/null @@ -1,21 +0,0 @@ -# Generated by Django 5.0.8 on 2024-12-31 18:29 - -import django.db.models.deletion -from django.conf import settings -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('integrations', '0001_initial'), - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ] - - operations = [ - migrations.AlterField( - model_name='immichintegration', - name='user', - field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), - ), - ] diff --git a/backend/server/integrations/migrations/0003_alter_immichintegration_user.py b/backend/server/integrations/migrations/0003_alter_immichintegration_user.py deleted file mode 100644 index 30bd443f..00000000 --- a/backend/server/integrations/migrations/0003_alter_immichintegration_user.py +++ /dev/null @@ -1,21 +0,0 @@ -# Generated by Django 5.0.8 on 2025-01-02 17:50 - -import django.db.models.deletion -from django.conf import settings -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('integrations', '0002_alter_immichintegration_user'), - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ] - - operations = [ - migrations.AlterField( - model_name='immichintegration', - name='user', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), - ), - ] diff --git a/frontend/src/lib/components/ImmichSelect.svelte b/frontend/src/lib/components/ImmichSelect.svelte index d5602b9d..8668f6ff 100644 --- a/frontend/src/lib/components/ImmichSelect.svelte +++ b/frontend/src/lib/components/ImmichSelect.svelte @@ -13,6 +13,7 @@ $: { if (currentAlbum) { + immichImages = []; fetchAlbumAssets(currentAlbum); } else { immichImages = []; @@ -104,29 +105,44 @@ {$t('immich.immich')} Immich Logo -
    - - - {#if searchOrSelect === 'search'} +
    +
    (currentAlbum = '')} + type="radio" + class="join-item btn" + bind:group={searchOrSelect} + value="search" + aria-label="Search" /> - - {:else} - - {/if} + +
    +
    + {#if searchOrSelect === 'search'} + + + + + {:else} + + {/if} +

    {immichError}

    diff --git a/frontend/src/locales/de.json b/frontend/src/locales/de.json index c2485cac..09043920 100644 --- a/frontend/src/locales/de.json +++ b/frontend/src/locales/de.json @@ -215,7 +215,8 @@ "start": "Start", "starting_airport": "Startflughafen", "to": "Zu", - "transportation_delete_confirm": "Sind Sie sicher, dass Sie diesen Transport löschen möchten? \nDiese Aktion kann nicht rückgängig gemacht werden." + "transportation_delete_confirm": "Sind Sie sicher, dass Sie diesen Transport löschen möchten? \nDiese Aktion kann nicht rückgängig gemacht werden.", + "show_map": "Karte anzeigen" }, "home": { "desc_1": "Entdecken, planen und erkunden Sie mit Leichtigkeit", @@ -500,5 +501,28 @@ "total_adventures": "Totale Abenteuer", "total_visited_regions": "Insgesamt besuchte Regionen", "welcome_back": "Willkommen zurück" + }, + "immich": { + "api_key": "Immich-API-Schlüssel", + "api_note": "Hinweis: Dies muss die URL zum Immich-API-Server sein, daher endet sie wahrscheinlich mit /api, es sei denn, Sie haben eine benutzerdefinierte Konfiguration.", + "disable": "Deaktivieren", + "enable_immich": "Immich aktivieren", + "imageid_required": "Bild-ID ist erforderlich", + "immich": "Immich", + "immich_desc": "Integrieren Sie Ihr Immich-Konto mit AdventureLog, damit Sie Ihre Fotobibliothek durchsuchen und Fotos für Ihre Abenteuer importieren können.", + "immich_disabled": "Immich-Integration erfolgreich deaktiviert!", + "immich_enabled": "Immich-Integration erfolgreich aktiviert!", + "immich_error": "Fehler beim Aktualisieren der Immich-Integration", + "immich_updated": "Immich-Einstellungen erfolgreich aktualisiert!", + "integration_enabled": "Integration aktiviert", + "integration_fetch_error": "Fehler beim Abrufen der Daten aus der Immich-Integration", + "integration_missing": "Im Backend fehlt die Immich-Integration", + "load_more": "Mehr laden", + "no_items_found": "Keine Artikel gefunden", + "query_required": "Abfrage ist erforderlich", + "server_down": "Der Immich-Server ist derzeit ausgefallen oder nicht erreichbar", + "server_url": "Immich-Server-URL", + "update_integration": "Update-Integration", + "immich_integration": "Immich-Integration" } } diff --git a/frontend/src/locales/en.json b/frontend/src/locales/en.json index a065e79c..2acebc9e 100644 --- a/frontend/src/locales/en.json +++ b/frontend/src/locales/en.json @@ -510,6 +510,19 @@ "server_down": "The Immich server is currently down or unreachable", "no_items_found": "No items found", "imageid_required": "Image ID is required", - "load_more": "Load More" + "load_more": "Load More", + "immich_updated": "Immich settings updated successfully!", + "immich_enabled": "Immich integration enabled successfully!", + "immich_error": "Error updating Immich integration", + "immich_disabled": "Immich integration disabled successfully!", + "immich_desc": "Integrate your Immich account with AdventureLog to allow you to search your photos library and import photos for your adventures.", + "integration_enabled": "Integration Enabled", + "disable": "Disable", + "server_url": "Immich Server URL", + "api_note": "Note: this must be the URL to the Immich API server so it likely ends with /api unless you have a custom config.", + "api_key": "Immich API Key", + "enable_immich": "Enable Immich", + "update_integration": "Update Integration", + "immich_integration": "Immich Integration" } } diff --git a/frontend/src/locales/es.json b/frontend/src/locales/es.json index 1164d25e..aa23a71b 100644 --- a/frontend/src/locales/es.json +++ b/frontend/src/locales/es.json @@ -262,7 +262,8 @@ "start": "Comenzar", "starting_airport": "Aeropuerto de inicio", "to": "A", - "transportation_delete_confirm": "¿Está seguro de que desea eliminar este transporte? \nEsta acción no se puede deshacer." + "transportation_delete_confirm": "¿Está seguro de que desea eliminar este transporte? \nEsta acción no se puede deshacer.", + "show_map": "Mostrar mapa" }, "worldtravel": { "all": "Todo", @@ -500,5 +501,28 @@ "total_adventures": "Aventuras totales", "total_visited_regions": "Total de regiones visitadas", "welcome_back": "Bienvenido de nuevo" + }, + "immich": { + "api_key": "Clave API de Immich", + "api_note": "Nota: esta debe ser la URL del servidor API de Immich, por lo que probablemente termine con /api a menos que tenga una configuración personalizada.", + "disable": "Desactivar", + "enable_immich": "Habilitar Immich", + "imageid_required": "Se requiere identificación con imagen", + "immich": "immicha", + "immich_desc": "Integre su cuenta de Immich con AdventureLog para permitirle buscar en su biblioteca de fotos e importar fotos para sus aventuras.", + "immich_disabled": "¡La integración de Immich se deshabilitó exitosamente!", + "immich_enabled": "¡La integración de Immich se habilitó exitosamente!", + "immich_error": "Error al actualizar la integración de Immich", + "immich_updated": "¡La configuración de Immich se actualizó exitosamente!", + "integration_enabled": "Integración habilitada", + "integration_fetch_error": "Error al obtener datos de la integración de Immich", + "integration_missing": "Falta la integración de Immich en el backend", + "load_more": "Cargar más", + "no_items_found": "No se encontraron artículos", + "query_required": "Se requiere consulta", + "server_down": "El servidor Immich está actualmente inactivo o inaccesible", + "server_url": "URL del servidor Immich", + "update_integration": "Integración de actualización", + "immich_integration": "Integración Immich" } } diff --git a/frontend/src/locales/fr.json b/frontend/src/locales/fr.json index c66bf036..eaa8842e 100644 --- a/frontend/src/locales/fr.json +++ b/frontend/src/locales/fr.json @@ -215,7 +215,8 @@ "start": "Commencer", "starting_airport": "Aéroport de départ", "to": "À", - "transportation_delete_confirm": "Etes-vous sûr de vouloir supprimer ce transport ? \nCette action ne peut pas être annulée." + "transportation_delete_confirm": "Etes-vous sûr de vouloir supprimer ce transport ? \nCette action ne peut pas être annulée.", + "show_map": "Afficher la carte" }, "home": { "desc_1": "Découvrez, planifiez et explorez en toute simplicité", @@ -500,5 +501,28 @@ "total_adventures": "Aventures totales", "total_visited_regions": "Total des régions visitées", "welcome_back": "Content de te revoir" + }, + "immich": { + "api_key": "Clé API Immich", + "api_note": "Remarque : il doit s'agir de l'URL du serveur API Immich, elle se termine donc probablement par /api, sauf si vous disposez d'une configuration personnalisée.", + "disable": "Désactiver", + "enable_immich": "Activer Immich", + "imageid_required": "L'identifiant de l'image est requis", + "immich": "Immich", + "immich_desc": "Intégrez votre compte Immich à AdventureLog pour vous permettre de rechercher dans votre bibliothèque de photos et d'importer des photos pour vos aventures.", + "immich_disabled": "Intégration Immich désactivée avec succès !", + "immich_enabled": "Intégration Immich activée avec succès !", + "immich_error": "Erreur lors de la mise à jour de l'intégration Immich", + "immich_integration": "Intégration Immich", + "immich_updated": "Paramètres Immich mis à jour avec succès !", + "integration_enabled": "Intégration activée", + "integration_fetch_error": "Erreur lors de la récupération des données de l'intégration Immich", + "integration_missing": "L'intégration Immich est absente du backend", + "load_more": "Charger plus", + "no_items_found": "Aucun article trouvé", + "query_required": "La requête est obligatoire", + "server_down": "Le serveur Immich est actuellement en panne ou inaccessible", + "server_url": "URL du serveur Immich", + "update_integration": "Intégration des mises à jour" } } diff --git a/frontend/src/locales/it.json b/frontend/src/locales/it.json index 21bee779..0b880a93 100644 --- a/frontend/src/locales/it.json +++ b/frontend/src/locales/it.json @@ -215,7 +215,8 @@ "start": "Inizio", "starting_airport": "Inizio aeroporto", "to": "A", - "transportation_delete_confirm": "Sei sicuro di voler eliminare questo trasporto? \nQuesta azione non può essere annullata." + "transportation_delete_confirm": "Sei sicuro di voler eliminare questo trasporto? \nQuesta azione non può essere annullata.", + "show_map": "Mostra mappa" }, "home": { "desc_1": "Scopri, pianifica ed esplora con facilità", @@ -500,5 +501,28 @@ "total_adventures": "Avventure totali", "total_visited_regions": "Totale regioni visitate", "welcome_back": "Bentornato" + }, + "immich": { + "api_key": "Chiave API Immich", + "api_note": "Nota: questo deve essere l'URL del server API Immich, quindi probabilmente termina con /api a meno che tu non abbia una configurazione personalizzata.", + "disable": "Disabilita", + "enable_immich": "Abilita Immich", + "imageid_required": "L'ID immagine è obbligatorio", + "immich": "Immich", + "immich_desc": "Integra il tuo account Immich con AdventureLog per consentirti di cercare nella tua libreria di foto e importare foto per le tue avventure.", + "immich_disabled": "Integrazione Immich disabilitata con successo!", + "immich_enabled": "Integrazione Immich abilitata con successo!", + "immich_error": "Errore durante l'aggiornamento dell'integrazione Immich", + "immich_integration": "Integrazione di Immich", + "immich_updated": "Impostazioni Immich aggiornate con successo!", + "integration_enabled": "Integrazione abilitata", + "integration_fetch_error": "Errore durante il recupero dei dati dall'integrazione Immich", + "integration_missing": "L'integrazione Immich manca dal backend", + "load_more": "Carica altro", + "no_items_found": "Nessun articolo trovato", + "query_required": "La domanda è obbligatoria", + "server_down": "Il server Immich è attualmente inattivo o irraggiungibile", + "server_url": "URL del server Immich", + "update_integration": "Aggiorna integrazione" } } diff --git a/frontend/src/locales/nl.json b/frontend/src/locales/nl.json index c2a59a57..f00fac4d 100644 --- a/frontend/src/locales/nl.json +++ b/frontend/src/locales/nl.json @@ -215,7 +215,8 @@ "starting_airport": "Startende luchthaven", "to": "Naar", "transportation_delete_confirm": "Weet u zeker dat u dit transport wilt verwijderen? \nDeze actie kan niet ongedaan worden gemaakt.", - "ending_airport": "Einde luchthaven" + "ending_airport": "Einde luchthaven", + "show_map": "Toon kaart" }, "home": { "desc_1": "Ontdek, plan en verken met gemak", @@ -500,5 +501,28 @@ "total_adventures": "Totale avonturen", "total_visited_regions": "Totaal bezochte regio's", "welcome_back": "Welkom terug" + }, + "immich": { + "api_key": "Immich API-sleutel", + "api_note": "Let op: dit moet de URL naar de Immich API-server zijn, dus deze eindigt waarschijnlijk op /api, tenzij je een aangepaste configuratie hebt.", + "disable": "Uitzetten", + "enable_immich": "Schakel Immich in", + "imageid_required": "Afbeeldings-ID is vereist", + "immich": "Immich", + "immich_desc": "Integreer uw Immich-account met AdventureLog zodat u in uw fotobibliotheek kunt zoeken en foto's voor uw avonturen kunt importeren.", + "immich_disabled": "Immich-integratie succesvol uitgeschakeld!", + "immich_enabled": "Immich-integratie succesvol ingeschakeld!", + "immich_error": "Fout bij updaten van Immich-integratie", + "immich_integration": "Immich-integratie", + "immich_updated": "Immich-instellingen zijn succesvol bijgewerkt!", + "integration_enabled": "Integratie ingeschakeld", + "integration_fetch_error": "Fout bij het ophalen van gegevens uit de Immich-integratie", + "integration_missing": "De Immich-integratie ontbreekt in de backend", + "load_more": "Laad meer", + "no_items_found": "Geen artikelen gevonden", + "query_required": "Er is een zoekopdracht vereist", + "server_down": "De Immich-server is momenteel offline of onbereikbaar", + "server_url": "Immich-server-URL", + "update_integration": "Integratie bijwerken" } } diff --git a/frontend/src/locales/pl.json b/frontend/src/locales/pl.json index eac96345..b9a13cfb 100644 --- a/frontend/src/locales/pl.json +++ b/frontend/src/locales/pl.json @@ -262,7 +262,8 @@ "start": "Start", "starting_airport": "Początkowe lotnisko", "to": "Do", - "transportation_delete_confirm": "Czy na pewno chcesz usunąć ten transport? \nTej akcji nie można cofnąć." + "transportation_delete_confirm": "Czy na pewno chcesz usunąć ten transport? \nTej akcji nie można cofnąć.", + "show_map": "Pokaż mapę" }, "worldtravel": { "country_list": "Lista krajów", @@ -500,5 +501,28 @@ "total_adventures": "Totalne przygody", "total_visited_regions": "Łączna liczba odwiedzonych regionów", "welcome_back": "Witamy z powrotem" + }, + "immich": { + "api_key": "Klucz API Immicha", + "api_note": "Uwaga: musi to być adres URL serwera API Immich, więc prawdopodobnie kończy się na /api, chyba że masz niestandardową konfigurację.", + "disable": "Wyłączyć", + "enable_immich": "Włącz Immicha", + "immich": "Immich", + "immich_enabled": "Integracja z Immich została pomyślnie włączona!", + "immich_error": "Błąd podczas aktualizacji integracji Immich", + "immich_integration": "Integracja Immicha", + "immich_updated": "Ustawienia Immich zostały pomyślnie zaktualizowane!", + "integration_enabled": "Integracja włączona", + "integration_fetch_error": "Błąd podczas pobierania danych z integracji Immich", + "integration_missing": "W backendie brakuje integracji z Immich", + "load_more": "Załaduj więcej", + "no_items_found": "Nie znaleziono żadnych elementów", + "query_required": "Zapytanie jest wymagane", + "server_down": "Serwer Immich jest obecnie wyłączony lub nieosiągalny", + "server_url": "Adres URL serwera Immich", + "update_integration": "Zaktualizuj integrację", + "imageid_required": "Wymagany jest identyfikator obrazu", + "immich_desc": "Zintegruj swoje konto Immich z AdventureLog, aby móc przeszukiwać bibliotekę zdjęć i importować zdjęcia do swoich przygód.", + "immich_disabled": "Integracja z Immich została pomyślnie wyłączona!" } } diff --git a/frontend/src/locales/sv.json b/frontend/src/locales/sv.json index 7beccea6..a4c443ca 100644 --- a/frontend/src/locales/sv.json +++ b/frontend/src/locales/sv.json @@ -1,5 +1,4 @@ { - "about": { "about": "Om", "close": "Stäng", @@ -216,7 +215,8 @@ "start": "Start", "starting_airport": "Startar flygplats", "to": "Till", - "transportation_delete_confirm": "Är du säker på att du vill ta bort denna transport? \nDenna åtgärd kan inte ångras." + "transportation_delete_confirm": "Är du säker på att du vill ta bort denna transport? \nDenna åtgärd kan inte ångras.", + "show_map": "Visa karta" }, "home": { "desc_1": "Upptäck, planera och utforska med lätthet", @@ -501,5 +501,28 @@ "total_adventures": "Totala äventyr", "total_visited_regions": "Totalt antal besökta regioner", "welcome_back": "Välkommen tillbaka" + }, + "immich": { + "api_key": "Immich API-nyckel", + "api_note": "Obs: detta måste vara URL:en till Immich API-servern så den slutar troligen med /api om du inte har en anpassad konfiguration.", + "disable": "Inaktivera", + "enable_immich": "Aktivera Immich", + "imageid_required": "Bild-ID krävs", + "immich": "Immich", + "immich_desc": "Integrera ditt Immich-konto med AdventureLog så att du kan söka i ditt fotobibliotek och importera bilder för dina äventyr.", + "immich_disabled": "Immich-integrationen inaktiverades framgångsrikt!", + "immich_enabled": "Immich-integrationen har aktiverats framgångsrikt!", + "immich_error": "Fel vid uppdatering av Immich-integration", + "immich_integration": "Immich Integration", + "immich_updated": "Immich-inställningarna har uppdaterats framgångsrikt!", + "integration_enabled": "Integration aktiverad", + "integration_fetch_error": "Fel vid hämtning av data från Immich-integrationen", + "integration_missing": "Immich-integrationen saknas i backend", + "load_more": "Ladda mer", + "no_items_found": "Inga föremål hittades", + "query_required": "Fråga krävs", + "server_down": "Immich-servern är för närvarande nere eller kan inte nås", + "server_url": "Immich Server URL", + "update_integration": "Uppdatera integration" } } diff --git a/frontend/src/locales/zh.json b/frontend/src/locales/zh.json index 0a686a2a..4444be97 100644 --- a/frontend/src/locales/zh.json +++ b/frontend/src/locales/zh.json @@ -215,7 +215,8 @@ "start": "开始", "starting_airport": "出发机场", "to": "到", - "transportation_delete_confirm": "您确定要删除此交通工具吗?\n此操作无法撤消。" + "transportation_delete_confirm": "您确定要删除此交通工具吗?\n此操作无法撤消。", + "show_map": "显示地图" }, "home": { "desc_1": "轻松发现、规划和探索", @@ -500,5 +501,28 @@ "total_adventures": "全面冒险", "total_visited_regions": "总访问地区", "welcome_back": "欢迎回来" + }, + "immich": { + "api_key": "伊米奇 API 密钥", + "api_note": "注意:这必须是 Immich API 服务器的 URL,因此它可能以 /api 结尾,除非您有自定义配置。", + "disable": "禁用", + "enable_immich": "启用伊米奇", + "imageid_required": "需要图像 ID", + "immich": "伊米奇", + "immich_desc": "将您的 Immich 帐户与 AdventureLog 集成,以便您搜索照片库并导入冒险照片。", + "immich_disabled": "Immich 集成成功禁用!", + "immich_enabled": "Immich 集成成功启用!", + "immich_error": "更新 Immich 集成时出错", + "immich_integration": "伊米奇整合", + "immich_updated": "Immich 设置更新成功!", + "integration_enabled": "启用集成", + "integration_fetch_error": "从 Immich 集成获取数据时出错", + "integration_missing": "后端缺少 Immich 集成", + "load_more": "加载更多", + "no_items_found": "没有找到物品", + "query_required": "需要查询", + "server_down": "Immich 服务器当前已关闭或无法访问", + "server_url": "伊米奇服务器网址", + "update_integration": "更新集成" } } diff --git a/frontend/src/routes/settings/+page.svelte b/frontend/src/routes/settings/+page.svelte index 800084a2..5b2c305d 100644 --- a/frontend/src/routes/settings/+page.svelte +++ b/frontend/src/routes/settings/+page.svelte @@ -152,10 +152,10 @@ }); let data = await res.json(); if (res.ok) { - addToast('success', $t('settings.immich_enabled')); + addToast('success', $t('immich.immich_enabled')); immichIntegration = data; } else { - addToast('error', $t('settings.immich_error')); + addToast('error', $t('immich.immich_error')); } } else { let res = await fetch(`/api/integrations/immich/${immichIntegration.id}/`, { @@ -167,10 +167,10 @@ }); let data = await res.json(); if (res.ok) { - addToast('success', $t('settings.immich_updated')); + addToast('success', $t('immich.immich_updated')); immichIntegration = data; } else { - addToast('error', $t('settings.immich_error')); + addToast('error', $t('immich.immich_error')); } } } @@ -181,10 +181,10 @@ method: 'DELETE' }); if (res.ok) { - addToast('success', $t('settings.immich_disabled')); + addToast('success', $t('immich.immich_disabled')); immichIntegration = null; } else { - addToast('error', $t('settings.immich_error')); + addToast('error', $t('immich.immich_error')); } } } @@ -427,20 +427,16 @@

    - Immich Integration Immich Logo + {$t('immich.immich_integration')} + Immich

    - Integrate your Immich account with AdventureLog to allow you to search your photos library - and import photos for your adventures. + {$t('immich.immich_desc')}

    {#if immichIntegration}
    -
    Integration Enabled
    +
    {$t('immich.integration_enabled')}
    - +
    {/if} @@ -456,38 +454,39 @@
    {$t('immich.server_url')} {#if newImmichIntegration.server_url && !newImmichIntegration.server_url.endsWith('api')}

    - Note: this must be the URL to the Immich API server so it likely ends with /api - unless you have a custom config. + {$t('immich.api_note')}

    {/if}
    {$t('immich.api_key')}
    {!immichIntegration?.id + ? $t('immich.enable_immich') + : $t('immich.update_integration')}
    {/if} From 73036dcef849d90525019334c0de3fb31a6aad7a Mon Sep 17 00:00:00 2001 From: Sean Morley Date: Thu, 2 Jan 2025 18:38:29 -0500 Subject: [PATCH 012/209] fix: put languages in locale selection dropdown into native language --- frontend/src/lib/components/Navbar.svelte | 16 ++++++++++++++-- frontend/src/locales/de.json | 12 +----------- frontend/src/locales/en.json | 12 +----------- frontend/src/locales/es.json | 12 +----------- frontend/src/locales/fr.json | 12 +----------- frontend/src/locales/it.json | 12 +----------- frontend/src/locales/nl.json | 12 +----------- frontend/src/locales/pl.json | 12 +----------- frontend/src/locales/sv.json | 12 +----------- frontend/src/locales/zh.json | 12 +----------- 10 files changed, 23 insertions(+), 101 deletions(-) diff --git a/frontend/src/lib/components/Navbar.svelte b/frontend/src/lib/components/Navbar.svelte index fbe83374..bbfbaf5f 100644 --- a/frontend/src/lib/components/Navbar.svelte +++ b/frontend/src/lib/components/Navbar.svelte @@ -13,6 +13,18 @@ import { t, locale, locales } from 'svelte-i18n'; import { themes } from '$lib'; + let languages: { [key: string]: string } = { + en: 'English', + de: 'Deutsch', + es: 'Español', + fr: 'Français', + it: 'Italiano', + nl: 'Nederlands', + sv: 'Svenska', + zh: '中文', + pl: 'Polski' + }; + let query: string = ''; let isAboutModalOpen: boolean = false; @@ -236,8 +248,8 @@ on:change={submitLocaleChange} bind:value={$locale} > - {#each $locales as loc} - + {#each $locales as loc (loc)} + {/each} diff --git a/frontend/src/locales/de.json b/frontend/src/locales/de.json index 09043920..a8edb0d6 100644 --- a/frontend/src/locales/de.json +++ b/frontend/src/locales/de.json @@ -455,17 +455,7 @@ "show_visited_regions": "Besuchte Regionen anzeigen", "view_details": "Details anzeigen" }, - "languages": { - "de": "Deutsch", - "en": "Englisch", - "es": "Spanisch", - "fr": "Französisch", - "it": "Italienisch", - "nl": "Niederländisch", - "sv": "Schwedisch", - "zh": "chinesisch", - "pl": "Polnisch" - }, + "languages": {}, "share": { "no_users_shared": "Keine Benutzer geteilt mit", "not_shared_with": "Nicht geteilt mit", diff --git a/frontend/src/locales/en.json b/frontend/src/locales/en.json index 2acebc9e..d64bdaa2 100644 --- a/frontend/src/locales/en.json +++ b/frontend/src/locales/en.json @@ -467,17 +467,7 @@ "set_public": "In order to allow users to share with you, you need your profile set to public.", "go_to_settings": "Go to settings" }, - "languages": { - "en": "English", - "de": "German", - "es": "Spanish", - "fr": "French", - "it": "Italian", - "nl": "Dutch", - "sv": "Swedish", - "zh": "Chinese", - "pl": "Polish" - }, + "languages": {}, "profile": { "member_since": "Member since", "user_stats": "User Stats", diff --git a/frontend/src/locales/es.json b/frontend/src/locales/es.json index aa23a71b..a66b5ca0 100644 --- a/frontend/src/locales/es.json +++ b/frontend/src/locales/es.json @@ -467,17 +467,7 @@ "no_shared_found": "No se encontraron colecciones que se compartan contigo.", "set_public": "Para permitir que los usuarios compartan contenido con usted, necesita que su perfil esté configurado como público." }, - "languages": { - "de": "Alemán", - "en": "Inglés", - "es": "Español", - "fr": "Francés", - "it": "italiano", - "nl": "Holandés", - "sv": "sueco", - "zh": "Chino", - "pl": "Polaco" - }, + "languages": {}, "profile": { "member_since": "Miembro desde", "user_stats": "Estadísticas de usuario", diff --git a/frontend/src/locales/fr.json b/frontend/src/locales/fr.json index eaa8842e..a6aa22df 100644 --- a/frontend/src/locales/fr.json +++ b/frontend/src/locales/fr.json @@ -455,17 +455,7 @@ "show_visited_regions": "Afficher les régions visitées", "view_details": "Afficher les détails" }, - "languages": { - "de": "Allemand", - "en": "Anglais", - "es": "Espagnol", - "fr": "Français", - "it": "italien", - "nl": "Néerlandais", - "sv": "suédois", - "zh": "Chinois", - "pl": "Polonais" - }, + "languages": {}, "share": { "no_users_shared": "Aucun utilisateur partagé avec", "not_shared_with": "Non partagé avec", diff --git a/frontend/src/locales/it.json b/frontend/src/locales/it.json index 0b880a93..8f93cdd4 100644 --- a/frontend/src/locales/it.json +++ b/frontend/src/locales/it.json @@ -455,17 +455,7 @@ "show_visited_regions": "Mostra regioni visitate", "view_details": "Visualizza dettagli" }, - "languages": { - "de": "tedesco", - "en": "Inglese", - "es": "spagnolo", - "fr": "francese", - "it": "Italiano", - "nl": "Olandese", - "sv": "svedese", - "zh": "cinese", - "pl": "Polacco" - }, + "languages": {}, "share": { "no_users_shared": "Nessun utente condiviso con", "not_shared_with": "Non condiviso con", diff --git a/frontend/src/locales/nl.json b/frontend/src/locales/nl.json index f00fac4d..149babae 100644 --- a/frontend/src/locales/nl.json +++ b/frontend/src/locales/nl.json @@ -455,17 +455,7 @@ "show_visited_regions": "Toon bezochte regio's", "view_details": "Details bekijken" }, - "languages": { - "de": "Duits", - "en": "Engels", - "es": "Spaans", - "fr": "Frans", - "it": "Italiaans", - "nl": "Nederlands", - "sv": "Zweeds", - "zh": "Chinese", - "pl": "Pools" - }, + "languages": {}, "share": { "no_users_shared": "Er zijn geen gebruikers gedeeld", "not_shared_with": "Niet gedeeld met", diff --git a/frontend/src/locales/pl.json b/frontend/src/locales/pl.json index b9a13cfb..3e369b9e 100644 --- a/frontend/src/locales/pl.json +++ b/frontend/src/locales/pl.json @@ -467,17 +467,7 @@ "set_public": "Aby umożliwić użytkownikom udostępnianie Tobie, musisz ustawić swój profil jako publiczny.", "go_to_settings": "Przejdź do ustawień" }, - "languages": { - "en": "Angielski", - "de": "Niemiecki", - "es": "Hiszpański", - "fr": "Francuski", - "it": "Włoski", - "nl": "Holenderski", - "sv": "Szwedzki", - "zh": "Chiński", - "pl": "Polski" - }, + "languages": {}, "profile": { "member_since": "Użytkownik od", "user_stats": "Statystyki użytkownika", diff --git a/frontend/src/locales/sv.json b/frontend/src/locales/sv.json index a4c443ca..214393c5 100644 --- a/frontend/src/locales/sv.json +++ b/frontend/src/locales/sv.json @@ -455,17 +455,7 @@ "show_visited_regions": "Visa besökta regioner", "view_details": "Visa detaljer" }, - "languages": { - "de": "tyska", - "en": "engelska", - "es": "spanska", - "fr": "franska", - "it": "italienska", - "nl": "holländska", - "sv": "svenska", - "zh": "kinesiska", - "pl": "polska" - }, + "languages": {}, "share": { "no_users_shared": "Inga användare delas med", "not_shared_with": "Inte delad med", diff --git a/frontend/src/locales/zh.json b/frontend/src/locales/zh.json index 4444be97..f9ee9071 100644 --- a/frontend/src/locales/zh.json +++ b/frontend/src/locales/zh.json @@ -455,17 +455,7 @@ "show_visited_regions": "显示访问过的地区", "view_details": "查看详情" }, - "languages": { - "de": "德语", - "en": "英语", - "es": "西班牙语", - "fr": "法语", - "it": "意大利语", - "nl": "荷兰语", - "sv": "瑞典", - "zh": "中国人", - "pl": "波兰语" - }, + "languages": {}, "share": { "no_users_shared": "没有与之共享的用户", "not_shared_with": "不与共享", From 991efa8d08eeb4328dc5dd02c45b9bf23f94c600 Mon Sep 17 00:00:00 2001 From: Sean Morley Date: Thu, 2 Jan 2025 19:00:11 -0500 Subject: [PATCH 013/209] docs: add Immich integration documentation and update configuration menu --- documentation/.vitepress/config.mts | 4 +++ .../docs/configuration/immich_integration.md | 28 +++++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 documentation/docs/configuration/immich_integration.md diff --git a/documentation/.vitepress/config.mts b/documentation/.vitepress/config.mts index 1c47f293..72316c3a 100644 --- a/documentation/.vitepress/config.mts +++ b/documentation/.vitepress/config.mts @@ -87,6 +87,10 @@ export default defineConfig({ text: "Configuration", collapsed: false, items: [ + { + text: "Immich Integration", + link: "/docs/configuration/immich_integration", + }, { text: "Update App", link: "/docs/configuration/updating", diff --git a/documentation/docs/configuration/immich_integration.md b/documentation/docs/configuration/immich_integration.md new file mode 100644 index 00000000..117fd31f --- /dev/null +++ b/documentation/docs/configuration/immich_integration.md @@ -0,0 +1,28 @@ +# Immich Integration + +### What is Immich? + + + +![Immich Banner](https://repository-images.githubusercontent.com/455229168/ebba3238-9ef5-4891-ad58-a3b0223b12bd) + +Immich is a self-hosted, open-source platform that allows users to backup and manage their photos and videos similar to Google Photos, but with the advantage of storing data on their own private server, ensuring greater privacy and control over their media. + +- [Immich Website and Documentation](https://immich.app/) +- [GitHub Repository](https://github.com/immich-app/immich) + +### How to integrate Immich with AdventureLog? + +To integrate Immich with AdventureLog, you need to have an Immich server running and accessible from where AdventureLog is running. + +1. Obtain the Immich API Key from the Immich server. + - In the Immich web interface, click on your user profile picture, go to `Account Settings` > `API Keys`. + - Click `New API Key` and name it something like `AdventureLog`. + - Copy the generated API Key, you will need it in the next step. +2. Go to the AdventureLog web interface, click on your user profile picture, go to `Settings` and scroll down to the `Immich Integration` section. + - Enter the URL of your Immich server, e.g. `https://immich.example.com`. + - Paste the API Key you obtained in the previous step. + - Click `Enable Immich` to save the settings. +3. Now, when you are adding images to an adventure, you will see an option to search for images in Immich or upload from an album. + +Enjoy the privacy and control of managing your travel media with Immich and AdventureLog! 🎉 From c6fa603a93e708a4ba3cb04db9c837f9c8456947 Mon Sep 17 00:00:00 2001 From: Sean Morley Date: Thu, 2 Jan 2025 23:25:58 -0500 Subject: [PATCH 014/209] feat: add primary image functionality to AdventureImage model and update related components --- .../0017_adventureimage_is_primary.py | 18 ++++++++ backend/server/adventures/models.py | 1 + backend/server/adventures/serializers.py | 2 +- backend/server/adventures/views.py | 22 ++++++++++ .../src/lib/components/AdventureModal.svelte | 44 +++++++++++++++++-- .../src/lib/components/CardCarousel.svelte | 19 +++++++- frontend/src/lib/types.ts | 1 + .../src/routes/adventures/[id]/+page.svelte | 10 +++++ 8 files changed, 112 insertions(+), 5 deletions(-) create mode 100644 backend/server/adventures/migrations/0017_adventureimage_is_primary.py diff --git a/backend/server/adventures/migrations/0017_adventureimage_is_primary.py b/backend/server/adventures/migrations/0017_adventureimage_is_primary.py new file mode 100644 index 00000000..9a920a39 --- /dev/null +++ b/backend/server/adventures/migrations/0017_adventureimage_is_primary.py @@ -0,0 +1,18 @@ +# Generated by Django 5.0.8 on 2025-01-03 04:05 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('adventures', '0016_alter_adventureimage_image'), + ] + + operations = [ + migrations.AddField( + model_name='adventureimage', + name='is_primary', + field=models.BooleanField(default=False), + ), + ] diff --git a/backend/server/adventures/models.py b/backend/server/adventures/models.py index 68c8ad66..c77bc4dd 100644 --- a/backend/server/adventures/models.py +++ b/backend/server/adventures/models.py @@ -280,6 +280,7 @@ class AdventureImage(models.Model): upload_to=PathAndRename('images/') # Use the callable class here ) adventure = models.ForeignKey(Adventure, related_name='images', on_delete=models.CASCADE) + is_primary = models.BooleanField(default=False) def __str__(self): return self.image.url diff --git a/backend/server/adventures/serializers.py b/backend/server/adventures/serializers.py index 45a2141b..d78e93d3 100644 --- a/backend/server/adventures/serializers.py +++ b/backend/server/adventures/serializers.py @@ -8,7 +8,7 @@ from main.utils import CustomModelSerializer class AdventureImageSerializer(CustomModelSerializer): class Meta: model = AdventureImage - fields = ['id', 'image', 'adventure'] + fields = ['id', 'image', 'adventure', 'is_primary'] read_only_fields = ['id'] def to_representation(self, instance): diff --git a/backend/server/adventures/views.py b/backend/server/adventures/views.py index b1c39561..3ee3e793 100644 --- a/backend/server/adventures/views.py +++ b/backend/server/adventures/views.py @@ -1032,7 +1032,29 @@ class AdventureImageViewSet(viewsets.ModelViewSet): @action(detail=True, methods=['post']) def image_delete(self, request, *args, **kwargs): return self.destroy(request, *args, **kwargs) + + @action(detail=True, methods=['post']) + def toggle_primary(self, request, *args, **kwargs): + # Makes the image the primary image for the adventure, if there is already a primary image linked to the adventure, it is set to false and the new image is set to true. make sure that the permission is set to the owner of the adventure + if not request.user.is_authenticated: + return Response({"error": "User is not authenticated"}, status=status.HTTP_401_UNAUTHORIZED) + + instance = self.get_object() + adventure = instance.adventure + if adventure.user_id != request.user: + return Response({"error": "User does not own this adventure"}, status=status.HTTP_403_FORBIDDEN) + + # Check if the image is already the primary image + if instance.is_primary: + return Response({"error": "Image is already the primary image"}, status=status.HTTP_400_BAD_REQUEST) + + # Set the current primary image to false + AdventureImage.objects.filter(adventure=adventure, is_primary=True).update(is_primary=False) + # Set the new image to true + instance.is_primary = True + instance.save() + return Response({"success": "Image set as primary image"}) def create(self, request, *args, **kwargs): if not request.user.is_authenticated: diff --git a/frontend/src/lib/components/AdventureModal.svelte b/frontend/src/lib/components/AdventureModal.svelte index 550c4194..138ff3d0 100644 --- a/frontend/src/lib/components/AdventureModal.svelte +++ b/frontend/src/lib/components/AdventureModal.svelte @@ -21,7 +21,7 @@ let query: string = ''; let places: OpenStreetMapPlace[] = []; - let images: { id: string; image: string }[] = []; + let images: { id: string; image: string; is_primary: boolean }[] = []; let warningMessage: string = ''; let constrainDates: boolean = false; @@ -34,6 +34,9 @@ import MarkdownEditor from './MarkdownEditor.svelte'; import ImmichSelect from './ImmichSelect.svelte'; + import Star from '~icons/mdi/star'; + import Crown from '~icons/mdi/crown'; + let wikiError: string = ''; let noPlaces: boolean = false; @@ -179,6 +182,25 @@ } } + async function makePrimaryImage(image_id: string) { + let res = await fetch(`/api/images/${image_id}/toggle_primary`, { + method: 'POST' + }); + if (res.ok) { + images = images.map((image) => { + if (image.id === image_id) { + image.is_primary = true; + } else { + image.is_primary = false; + } + return image; + }); + adventure.images = images; + } else { + console.error('Error in makePrimaryImage:', res); + } + } + async function fetchImage() { try { let res = await fetch(url); @@ -208,7 +230,8 @@ // Assuming the first object in the array is the new image let newImage = { id: rawData[1], - image: rawData[2] // This is the URL for the image + image: rawData[2], // This is the URL for the image + is_primary: false }; console.log('New Image:', newImage); @@ -249,7 +272,7 @@ if (res2.ok) { let newData = deserialize(await res2.text()) as { data: { id: string; image: string } }; console.log(newData); - let newImage = { id: newData.data.id, image: newData.data.image }; + let newImage = { id: newData.data.id, image: newData.data.image, is_primary: false }; console.log(newImage); images = [...images, newImage]; adventure.images = images; @@ -1038,6 +1061,21 @@ it would also work to just use on:click on the MapLibre component itself. --> > ✕ + {#if !image.is_primary} + + {:else} + + +
    + +
    + {/if} {image.id} - adventure.images.map((image) => ({ image: image.image, adventure: adventure })) + adventure.images.map((image) => ({ + image: image.image, + adventure: adventure, + is_primary: image.is_primary + })) ); $: { @@ -18,6 +22,19 @@ } } + $: { + // sort so that any image in adventure_images .is_primary is first + adventure_images.sort((a, b) => { + if (a.is_primary && !b.is_primary) { + return -1; + } else if (!a.is_primary && b.is_primary) { + return 1; + } else { + return 0; + } + }); + } + function changeSlide(direction: string) { if (direction === 'next' && currentSlide < adventure_images.length - 1) { currentSlide = currentSlide + 1; diff --git a/frontend/src/lib/types.ts b/frontend/src/lib/types.ts index a2e6d3ca..ea9e1fb3 100644 --- a/frontend/src/lib/types.ts +++ b/frontend/src/lib/types.ts @@ -24,6 +24,7 @@ export type Adventure = { images: { id: string; image: string; + is_primary: boolean; }[]; visits: { id: string; diff --git a/frontend/src/routes/adventures/[id]/+page.svelte b/frontend/src/routes/adventures/[id]/+page.svelte index f151bb4a..21b622f4 100644 --- a/frontend/src/routes/adventures/[id]/+page.svelte +++ b/frontend/src/routes/adventures/[id]/+page.svelte @@ -34,6 +34,16 @@ onMount(() => { if (data.props.adventure) { adventure = data.props.adventure; + // sort so that any image in adventure_images .is_primary is first + adventure.images.sort((a, b) => { + if (a.is_primary && !b.is_primary) { + return -1; + } else if (!a.is_primary && b.is_primary) { + return 1; + } else { + return 0; + } + }); } else { notFound = true; } From 6651557738fe9f1ab796ca776dc37906eb3e66fc Mon Sep 17 00:00:00 2001 From: Sean Morley Date: Fri, 3 Jan 2025 09:47:05 -0500 Subject: [PATCH 015/209] feat: include adventure visits in collection update requests --- frontend/src/lib/components/AdventureCard.svelte | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/lib/components/AdventureCard.svelte b/frontend/src/lib/components/AdventureCard.svelte index b77b8eaa..49990d55 100644 --- a/frontend/src/lib/components/AdventureCard.svelte +++ b/frontend/src/lib/components/AdventureCard.svelte @@ -80,7 +80,7 @@ headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ collection: null }) + body: JSON.stringify({ collection: null, visits: adventure.visits }) }); if (res.ok) { addToast('info', `${$t('adventures.collection_remove_success')}`); @@ -97,7 +97,7 @@ headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ collection: collectionId }) + body: JSON.stringify({ collection: collectionId, visits: adventure.visits }) }); if (res.ok) { console.log('Adventure linked to collection'); From 57e367d112def03a3c2bda086b055b2e5ecb201b Mon Sep 17 00:00:00 2001 From: Sean Morley Date: Fri, 3 Jan 2025 09:53:23 -0500 Subject: [PATCH 016/209] refactor: update AdventureSerializer to handle visits data more gracefully and remove visits from request body in AdventureCard --- backend/server/adventures/serializers.py | 36 ++++++++++--------- .../src/lib/components/AdventureCard.svelte | 4 +-- 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/backend/server/adventures/serializers.py b/backend/server/adventures/serializers.py index d78e93d3..2c677f73 100644 --- a/backend/server/adventures/serializers.py +++ b/backend/server/adventures/serializers.py @@ -116,7 +116,7 @@ class AdventureSerializer(CustomModelSerializer): return False def create(self, validated_data): - visits_data = validated_data.pop('visits', []) + visits_data = validated_data.pop('visits', None) category_data = validated_data.pop('category', None) print(category_data) adventure = Adventure.objects.create(**validated_data) @@ -131,6 +131,7 @@ class AdventureSerializer(CustomModelSerializer): return adventure def update(self, instance, validated_data): + has_visits = 'visits' in validated_data visits_data = validated_data.pop('visits', []) category_data = validated_data.pop('category', None) @@ -142,24 +143,25 @@ class AdventureSerializer(CustomModelSerializer): instance.category = category instance.save() - current_visits = instance.visits.all() - current_visit_ids = set(current_visits.values_list('id', flat=True)) + if has_visits: + current_visits = instance.visits.all() + current_visit_ids = set(current_visits.values_list('id', flat=True)) - updated_visit_ids = set() - for visit_data in visits_data: - visit_id = visit_data.get('id') - if visit_id and visit_id in current_visit_ids: - visit = current_visits.get(id=visit_id) - for attr, value in visit_data.items(): - setattr(visit, attr, value) - visit.save() - updated_visit_ids.add(visit_id) - else: - new_visit = Visit.objects.create(adventure=instance, **visit_data) - updated_visit_ids.add(new_visit.id) + updated_visit_ids = set() + for visit_data in visits_data: + visit_id = visit_data.get('id') + if visit_id and visit_id in current_visit_ids: + visit = current_visits.get(id=visit_id) + for attr, value in visit_data.items(): + setattr(visit, attr, value) + visit.save() + updated_visit_ids.add(visit_id) + else: + new_visit = Visit.objects.create(adventure=instance, **visit_data) + updated_visit_ids.add(new_visit.id) - visits_to_delete = current_visit_ids - updated_visit_ids - instance.visits.filter(id__in=visits_to_delete).delete() + visits_to_delete = current_visit_ids - updated_visit_ids + instance.visits.filter(id__in=visits_to_delete).delete() return instance diff --git a/frontend/src/lib/components/AdventureCard.svelte b/frontend/src/lib/components/AdventureCard.svelte index 49990d55..b77b8eaa 100644 --- a/frontend/src/lib/components/AdventureCard.svelte +++ b/frontend/src/lib/components/AdventureCard.svelte @@ -80,7 +80,7 @@ headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ collection: null, visits: adventure.visits }) + body: JSON.stringify({ collection: null }) }); if (res.ok) { addToast('info', `${$t('adventures.collection_remove_success')}`); @@ -97,7 +97,7 @@ headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ collection: collectionId, visits: adventure.visits }) + body: JSON.stringify({ collection: collectionId }) }); if (res.ok) { console.log('Adventure linked to collection'); From 3a024e1e18b844cbd587ebb3277f4bcde2b25399 Mon Sep 17 00:00:00 2001 From: Sean Morley Date: Fri, 3 Jan 2025 12:05:02 -0500 Subject: [PATCH 017/209] feat: implement logic to determine if an adventure will be marked as visited based on visit dates --- .../src/lib/components/AdventureModal.svelte | 49 +++++++++++++++---- 1 file changed, 39 insertions(+), 10 deletions(-) diff --git a/frontend/src/lib/components/AdventureModal.svelte b/frontend/src/lib/components/AdventureModal.svelte index 138ff3d0..584f72d0 100644 --- a/frontend/src/lib/components/AdventureModal.svelte +++ b/frontend/src/lib/components/AdventureModal.svelte @@ -164,6 +164,33 @@ close(); } + let willBeMarkedVisited: boolean = false; + + $: { + willBeMarkedVisited = false; // Reset before evaluating + + const today = new Date(); // Cache today's date to avoid redundant calculations + + for (const visit of adventure.visits) { + const startDate = new Date(visit.start_date); + const endDate = visit.end_date ? new Date(visit.end_date) : null; + + // If the visit has both a start date and an end date, check if it started by today + if (startDate && endDate && startDate <= today) { + willBeMarkedVisited = true; + break; // Exit the loop since we've determined the result + } + + // If the visit has a start date but no end date, check if it started by today + if (startDate && !endDate && startDate <= today) { + willBeMarkedVisited = true; + break; // Exit the loop since we've determined the result + } + } + + console.log('WMBV:', willBeMarkedVisited); + } + let previousCoords: { lat: number; lng: number } | null = null; $: if (markers.length > 0) { @@ -515,6 +542,9 @@ addToast('error', $t('adventures.adventure_update_error')); } } + if (adventure.is_visited) { + markVisited(); + } } @@ -761,7 +791,12 @@ it would also work to just use on:click on the MapLibre component itself. --> : $t('adventures.not_visited')}

    - {#if !reverseGeocodePlace.is_visited} + {#if !reverseGeocodePlace.is_visited && !willBeMarkedVisited} + + {/if} + {#if !reverseGeocodePlace.is_visited && willBeMarkedVisited} {/if} {/if} From 50e0d4a34e5b2bcd35420fb89da33f29cf13f9fd Mon Sep 17 00:00:00 2001 From: Sean Morley Date: Fri, 3 Jan 2025 12:12:09 -0500 Subject: [PATCH 018/209] feat: add localization for adventure visit confirmation message in multiple languages --- frontend/src/lib/components/AdventureModal.svelte | 4 ++-- frontend/src/locales/de.json | 3 ++- frontend/src/locales/en.json | 1 + frontend/src/locales/es.json | 3 ++- frontend/src/locales/fr.json | 3 ++- frontend/src/locales/it.json | 3 ++- frontend/src/locales/nl.json | 3 ++- frontend/src/locales/pl.json | 3 ++- frontend/src/locales/sv.json | 3 ++- frontend/src/locales/zh.json | 3 ++- 10 files changed, 19 insertions(+), 10 deletions(-) diff --git a/frontend/src/lib/components/AdventureModal.svelte b/frontend/src/lib/components/AdventureModal.svelte index 584f72d0..89e21220 100644 --- a/frontend/src/lib/components/AdventureModal.svelte +++ b/frontend/src/lib/components/AdventureModal.svelte @@ -813,8 +813,8 @@ it would also work to just use on:click on the MapLibre component itself. --> {reverseGeocodePlace.region}, - {reverseGeocodePlace.country} will be marked as visited once the adventure is - saved.
    {/if} diff --git a/frontend/src/locales/de.json b/frontend/src/locales/de.json index a8edb0d6..3bbd4259 100644 --- a/frontend/src/locales/de.json +++ b/frontend/src/locales/de.json @@ -216,7 +216,8 @@ "starting_airport": "Startflughafen", "to": "Zu", "transportation_delete_confirm": "Sind Sie sicher, dass Sie diesen Transport löschen möchten? \nDiese Aktion kann nicht rückgängig gemacht werden.", - "show_map": "Karte anzeigen" + "show_map": "Karte anzeigen", + "will_be_marked": "wird als besucht markiert, sobald das Abenteuer gespeichert ist." }, "home": { "desc_1": "Entdecken, planen und erkunden Sie mit Leichtigkeit", diff --git a/frontend/src/locales/en.json b/frontend/src/locales/en.json index d64bdaa2..9aaa4b23 100644 --- a/frontend/src/locales/en.json +++ b/frontend/src/locales/en.json @@ -229,6 +229,7 @@ "no_location_found": "No location found", "from": "From", "to": "To", + "will_be_marked": "will be marked as visited once the adventure is saved.", "start": "Start", "end": "End", "show_map": "Show Map", diff --git a/frontend/src/locales/es.json b/frontend/src/locales/es.json index a66b5ca0..11f54966 100644 --- a/frontend/src/locales/es.json +++ b/frontend/src/locales/es.json @@ -263,7 +263,8 @@ "starting_airport": "Aeropuerto de inicio", "to": "A", "transportation_delete_confirm": "¿Está seguro de que desea eliminar este transporte? \nEsta acción no se puede deshacer.", - "show_map": "Mostrar mapa" + "show_map": "Mostrar mapa", + "will_be_marked": "se marcará como visitado una vez guardada la aventura." }, "worldtravel": { "all": "Todo", diff --git a/frontend/src/locales/fr.json b/frontend/src/locales/fr.json index a6aa22df..e6f7573f 100644 --- a/frontend/src/locales/fr.json +++ b/frontend/src/locales/fr.json @@ -216,7 +216,8 @@ "starting_airport": "Aéroport de départ", "to": "À", "transportation_delete_confirm": "Etes-vous sûr de vouloir supprimer ce transport ? \nCette action ne peut pas être annulée.", - "show_map": "Afficher la carte" + "show_map": "Afficher la carte", + "will_be_marked": "sera marqué comme visité une fois l’aventure sauvegardée." }, "home": { "desc_1": "Découvrez, planifiez et explorez en toute simplicité", diff --git a/frontend/src/locales/it.json b/frontend/src/locales/it.json index 8f93cdd4..49b43d9c 100644 --- a/frontend/src/locales/it.json +++ b/frontend/src/locales/it.json @@ -216,7 +216,8 @@ "starting_airport": "Inizio aeroporto", "to": "A", "transportation_delete_confirm": "Sei sicuro di voler eliminare questo trasporto? \nQuesta azione non può essere annullata.", - "show_map": "Mostra mappa" + "show_map": "Mostra mappa", + "will_be_marked": "verrà contrassegnato come visitato una volta salvata l'avventura." }, "home": { "desc_1": "Scopri, pianifica ed esplora con facilità", diff --git a/frontend/src/locales/nl.json b/frontend/src/locales/nl.json index 149babae..c207f8a1 100644 --- a/frontend/src/locales/nl.json +++ b/frontend/src/locales/nl.json @@ -216,7 +216,8 @@ "to": "Naar", "transportation_delete_confirm": "Weet u zeker dat u dit transport wilt verwijderen? \nDeze actie kan niet ongedaan worden gemaakt.", "ending_airport": "Einde luchthaven", - "show_map": "Toon kaart" + "show_map": "Toon kaart", + "will_be_marked": "wordt gemarkeerd als bezocht zodra het avontuur is opgeslagen." }, "home": { "desc_1": "Ontdek, plan en verken met gemak", diff --git a/frontend/src/locales/pl.json b/frontend/src/locales/pl.json index 3e369b9e..73361d99 100644 --- a/frontend/src/locales/pl.json +++ b/frontend/src/locales/pl.json @@ -263,7 +263,8 @@ "starting_airport": "Początkowe lotnisko", "to": "Do", "transportation_delete_confirm": "Czy na pewno chcesz usunąć ten transport? \nTej akcji nie można cofnąć.", - "show_map": "Pokaż mapę" + "show_map": "Pokaż mapę", + "will_be_marked": "zostanie oznaczona jako odwiedzona po zapisaniu przygody." }, "worldtravel": { "country_list": "Lista krajów", diff --git a/frontend/src/locales/sv.json b/frontend/src/locales/sv.json index 214393c5..ee195bb2 100644 --- a/frontend/src/locales/sv.json +++ b/frontend/src/locales/sv.json @@ -216,7 +216,8 @@ "starting_airport": "Startar flygplats", "to": "Till", "transportation_delete_confirm": "Är du säker på att du vill ta bort denna transport? \nDenna åtgärd kan inte ångras.", - "show_map": "Visa karta" + "show_map": "Visa karta", + "will_be_marked": "kommer att markeras som besökt när äventyret har sparats." }, "home": { "desc_1": "Upptäck, planera och utforska med lätthet", diff --git a/frontend/src/locales/zh.json b/frontend/src/locales/zh.json index f9ee9071..659dd08d 100644 --- a/frontend/src/locales/zh.json +++ b/frontend/src/locales/zh.json @@ -216,7 +216,8 @@ "starting_airport": "出发机场", "to": "到", "transportation_delete_confirm": "您确定要删除此交通工具吗?\n此操作无法撤消。", - "show_map": "显示地图" + "show_map": "显示地图", + "will_be_marked": "保存冒险后将被标记为已访问。" }, "home": { "desc_1": "轻松发现、规划和探索", From 82a1134019f959c1bee688a7d57f69a15c181f12 Mon Sep 17 00:00:00 2001 From: Sean Morley Date: Fri, 3 Jan 2025 12:15:39 -0500 Subject: [PATCH 019/209] fix: prevent marking adventure as visited if the place is already visited --- frontend/src/lib/components/AdventureModal.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/lib/components/AdventureModal.svelte b/frontend/src/lib/components/AdventureModal.svelte index 89e21220..1d1b2c06 100644 --- a/frontend/src/lib/components/AdventureModal.svelte +++ b/frontend/src/lib/components/AdventureModal.svelte @@ -542,7 +542,7 @@ addToast('error', $t('adventures.adventure_update_error')); } } - if (adventure.is_visited) { + if (adventure.is_visited && !reverseGeocodePlace?.is_visited) { markVisited(); } } From 259f2e75c2e57130e5786c7c78e69822d552dbb8 Mon Sep 17 00:00:00 2001 From: Sean Morley Date: Fri, 3 Jan 2025 12:24:17 -0500 Subject: [PATCH 020/209] feat: version bump for release v0.8.0 --- documentation/.vitepress/config.mts | 4 + documentation/docs/changelogs/v0-8-0.md | 105 ++++++++++++++++++++++++ frontend/package.json | 2 +- frontend/src/lib/config.ts | 4 +- 4 files changed, 112 insertions(+), 3 deletions(-) create mode 100644 documentation/docs/changelogs/v0-8-0.md diff --git a/documentation/.vitepress/config.mts b/documentation/.vitepress/config.mts index 72316c3a..4f1264c6 100644 --- a/documentation/.vitepress/config.mts +++ b/documentation/.vitepress/config.mts @@ -135,6 +135,10 @@ export default defineConfig({ text: "Changelogs", collapsed: false, items: [ + { + text: "v0.8.0", + link: "/docs/changelogs/v0-8-0", + }, { text: "v0.7.1", link: "/docs/changelogs/v0-7-1", diff --git a/documentation/docs/changelogs/v0-8-0.md b/documentation/docs/changelogs/v0-8-0.md new file mode 100644 index 00000000..48d9c18e --- /dev/null +++ b/documentation/docs/changelogs/v0-8-0.md @@ -0,0 +1,105 @@ +# AdventureLog v0.8.0 - Immich Integration, Calendar and Customization + +Released 01-08-2025 + +Hi everyone! 🚀 +I’m thrilled to announce the release of **AdventureLog v0.8.0**, a huge update packed with new features, improvements, and enhancements. This release focuses on delivering a better user experience, improved functionality, and expanded customization options. Let’s dive into what’s new! + +--- + +## What's New ✨ + +### Immich Integration + +- AdventureLog now integrates seamlessly with [Immich](https://github.com/immich-app), the amazing self-hostable photo library. +- Import your photos from Immich directly into AdventureLog adventures and collections. + - Use Immich Smart Search to search images to import based on natural queries. + - Sort by photo album to easily import your trips photos to an adventure. + +### 🚗 Transportation + +- **New Transportation Edit Modal**: Includes detailed origin and destination location information for better trip planning. +- **Autocomplete for Airport Codes**: Quickly find and add airport codes while planning transportations. +- **New Transportation Card Design**: Redesigned for better clarity and aesthetics. + +--- + +### 📝 Notes and Checklists + +- **New Modals for Notes and Checklists**: Simplified creation and editing of your notes and checklists. +- **Delete Confirmation**: Added a confirmation step when deleting notes, checklists, or transportations to prevent accidental deletions. + +--- + +### 📍Adventures + +- **Markdown Editor and Preview**: Write and format adventure descriptions with a markdown editor. +- **Custom Categories**: Organize your adventures with personalized categories and icons. +- **Primary Images**: Adventure images can now be marked as the "primary image" and will be the first one to be displayed in adventure views. + +--- + +### 🗓️ Calendar + +- **Calendar View**: View your adventures and transportations in a calendar layout. +- **ICS File Export**: Export your calendar as an ICS file for use with external apps like Google Calendar or Outlook. + +--- + +### 🌐 Localization + +- Added support for **Polish** language (@dymek37). +- Improved Swedish language data (@nordtechtiger) + +--- + +### 🔒 Authentication + +- **New Authentication System**: Includes MFA for added security. +- **Admin Page Authentication**: Enhanced protection for admin operations. + > [!IMPORTANT] + > Ensure you know your credentials as you will be signed out after updating! + +--- + +### 🖌️ UI & Theming + +- **Nord Theme**: A sleek new theme option for a modern and clean interface. +- **New Home Dashboard**: A revamped dashboard experience to access everything you need quickly and view your travel stats. + +--- + +### ⚙️ Settings + +- **Overhauled Settings Page**: Redesigned for better navigation and usability. + +--- + +### 🐛 Bug Fixes and Improvements + +- Fixed the **NGINX Upload Size Bug**: Upload larger files without issues. +- **Prevents Duplicate Emails**: Improved account management; users can now add multiple emails to a single account. +- General **code cleanliness** for better performance and stability. +- Fixes Django Admin access through Traefik (@PascalBru) + +--- + +### 🌐 Infrastructure + +- Added **Kubernetes Configurations** for scalable deployments (@MaximUltimatum). +- Launched a **New [Documentation Site](https://adventurelog.app)** for better guidance and support. + +--- + +## Sponsorship 💖 + +[![Buy Me A Coffee](https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png)](https://www.buymeacoffee.com/seanmorley15) +As always, AdventureLog continues to grow thanks to your incredible support and feedback. If you love using the app and want to help shape its future, consider supporting me on **Buy Me A Coffee**. Your contributions go a long way in allowing for AdventureLog to continue to improve and thrive 😊 + +--- + +Enjoy the update! 🎉 +Feel free to share your feedback, ideas, or questions in the discussion below or on the official [discord server](https://discord.gg/wRbQ9Egr8C)! + +Happy travels, +**Sean Morley** (@seanmorley15) diff --git a/frontend/package.json b/frontend/package.json index e2b9a261..442a9d6a 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "adventurelog-frontend", - "version": "0.7.1", + "version": "0.8.0", "scripts": { "dev": "vite dev", "django": "cd .. && cd backend/server && python3 manage.py runserver", diff --git a/frontend/src/lib/config.ts b/frontend/src/lib/config.ts index 3b9fde7e..d2015826 100644 --- a/frontend/src/lib/config.ts +++ b/frontend/src/lib/config.ts @@ -1,4 +1,4 @@ -export let appVersion = 'v0.7.1'; -export let versionChangelog = 'https://github.com/seanmorley15/AdventureLog/releases/tag/v0.7.1'; +export let appVersion = 'v0.8.0'; +export let versionChangelog = 'https://github.com/seanmorley15/AdventureLog/releases/tag/v0.8.0'; export let appTitle = 'AdventureLog'; export let copyrightYear = '2023-2025'; From 230a786d6eeb15e6b2261670e730e8adb949445e Mon Sep 17 00:00:00 2001 From: Sean Morley Date: Fri, 3 Jan 2025 15:58:52 -0500 Subject: [PATCH 021/209] docs: add instructions for customizing email subject in admin site --- documentation/docs/configuration/email.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/documentation/docs/configuration/email.md b/documentation/docs/configuration/email.md index f3fb3130..53129105 100644 --- a/documentation/docs/configuration/email.md +++ b/documentation/docs/configuration/email.md @@ -22,3 +22,13 @@ environment: - EMAIL_HOST_PASSWORD='password' - DEFAULT_FROM_EMAIL='user@example.com' ``` + +## Customizing Emails + +By default, the email will display `[example.com]` in the subject. You can customize this in the admin site. + +1. Go to the admin site (serverurl/admin) +2. Click on `Sites` +3. Click on first site, it will probably be `example.com` +4. Change the `Domain name` and `Display name` to your desired values +5. Click `Save` From 56244329f52fd55f0ac7502848871df43e107146 Mon Sep 17 00:00:00 2001 From: Sean Morley Date: Fri, 3 Jan 2025 16:29:39 -0500 Subject: [PATCH 022/209] feat: configure REST framework renderers based on DEBUG setting --- backend/server/main/settings.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/backend/server/main/settings.py b/backend/server/main/settings.py index b934b990..5acecb74 100644 --- a/backend/server/main/settings.py +++ b/backend/server/main/settings.py @@ -220,6 +220,17 @@ REST_FRAMEWORK = { 'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.coreapi.AutoSchema', } +if DEBUG: + REST_FRAMEWORK['DEFAULT_RENDERER_CLASSES'] = ( + 'rest_framework.renderers.JSONRenderer', + 'rest_framework.renderers.BrowsableAPIRenderer', + ) +else: + REST_FRAMEWORK['DEFAULT_RENDERER_CLASSES'] = ( + 'rest_framework.renderers.JSONRenderer', + ) + + CORS_ALLOWED_ORIGINS = [origin.strip() for origin in getenv('CSRF_TRUSTED_ORIGINS', 'http://localhost').split(',') if origin.strip()] From a4df852744ad2f20270e11c90053e27de7c34793 Mon Sep 17 00:00:00 2001 From: Sean Morley Date: Fri, 3 Jan 2025 16:42:27 -0500 Subject: [PATCH 023/209] feat: update .env.example for demo database setup and add image search functionality in AdventureModal --- backend/server/.env.example | 13 ++++++++++++- frontend/src/lib/components/AdventureModal.svelte | 1 + 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/backend/server/.env.example b/backend/server/.env.example index 04eb77f1..4c1f9ad3 100644 --- a/backend/server/.env.example +++ b/backend/server/.env.example @@ -20,4 +20,15 @@ EMAIL_BACKEND='console' # EMAIL_USE_SSL=True # EMAIL_HOST_USER='user' # EMAIL_HOST_PASSWORD='password' -# DEFAULT_FROM_EMAIL='user@example.com' \ No newline at end of file +# DEFAULT_FROM_EMAIL='user@example.com' + + +# ------------------- # +# For Developers to start a Demo Database +# docker run --name postgres-admin -e POSTGRES_USER=admin -e POSTGRES_PASSWORD=admin -e POSTGRES_DB=admin -p 5432:5432 -d postgis/postgis:15-3.3 + +# PGHOST='localhost' +# PGDATABASE='admin' +# PGUSER='admin' +# PGPASSWORD='admin' +# ------------------- # \ No newline at end of file diff --git a/frontend/src/lib/components/AdventureModal.svelte b/frontend/src/lib/components/AdventureModal.svelte index 1d1b2c06..eda9df6d 100644 --- a/frontend/src/lib/components/AdventureModal.svelte +++ b/frontend/src/lib/components/AdventureModal.svelte @@ -545,6 +545,7 @@ if (adventure.is_visited && !reverseGeocodePlace?.is_visited) { markVisited(); } + imageSearch = adventure.name; } From 9e4846a66a34ecf11c19f6c6ad1b4c62fb3ecb62 Mon Sep 17 00:00:00 2001 From: Sean Morley Date: Fri, 3 Jan 2025 18:46:45 -0500 Subject: [PATCH 024/209] feat: enhance Immich integration documentation and add warnings for localhost usage --- documentation/docs/configuration/immich_integration.md | 2 +- frontend/src/lib/components/NoteModal.svelte | 2 +- frontend/src/lib/index.ts | 2 +- frontend/src/locales/en.json | 4 +++- frontend/src/routes/settings/+page.svelte | 10 ++++++++++ 5 files changed, 16 insertions(+), 4 deletions(-) diff --git a/documentation/docs/configuration/immich_integration.md b/documentation/docs/configuration/immich_integration.md index 117fd31f..a8b2ae15 100644 --- a/documentation/docs/configuration/immich_integration.md +++ b/documentation/docs/configuration/immich_integration.md @@ -20,7 +20,7 @@ To integrate Immich with AdventureLog, you need to have an Immich server running - Click `New API Key` and name it something like `AdventureLog`. - Copy the generated API Key, you will need it in the next step. 2. Go to the AdventureLog web interface, click on your user profile picture, go to `Settings` and scroll down to the `Immich Integration` section. - - Enter the URL of your Immich server, e.g. `https://immich.example.com`. + - Enter the URL of your Immich server, e.g. `https://immich.example.com`. Note that `localhost` or `127.0.0.1` will probably not work because Immich and AdventureLog are running on different docker networks. It is recommended to use the IP address of the server where Immich is running ex `http://my-server-ip:port` or a domain name. - Paste the API Key you obtained in the previous step. - Click `Enable Immich` to save the settings. 3. Now, when you are adding images to an adventure, you will see an option to search for images in Immich or upload from an album. diff --git a/frontend/src/lib/components/NoteModal.svelte b/frontend/src/lib/components/NoteModal.svelte index 83449849..a895f336 100644 --- a/frontend/src/lib/components/NoteModal.svelte +++ b/frontend/src/lib/components/NoteModal.svelte @@ -188,7 +188,7 @@

    {#if !isReadOnly} - + {:else if note}

    = newYearsStart && today <= newYearsEnd) { return { diff --git a/frontend/src/locales/en.json b/frontend/src/locales/en.json index 9aaa4b23..a7e668e8 100644 --- a/frontend/src/locales/en.json +++ b/frontend/src/locales/en.json @@ -514,6 +514,8 @@ "api_key": "Immich API Key", "enable_immich": "Enable Immich", "update_integration": "Update Integration", - "immich_integration": "Immich Integration" + "immich_integration": "Immich Integration", + "localhost_note": "Note: localhost will most likely not work unless you have setup docker networks accordingly. It is recommended to use the IP address of the server or the domain name.", + "documentation": "Immich Integration Documentation" } } diff --git a/frontend/src/routes/settings/+page.svelte b/frontend/src/routes/settings/+page.svelte index 5b2c305d..6c2b97f9 100644 --- a/frontend/src/routes/settings/+page.svelte +++ b/frontend/src/routes/settings/+page.svelte @@ -433,6 +433,11 @@

    {$t('immich.immich_desc')} + {$t('immich.documentation')}

    {#if immichIntegration}
    @@ -469,6 +474,11 @@ {$t('immich.api_note')}

    {/if} + {#if newImmichIntegration.server_url && (newImmichIntegration.server_url.indexOf('localhost') !== -1 || newImmichIntegration.server_url.indexOf('127.0.0.1') !== -1)} +

    + {$t('immich.localhost_note')} +

    + {/if}
    + + {#if user.is_staff} +
    +

    Administration Settings

    + +
    + {/if} + + +
    +

    Social and ODIC Authentication

    +
    +

    + Enable or disable social and OIDC authentication providers for your account. These + connections allow you to sign in with self hosted authentication identity providers like + Authentik or 3rd party providers like GitHub. +

    + + Launch Account Connections +
    +
    +

    From a5aa09ed7b4b4485006fda120e48a9f44e9fe1c3 Mon Sep 17 00:00:00 2001 From: Sean Morley Date: Mon, 6 Jan 2025 20:44:37 -0500 Subject: [PATCH 031/209] feat: add OverrideHostMiddleware to set HTTP_HOST from PUBLIC_URL environment variable and update nginx configuration --- backend/nginx.conf | 20 ++++++++++---------- backend/server/adventures/middleware.py | 21 ++++++++++++++++++++- backend/server/main/settings.py | 1 + 3 files changed, 31 insertions(+), 11 deletions(-) diff --git a/backend/nginx.conf b/backend/nginx.conf index a58fce59..ae9992ba 100644 --- a/backend/nginx.conf +++ b/backend/nginx.conf @@ -17,24 +17,24 @@ http { } server { - listen 80; # NGINX always listens on port 80 inside the container + listen 80; server_name localhost; location / { - proxy_pass http://server:8000; # Explicitly forward to Django service - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; + proxy_pass http://server:8000; # Forward to internal Gunicorn server + proxy_set_header Host $host; # Forward Host header from the request + proxy_set_header X-Real-IP $remote_addr; # Forward real IP + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # Forward original IP + proxy_set_header X-Forwarded-Proto $scheme; # Forward the protocol + proxy_set_header X-Forwarded-Host $host; # Forward the Host header } - location /static/ { - alias /code/staticfiles/; # Serve static files directly + alias /code/staticfiles/; } location /media/ { - alias /code/media/; # Serve media files directly + alias /code/media/; } - } +} } diff --git a/backend/server/adventures/middleware.py b/backend/server/adventures/middleware.py index 3cd97132..550e581f 100644 --- a/backend/server/adventures/middleware.py +++ b/backend/server/adventures/middleware.py @@ -20,4 +20,23 @@ class PrintCookiesMiddleware: def __call__(self, request): print(request.COOKIES) response = self.get_response(request) - return response \ No newline at end of file + return response + +# middlewares.py + +import os +from django.http import HttpRequest + +class OverrideHostMiddleware: + def __init__(self, get_response): + self.get_response = get_response + + def __call__(self, request: HttpRequest): + # Override the host with the PUBLIC_URL environment variable + public_url = os.getenv('PUBLIC_URL', None) + if public_url: + # Split the public URL to extract the host and port (if available) + host = public_url.split("//")[-1].split("/")[0] + request.META['HTTP_HOST'] = host # Override the HTTP_HOST header + response = self.get_response(request) + return response diff --git a/backend/server/main/settings.py b/backend/server/main/settings.py index c135d797..2b23b99e 100644 --- a/backend/server/main/settings.py +++ b/backend/server/main/settings.py @@ -69,6 +69,7 @@ MIDDLEWARE = ( 'corsheaders.middleware.CorsMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', + 'adventures.middleware.OverrideHostMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', From 548702890d44993c3a3a3007a7e2041d662d3d16 Mon Sep 17 00:00:00 2001 From: Sean Morley Date: Tue, 7 Jan 2025 09:58:39 -0500 Subject: [PATCH 032/209] feat: update NGINX configuration for improved proxy handling and enable social account login on GET requests --- backend/nginx.conf | 22 ++++++++-------- backend/server/main/settings.py | 2 ++ .../src/routes/_allauth/[...path]/+server.ts | 25 +++++++------------ 3 files changed, 22 insertions(+), 27 deletions(-) diff --git a/backend/nginx.conf b/backend/nginx.conf index ae9992ba..002aa448 100644 --- a/backend/nginx.conf +++ b/backend/nginx.conf @@ -17,24 +17,24 @@ http { } server { - listen 80; + listen 80; # NGINX always listens on port 80 inside the container server_name localhost; location / { - proxy_pass http://server:8000; # Forward to internal Gunicorn server - proxy_set_header Host $host; # Forward Host header from the request - proxy_set_header X-Real-IP $remote_addr; # Forward real IP - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # Forward original IP - proxy_set_header X-Forwarded-Proto $scheme; # Forward the protocol - proxy_set_header X-Forwarded-Host $host; # Forward the Host header + proxy_pass http://server:8000; # Explicitly forward to Django service + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; } + location /static/ { - alias /code/staticfiles/; + alias /code/staticfiles/; # Serve static files directly } location /media/ { - alias /code/media/; + alias /code/media/; # Serve media files directly } -} -} + } +} \ No newline at end of file diff --git a/backend/server/main/settings.py b/backend/server/main/settings.py index 2b23b99e..349f7bc1 100644 --- a/backend/server/main/settings.py +++ b/backend/server/main/settings.py @@ -186,6 +186,8 @@ FRONTEND_URL = getenv('FRONTEND_URL', 'http://localhost:3000') # Set login redirect URL to the frontend LOGIN_REDIRECT_URL = FRONTEND_URL +SOCIALACCOUNT_LOGIN_ON_GET = True + HEADLESS_FRONTEND_URLS = { "account_confirm_email": f"{FRONTEND_URL}/user/verify-email/{{key}}", "account_reset_password": f"{FRONTEND_URL}/user/reset-password", diff --git a/frontend/src/routes/_allauth/[...path]/+server.ts b/frontend/src/routes/_allauth/[...path]/+server.ts index 4d6bc326..681a3fa8 100644 --- a/frontend/src/routes/_allauth/[...path]/+server.ts +++ b/frontend/src/routes/_allauth/[...path]/+server.ts @@ -12,23 +12,23 @@ export async function GET(event) { /** @type {import('./$types').RequestHandler} */ export async function POST({ url, params, request, fetch, cookies }) { - const searchParam = url.search ? `${url.search}&format=json` : '?format=json'; - return handleRequest(url, params, request, fetch, cookies, searchParam, true); + const searchParam = url.search ? `${url.search}` : ''; + return handleRequest(url, params, request, fetch, cookies, searchParam, false); } export async function PATCH({ url, params, request, fetch, cookies }) { - const searchParam = url.search ? `${url.search}&format=json` : '?format=json'; - return handleRequest(url, params, request, fetch, cookies, searchParam, true); + const searchParam = url.search ? `${url.search}` : ''; + return handleRequest(url, params, request, fetch, cookies, searchParam, false); } export async function PUT({ url, params, request, fetch, cookies }) { - const searchParam = url.search ? `${url.search}&format=json` : '?format=json'; - return handleRequest(url, params, request, fetch, cookies, searchParam, true); + const searchParam = url.search ? `${url.search}` : ''; + return handleRequest(url, params, request, fetch, cookies, searchParam, false); } export async function DELETE({ url, params, request, fetch, cookies }) { - const searchParam = url.search ? `${url.search}&format=json` : '?format=json'; - return handleRequest(url, params, request, fetch, cookies, searchParam, true); + const searchParam = url.search ? `${url.search}` : ''; + return handleRequest(url, params, request, fetch, cookies, searchParam, false); } async function handleRequest( @@ -53,25 +53,18 @@ async function handleRequest( const headers = new Headers(request.headers); - // Delete existing csrf cookie by setting an expired date - cookies.delete('csrftoken', { path: '/' }); - - // Generate a new csrf token (using your existing fetchCSRFToken function) const csrfToken = await fetchCSRFToken(); if (!csrfToken) { return json({ error: 'CSRF token is missing or invalid' }, { status: 400 }); } - // Set the new csrf token in both headers and cookies - const cookieHeader = `csrftoken=${csrfToken}; Path=/; HttpOnly; SameSite=Lax`; - try { const response = await fetch(targetUrl, { method: request.method, headers: { ...Object.fromEntries(headers), 'X-CSRFToken': csrfToken, - Cookie: cookieHeader + Cookie: `csrftoken=${csrfToken}` }, body: request.method !== 'GET' && request.method !== 'HEAD' ? await request.text() : undefined, From f670fbc93abba0ad7811fe7a2cec35cd1f52a779 Mon Sep 17 00:00:00 2001 From: Sean Morley Date: Tue, 7 Jan 2025 10:27:11 -0500 Subject: [PATCH 033/209] feat: enhance superuser creation with email verification and update settings for two-factor authentication --- backend/entrypoint.sh | 25 ++++++++++++++++++++--- backend/server/main/settings.py | 6 +++--- frontend/src/locales/en.json | 1 + frontend/src/routes/settings/+page.svelte | 12 ++++++++--- 4 files changed, 35 insertions(+), 9 deletions(-) diff --git a/backend/entrypoint.sh b/backend/entrypoint.sh index 48adee30..26c3e0d7 100644 --- a/backend/entrypoint.sh +++ b/backend/entrypoint.sh @@ -20,19 +20,38 @@ done python manage.py migrate # Create superuser if environment variables are set and there are no users present at all. -if [ -n "$DJANGO_ADMIN_USERNAME" ] && [ -n "$DJANGO_ADMIN_PASSWORD" ]; then +if [ -n "$DJANGO_ADMIN_USERNAME" ] && [ -n "$DJANGO_ADMIN_PASSWORD" ] && [ -n "$DJANGO_ADMIN_EMAIL" ]; then echo "Creating superuser..." python manage.py shell << EOF from django.contrib.auth import get_user_model +from allauth.account.models import EmailAddress + User = get_user_model() -if User.objects.count() == 0: - User.objects.create_superuser('$DJANGO_ADMIN_USERNAME', '$DJANGO_ADMIN_EMAIL', '$DJANGO_ADMIN_PASSWORD') + +# Check if the user already exists +if not User.objects.filter(username='$DJANGO_ADMIN_USERNAME').exists(): + # Create the superuser + superuser = User.objects.create_superuser( + username='$DJANGO_ADMIN_USERNAME', + email='$DJANGO_ADMIN_EMAIL', + password='$DJANGO_ADMIN_PASSWORD' + ) print("Superuser created successfully.") + + # Create the EmailAddress object for AllAuth + EmailAddress.objects.create( + user=superuser, + email='$DJANGO_ADMIN_EMAIL', + verified=True, + primary=True + ) + print("EmailAddress object created successfully for AllAuth.") else: print("Superuser already exists.") EOF fi + # Sync the countries and world travel regions python manage.py download-countries diff --git a/backend/server/main/settings.py b/backend/server/main/settings.py index 349f7bc1..aa573884 100644 --- a/backend/server/main/settings.py +++ b/backend/server/main/settings.py @@ -42,7 +42,7 @@ INSTALLED_APPS = ( 'django.contrib.messages', 'django.contrib.staticfiles', 'django.contrib.sites', - "allauth_ui", + # "allauth_ui", 'rest_framework', 'rest_framework.authtoken', 'allauth', @@ -59,8 +59,8 @@ INSTALLED_APPS = ( 'users', 'integrations', 'django.contrib.gis', - 'widget_tweaks', - 'slippers', + # 'widget_tweaks', + # 'slippers', ) diff --git a/frontend/src/locales/en.json b/frontend/src/locales/en.json index 050ae37d..b3a1f430 100644 --- a/frontend/src/locales/en.json +++ b/frontend/src/locales/en.json @@ -307,6 +307,7 @@ "settings_page": "Settings Page", "account_settings": "User Account Settings", "update": "Update", + "no_verified_email_warning": "You must have a verified email address to enable two-factor authentication.", "password_change": "Change Password", "new_password": "New Password", "confirm_new_password": "Confirm New Password", diff --git a/frontend/src/routes/settings/+page.svelte b/frontend/src/routes/settings/+page.svelte index 65b0905a..cdba5c38 100644 --- a/frontend/src/routes/settings/+page.svelte +++ b/frontend/src/routes/settings/+page.svelte @@ -416,9 +416,15 @@
    {#if !data.props.authenticators}

    {$t('settings.mfa_not_enabled')}

    - + {#if !emails.some((e) => e.verified)} +
    + {$t('settings.no_verified_email_warning')} +
    + {:else} + + {/if} {:else} Date: Wed, 8 Jan 2025 17:08:53 -0500 Subject: [PATCH 034/209] feat: enhance adventure sorting by adding date and rating filters, update admin settings localization for OIDC --- backend/server/adventures/views.py | 13 +++++++++++-- frontend/src/locales/de.json | 13 +++++++++++-- frontend/src/locales/en.json | 9 ++++++++- frontend/src/locales/es.json | 13 +++++++++++-- frontend/src/locales/fr.json | 13 +++++++++++-- frontend/src/locales/it.json | 13 +++++++++++-- frontend/src/locales/nl.json | 13 +++++++++++-- frontend/src/locales/pl.json | 13 +++++++++++-- frontend/src/locales/sv.json | 13 +++++++++++-- frontend/src/locales/zh.json | 13 +++++++++++-- frontend/src/routes/settings/+page.svelte | 20 ++++++++++---------- 11 files changed, 117 insertions(+), 29 deletions(-) diff --git a/backend/server/adventures/views.py b/backend/server/adventures/views.py index 3ee3e793..5c0c5931 100644 --- a/backend/server/adventures/views.py +++ b/backend/server/adventures/views.py @@ -20,6 +20,7 @@ from django.contrib.auth import get_user_model from icalendar import Calendar, Event, vText, vCalAddress from django.http import HttpResponse from datetime import datetime +from django.db.models import Min User = get_user_model() @@ -44,17 +45,25 @@ class AdventureViewSet(viewsets.ModelViewSet): order_direction = self.request.query_params.get('order_direction', 'asc') include_collections = self.request.query_params.get('include_collections', 'true') - valid_order_by = ['name', 'type', 'start_date', 'rating', 'updated_at'] + valid_order_by = ['name', 'type', 'date', 'rating', 'updated_at'] if order_by not in valid_order_by: order_by = 'name' if order_direction not in ['asc', 'desc']: order_direction = 'asc' + if order_by == 'date': + # order by the earliest visit object associated with the adventure + queryset = queryset.annotate(earliest_visit=Min('visits__start_date')) + queryset = queryset.filter(earliest_visit__isnull=False) + ordering = 'earliest_visit' # Apply case-insensitive sorting for the 'name' field - if order_by == 'name': + elif order_by == 'name': queryset = queryset.annotate(lower_name=Lower('name')) ordering = 'lower_name' + elif order_by == 'rating': + queryset = queryset.filter(rating__isnull=False) + ordering = 'rating' else: ordering = order_by diff --git a/frontend/src/locales/de.json b/frontend/src/locales/de.json index dfb35ccb..e8f982d0 100644 --- a/frontend/src/locales/de.json +++ b/frontend/src/locales/de.json @@ -284,7 +284,8 @@ "email_required": "E-Mail ist erforderlich", "both_passwords_required": "Beide Passwörter sind erforderlich", "new_password": "Neues Passwort", - "reset_failed": "Passwort konnte nicht zurückgesetzt werden" + "reset_failed": "Passwort konnte nicht zurückgesetzt werden", + "or_3rd_party": "Oder melden Sie sich bei einem Drittanbieter an" }, "users": { "no_users_found": "Keine Benutzer mit öffentlichen Profilen gefunden." @@ -369,7 +370,15 @@ "csrf_failed": "CSRF-Token konnte nicht abgerufen werden", "duplicate_email": "Diese E-Mail-Adresse wird bereits verwendet.", "email_taken": "Diese E-Mail-Adresse wird bereits verwendet.", - "username_taken": "Dieser Benutzername wird bereits verwendet." + "username_taken": "Dieser Benutzername wird bereits verwendet.", + "administration_settings": "Verwaltungseinstellungen", + "documentation_link": "Dokumentationslink", + "launch_account_connections": "Kontoverbindungen starten", + "launch_administration_panel": "Starten Sie das Administrationspanel", + "no_verified_email_warning": "Sie müssen über eine verifizierte E-Mail-Adresse verfügen, um die Zwei-Faktor-Authentifizierung zu aktivieren.", + "social_auth_desc": "Aktivieren oder deaktivieren Sie soziale und OIDC-Authentifizierungsanbieter für Ihr Konto. \nMit diesen Verbindungen können Sie sich bei selbst gehosteten Authentifizierungsidentitätsanbietern wie Authentik oder Drittanbietern wie GitHub anmelden.", + "social_auth_desc_2": "Diese Einstellungen werden auf dem AdventureLog-Server verwaltet und müssen vom Administrator manuell aktiviert werden.", + "social_oidc_auth": "Soziale und OIDC-Authentifizierung" }, "checklist": { "add_item": "Artikel hinzufügen", diff --git a/frontend/src/locales/en.json b/frontend/src/locales/en.json index b3a1f430..a46986d2 100644 --- a/frontend/src/locales/en.json +++ b/frontend/src/locales/en.json @@ -371,7 +371,14 @@ "duplicate_email": "This email address is already in use.", "csrf_failed": "Failed to fetch CSRF token", "email_taken": "This email address is already in use.", - "username_taken": "This username is already in use." + "username_taken": "This username is already in use.", + "administration_settings": "Administration Settings", + "launch_administration_panel": "Launch Administration Panel", + "social_oidc_auth": "Social and OIDC Authentication", + "social_auth_desc": "Enable or disable social and OIDC authentication providers for your account. These connections allow you to sign in with self hosted authentication identity providers like Authentik or 3rd party providers like GitHub.", + "social_auth_desc_2": "These settings are managed in the AdventureLog server and must be manually enabled by the administrator.", + "documentation_link": "Documentation Link", + "launch_account_connections": "Launch Account Connections" }, "collection": { "collection_created": "Collection created successfully!", diff --git a/frontend/src/locales/es.json b/frontend/src/locales/es.json index 0211650a..f8d33abb 100644 --- a/frontend/src/locales/es.json +++ b/frontend/src/locales/es.json @@ -295,7 +295,8 @@ "email_required": "Se requiere correo electrónico", "both_passwords_required": "Se requieren ambas contraseñas", "new_password": "Nueva contraseña", - "reset_failed": "No se pudo restablecer la contraseña" + "reset_failed": "No se pudo restablecer la contraseña", + "or_3rd_party": "O inicie sesión con un servicio de terceros" }, "users": { "no_users_found": "No se encontraron usuarios con perfiles públicos." @@ -369,7 +370,15 @@ "csrf_failed": "No se pudo recuperar el token CSRF", "duplicate_email": "Esta dirección de correo electrónico ya está en uso.", "email_taken": "Esta dirección de correo electrónico ya está en uso.", - "username_taken": "Este nombre de usuario ya está en uso." + "username_taken": "Este nombre de usuario ya está en uso.", + "administration_settings": "Configuración de administración", + "documentation_link": "Enlace de documentación", + "launch_account_connections": "Iniciar conexiones de cuenta", + "launch_administration_panel": "Iniciar el panel de administración", + "no_verified_email_warning": "Debe tener una dirección de correo electrónico verificada para habilitar la autenticación de dos factores.", + "social_auth_desc": "Habilite o deshabilite los proveedores de autenticación social y OIDC para su cuenta. \nEstas conexiones le permiten iniciar sesión con proveedores de identidad de autenticación autohospedados como Authentik o proveedores externos como GitHub.", + "social_auth_desc_2": "Estas configuraciones se administran en el servidor AdventureLog y el administrador debe habilitarlas manualmente.", + "social_oidc_auth": "Autenticación social y OIDC" }, "checklist": { "add_item": "Agregar artículo", diff --git a/frontend/src/locales/fr.json b/frontend/src/locales/fr.json index 45726194..5d432376 100644 --- a/frontend/src/locales/fr.json +++ b/frontend/src/locales/fr.json @@ -284,7 +284,8 @@ "email_required": "L'e-mail est requis", "both_passwords_required": "Les deux mots de passe sont requis", "new_password": "Nouveau mot de passe", - "reset_failed": "Échec de la réinitialisation du mot de passe" + "reset_failed": "Échec de la réinitialisation du mot de passe", + "or_3rd_party": "Ou connectez-vous avec un service tiers" }, "users": { "no_users_found": "Aucun utilisateur trouvé avec des profils publics." @@ -369,7 +370,15 @@ "csrf_failed": "Échec de la récupération du jeton CSRF", "duplicate_email": "Cette adresse e-mail est déjà utilisée.", "email_taken": "Cette adresse e-mail est déjà utilisée.", - "username_taken": "Ce nom d'utilisateur est déjà utilisé." + "username_taken": "Ce nom d'utilisateur est déjà utilisé.", + "administration_settings": "Paramètres d'administration", + "documentation_link": "Lien vers la documentation", + "launch_account_connections": "Lancer les connexions au compte", + "launch_administration_panel": "Lancer le panneau d'administration", + "no_verified_email_warning": "Vous devez disposer d'une adresse e-mail vérifiée pour activer l'authentification à deux facteurs.", + "social_auth_desc": "Activez ou désactivez les fournisseurs d'authentification sociale et OIDC pour votre compte. \nCes connexions vous permettent de vous connecter avec des fournisseurs d'identité d'authentification auto-hébergés comme Authentik ou des fournisseurs tiers comme GitHub.", + "social_auth_desc_2": "Ces paramètres sont gérés sur le serveur AdventureLog et doivent être activés manuellement par l'administrateur.", + "social_oidc_auth": "Authentification sociale et OIDC" }, "checklist": { "add_item": "Ajouter un article", diff --git a/frontend/src/locales/it.json b/frontend/src/locales/it.json index 9c06a229..025f9e0d 100644 --- a/frontend/src/locales/it.json +++ b/frontend/src/locales/it.json @@ -284,7 +284,8 @@ "email_required": "L'e-mail è obbligatoria", "both_passwords_required": "Sono necessarie entrambe le password", "new_password": "Nuova parola d'ordine", - "reset_failed": "Impossibile reimpostare la password" + "reset_failed": "Impossibile reimpostare la password", + "or_3rd_party": "Oppure accedi con un servizio di terze parti" }, "users": { "no_users_found": "Nessun utente trovato con profili pubblici." @@ -369,7 +370,15 @@ "csrf_failed": "Impossibile recuperare il token CSRF", "duplicate_email": "Questo indirizzo email è già in uso.", "email_taken": "Questo indirizzo email è già in uso.", - "username_taken": "Questo nome utente è già in uso." + "username_taken": "Questo nome utente è già in uso.", + "administration_settings": "Impostazioni di amministrazione", + "documentation_link": "Collegamento alla documentazione", + "launch_account_connections": "Avvia Connessioni account", + "launch_administration_panel": "Avvia il pannello di amministrazione", + "no_verified_email_warning": "È necessario disporre di un indirizzo e-mail verificato per abilitare l'autenticazione a due fattori.", + "social_auth_desc": "Abilita o disabilita i provider di autenticazione social e OIDC per il tuo account. \nQueste connessioni ti consentono di accedere con provider di identità di autenticazione self-hosted come Authentik o provider di terze parti come GitHub.", + "social_auth_desc_2": "Queste impostazioni sono gestite nel server AdventureLog e devono essere abilitate manualmente dall'amministratore.", + "social_oidc_auth": "Autenticazione sociale e OIDC" }, "checklist": { "add_item": "Aggiungi articolo", diff --git a/frontend/src/locales/nl.json b/frontend/src/locales/nl.json index 91fb42bc..750bbd7b 100644 --- a/frontend/src/locales/nl.json +++ b/frontend/src/locales/nl.json @@ -284,7 +284,8 @@ "email_required": "E-mail is vereist", "both_passwords_required": "Beide wachtwoorden zijn vereist", "new_password": "Nieuw wachtwoord", - "reset_failed": "Kan het wachtwoord niet opnieuw instellen" + "reset_failed": "Kan het wachtwoord niet opnieuw instellen", + "or_3rd_party": "Of log in met een service van derden" }, "users": { "no_users_found": "Er zijn geen gebruikers gevonden met openbare profielen." @@ -369,7 +370,15 @@ "csrf_failed": "Kan CSRF-token niet ophalen", "duplicate_email": "Dit e-mailadres is al in gebruik.", "email_taken": "Dit e-mailadres is al in gebruik.", - "username_taken": "Deze gebruikersnaam is al in gebruik." + "username_taken": "Deze gebruikersnaam is al in gebruik.", + "administration_settings": "Beheerinstellingen", + "documentation_link": "Documentatielink", + "launch_account_connections": "Start Accountverbindingen", + "launch_administration_panel": "Start het Beheerpaneel", + "no_verified_email_warning": "U moet een geverifieerd e-mailadres hebben om tweefactorauthenticatie in te schakelen.", + "social_auth_desc": "Schakel sociale en OIDC-authenticatieproviders in of uit voor uw account. \nMet deze verbindingen kunt u inloggen met zelfgehoste authenticatie-identiteitsproviders zoals Authentik of externe providers zoals GitHub.", + "social_auth_desc_2": "Deze instellingen worden beheerd op de AdventureLog-server en moeten handmatig worden ingeschakeld door de beheerder.", + "social_oidc_auth": "Sociale en OIDC-authenticatie" }, "checklist": { "add_item": "Artikel toevoegen", diff --git a/frontend/src/locales/pl.json b/frontend/src/locales/pl.json index 641aa955..189be2cb 100644 --- a/frontend/src/locales/pl.json +++ b/frontend/src/locales/pl.json @@ -295,7 +295,8 @@ "email_required": "Adres e-mail jest wymagany", "both_passwords_required": "Obydwa hasła są wymagane", "new_password": "Nowe hasło", - "reset_failed": "Nie udało się zresetować hasła" + "reset_failed": "Nie udało się zresetować hasła", + "or_3rd_party": "Lub zaloguj się za pomocą usługi strony trzeciej" }, "users": { "no_users_found": "Nie znaleziono użytkowników z publicznymi profilami." @@ -369,7 +370,15 @@ "csrf_failed": "Nie udało się pobrać tokena CSRF", "duplicate_email": "Ten adres e-mail jest już używany.", "email_taken": "Ten adres e-mail jest już używany.", - "username_taken": "Ta nazwa użytkownika jest już używana." + "username_taken": "Ta nazwa użytkownika jest już używana.", + "administration_settings": "Ustawienia administracyjne", + "documentation_link": "Link do dokumentacji", + "launch_account_connections": "Uruchom Połączenia kont", + "launch_administration_panel": "Uruchom Panel administracyjny", + "no_verified_email_warning": "Aby włączyć uwierzytelnianie dwuskładnikowe, musisz mieć zweryfikowany adres e-mail.", + "social_auth_desc": "Włącz lub wyłącz dostawców uwierzytelniania społecznościowego i OIDC dla swojego konta. \nPołączenia te umożliwiają logowanie się za pośrednictwem dostawców tożsamości uwierzytelniających, takich jak Authentik, lub dostawców zewnętrznych, takich jak GitHub.", + "social_auth_desc_2": "Ustawienia te są zarządzane na serwerze AdventureLog i muszą zostać włączone ręcznie przez administratora.", + "social_oidc_auth": "Uwierzytelnianie społecznościowe i OIDC" }, "collection": { "collection_created": "Kolekcja została pomyślnie utworzona!", diff --git a/frontend/src/locales/sv.json b/frontend/src/locales/sv.json index 505fbb6b..7f7baabb 100644 --- a/frontend/src/locales/sv.json +++ b/frontend/src/locales/sv.json @@ -295,7 +295,8 @@ "email_required": "E-post krävs", "both_passwords_required": "Båda lösenorden krävs", "new_password": "Nytt lösenord", - "reset_failed": "Det gick inte att återställa lösenordet" + "reset_failed": "Det gick inte att återställa lösenordet", + "or_3rd_party": "Eller logga in med en tredjepartstjänst" }, "users": { "no_users_found": "Inga användare hittades med offentliga profiler." @@ -369,7 +370,15 @@ "csrf_failed": "Det gick inte att hämta CSRF-token", "duplicate_email": "Den här e-postadressen används redan.", "email_taken": "Den här e-postadressen används redan.", - "username_taken": "Detta användarnamn används redan." + "username_taken": "Detta användarnamn används redan.", + "administration_settings": "Administrationsinställningar", + "documentation_link": "Dokumentationslänk", + "launch_account_connections": "Starta kontoanslutningar", + "launch_administration_panel": "Starta administrationspanelen", + "no_verified_email_warning": "Du måste ha en verifierad e-postadress för att aktivera tvåfaktorsautentisering.", + "social_auth_desc": "Aktivera eller inaktivera sociala och OIDC-autentiseringsleverantörer för ditt konto. \nDessa anslutningar gör att du kan logga in med leverantörer av autentiseringsidentitetsidentitet som är värd för dig som Authentik eller tredjepartsleverantörer som GitHub.", + "social_auth_desc_2": "Dessa inställningar hanteras i AdventureLog-servern och måste aktiveras manuellt av administratören.", + "social_oidc_auth": "Social och OIDC-autentisering" }, "checklist": { "add_item": "Lägg till objekt", diff --git a/frontend/src/locales/zh.json b/frontend/src/locales/zh.json index 86674c52..d9b5be1b 100644 --- a/frontend/src/locales/zh.json +++ b/frontend/src/locales/zh.json @@ -284,7 +284,8 @@ "email_required": "电子邮件为必填项", "both_passwords_required": "两个密码都需要", "new_password": "新密码", - "reset_failed": "重置密码失败" + "reset_failed": "重置密码失败", + "or_3rd_party": "或者使用第三方服务登录" }, "worldtravel": { "all": "全部", @@ -369,7 +370,15 @@ "csrf_failed": "获取 CSRF 令牌失败", "duplicate_email": "该电子邮件地址已被使用。", "email_taken": "该电子邮件地址已被使用。", - "username_taken": "该用户名已被使用。" + "username_taken": "该用户名已被使用。", + "administration_settings": "管理设置", + "documentation_link": "文档链接", + "launch_account_connections": "启动帐户连接", + "launch_administration_panel": "启动管理面板", + "no_verified_email_warning": "您必须拥有经过验证的电子邮件地址才能启用双因素身份验证。", + "social_auth_desc": "为您的帐户启用或禁用社交和 OIDC 身份验证提供商。\n这些连接允许您使用自托管身份验证身份提供商(如 Authentik)或第三方提供商(如 GitHub)登录。", + "social_auth_desc_2": "这些设置在 AdventureLog 服务器中进行管理,并且必须由管理员手动启用。", + "social_oidc_auth": "社交和 OIDC 身份验证" }, "checklist": { "add_item": "添加项目", diff --git a/frontend/src/routes/settings/+page.svelte b/frontend/src/routes/settings/+page.svelte index cdba5c38..245b9573 100644 --- a/frontend/src/routes/settings/+page.svelte +++ b/frontend/src/routes/settings/+page.svelte @@ -436,10 +436,12 @@ {#if user.is_staff}
    -

    Administration Settings

    +

    + {$t('settings.administration_settings')} +

    Launch Administration Pannel{$t('settings.launch_administration_panel')}
    @@ -447,12 +449,10 @@
    -

    Social and ODIC Authentication

    +

    {$t('settings.social_oidc_auth')}

    - Enable or disable social and OIDC authentication providers for your account. These - connections allow you to sign in with self hosted authentication identity providers like - Authentik or 3rd party providers like GitHub. + {$t('settings.social_auth_desc')}

    Launch Account Connections{$t('settings.launch_account_connections')}
    From 44810e634373eeae5707ede5a68b46b5292e6694 Mon Sep 17 00:00:00 2001 From: Sean Morley Date: Thu, 9 Jan 2025 11:11:02 -0500 Subject: [PATCH 035/209] feat: add City model and serializer, update RegionCard and create CityCard component, enhance admin interface for City management --- backend/server/adventures/admin.py | 13 +- backend/server/main/settings.py | 2 +- .../management/commands/download-countries.py | 67 +++++++- .../worldtravel/migrations/0012_city.py | 27 ++++ backend/server/worldtravel/models.py | 13 ++ backend/server/worldtravel/serializers.py | 8 +- backend/server/worldtravel/urls.py | 6 +- backend/server/worldtravel/views.py | 12 +- frontend/src/lib/components/CityCard.svelte | 36 +++++ frontend/src/lib/components/RegionCard.svelte | 15 +- frontend/src/lib/types.ts | 12 +- frontend/src/locales/en.json | 3 +- .../worldtravel/[id]/[id]/+page.server.ts | 65 ++++++++ .../routes/worldtravel/[id]/[id]/+page.svelte | 148 ++++++++++++++++++ 14 files changed, 409 insertions(+), 18 deletions(-) create mode 100644 backend/server/worldtravel/migrations/0012_city.py create mode 100644 frontend/src/lib/components/CityCard.svelte create mode 100644 frontend/src/routes/worldtravel/[id]/[id]/+page.server.ts create mode 100644 frontend/src/routes/worldtravel/[id]/[id]/+page.svelte diff --git a/backend/server/adventures/admin.py b/backend/server/adventures/admin.py index be1793b1..e809aa4f 100644 --- a/backend/server/adventures/admin.py +++ b/backend/server/adventures/admin.py @@ -2,7 +2,7 @@ import os from django.contrib import admin 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 worldtravel.models import Country, Region, VisitedRegion, City from allauth.account.decorators import secure_admin_login admin.autodiscover() @@ -51,6 +51,16 @@ class RegionAdmin(admin.ModelAdmin): number_of_visits.short_description = 'Number of Visits' +class CityAdmin(admin.ModelAdmin): + list_display = ('name', 'region', 'country') + list_filter = ('region', 'region__country') + search_fields = ('name', 'region__name', 'region__country__name') + + def country(self, obj): + return obj.region.country.name + + country.short_description = 'Country' + from django.contrib import admin from django.contrib.auth.admin import UserAdmin from users.models import CustomUser @@ -127,6 +137,7 @@ admin.site.register(Checklist) admin.site.register(ChecklistItem) admin.site.register(AdventureImage, AdventureImageAdmin) admin.site.register(Category, CategoryAdmin) +admin.site.register(City, CityAdmin) admin.site.site_header = 'AdventureLog Admin' admin.site.site_title = 'AdventureLog Admin Site' diff --git a/backend/server/main/settings.py b/backend/server/main/settings.py index aa573884..06bd33b5 100644 --- a/backend/server/main/settings.py +++ b/backend/server/main/settings.py @@ -276,4 +276,4 @@ LOGGING = { }, } # https://github.com/dr5hn/countries-states-cities-database/tags -COUNTRY_REGION_JSON_VERSION = 'v2.4' \ No newline at end of file +COUNTRY_REGION_JSON_VERSION = 'v2.5' \ No newline at end of file diff --git a/backend/server/worldtravel/management/commands/download-countries.py b/backend/server/worldtravel/management/commands/download-countries.py index 06382cbb..1b741bfd 100644 --- a/backend/server/worldtravel/management/commands/download-countries.py +++ b/backend/server/worldtravel/management/commands/download-countries.py @@ -1,7 +1,7 @@ import os from django.core.management.base import BaseCommand import requests -from worldtravel.models import Country, Region +from worldtravel.models import Country, Region, City from django.db import transaction import json @@ -37,16 +37,28 @@ def saveCountryFlag(country_code): class Command(BaseCommand): help = 'Imports the world travel data' + def add_arguments(self, parser): + parser.add_argument('--force', action='store_true', help='Force download the countries+regions+states.json file') + def handle(self, *args, **options): - countries_json_path = os.path.join(settings.MEDIA_ROOT, f'countries+regions-{COUNTRY_REGION_JSON_VERSION}.json') - if not os.path.exists(countries_json_path): - res = requests.get(f'https://raw.githubusercontent.com/dr5hn/countries-states-cities-database/{COUNTRY_REGION_JSON_VERSION}/countries%2Bstates.json') + force = options['force'] + countries_json_path = os.path.join(settings.MEDIA_ROOT, f'countries+regions+states-{COUNTRY_REGION_JSON_VERSION}.json') + if not os.path.exists(countries_json_path) or force: + res = requests.get(f'https://raw.githubusercontent.com/dr5hn/countries-states-cities-database/{COUNTRY_REGION_JSON_VERSION}/json/countries%2Bstates%2Bcities.json') if res.status_code == 200: with open(countries_json_path, 'w') as f: f.write(res.text) else: - self.stdout.write(self.style.ERROR('Error downloading countries+regions.json')) + self.stdout.write(self.style.ERROR('Error downloading countries+regions+states.json')) return + elif not os.path.isfile(countries_json_path): + self.stdout.write(self.style.ERROR('countries+regions+states.json is not a file')) + return + elif os.path.getsize(countries_json_path) == 0: + self.stdout.write(self.style.ERROR('countries+regions+states.json is empty')) + else: + self.stdout.write(self.style.SUCCESS('countries+regions+states.json already exists')) + return with open(countries_json_path, 'r') as f: data = json.load(f) @@ -54,14 +66,18 @@ class Command(BaseCommand): with transaction.atomic(): existing_countries = {country.country_code: country for country in Country.objects.all()} existing_regions = {region.id: region for region in Region.objects.all()} + existing_cities = {city.id: city for city in City.objects.all()} countries_to_create = [] regions_to_create = [] countries_to_update = [] regions_to_update = [] + cities_to_create = [] + cities_to_update = [] processed_country_codes = set() processed_region_ids = set() + processed_city_ids = set() for country in data: country_code = country['iso2'] @@ -102,6 +118,11 @@ class Command(BaseCommand): latitude = round(float(state['latitude']), 6) if state['latitude'] else None longitude = round(float(state['longitude']), 6) if state['longitude'] else None + # Check for duplicate regions + if state_id in processed_region_ids: + self.stdout.write(self.style.ERROR(f'State {state_id} already processed')) + continue + processed_region_ids.add(state_id) if state_id in existing_regions: @@ -121,6 +142,39 @@ class Command(BaseCommand): ) regions_to_create.append(region_obj) self.stdout.write(self.style.SUCCESS(f'State {state_id} prepared')) + + if 'cities' in state and len(state['cities']) > 0: + for city in state['cities']: + city_id = f"{state_id}-{city['id']}" + city_name = city['name'] + latitude = round(float(city['latitude']), 6) if city['latitude'] else None + longitude = round(float(city['longitude']), 6) if city['longitude'] else None + + # Check for duplicate cities + if city_id in processed_city_ids: + self.stdout.write(self.style.ERROR(f'City {city_id} already processed')) + continue + + processed_city_ids.add(city_id) + + if city_id in existing_cities: + city_obj = existing_cities[city_id] + city_obj.name = city_name + city_obj.region = region_obj + city_obj.longitude = longitude + city_obj.latitude = latitude + cities_to_update.append(city_obj) + else: + city_obj = City( + id=city_id, + name=city_name, + region=region_obj, + longitude=longitude, + latitude=latitude + ) + cities_to_create.append(city_obj) + self.stdout.write(self.style.SUCCESS(f'City {city_id} prepared')) + else: state_id = f"{country_code}-00" processed_region_ids.add(state_id) @@ -141,13 +195,16 @@ class Command(BaseCommand): # Bulk create new countries and regions Country.objects.bulk_create(countries_to_create) Region.objects.bulk_create(regions_to_create) + City.objects.bulk_create(cities_to_create) # Bulk update existing countries and regions Country.objects.bulk_update(countries_to_update, ['name', 'subregion', 'capital']) Region.objects.bulk_update(regions_to_update, ['name', 'country', 'longitude', 'latitude']) + City.objects.bulk_update(cities_to_update, ['name', 'region', 'longitude', 'latitude']) # Delete countries and regions that are no longer in the data Country.objects.exclude(country_code__in=processed_country_codes).delete() Region.objects.exclude(id__in=processed_region_ids).delete() + City.objects.exclude(id__in=processed_city_ids).delete() self.stdout.write(self.style.SUCCESS('All data imported successfully')) \ No newline at end of file diff --git a/backend/server/worldtravel/migrations/0012_city.py b/backend/server/worldtravel/migrations/0012_city.py new file mode 100644 index 00000000..d14b0883 --- /dev/null +++ b/backend/server/worldtravel/migrations/0012_city.py @@ -0,0 +1,27 @@ +# Generated by Django 5.0.8 on 2025-01-09 15:11 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('worldtravel', '0011_country_latitude_country_longitude'), + ] + + operations = [ + migrations.CreateModel( + name='City', + fields=[ + ('id', models.CharField(primary_key=True, serialize=False)), + ('name', models.CharField(max_length=100)), + ('longitude', models.DecimalField(blank=True, decimal_places=6, max_digits=9, null=True)), + ('latitude', models.DecimalField(blank=True, decimal_places=6, max_digits=9, null=True)), + ('region', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='worldtravel.region')), + ], + options={ + 'verbose_name_plural': 'Cities', + }, + ), + ] diff --git a/backend/server/worldtravel/models.py b/backend/server/worldtravel/models.py index 2acc629c..296b3bde 100644 --- a/backend/server/worldtravel/models.py +++ b/backend/server/worldtravel/models.py @@ -34,6 +34,19 @@ class Region(models.Model): def __str__(self): return self.name + +class City(models.Model): + id = models.CharField(primary_key=True) + name = models.CharField(max_length=100) + region = models.ForeignKey(Region, on_delete=models.CASCADE) + longitude = models.DecimalField(max_digits=9, decimal_places=6, null=True, blank=True) + latitude = models.DecimalField(max_digits=9, decimal_places=6, null=True, blank=True) + + class Meta: + verbose_name_plural = "Cities" + + def __str__(self): + return self.name class VisitedRegion(models.Model): id = models.AutoField(primary_key=True) diff --git a/backend/server/worldtravel/serializers.py b/backend/server/worldtravel/serializers.py index 70f569bb..134658a1 100644 --- a/backend/server/worldtravel/serializers.py +++ b/backend/server/worldtravel/serializers.py @@ -1,5 +1,5 @@ import os -from .models import Country, Region, VisitedRegion +from .models import Country, Region, VisitedRegion, City from rest_framework import serializers from main.utils import CustomModelSerializer @@ -38,6 +38,12 @@ class RegionSerializer(serializers.ModelSerializer): fields = '__all__' read_only_fields = ['id', 'name', 'country', 'longitude', 'latitude'] +class CitySerializer(serializers.ModelSerializer): + class Meta: + model = City + fields = '__all__' + read_only_fields = ['id', 'name', 'region', 'longitude', 'latitude'] + class VisitedRegionSerializer(CustomModelSerializer): longitude = serializers.DecimalField(source='region.longitude', max_digits=9, decimal_places=6, read_only=True) latitude = serializers.DecimalField(source='region.latitude', max_digits=9, decimal_places=6, read_only=True) diff --git a/backend/server/worldtravel/urls.py b/backend/server/worldtravel/urls.py index 46fe197d..e20717c1 100644 --- a/backend/server/worldtravel/urls.py +++ b/backend/server/worldtravel/urls.py @@ -2,8 +2,7 @@ from django.urls import include, path from rest_framework.routers import DefaultRouter -from .views import CountryViewSet, RegionViewSet, VisitedRegionViewSet, regions_by_country, visits_by_country - +from .views import CountryViewSet, RegionViewSet, VisitedRegionViewSet, regions_by_country, visits_by_country, cities_by_region router = DefaultRouter() router.register(r'countries', CountryViewSet, basename='countries') router.register(r'regions', RegionViewSet, basename='regions') @@ -12,5 +11,6 @@ router.register(r'visitedregion', VisitedRegionViewSet, basename='visitedregion' urlpatterns = [ path('', include(router.urls)), path('/regions/', regions_by_country, name='regions-by-country'), - path('/visits/', visits_by_country, name='visits-by-country') + path('/visits/', visits_by_country, name='visits-by-country'), + path('regions//cities/', cities_by_region, name='cities-by-region'), ] diff --git a/backend/server/worldtravel/views.py b/backend/server/worldtravel/views.py index 5a92e39d..0c41aa18 100644 --- a/backend/server/worldtravel/views.py +++ b/backend/server/worldtravel/views.py @@ -1,6 +1,6 @@ from django.shortcuts import render -from .models import Country, Region, VisitedRegion -from .serializers import CountrySerializer, RegionSerializer, VisitedRegionSerializer +from .models import Country, Region, VisitedRegion, City +from .serializers import CitySerializer, CountrySerializer, RegionSerializer, VisitedRegionSerializer from rest_framework import viewsets, status from rest_framework.permissions import IsAuthenticated from django.shortcuts import get_object_or_404 @@ -33,6 +33,14 @@ def visits_by_country(request, country_code): serializer = VisitedRegionSerializer(visits, many=True) return Response(serializer.data) +@api_view(['GET']) +@permission_classes([IsAuthenticated]) +def cities_by_region(request, region_id): + region = get_object_or_404(Region, id=region_id) + cities = City.objects.filter(region=region).order_by('name') + serializer = CitySerializer(cities, many=True) + return Response(serializer.data) + class CountryViewSet(viewsets.ReadOnlyModelViewSet): queryset = Country.objects.all().order_by('name') serializer_class = CountrySerializer diff --git a/frontend/src/lib/components/CityCard.svelte b/frontend/src/lib/components/CityCard.svelte new file mode 100644 index 00000000..619a27ad --- /dev/null +++ b/frontend/src/lib/components/CityCard.svelte @@ -0,0 +1,36 @@ + + +
    +
    +

    {city.name}

    +
    +
    {city.region}
    + +
    + +
    + +
    +
    +
    diff --git a/frontend/src/lib/components/RegionCard.svelte b/frontend/src/lib/components/RegionCard.svelte index c9754f07..f8fc3334 100644 --- a/frontend/src/lib/components/RegionCard.svelte +++ b/frontend/src/lib/components/RegionCard.svelte @@ -4,12 +4,18 @@ import type { Region, VisitedRegion } from '$lib/types'; import { createEventDispatcher } from 'svelte'; const dispatch = createEventDispatcher(); + import { t } from 'svelte-i18n'; export let region: Region; export let visited: boolean; export let visit_id: number | undefined | null; + function goToCity() { + console.log(region); + goto(`/worldtravel/${region.id.split('-')[0]}/${region.id}`); + } + async function markVisited() { let res = await fetch(`/worldtravel?/markVisited`, { method: 'POST', @@ -65,11 +71,16 @@
    {#if !visited} - + {/if} {#if visited} - + {/if} +

    diff --git a/frontend/src/lib/types.ts b/frontend/src/lib/types.ts index afac6a14..aa244d3f 100644 --- a/frontend/src/lib/types.ts +++ b/frontend/src/lib/types.ts @@ -57,13 +57,21 @@ export type Country = { }; export type Region = { - id: number; + id: string; name: string; - country: number; + country: string; latitude: number; longitude: number; }; +export type City = { + id: string; + name: string; + latitude: number | null; + longitude: number | null; + region: string; +}; + export type VisitedRegion = { id: number; region: number; diff --git a/frontend/src/locales/en.json b/frontend/src/locales/en.json index a46986d2..9f8c2e9d 100644 --- a/frontend/src/locales/en.json +++ b/frontend/src/locales/en.json @@ -275,7 +275,8 @@ "completely_visited": "Completely Visited", "all_subregions": "All Subregions", "clear_search": "Clear Search", - "no_countries_found": "No countries found" + "no_countries_found": "No countries found", + "view_cities": "View Cities" }, "auth": { "username": "Username", diff --git a/frontend/src/routes/worldtravel/[id]/[id]/+page.server.ts b/frontend/src/routes/worldtravel/[id]/[id]/+page.server.ts new file mode 100644 index 00000000..79a0d954 --- /dev/null +++ b/frontend/src/routes/worldtravel/[id]/[id]/+page.server.ts @@ -0,0 +1,65 @@ +const PUBLIC_SERVER_URL = process.env['PUBLIC_SERVER_URL']; +import type { City, Country, Region, VisitedRegion } from '$lib/types'; +import { redirect } from '@sveltejs/kit'; +import type { PageServerLoad } from './$types'; + +const endpoint = PUBLIC_SERVER_URL || 'http://localhost:8000'; + +export const load = (async (event) => { + const id = event.params.id.toUpperCase(); + + let cities: City[] = []; + let region = {} as Region; + + let sessionId = event.cookies.get('sessionid'); + + if (!sessionId) { + return redirect(302, '/login'); + } + + let res = await fetch(`${endpoint}/api/regions/${id}/cities/`, { + method: 'GET', + headers: { + Cookie: `sessionid=${sessionId}` + } + }); + if (!res.ok) { + console.error('Failed to fetch regions'); + return redirect(302, '/404'); + } else { + cities = (await res.json()) as City[]; + } + + // res = await fetch(`${endpoint}/api/${id}/visits/`, { + // method: 'GET', + // headers: { + // Cookie: `sessionid=${sessionId}` + // } + // }); + // if (!res.ok) { + // console.error('Failed to fetch visited regions'); + // return { status: 500 }; + // } else { + // visitedRegions = (await res.json()) as VisitedRegion[]; + // } + + res = await fetch(`${endpoint}/api/regions/${id}/`, { + method: 'GET', + headers: { + Cookie: `sessionid=${sessionId}` + } + }); + if (!res.ok) { + console.error('Failed to fetch country'); + return { status: 500 }; + } else { + region = (await res.json()) as Region; + } + + return { + props: { + cities, + region + } + }; +}) satisfies PageServerLoad; diff --git a/frontend/src/routes/worldtravel/[id]/[id]/+page.svelte b/frontend/src/routes/worldtravel/[id]/[id]/+page.svelte new file mode 100644 index 00000000..80b41089 --- /dev/null +++ b/frontend/src/routes/worldtravel/[id]/[id]/+page.svelte @@ -0,0 +1,148 @@ + + +

    {$t('worldtravel.country_list')}

    + +

    + {allCities.length} + Cities Found +

    + + + +
    + + {#if searchQuery.length > 0} + +
    + +
    + {/if} +
    + +
    + + + + {#each filteredCities as city} + {#if city.latitude && city.longitude} + + + {city.name} + + + {/if} + {/each} + +
    + +
    + {#each filteredCities as city} + + {/each} +
    + +{#if filteredCities.length === 0} +

    {$t('worldtravel.no_countries_found')}

    +{/if} + + + Countries | World Travel + + From 80cec30fda19c819e36ad42d900477fdd430ff00 Mon Sep 17 00:00:00 2001 From: Sean Morley Date: Thu, 9 Jan 2025 12:38:29 -0500 Subject: [PATCH 036/209] feat: add VisitedCity model and serializer, update admin interface, and enhance city visit tracking functionality --- backend/server/adventures/admin.py | 3 +- .../migrations/0013_visitedcity.py | 24 +++ backend/server/worldtravel/models.py | 19 +- backend/server/worldtravel/serializers.py | 12 +- backend/server/worldtravel/urls.py | 4 +- backend/server/worldtravel/views.py | 53 +++++- frontend/src/lib/components/CityCard.svelte | 59 ++++-- frontend/src/lib/components/RegionCard.svelte | 40 ++-- frontend/src/lib/types.ts | 11 +- frontend/src/locales/en.json | 3 +- frontend/src/routes/api/[...path]/+server.ts | 2 +- .../src/routes/worldtravel/+page.server.ts | 77 -------- .../src/routes/worldtravel/[id]/+page.svelte | 62 +++--- .../worldtravel/[id]/[id]/+page.server.ts | 32 ++-- .../routes/worldtravel/[id]/[id]/+page.svelte | 177 +++++++++++++----- 15 files changed, 344 insertions(+), 234 deletions(-) create mode 100644 backend/server/worldtravel/migrations/0013_visitedcity.py diff --git a/backend/server/adventures/admin.py b/backend/server/adventures/admin.py index e809aa4f..5c393014 100644 --- a/backend/server/adventures/admin.py +++ b/backend/server/adventures/admin.py @@ -2,7 +2,7 @@ import os from django.contrib import admin 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, City +from worldtravel.models import Country, Region, VisitedRegion, City, VisitedCity from allauth.account.decorators import secure_admin_login admin.autodiscover() @@ -138,6 +138,7 @@ admin.site.register(ChecklistItem) admin.site.register(AdventureImage, AdventureImageAdmin) admin.site.register(Category, CategoryAdmin) admin.site.register(City, CityAdmin) +admin.site.register(VisitedCity) admin.site.site_header = 'AdventureLog Admin' admin.site.site_title = 'AdventureLog Admin Site' diff --git a/backend/server/worldtravel/migrations/0013_visitedcity.py b/backend/server/worldtravel/migrations/0013_visitedcity.py new file mode 100644 index 00000000..3b1e2946 --- /dev/null +++ b/backend/server/worldtravel/migrations/0013_visitedcity.py @@ -0,0 +1,24 @@ +# Generated by Django 5.0.8 on 2025-01-09 17:00 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('worldtravel', '0012_city'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='VisitedCity', + fields=[ + ('id', models.AutoField(primary_key=True, serialize=False)), + ('city', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='worldtravel.city')), + ('user_id', models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + ), + ] diff --git a/backend/server/worldtravel/models.py b/backend/server/worldtravel/models.py index 296b3bde..9e83f591 100644 --- a/backend/server/worldtravel/models.py +++ b/backend/server/worldtravel/models.py @@ -60,4 +60,21 @@ class VisitedRegion(models.Model): def save(self, *args, **kwargs): if VisitedRegion.objects.filter(user_id=self.user_id, region=self.region).exists(): raise ValidationError("Region already visited by user.") - super().save(*args, **kwargs) \ No newline at end of file + super().save(*args, **kwargs) + +class VisitedCity(models.Model): + id = models.AutoField(primary_key=True) + user_id = models.ForeignKey( + User, on_delete=models.CASCADE, default=default_user_id) + city = models.ForeignKey(City, on_delete=models.CASCADE) + + def __str__(self): + return f'{self.city.name} ({self.city.region.name}) visited by: {self.user_id.username}' + + def save(self, *args, **kwargs): + if VisitedCity.objects.filter(user_id=self.user_id, city=self.city).exists(): + raise ValidationError("City already visited by user.") + super().save(*args, **kwargs) + + class Meta: + verbose_name_plural = "Visited Cities" \ No newline at end of file diff --git a/backend/server/worldtravel/serializers.py b/backend/server/worldtravel/serializers.py index 134658a1..cccf7546 100644 --- a/backend/server/worldtravel/serializers.py +++ b/backend/server/worldtravel/serializers.py @@ -1,5 +1,5 @@ import os -from .models import Country, Region, VisitedRegion, City +from .models import Country, Region, VisitedRegion, City, VisitedCity from rest_framework import serializers from main.utils import CustomModelSerializer @@ -52,4 +52,14 @@ class VisitedRegionSerializer(CustomModelSerializer): class Meta: model = VisitedRegion fields = ['id', 'user_id', 'region', 'longitude', 'latitude', 'name'] + read_only_fields = ['user_id', 'id', 'longitude', 'latitude', 'name'] + +class VisitedCitySerializer(CustomModelSerializer): + longitude = serializers.DecimalField(source='city.longitude', max_digits=9, decimal_places=6, read_only=True) + latitude = serializers.DecimalField(source='city.latitude', max_digits=9, decimal_places=6, read_only=True) + name = serializers.CharField(source='city.name', read_only=True) + + class Meta: + model = VisitedCity + fields = ['id', 'user_id', 'city', 'longitude', 'latitude', 'name'] read_only_fields = ['user_id', 'id', 'longitude', 'latitude', 'name'] \ No newline at end of file diff --git a/backend/server/worldtravel/urls.py b/backend/server/worldtravel/urls.py index e20717c1..f28bedab 100644 --- a/backend/server/worldtravel/urls.py +++ b/backend/server/worldtravel/urls.py @@ -2,15 +2,17 @@ from django.urls import include, path from rest_framework.routers import DefaultRouter -from .views import CountryViewSet, RegionViewSet, VisitedRegionViewSet, regions_by_country, visits_by_country, cities_by_region +from .views import CountryViewSet, RegionViewSet, VisitedRegionViewSet, regions_by_country, visits_by_country, cities_by_region, VisitedCityViewSet, visits_by_region router = DefaultRouter() router.register(r'countries', CountryViewSet, basename='countries') router.register(r'regions', RegionViewSet, basename='regions') router.register(r'visitedregion', VisitedRegionViewSet, basename='visitedregion') +router.register(r'visitedcity', VisitedCityViewSet, basename='visitedcity') urlpatterns = [ path('', include(router.urls)), path('/regions/', regions_by_country, name='regions-by-country'), path('/visits/', visits_by_country, name='visits-by-country'), path('regions//cities/', cities_by_region, name='cities-by-region'), + path('regions//cities/visits/', visits_by_region, name='visits-by-region'), ] diff --git a/backend/server/worldtravel/views.py b/backend/server/worldtravel/views.py index 0c41aa18..6cb2575c 100644 --- a/backend/server/worldtravel/views.py +++ b/backend/server/worldtravel/views.py @@ -1,6 +1,6 @@ from django.shortcuts import render -from .models import Country, Region, VisitedRegion, City -from .serializers import CitySerializer, CountrySerializer, RegionSerializer, VisitedRegionSerializer +from .models import Country, Region, VisitedRegion, City, VisitedCity +from .serializers import CitySerializer, CountrySerializer, RegionSerializer, VisitedRegionSerializer, VisitedCitySerializer from rest_framework import viewsets, status from rest_framework.permissions import IsAuthenticated from django.shortcuts import get_object_or_404 @@ -41,6 +41,15 @@ def cities_by_region(request, region_id): serializer = CitySerializer(cities, many=True) return Response(serializer.data) +@api_view(['GET']) +@permission_classes([IsAuthenticated]) +def visits_by_region(request, region_id): + region = get_object_or_404(Region, id=region_id) + visits = VisitedCity.objects.filter(city__region=region, user_id=request.user.id) + + serializer = VisitedCitySerializer(visits, many=True) + return Response(serializer.data) + class CountryViewSet(viewsets.ReadOnlyModelViewSet): queryset = Country.objects.all().order_by('name') serializer_class = CountrySerializer @@ -101,4 +110,42 @@ class VisitedRegionViewSet(viewsets.ModelViewSet): serializer.is_valid(raise_exception=True) self.perform_create(serializer) headers = self.get_success_headers(serializer.data) - return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers) \ No newline at end of file + return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers) + + def destroy(self, request, **kwargs): + # delete by region id + region = get_object_or_404(Region, id=kwargs['pk']) + visited_region = VisitedRegion.objects.filter(user_id=request.user.id, region=region) + if visited_region.exists(): + visited_region.delete() + return Response(status=status.HTTP_204_NO_CONTENT) + else: + return Response({"error": "Visited region not found."}, status=status.HTTP_404_NOT_FOUND) + +class VisitedCityViewSet(viewsets.ModelViewSet): + serializer_class = VisitedCitySerializer + permission_classes = [IsAuthenticated] + + def get_queryset(self): + return VisitedCity.objects.filter(user_id=self.request.user.id) + + def perform_create(self, serializer): + serializer.save(user_id=self.request.user) + + def create(self, request, *args, **kwargs): + request.data['user_id'] = request.user + serializer = self.get_serializer(data=request.data) + serializer.is_valid(raise_exception=True) + self.perform_create(serializer) + headers = self.get_success_headers(serializer.data) + return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers) + + def destroy(self, request, **kwargs): + # delete by city id + city = get_object_or_404(City, id=kwargs['pk']) + visited_city = VisitedCity.objects.filter(user_id=request.user.id, city=city) + if visited_city.exists(): + visited_city.delete() + return Response(status=status.HTTP_204_NO_CONTENT) + else: + return Response({"error": "Visited city not found."}, status=status.HTTP_404_NOT_FOUND) \ No newline at end of file diff --git a/frontend/src/lib/components/CityCard.svelte b/frontend/src/lib/components/CityCard.svelte index 619a27ad..20619e01 100644 --- a/frontend/src/lib/components/CityCard.svelte +++ b/frontend/src/lib/components/CityCard.svelte @@ -1,13 +1,42 @@

    {city.name}

    -
    {city.region}
    - +
    {city.id}
    + {#if !visited} + + {/if} + {#if visited} + + {/if}
    diff --git a/frontend/src/lib/components/RegionCard.svelte b/frontend/src/lib/components/RegionCard.svelte index f8fc3334..5351c840 100644 --- a/frontend/src/lib/components/RegionCard.svelte +++ b/frontend/src/lib/components/RegionCard.svelte @@ -9,55 +9,39 @@ export let region: Region; export let visited: boolean; - export let visit_id: number | undefined | null; - function goToCity() { console.log(region); goto(`/worldtravel/${region.id.split('-')[0]}/${region.id}`); } async function markVisited() { - let res = await fetch(`/worldtravel?/markVisited`, { + let res = await fetch(`/api/visitedregion/`, { + headers: { 'Content-Type': 'application/json' }, method: 'POST', - body: JSON.stringify({ regionId: region.id }) + body: JSON.stringify({ region: region.id }) }); if (res.ok) { - // visited = true; - const result = await res.json(); - const data = JSON.parse(result.data); - if (data[1] !== undefined) { - console.log('New adventure created with id:', data[3]); - let visit_id = data[3]; - let region_id = data[5]; - let user_id = data[4]; - - let newVisit: VisitedRegion = { - id: visit_id, - region: region_id, - user_id: user_id, - longitude: 0, - latitude: 0, - name: '' - }; - addToast('success', `Visit to ${region.name} marked`); - dispatch('visit', newVisit); - } + visited = true; + let data = await res.json(); + addToast('success', `Visit to ${region.name} marked`); + dispatch('visit', data); } else { console.error('Failed to mark region as visited'); addToast('error', `Failed to mark visit to ${region.name}`); } } async function removeVisit() { - let res = await fetch(`/worldtravel?/removeVisited`, { - method: 'POST', - body: JSON.stringify({ visitId: visit_id }) + let res = await fetch(`/api/visitedregion/${region.id}`, { + headers: { 'Content-Type': 'application/json' }, + method: 'DELETE' }); if (res.ok) { visited = false; addToast('info', `Visit to ${region.name} removed`); - dispatch('remove', null); + dispatch('remove', region); } else { console.error('Failed to remove visit'); + addToast('error', `Failed to remove visit to ${region.name}`); } } diff --git a/frontend/src/lib/types.ts b/frontend/src/lib/types.ts index aa244d3f..ac153ca4 100644 --- a/frontend/src/lib/types.ts +++ b/frontend/src/lib/types.ts @@ -74,7 +74,16 @@ export type City = { export type VisitedRegion = { id: number; - region: number; + region: string; + user_id: string; + longitude: number; + latitude: number; + name: string; +}; + +export type VisitedCity = { + id: number; + city: string; user_id: string; longitude: number; latitude: number; diff --git a/frontend/src/locales/en.json b/frontend/src/locales/en.json index 9f8c2e9d..90deaefd 100644 --- a/frontend/src/locales/en.json +++ b/frontend/src/locales/en.json @@ -276,7 +276,8 @@ "all_subregions": "All Subregions", "clear_search": "Clear Search", "no_countries_found": "No countries found", - "view_cities": "View Cities" + "view_cities": "View Cities", + "no_cities_found": "No cities found" }, "auth": { "username": "Username", diff --git a/frontend/src/routes/api/[...path]/+server.ts b/frontend/src/routes/api/[...path]/+server.ts index c77044c7..815d4a7f 100644 --- a/frontend/src/routes/api/[...path]/+server.ts +++ b/frontend/src/routes/api/[...path]/+server.ts @@ -12,7 +12,7 @@ export async function GET(event) { /** @type {import('./$types').RequestHandler} */ export async function POST({ url, params, request, fetch, cookies }) { - const searchParam = url.search ? `${url.search}&format=json` : '?format=json'; + const searchParam = url.search ? `${url.search}` : ''; return handleRequest(url, params, request, fetch, cookies, searchParam, true); } diff --git a/frontend/src/routes/worldtravel/+page.server.ts b/frontend/src/routes/worldtravel/+page.server.ts index ec696bfa..b84ae39c 100644 --- a/frontend/src/routes/worldtravel/+page.server.ts +++ b/frontend/src/routes/worldtravel/+page.server.ts @@ -30,80 +30,3 @@ export const load = (async (event) => { } } }) satisfies PageServerLoad; - -export const actions: Actions = { - markVisited: async (event) => { - const body = await event.request.json(); - - if (!body || !body.regionId) { - return { - status: 400 - }; - } - - let sessionId = event.cookies.get('sessionid'); - - if (!event.locals.user || !sessionId) { - return redirect(302, '/login'); - } - - let csrfToken = await fetchCSRFToken(); - - const res = await fetch(`${endpoint}/api/visitedregion/`, { - method: 'POST', - headers: { - Cookie: `sessionid=${sessionId}; csrftoken=${csrfToken}`, - 'Content-Type': 'application/json', - 'X-CSRFToken': csrfToken - }, - body: JSON.stringify({ region: body.regionId }) - }); - - if (!res.ok) { - console.error('Failed to mark country as visited'); - return { status: 500 }; - } else { - return { - status: 200, - data: await res.json() - }; - } - }, - removeVisited: async (event) => { - const body = await event.request.json(); - - if (!body || !body.visitId) { - return { - status: 400 - }; - } - - const visitId = body.visitId as number; - - let sessionId = event.cookies.get('sessionid'); - - if (!event.locals.user || !sessionId) { - return redirect(302, '/login'); - } - - let csrfToken = await fetchCSRFToken(); - - const res = await fetch(`${endpoint}/api/visitedregion/${visitId}/`, { - method: 'DELETE', - headers: { - Cookie: `sessionid=${sessionId}; csrftoken=${csrfToken}`, - 'Content-Type': 'application/json', - 'X-CSRFToken': csrfToken - } - }); - - if (res.status !== 204) { - console.error('Failed to remove country from visited'); - return { status: 500 }; - } else { - return { - status: 200 - }; - } - } -}; diff --git a/frontend/src/routes/worldtravel/[id]/+page.svelte b/frontend/src/routes/worldtravel/[id]/+page.svelte index 53224e16..8092df93 100644 --- a/frontend/src/routes/worldtravel/[id]/+page.svelte +++ b/frontend/src/routes/worldtravel/[id]/+page.svelte @@ -24,7 +24,7 @@ visitedRegions = visitedRegions.filter( (visitedRegion) => visitedRegion.region !== region.id ); - removeVisit(region, visitedRegion.id); + removeVisit(region); } else { markVisited(region); } @@ -32,48 +32,32 @@ } async function markVisited(region: Region) { - let res = await fetch(`/worldtravel?/markVisited`, { + let res = await fetch(`/api/visitedregion/`, { method: 'POST', - body: JSON.stringify({ regionId: region.id }) + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ region: region.id }) }); - if (res.ok) { - // visited = true; - const result = await res.json(); - const data = JSON.parse(result.data); - if (data[1] !== undefined) { - console.log('New adventure created with id:', data[3]); - let visit_id = data[3]; - let region_id = data[5]; - let user_id = data[4]; - - visitedRegions = [ - ...visitedRegions, - { - id: visit_id, - region: region_id, - user_id: user_id, - longitude: 0, - latitude: 0, - name: '' - } - ]; - - addToast('success', `Visit to ${region.name} marked`); - } - } else { + if (!res.ok) { console.error('Failed to mark region as visited'); addToast('error', `Failed to mark visit to ${region.name}`); + return; + } else { + visitedRegions = [...visitedRegions, await res.json()]; + addToast('success', `Visit to ${region.name} marked`); } } - async function removeVisit(region: Region, visitId: number) { - let res = await fetch(`/worldtravel?/removeVisited`, { - method: 'POST', - body: JSON.stringify({ visitId: visitId }) + async function removeVisit(region: Region) { + let res = await fetch(`/api/visitedregion/${region.id}`, { + headers: { 'Content-Type': 'application/json' }, + method: 'DELETE' }); - if (res.ok) { - addToast('info', `Visit to ${region.name} removed`); - } else { + if (!res.ok) { console.error('Failed to remove visit'); + addToast('error', `Failed to remove visit to ${region.name}`); + return; + } else { + visitedRegions = visitedRegions.filter((visitedRegion) => visitedRegion.region !== region.id); + addToast('info', `Visit to ${region.name} removed`); } } @@ -110,8 +94,12 @@ visitedRegions = [...visitedRegions, e.detail]; numVisitedRegions++; }} - visit_id={visitedRegions.find((visitedRegion) => visitedRegion.region === region.id)?.id} - on:remove={() => numVisitedRegions--} + on:remove={() => { + visitedRegions = visitedRegions.filter( + (visitedRegion) => visitedRegion.region !== region.id + ); + numVisitedRegions--; + }} /> {/each}
    diff --git a/frontend/src/routes/worldtravel/[id]/[id]/+page.server.ts b/frontend/src/routes/worldtravel/[id]/[id]/+page.server.ts index 79a0d954..b92b47ae 100644 --- a/frontend/src/routes/worldtravel/[id]/[id]/+page.server.ts +++ b/frontend/src/routes/worldtravel/[id]/[id]/+page.server.ts @@ -1,5 +1,5 @@ const PUBLIC_SERVER_URL = process.env['PUBLIC_SERVER_URL']; -import type { City, Country, Region, VisitedRegion } from '$lib/types'; +import type { City, Country, Region, VisitedCity, VisitedRegion } from '$lib/types'; import { redirect } from '@sveltejs/kit'; import type { PageServerLoad } from './$types'; @@ -10,6 +10,7 @@ export const load = (async (event) => { let cities: City[] = []; let region = {} as Region; + let visitedCities: VisitedCity[] = []; let sessionId = event.cookies.get('sessionid'); @@ -30,19 +31,6 @@ export const load = (async (event) => { cities = (await res.json()) as City[]; } - // res = await fetch(`${endpoint}/api/${id}/visits/`, { - // method: 'GET', - // headers: { - // Cookie: `sessionid=${sessionId}` - // } - // }); - // if (!res.ok) { - // console.error('Failed to fetch visited regions'); - // return { status: 500 }; - // } else { - // visitedRegions = (await res.json()) as VisitedRegion[]; - // } - res = await fetch(`${endpoint}/api/regions/${id}/`, { method: 'GET', headers: { @@ -56,10 +44,24 @@ export const load = (async (event) => { region = (await res.json()) as Region; } + res = await fetch(`${endpoint}/api/regions/${region.id}/cities/visits/`, { + method: 'GET', + headers: { + Cookie: `sessionid=${sessionId}` + } + }); + if (!res.ok) { + console.error('Failed to fetch visited regions'); + return { status: 500 }; + } else { + visitedCities = (await res.json()) as VisitedCity[]; + } + return { props: { cities, - region + region, + visitedCities } }; }) satisfies PageServerLoad; diff --git a/frontend/src/routes/worldtravel/[id]/[id]/+page.svelte b/frontend/src/routes/worldtravel/[id]/[id]/+page.svelte index 80b41089..aa230e50 100644 --- a/frontend/src/routes/worldtravel/[id]/[id]/+page.svelte +++ b/frontend/src/routes/worldtravel/[id]/[id]/+page.svelte @@ -1,8 +1,7 @@ -

    {$t('worldtravel.country_list')}

    +

    Cities in {data.props?.region.name}

    {allCities.length} Cities Found

    +
    +
    +
    +
    City Stats
    +
    {numVisitedCities}/{numCities} Visited
    + {#if numCities === numVisitedCities} +
    You've visited all cities in {data.props?.region.name} 🎉!
    + {:else} +
    Keep exploring!
    + {/if} +
    +
    +
    + -
    - - {#if searchQuery.length > 0} - -
    - -
    - {/if} -
    +{#if allCities.length > 0} +
    + + {#if searchQuery.length > 0} + +
    + +
    + {/if} +
    -
    - +
    + - - {#each filteredCities as city} - {#if city.latitude && city.longitude} - - - {city.name} - - - {/if} - {/each} - -
    + + {#each filteredCities as city} + {#if city.latitude && city.longitude} + + + {city.name} + + + {/if} + {/each} + +
    +{/if}
    {#each filteredCities as city} - + visitedCity.city === city.id)} + on:visit={(e) => { + visitedCities = [...visitedCities, e.detail]; + }} + on:remove={() => { + visitedCities = visitedCities.filter((visitedCity) => visitedCity.city !== city.id); + }} + /> {/each}
    {#if filteredCities.length === 0} -

    {$t('worldtravel.no_countries_found')}

    +

    {$t('worldtravel.no_cities_found')}

    {/if} From abe870506f74991464e01711b01edb2c456d9a73 Mon Sep 17 00:00:00 2001 From: Sean Morley Date: Thu, 9 Jan 2025 13:53:16 -0500 Subject: [PATCH 037/209] feat: enhance region visit tracking with improved toast messages, update localization, and modify page titles --- .../management/commands/download-countries.py | 2 +- .../0014_alter_visitedcity_options.py | 17 ++++++++++++++ frontend/src/lib/components/RegionCard.svelte | 15 +++++++----- frontend/src/locales/en.json | 12 +++++++++- .../src/routes/worldtravel/[id]/+page.svelte | 23 +++++++++++-------- .../routes/worldtravel/[id]/[id]/+page.svelte | 2 +- 6 files changed, 52 insertions(+), 19 deletions(-) create mode 100644 backend/server/worldtravel/migrations/0014_alter_visitedcity_options.py diff --git a/backend/server/worldtravel/management/commands/download-countries.py b/backend/server/worldtravel/management/commands/download-countries.py index 1b741bfd..a11cd9cc 100644 --- a/backend/server/worldtravel/management/commands/download-countries.py +++ b/backend/server/worldtravel/management/commands/download-countries.py @@ -57,7 +57,7 @@ class Command(BaseCommand): elif os.path.getsize(countries_json_path) == 0: self.stdout.write(self.style.ERROR('countries+regions+states.json is empty')) else: - self.stdout.write(self.style.SUCCESS('countries+regions+states.json already exists')) + self.stdout.write(self.style.SUCCESS('Latest country, region, and state data already downloaded.')) return with open(countries_json_path, 'r') as f: diff --git a/backend/server/worldtravel/migrations/0014_alter_visitedcity_options.py b/backend/server/worldtravel/migrations/0014_alter_visitedcity_options.py new file mode 100644 index 00000000..f2ea944b --- /dev/null +++ b/backend/server/worldtravel/migrations/0014_alter_visitedcity_options.py @@ -0,0 +1,17 @@ +# Generated by Django 5.0.8 on 2025-01-09 18:08 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('worldtravel', '0013_visitedcity'), + ] + + operations = [ + migrations.AlterModelOptions( + name='visitedcity', + options={'verbose_name_plural': 'Visited Cities'}, + ), + ] diff --git a/frontend/src/lib/components/RegionCard.svelte b/frontend/src/lib/components/RegionCard.svelte index 5351c840..536427b7 100644 --- a/frontend/src/lib/components/RegionCard.svelte +++ b/frontend/src/lib/components/RegionCard.svelte @@ -23,11 +23,14 @@ if (res.ok) { visited = true; let data = await res.json(); - addToast('success', `Visit to ${region.name} marked`); + addToast( + 'success', + `${$t('worldtravel.visit_to')} ${region.name} ${$t('worldtravel.marked_visited')}` + ); dispatch('visit', data); } else { - console.error('Failed to mark region as visited'); - addToast('error', `Failed to mark visit to ${region.name}`); + console.error($t('worldtravel.region_failed_visited')); + addToast('error', `${$t('worldtravel.failed_to_mark_visit')} ${region.name}`); } } async function removeVisit() { @@ -37,11 +40,11 @@ }); if (res.ok) { visited = false; - addToast('info', `Visit to ${region.name} removed`); + addToast('info', `${$t('worldtravel.visit_to')} ${region.name} ${$t('worldtravel.removed')}`); dispatch('remove', region); } else { - console.error('Failed to remove visit'); - addToast('error', `Failed to remove visit to ${region.name}`); + console.error($t('worldtravel.visit_remove_failed')); + addToast('error', `${$t('worldtravel.failed_to_remove_visit')} ${region.name}`); } } diff --git a/frontend/src/locales/en.json b/frontend/src/locales/en.json index 90deaefd..5ac53670 100644 --- a/frontend/src/locales/en.json +++ b/frontend/src/locales/en.json @@ -277,7 +277,17 @@ "clear_search": "Clear Search", "no_countries_found": "No countries found", "view_cities": "View Cities", - "no_cities_found": "No cities found" + "no_cities_found": "No cities found", + "visit_to": "Visit to", + "region_failed_visited": "Failed to mark region as visited", + "failed_to_mark_visit": "Failed to mark visit to", + "visit_remove_failed": "Failed to remove visit", + "removed": "removed", + "failed_to_remove_visit": "Failed to remove visit to", + "marked_visited": "marked as visited", + "regions_in": "Regions in", + "region_stats": "Region Stats", + "all_visited": "You've visited all regions in" }, "auth": { "username": "Username", diff --git a/frontend/src/routes/worldtravel/[id]/+page.svelte b/frontend/src/routes/worldtravel/[id]/+page.svelte index 8092df93..f0ca9fbc 100644 --- a/frontend/src/routes/worldtravel/[id]/+page.svelte +++ b/frontend/src/routes/worldtravel/[id]/+page.svelte @@ -39,11 +39,14 @@ }); if (!res.ok) { console.error('Failed to mark region as visited'); - addToast('error', `Failed to mark visit to ${region.name}`); + addToast('error', `${$t('worldtravel.failed_to_mark_visit')} ${region.name}`); return; } else { visitedRegions = [...visitedRegions, await res.json()]; - addToast('success', `Visit to ${region.name} marked`); + addToast( + 'success', + `${$t('worldtravel.visit_to')} ${region.name} ${$t('worldtravel.marked_visited')}` + ); } } async function removeVisit(region: Region) { @@ -52,12 +55,12 @@ method: 'DELETE' }); if (!res.ok) { - console.error('Failed to remove visit'); - addToast('error', `Failed to remove visit to ${region.name}`); + console.error($t('worldtravel.region_failed_visited')); + addToast('error', `${$t('worldtravel.failed_to_mark_visit')} ${region.name}`); return; } else { visitedRegions = visitedRegions.filter((visitedRegion) => visitedRegion.region !== region.id); - addToast('info', `Visit to ${region.name} removed`); + addToast('info', `${$t('worldtravel.visit_to')} ${region.name} ${$t('worldtravel.removed')}`); } } @@ -70,16 +73,16 @@ ); -

    Regions in {country?.name}

    +

    {$t('worldtravel.regions_in')} {country?.name}

    -
    Region Stats
    -
    {numVisitedRegions}/{numRegions} Visited
    +
    {$t('worldtravel.region_stats')}
    +
    {numVisitedRegions}/{numRegions} {$t('adventures.visited')}
    {#if numRegions === numVisitedRegions} -
    You've visited all regions in {country?.name} 🎉!
    +
    {$t('worldtravel.all_visited')} {country?.name} 🎉!
    {:else} -
    Keep exploring!
    +
    {$t('adventures.keep_exploring')}
    {/if}
    diff --git a/frontend/src/routes/worldtravel/[id]/[id]/+page.svelte b/frontend/src/routes/worldtravel/[id]/[id]/+page.svelte index aa230e50..9801823e 100644 --- a/frontend/src/routes/worldtravel/[id]/[id]/+page.svelte +++ b/frontend/src/routes/worldtravel/[id]/[id]/+page.svelte @@ -218,6 +218,6 @@ {/if} - Countries | World Travel + Cities in {data.props?.region.name} | World Travel From 22790ae7c0b50307941e5793b4a4bfc9e0f68ae8 Mon Sep 17 00:00:00 2001 From: Sean Morley Date: Thu, 9 Jan 2025 14:58:45 -0500 Subject: [PATCH 038/209] feat: add num_cities field to RegionSerializer, update RegionCard to display city count, and enhance CSRF token handling --- backend/server/worldtravel/serializers.py | 6 +++++- frontend/src/hooks.server.ts | 1 + frontend/src/lib/components/RegionCard.svelte | 17 +++++++++++++---- frontend/src/lib/types.ts | 1 + frontend/src/locales/en.json | 3 ++- .../src/routes/_allauth/[...path]/+server.ts | 9 ++++++++- 6 files changed, 30 insertions(+), 7 deletions(-) diff --git a/backend/server/worldtravel/serializers.py b/backend/server/worldtravel/serializers.py index cccf7546..99c73798 100644 --- a/backend/server/worldtravel/serializers.py +++ b/backend/server/worldtravel/serializers.py @@ -33,10 +33,14 @@ class CountrySerializer(serializers.ModelSerializer): class RegionSerializer(serializers.ModelSerializer): + num_cities = serializers.SerializerMethodField() class Meta: model = Region fields = '__all__' - read_only_fields = ['id', 'name', 'country', 'longitude', 'latitude'] + read_only_fields = ['id', 'name', 'country', 'longitude', 'latitude', 'num_cities'] + + def get_num_cities(self, obj): + return City.objects.filter(region=obj).count() class CitySerializer(serializers.ModelSerializer): class Meta: diff --git a/frontend/src/hooks.server.ts b/frontend/src/hooks.server.ts index 91e1b607..12cd0171 100644 --- a/frontend/src/hooks.server.ts +++ b/frontend/src/hooks.server.ts @@ -3,6 +3,7 @@ import { sequence } from '@sveltejs/kit/hooks'; const PUBLIC_SERVER_URL = process.env['PUBLIC_SERVER_URL']; export const authHook: Handle = async ({ event, resolve }) => { + event.cookies.delete('csrftoken', { path: '/' }); try { let sessionid = event.cookies.get('sessionid'); diff --git a/frontend/src/lib/components/RegionCard.svelte b/frontend/src/lib/components/RegionCard.svelte index 536427b7..5acd3a9a 100644 --- a/frontend/src/lib/components/RegionCard.svelte +++ b/frontend/src/lib/components/RegionCard.svelte @@ -54,7 +54,14 @@ >

    {region.name}

    -

    {region.id}

    +
    +
    +

    {region.id}

    +
    +
    +

    {region.num_cities} {$t('worldtravel.cities')}

    +
    +
    {#if !visited} @@ -65,9 +72,11 @@ {#if visited} {/if} - + {#if region.num_cities > 0} + + {/if}
    diff --git a/frontend/src/lib/types.ts b/frontend/src/lib/types.ts index ac153ca4..e1e76271 100644 --- a/frontend/src/lib/types.ts +++ b/frontend/src/lib/types.ts @@ -62,6 +62,7 @@ export type Region = { country: string; latitude: number; longitude: number; + num_cities: number; }; export type City = { diff --git a/frontend/src/locales/en.json b/frontend/src/locales/en.json index 5ac53670..f3f8b494 100644 --- a/frontend/src/locales/en.json +++ b/frontend/src/locales/en.json @@ -287,7 +287,8 @@ "marked_visited": "marked as visited", "regions_in": "Regions in", "region_stats": "Region Stats", - "all_visited": "You've visited all regions in" + "all_visited": "You've visited all regions in", + "cities": "cities" }, "auth": { "username": "Username", diff --git a/frontend/src/routes/_allauth/[...path]/+server.ts b/frontend/src/routes/_allauth/[...path]/+server.ts index 681a3fa8..9b092058 100644 --- a/frontend/src/routes/_allauth/[...path]/+server.ts +++ b/frontend/src/routes/_allauth/[...path]/+server.ts @@ -53,18 +53,25 @@ async function handleRequest( const headers = new Headers(request.headers); + // Delete existing csrf cookie by setting an expired date + cookies.delete('csrftoken', { path: '/' }); + + // Generate a new csrf token (using your existing fetchCSRFToken function) const csrfToken = await fetchCSRFToken(); if (!csrfToken) { return json({ error: 'CSRF token is missing or invalid' }, { status: 400 }); } + // Set the new csrf token in both headers and cookies + const cookieHeader = `csrftoken=${csrfToken}; Path=/; HttpOnly; SameSite=Lax`; + try { const response = await fetch(targetUrl, { method: request.method, headers: { ...Object.fromEntries(headers), 'X-CSRFToken': csrfToken, - Cookie: `csrftoken=${csrfToken}` + Cookie: cookieHeader }, body: request.method !== 'GET' && request.method !== 'HEAD' ? await request.text() : undefined, From 013a2cc751caadd1607f4e90bd237dca4d55ef3e Mon Sep 17 00:00:00 2001 From: Sean Morley Date: Thu, 9 Jan 2025 18:25:51 -0500 Subject: [PATCH 039/209] feat: add password validation and error messages, enhance CityCard styling, and update localization for email and password prompts --- frontend/src/lib/components/CityCard.svelte | 8 +++----- frontend/src/locales/de.json | 19 +++++++++++++++++-- frontend/src/locales/en.json | 4 +++- frontend/src/locales/es.json | 19 +++++++++++++++++-- frontend/src/locales/fr.json | 19 +++++++++++++++++-- frontend/src/locales/it.json | 19 +++++++++++++++++-- frontend/src/locales/nl.json | 19 +++++++++++++++++-- frontend/src/locales/pl.json | 19 +++++++++++++++++-- frontend/src/locales/sv.json | 19 +++++++++++++++++-- frontend/src/locales/zh.json | 19 +++++++++++++++++-- frontend/src/routes/settings/+page.server.ts | 5 +++++ frontend/src/routes/settings/+page.svelte | 9 +++++++-- 12 files changed, 154 insertions(+), 24 deletions(-) diff --git a/frontend/src/lib/components/CityCard.svelte b/frontend/src/lib/components/CityCard.svelte index 20619e01..5678af6c 100644 --- a/frontend/src/lib/components/CityCard.svelte +++ b/frontend/src/lib/components/CityCard.svelte @@ -45,7 +45,9 @@

    {city.name}

    -
    {city.id}
    +
    {city.id}
    +
    +
    {#if !visited} {$t('adventures.remove')} {/if}
    - -
    - -
    diff --git a/frontend/src/locales/de.json b/frontend/src/locales/de.json index e8f982d0..526a96eb 100644 --- a/frontend/src/locales/de.json +++ b/frontend/src/locales/de.json @@ -299,7 +299,20 @@ "no_countries_found": "Keine Länder gefunden", "not_visited": "Nicht besucht", "num_countries": "Länder gefunden", - "partially_visited": "Teilweise besucht" + "partially_visited": "Teilweise besucht", + "all_visited": "Sie haben alle Regionen in besucht", + "cities": "Städte", + "failed_to_mark_visit": "Der Besuch konnte nicht markiert werden", + "failed_to_remove_visit": "Der Besuch von konnte nicht entfernt werden", + "marked_visited": "als besucht markiert", + "no_cities_found": "Keine Städte gefunden", + "region_failed_visited": "Die Region konnte nicht als besucht markiert werden", + "region_stats": "Regionsstatistiken", + "regions_in": "Regionen in", + "removed": "ENTFERNT", + "view_cities": "Städte anzeigen", + "visit_remove_failed": "Der Besuch konnte nicht entfernt werden", + "visit_to": "Besuch bei" }, "settings": { "account_settings": "Benutzerkontoeinstellungen", @@ -378,7 +391,9 @@ "no_verified_email_warning": "Sie müssen über eine verifizierte E-Mail-Adresse verfügen, um die Zwei-Faktor-Authentifizierung zu aktivieren.", "social_auth_desc": "Aktivieren oder deaktivieren Sie soziale und OIDC-Authentifizierungsanbieter für Ihr Konto. \nMit diesen Verbindungen können Sie sich bei selbst gehosteten Authentifizierungsidentitätsanbietern wie Authentik oder Drittanbietern wie GitHub anmelden.", "social_auth_desc_2": "Diese Einstellungen werden auf dem AdventureLog-Server verwaltet und müssen vom Administrator manuell aktiviert werden.", - "social_oidc_auth": "Soziale und OIDC-Authentifizierung" + "social_oidc_auth": "Soziale und OIDC-Authentifizierung", + "add_email": "E-Mail hinzufügen", + "password_too_short": "Das Passwort muss mindestens 6 Zeichen lang sein" }, "checklist": { "add_item": "Artikel hinzufügen", diff --git a/frontend/src/locales/en.json b/frontend/src/locales/en.json index f3f8b494..4e98a1a7 100644 --- a/frontend/src/locales/en.json +++ b/frontend/src/locales/en.json @@ -391,7 +391,9 @@ "social_auth_desc": "Enable or disable social and OIDC authentication providers for your account. These connections allow you to sign in with self hosted authentication identity providers like Authentik or 3rd party providers like GitHub.", "social_auth_desc_2": "These settings are managed in the AdventureLog server and must be manually enabled by the administrator.", "documentation_link": "Documentation Link", - "launch_account_connections": "Launch Account Connections" + "launch_account_connections": "Launch Account Connections", + "password_too_short": "Password must be at least 6 characters", + "add_email": "Add Email" }, "collection": { "collection_created": "Collection created successfully!", diff --git a/frontend/src/locales/es.json b/frontend/src/locales/es.json index f8d33abb..f0a488b3 100644 --- a/frontend/src/locales/es.json +++ b/frontend/src/locales/es.json @@ -275,7 +275,20 @@ "not_visited": "No visitado", "num_countries": "países encontrados", "partially_visited": "Parcialmente visitado", - "country_list": "Lista de países" + "country_list": "Lista de países", + "all_visited": "Has visitado todas las regiones en", + "cities": "ciudades", + "failed_to_mark_visit": "No se pudo marcar la visita a", + "failed_to_remove_visit": "No se pudo eliminar la visita a", + "marked_visited": "marcado como visitado", + "no_cities_found": "No se encontraron ciudades", + "region_failed_visited": "No se pudo marcar la región como visitada", + "region_stats": "Estadísticas de la región", + "regions_in": "Regiones en", + "removed": "remoto", + "view_cities": "Ver ciudades", + "visit_remove_failed": "No se pudo eliminar la visita", + "visit_to": "Visita a" }, "auth": { "forgot_password": "¿Has olvidado tu contraseña?", @@ -378,7 +391,9 @@ "no_verified_email_warning": "Debe tener una dirección de correo electrónico verificada para habilitar la autenticación de dos factores.", "social_auth_desc": "Habilite o deshabilite los proveedores de autenticación social y OIDC para su cuenta. \nEstas conexiones le permiten iniciar sesión con proveedores de identidad de autenticación autohospedados como Authentik o proveedores externos como GitHub.", "social_auth_desc_2": "Estas configuraciones se administran en el servidor AdventureLog y el administrador debe habilitarlas manualmente.", - "social_oidc_auth": "Autenticación social y OIDC" + "social_oidc_auth": "Autenticación social y OIDC", + "add_email": "Agregar correo electrónico", + "password_too_short": "La contraseña debe tener al menos 6 caracteres." }, "checklist": { "add_item": "Agregar artículo", diff --git a/frontend/src/locales/fr.json b/frontend/src/locales/fr.json index 5d432376..d7937790 100644 --- a/frontend/src/locales/fr.json +++ b/frontend/src/locales/fr.json @@ -299,7 +299,20 @@ "no_countries_found": "Aucun pays trouvé", "not_visited": "Non visité", "num_countries": "pays trouvés", - "partially_visited": "Partiellement visité" + "partially_visited": "Partiellement visité", + "all_visited": "Vous avez visité toutes les régions de", + "cities": "villes", + "failed_to_mark_visit": "Échec de la notation de la visite à", + "failed_to_remove_visit": "Échec de la suppression de la visite à", + "marked_visited": "marqué comme visité", + "no_cities_found": "Aucune ville trouvée", + "region_failed_visited": "Échec du marquage de la région comme visitée", + "region_stats": "Statistiques de la région", + "regions_in": "Régions dans", + "removed": "supprimé", + "view_cities": "Voir les villes", + "visit_remove_failed": "Échec de la suppression de la visite", + "visit_to": "Visite à" }, "settings": { "account_settings": "Paramètres du compte utilisateur", @@ -378,7 +391,9 @@ "no_verified_email_warning": "Vous devez disposer d'une adresse e-mail vérifiée pour activer l'authentification à deux facteurs.", "social_auth_desc": "Activez ou désactivez les fournisseurs d'authentification sociale et OIDC pour votre compte. \nCes connexions vous permettent de vous connecter avec des fournisseurs d'identité d'authentification auto-hébergés comme Authentik ou des fournisseurs tiers comme GitHub.", "social_auth_desc_2": "Ces paramètres sont gérés sur le serveur AdventureLog et doivent être activés manuellement par l'administrateur.", - "social_oidc_auth": "Authentification sociale et OIDC" + "social_oidc_auth": "Authentification sociale et OIDC", + "add_email": "Ajouter un e-mail", + "password_too_short": "Le mot de passe doit contenir au moins 6 caractères" }, "checklist": { "add_item": "Ajouter un article", diff --git a/frontend/src/locales/it.json b/frontend/src/locales/it.json index 025f9e0d..85bb8ca7 100644 --- a/frontend/src/locales/it.json +++ b/frontend/src/locales/it.json @@ -299,7 +299,20 @@ "no_countries_found": "Nessun paese trovato", "not_visited": "Non visitato", "num_countries": "paesi trovati", - "partially_visited": "Parzialmente visitato" + "partially_visited": "Parzialmente visitato", + "all_visited": "Hai visitato tutte le regioni in", + "cities": "città", + "failed_to_mark_visit": "Impossibile contrassegnare la visita a", + "failed_to_remove_visit": "Impossibile rimuovere la visita a", + "marked_visited": "contrassegnato come visitato", + "no_cities_found": "Nessuna città trovata", + "region_failed_visited": "Impossibile contrassegnare la regione come visitata", + "region_stats": "Statistiche della regione", + "regions_in": "Regioni dentro", + "removed": "RIMOSSO", + "view_cities": "Visualizza città", + "visit_remove_failed": "Impossibile rimuovere la visita", + "visit_to": "Visita a" }, "settings": { "account_settings": "Impostazioni dell'account utente", @@ -378,7 +391,9 @@ "no_verified_email_warning": "È necessario disporre di un indirizzo e-mail verificato per abilitare l'autenticazione a due fattori.", "social_auth_desc": "Abilita o disabilita i provider di autenticazione social e OIDC per il tuo account. \nQueste connessioni ti consentono di accedere con provider di identità di autenticazione self-hosted come Authentik o provider di terze parti come GitHub.", "social_auth_desc_2": "Queste impostazioni sono gestite nel server AdventureLog e devono essere abilitate manualmente dall'amministratore.", - "social_oidc_auth": "Autenticazione sociale e OIDC" + "social_oidc_auth": "Autenticazione sociale e OIDC", + "add_email": "Aggiungi e-mail", + "password_too_short": "La password deve contenere almeno 6 caratteri" }, "checklist": { "add_item": "Aggiungi articolo", diff --git a/frontend/src/locales/nl.json b/frontend/src/locales/nl.json index 750bbd7b..30660fa1 100644 --- a/frontend/src/locales/nl.json +++ b/frontend/src/locales/nl.json @@ -299,7 +299,20 @@ "no_countries_found": "Geen landen gevonden", "not_visited": "Niet bezocht", "num_countries": "landen gevonden", - "partially_visited": "Gedeeltelijk bezocht" + "partially_visited": "Gedeeltelijk bezocht", + "all_visited": "Je hebt alle regio's in bezocht", + "cities": "steden", + "failed_to_mark_visit": "Kan bezoek aan niet markeren", + "failed_to_remove_visit": "Kan bezoek aan niet verwijderen", + "marked_visited": "gemarkeerd als bezocht", + "no_cities_found": "Geen steden gevonden", + "region_failed_visited": "Kan de regio niet als bezocht markeren", + "region_stats": "Regiostatistieken", + "regions_in": "Regio's binnen", + "removed": "VERWIJDERD", + "view_cities": "Steden bekijken", + "visit_remove_failed": "Kan bezoek niet verwijderen", + "visit_to": "Bezoek aan" }, "settings": { "account_settings": "Gebruikersaccount instellingen", @@ -378,7 +391,9 @@ "no_verified_email_warning": "U moet een geverifieerd e-mailadres hebben om tweefactorauthenticatie in te schakelen.", "social_auth_desc": "Schakel sociale en OIDC-authenticatieproviders in of uit voor uw account. \nMet deze verbindingen kunt u inloggen met zelfgehoste authenticatie-identiteitsproviders zoals Authentik of externe providers zoals GitHub.", "social_auth_desc_2": "Deze instellingen worden beheerd op de AdventureLog-server en moeten handmatig worden ingeschakeld door de beheerder.", - "social_oidc_auth": "Sociale en OIDC-authenticatie" + "social_oidc_auth": "Sociale en OIDC-authenticatie", + "add_email": "E-mail toevoegen", + "password_too_short": "Wachtwoord moet minimaal 6 tekens lang zijn" }, "checklist": { "add_item": "Artikel toevoegen", diff --git a/frontend/src/locales/pl.json b/frontend/src/locales/pl.json index 189be2cb..d70b73e0 100644 --- a/frontend/src/locales/pl.json +++ b/frontend/src/locales/pl.json @@ -275,7 +275,20 @@ "completely_visited": "Całkowicie odwiedzone", "all_subregions": "Wszystkie podregiony", "clear_search": "Wyczyść wyszukiwanie", - "no_countries_found": "Nie znaleziono krajów" + "no_countries_found": "Nie znaleziono krajów", + "all_visited": "Odwiedziłeś wszystkie regiony w", + "cities": "miasta", + "failed_to_mark_visit": "Nie udało się oznaczyć wizyty w", + "failed_to_remove_visit": "Nie udało się usunąć wizyty w", + "marked_visited": "oznaczone jako odwiedzone", + "no_cities_found": "Nie znaleziono żadnych miast", + "region_failed_visited": "Nie udało się oznaczyć regionu jako odwiedzony", + "region_stats": "Statystyki regionu", + "regions_in": "Regiony w", + "removed": "REMOVED", + "view_cities": "Zobacz Miasta", + "visit_remove_failed": "Nie udało się usunąć wizyty", + "visit_to": "Wizyta w" }, "auth": { "username": "Nazwa użytkownika", @@ -378,7 +391,9 @@ "no_verified_email_warning": "Aby włączyć uwierzytelnianie dwuskładnikowe, musisz mieć zweryfikowany adres e-mail.", "social_auth_desc": "Włącz lub wyłącz dostawców uwierzytelniania społecznościowego i OIDC dla swojego konta. \nPołączenia te umożliwiają logowanie się za pośrednictwem dostawców tożsamości uwierzytelniających, takich jak Authentik, lub dostawców zewnętrznych, takich jak GitHub.", "social_auth_desc_2": "Ustawienia te są zarządzane na serwerze AdventureLog i muszą zostać włączone ręcznie przez administratora.", - "social_oidc_auth": "Uwierzytelnianie społecznościowe i OIDC" + "social_oidc_auth": "Uwierzytelnianie społecznościowe i OIDC", + "add_email": "Dodaj e-mail", + "password_too_short": "Hasło musi mieć co najmniej 6 znaków" }, "collection": { "collection_created": "Kolekcja została pomyślnie utworzona!", diff --git a/frontend/src/locales/sv.json b/frontend/src/locales/sv.json index 7f7baabb..c9afb145 100644 --- a/frontend/src/locales/sv.json +++ b/frontend/src/locales/sv.json @@ -275,7 +275,20 @@ "no_countries_found": "Inga länder hittades", "not_visited": "Ej besökta", "num_countries": "länder hittades", - "partially_visited": "Delvis besökta" + "partially_visited": "Delvis besökta", + "all_visited": "Du har besökt alla regioner i", + "cities": "städer", + "failed_to_mark_visit": "Det gick inte att markera besök till", + "failed_to_remove_visit": "Det gick inte att ta bort besök på", + "marked_visited": "markerad som besökt", + "no_cities_found": "Inga städer hittades", + "region_failed_visited": "Det gick inte att markera regionen som besökt", + "region_stats": "Regionstatistik", + "regions_in": "Regioner i", + "removed": "tas bort", + "view_cities": "Visa städer", + "visit_remove_failed": "Det gick inte att ta bort besöket", + "visit_to": "Besök till" }, "auth": { "confirm_password": "Bekräfta lösenord", @@ -378,7 +391,9 @@ "no_verified_email_warning": "Du måste ha en verifierad e-postadress för att aktivera tvåfaktorsautentisering.", "social_auth_desc": "Aktivera eller inaktivera sociala och OIDC-autentiseringsleverantörer för ditt konto. \nDessa anslutningar gör att du kan logga in med leverantörer av autentiseringsidentitetsidentitet som är värd för dig som Authentik eller tredjepartsleverantörer som GitHub.", "social_auth_desc_2": "Dessa inställningar hanteras i AdventureLog-servern och måste aktiveras manuellt av administratören.", - "social_oidc_auth": "Social och OIDC-autentisering" + "social_oidc_auth": "Social och OIDC-autentisering", + "add_email": "Lägg till e-post", + "password_too_short": "Lösenordet måste bestå av minst 6 tecken" }, "checklist": { "add_item": "Lägg till objekt", diff --git a/frontend/src/locales/zh.json b/frontend/src/locales/zh.json index d9b5be1b..99a5fadf 100644 --- a/frontend/src/locales/zh.json +++ b/frontend/src/locales/zh.json @@ -296,7 +296,20 @@ "no_countries_found": "没有找到国家", "not_visited": "未访问过", "num_countries": "找到的国家", - "partially_visited": "部分访问" + "partially_visited": "部分访问", + "all_visited": "您已访问过所有地区", + "cities": "城市", + "failed_to_mark_visit": "无法标记访问", + "failed_to_remove_visit": "无法删除对的访问", + "marked_visited": "标记为已访问", + "no_cities_found": "没有找到城市", + "region_failed_visited": "无法将区域标记为已访问", + "region_stats": "地区统计", + "regions_in": "地区位于", + "removed": "已删除", + "view_cities": "查看城市", + "visit_remove_failed": "删除访问失败", + "visit_to": "参观" }, "users": { "no_users_found": "未找到具有公开个人资料的用户。" @@ -378,7 +391,9 @@ "no_verified_email_warning": "您必须拥有经过验证的电子邮件地址才能启用双因素身份验证。", "social_auth_desc": "为您的帐户启用或禁用社交和 OIDC 身份验证提供商。\n这些连接允许您使用自托管身份验证身份提供商(如 Authentik)或第三方提供商(如 GitHub)登录。", "social_auth_desc_2": "这些设置在 AdventureLog 服务器中进行管理,并且必须由管理员手动启用。", - "social_oidc_auth": "社交和 OIDC 身份验证" + "social_oidc_auth": "社交和 OIDC 身份验证", + "add_email": "添加电子邮件", + "password_too_short": "密码必须至少为 6 个字符" }, "checklist": { "add_item": "添加项目", diff --git a/frontend/src/routes/settings/+page.server.ts b/frontend/src/routes/settings/+page.server.ts index 20bdb241..44bfe970 100644 --- a/frontend/src/routes/settings/+page.server.ts +++ b/frontend/src/routes/settings/+page.server.ts @@ -194,10 +194,15 @@ export const actions: Actions = { if (password1 !== password2) { return fail(400, { message: 'settings.password_does_not_match' }); } + if (!current_password) { current_password = null; } + if (password1 && password1?.length < 6) { + return fail(400, { message: 'settings.password_too_short' }); + } + let csrfToken = await fetchCSRFToken(); if (current_password) { diff --git a/frontend/src/routes/settings/+page.svelte b/frontend/src/routes/settings/+page.svelte index 245b9573..0fbe15c1 100644 --- a/frontend/src/routes/settings/+page.svelte +++ b/frontend/src/routes/settings/+page.svelte @@ -345,6 +345,11 @@ class="block w-full mt-1 input input-bordered input-primary" />
    + {#if $page.form?.message} +
    + {$t($page.form?.message)} +
    + {/if}
    - + - +
    From de8764499b2551f31e32c865f3febe2cea372a90 Mon Sep 17 00:00:00 2001 From: Sean Morley Date: Thu, 9 Jan 2025 19:40:23 -0500 Subject: [PATCH 040/209] feat: enhance city and region visit tracking, update AdventureModal and serializers for improved data handling --- backend/server/adventures/views.py | 39 ++++++----- backend/server/worldtravel/views.py | 4 ++ .../src/lib/components/AdventureModal.svelte | 65 ++++++++++++++----- frontend/src/lib/types.ts | 7 +- 4 files changed, 82 insertions(+), 33 deletions(-) diff --git a/backend/server/adventures/views.py b/backend/server/adventures/views.py index 5c0c5931..1ce81f50 100644 --- a/backend/server/adventures/views.py +++ b/backend/server/adventures/views.py @@ -8,7 +8,7 @@ from django.db.models.functions import Lower from rest_framework.response import Response from .models import Adventure, Checklist, Collection, Transportation, Note, AdventureImage, Category from django.core.exceptions import PermissionDenied -from worldtravel.models import VisitedRegion, Region, Country +from worldtravel.models import VisitedCity, VisitedRegion, Region, Country, City from .serializers import AdventureImageSerializer, AdventureSerializer, CategorySerializer, CollectionSerializer, NoteSerializer, TransportationSerializer, ChecklistSerializer from rest_framework.permissions import IsAuthenticated from django.db.models import Q @@ -1159,41 +1159,48 @@ class ReverseGeocodeViewSet(viewsets.ViewSet): Returns a dictionary containing the region name, country name, and ISO code if found. """ iso_code = None - town = None - city = None - county = None + town_city_or_county = None display_name = None country_code = None + city = None + + # town = None + # city = None + # county = None + if 'address' in data.keys(): keys = data['address'].keys() for key in keys: if key.find("ISO") != -1: iso_code = data['address'][key] if 'town' in keys: - town = data['address']['town'] + town_city_or_county = data['address']['town'] if 'county' in keys: - county = data['address']['county'] + town_city_or_county = data['address']['county'] if 'city' in keys: - city = data['address']['city'] + town_city_or_county = data['address']['city'] if not iso_code: return {"error": "No region found"} + region = Region.objects.filter(id=iso_code).first() visited_region = VisitedRegion.objects.filter(region=region, user_id=self.request.user).first() - is_visited = False + + region_visited = False + city_visited = False country_code = iso_code[:2] if region: - if city: - display_name = f"{city}, {region.name}, {country_code}" - elif town: - display_name = f"{town}, {region.name}, {country_code}" - elif county: - display_name = f"{county}, {region.name}, {country_code}" + if town_city_or_county: + display_name = f"{town_city_or_county}, {region.name}, {country_code}" + city = City.objects.filter(name__contains=town_city_or_county, region=region).first() + visited_city = VisitedCity.objects.filter(city=city, user_id=self.request.user).first() if visited_region: - is_visited = True + region_visited = True + if visited_city: + city_visited = True if region: - return {"id": iso_code, "region": region.name, "country": region.country.name, "is_visited": is_visited, "display_name": display_name} + return {"region_id": iso_code, "region": region.name, "country": region.country.name, "region_visited": region_visited, "display_name": display_name, "city": city.name if city else None, "city_id": city.id if city else None, "city_visited": city_visited} return {"error": "No region found"} @action(detail=False, methods=['get']) diff --git a/backend/server/worldtravel/views.py b/backend/server/worldtravel/views.py index 6cb2575c..c77309d2 100644 --- a/backend/server/worldtravel/views.py +++ b/backend/server/worldtravel/views.py @@ -137,6 +137,10 @@ class VisitedCityViewSet(viewsets.ModelViewSet): serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) self.perform_create(serializer) + # if the region is not visited, visit it + region = serializer.validated_data['city'].region + if not VisitedRegion.objects.filter(user_id=request.user.id, region=region).exists(): + VisitedRegion.objects.create(user_id=request.user, region=region) headers = self.get_success_headers(serializer.data) return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers) diff --git a/frontend/src/lib/components/AdventureModal.svelte b/frontend/src/lib/components/AdventureModal.svelte index eda9df6d..9e014e88 100644 --- a/frontend/src/lib/components/AdventureModal.svelte +++ b/frontend/src/lib/components/AdventureModal.svelte @@ -365,15 +365,31 @@ async function markVisited() { console.log(reverseGeocodePlace); if (reverseGeocodePlace) { - let res = await fetch(`/worldtravel?/markVisited`, { - method: 'POST', - body: JSON.stringify({ regionId: reverseGeocodePlace.id }) - }); - if (res.ok) { - reverseGeocodePlace.is_visited = true; - addToast('success', `Visit to ${reverseGeocodePlace.region} marked`); - } else { - addToast('error', `Failed to mark visit to ${reverseGeocodePlace.region}`); + if (!reverseGeocodePlace.region_visited && reverseGeocodePlace.region_id) { + let region_res = await fetch(`/api/visitedregion`, { + headers: { 'Content-Type': 'application/json' }, + method: 'POST', + body: JSON.stringify({ region: reverseGeocodePlace.region_id }) + }); + if (region_res.ok) { + reverseGeocodePlace.region_visited = true; + addToast('success', `Visit to ${reverseGeocodePlace.region} marked`); + } else { + addToast('error', `Failed to mark visit to ${reverseGeocodePlace.region}`); + } + } + if (!reverseGeocodePlace.city_visited && reverseGeocodePlace.city_id != null) { + let city_res = await fetch(`/api/visitedcity`, { + headers: { 'Content-Type': 'application/json' }, + method: 'POST', + body: JSON.stringify({ city: reverseGeocodePlace.city_id }) + }); + if (city_res.ok) { + reverseGeocodePlace.city_visited = true; + addToast('success', `Visit to ${reverseGeocodePlace.city} marked`); + } else { + addToast('error', `Failed to mark visit to ${reverseGeocodePlace.city}`); + } } } } @@ -542,7 +558,10 @@ addToast('error', $t('adventures.adventure_update_error')); } } - if (adventure.is_visited && !reverseGeocodePlace?.is_visited) { + if ( + (adventure.is_visited && !reverseGeocodePlace?.region_visited) || + !reverseGeocodePlace?.city_visited + ) { markVisited(); } imageSearch = adventure.name; @@ -785,19 +804,33 @@ it would also work to just use on:click on the MapLibre component itself. --> {#if reverseGeocodePlace}
    -

    {reverseGeocodePlace.region}, {reverseGeocodePlace.country}

    - {reverseGeocodePlace.is_visited + {reverseGeocodePlace.city + ? reverseGeocodePlace.city + ', ' + : ''}{reverseGeocodePlace.region}, + {reverseGeocodePlace.country} +

    +

    + {reverseGeocodePlace.region}: + {reverseGeocodePlace.region_visited ? $t('adventures.visited') : $t('adventures.not_visited')}

    + {#if reverseGeocodePlace.city} +

    + {reverseGeocodePlace.city}: + {reverseGeocodePlace.city_visited + ? $t('adventures.visited') + : $t('adventures.not_visited')} +

    + {/if}
    - {#if !reverseGeocodePlace.is_visited && !willBeMarkedVisited} + {#if !reverseGeocodePlace.region_visited || (!reverseGeocodePlace.city_visited && !willBeMarkedVisited)} {/if} - {#if !reverseGeocodePlace.is_visited && willBeMarkedVisited} + {#if !reverseGeocodePlace.region_visited || (!reverseGeocodePlace.city_visited && willBeMarkedVisited)}
    {/if} diff --git a/frontend/src/routes/settings/+page.svelte b/frontend/src/routes/settings/+page.svelte index 0fbe15c1..303eeed5 100644 --- a/frontend/src/routes/settings/+page.svelte +++ b/frontend/src/routes/settings/+page.svelte @@ -62,7 +62,10 @@ }); let data = await res.json(); if (res.ok) { - addToast('success', `${data.new_regions} ${$t('adventures.regions_updated')}`); + addToast( + 'success', + `${data.new_regions} ${$t('adventures.regions_updated')}. ${data.new_cities} ${$t('adventures.cities_updated')}.` + ); } else { addToast('error', $t('adventures.error_updating_regions')); } From 62efa2478eab9ffab286a39f547b9c29bf2c1ca2 Mon Sep 17 00:00:00 2001 From: Sean Morley Date: Wed, 15 Jan 2025 15:39:21 -0500 Subject: [PATCH 091/209] feat: add OverpassViewSet and implement osmTagToEmoji function; update requirements and aria-labels --- backend/server/adventures/urls.py | 3 +- backend/server/adventures/views.py | 182 +++++++++++++++ backend/server/requirements.txt | 3 +- frontend/src/lib/index.ts | 117 ++++++++++ .../src/routes/collections/[id]/+page.svelte | 210 +++++++++++++++++- frontend/src/routes/search/+page.svelte | 2 +- 6 files changed, 512 insertions(+), 5 deletions(-) diff --git a/backend/server/adventures/urls.py b/backend/server/adventures/urls.py index d0355229..993ce853 100644 --- a/backend/server/adventures/urls.py +++ b/backend/server/adventures/urls.py @@ -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, IcsCalendarGeneratorViewSet +from .views import AdventureViewSet, ChecklistViewSet, CollectionViewSet, NoteViewSet, StatsViewSet, GenerateDescription, ActivityTypesView, TransportationViewSet, AdventureImageViewSet, ReverseGeocodeViewSet, CategoryViewSet, IcsCalendarGeneratorViewSet, OverpassViewSet router = DefaultRouter() router.register(r'adventures', AdventureViewSet, basename='adventures') @@ -15,6 +15,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') +router.register(r'overpass', OverpassViewSet, basename='overpass') urlpatterns = [ diff --git a/backend/server/adventures/views.py b/backend/server/adventures/views.py index 2dbb0134..1961fdc6 100644 --- a/backend/server/adventures/views.py +++ b/backend/server/adventures/views.py @@ -21,6 +21,7 @@ from icalendar import Calendar, Event, vText, vCalAddress from django.http import HttpResponse from datetime import datetime from django.db.models import Max +from overpy import Overpass User = get_user_model() @@ -1329,3 +1330,184 @@ class IcsCalendarGeneratorViewSet(viewsets.ViewSet): response = HttpResponse(cal.to_ical(), content_type='text/calendar') response['Content-Disposition'] = 'attachment; filename=adventures.ics' return response + +class OverpassViewSet(viewsets.ViewSet): + permission_classes = [IsAuthenticated] + BASE_URL = "https://overpass-api.de/api/interpreter" + HEADERS = {'User-Agent': 'AdventureLog Server'} + + def make_overpass_query(self, query): + """ + Sends a query to the Overpass API and returns the response data. + Args: + query (str): The Overpass QL query string. + Returns: + dict: Parsed JSON response from the Overpass API. + Raises: + Response: DRF Response object with an error message in case of failure. + """ + url = f"{self.BASE_URL}?data={query}" + try: + response = requests.get(url, headers=self.HEADERS) + response.raise_for_status() # Raise an exception for HTTP errors + return response.json() + except requests.exceptions.RequestException as e: + return Response({"error": str(e)}, status=500) + except requests.exceptions.JSONDecodeError: + return Response({"error": "Invalid response from Overpass API"}, status=400) + + def parse_overpass_response(self, data, request): + """ + Parses the JSON response from the Overpass API and extracts relevant data, + turning it into an adventure-structured object. + + Args: + response (dict): The JSON response from the Overpass API. + + Returns: + list: A list of adventure objects with structured data. + """ + # Extract elements (nodes/ways/relations) from the response + nodes = data.get('elements', []) + adventures = [] + + # include all entries, even the ones that do not have lat long + all = request.query_params.get('all', False) + + for node in nodes: + # Ensure we are working with a "node" type (can also handle "way" or "relation" if needed) + if node.get('type') not in ['node', 'way', 'relation']: + continue + + # Extract tags and general data + tags = node.get('tags', {}) + adventure = { + "id": node.get('id'), # Include the unique OSM ID + "type": node.get('type'), # Type of element (node, way, relation) + "name": tags.get('name', tags.get('official_name', '')), # Fallback to 'official_name' + "description": tags.get('description', None), # Additional descriptive information + "latitude": node.get('lat', None), # Use None for consistency with missing values + "longitude": node.get('lon', None), + "address": { + "city": tags.get('addr:city', None), + "housenumber": tags.get('addr:housenumber', None), + "postcode": tags.get('addr:postcode', None), + "state": tags.get('addr:state', None), + "street": tags.get('addr:street', None), + "country": tags.get('addr:country', None), # Add 'country' if available + "suburb": tags.get('addr:suburb', None), # Add 'suburb' for more granularity + }, + "feature_id": tags.get('gnis:feature_id', None), + "tag": next((tags.get(key, None) for key in ['leisure', 'tourism', 'natural', 'historic', 'amenity'] if key in tags), None), + "contact": { + "phone": tags.get('phone', None), + "email": tags.get('contact:email', None), + "website": tags.get('website', None), + "facebook": tags.get('contact:facebook', None), # Social media links + "twitter": tags.get('contact:twitter', None), + }, + # "tags": tags, # Include all raw tags for future use + } + + # Filter out adventures with no meaningful data + if any([ + adventure["name"], + adventure["latitude"], + adventure["longitude"], + ] + ) or all: + adventures.append(adventure) + + return adventures + + + @action(detail=False, methods=['get']) + def query(self, request): + """ + Radius-based search for tourism-related locations around given coordinates. + """ + lat = request.query_params.get('lat') + lon = request.query_params.get('lon') + radius = request.query_params.get('radius', '1000') # Default radius: 1000 meters + + valid_categories = ['lodging', 'food', 'tourism'] + category = request.query_params.get('category', 'all') + if category not in valid_categories: + return Response({"error": f"Invalid category. Valid categories: {', '.join(valid_categories)}"}, status=400) + + if category == 'tourism': + query = f""" + [out:json]; + ( + node(around:{radius},{lat},{lon})["tourism"]; + node(around:{radius},{lat},{lon})["leisure"]; + node(around:{radius},{lat},{lon})["historic"]; + node(around:{radius},{lat},{lon})["sport"]; + node(around:{radius},{lat},{lon})["natural"]; + node(around:{radius},{lat},{lon})["attraction"]; + node(around:{radius},{lat},{lon})["museum"]; + node(around:{radius},{lat},{lon})["zoo"]; + node(around:{radius},{lat},{lon})["aquarium"]; + ); + out; + """ + if category == 'lodging': + query = f""" + [out:json]; + ( + node(around:{radius},{lat},{lon})["tourism"="hotel"]; + node(around:{radius},{lat},{lon})["tourism"="motel"]; + node(around:{radius},{lat},{lon})["tourism"="guest_house"]; + node(around:{radius},{lat},{lon})["tourism"="hostel"]; + node(around:{radius},{lat},{lon})["tourism"="camp_site"]; + node(around:{radius},{lat},{lon})["tourism"="caravan_site"]; + node(around:{radius},{lat},{lon})["tourism"="chalet"]; + node(around:{radius},{lat},{lon})["tourism"="alpine_hut"]; + node(around:{radius},{lat},{lon})["tourism"="apartment"]; + ); + out; + """ + if category == 'food': + query = f""" + [out:json]; + ( + node(around:{radius},{lat},{lon})["amenity"="restaurant"]; + node(around:{radius},{lat},{lon})["amenity"="cafe"]; + node(around:{radius},{lat},{lon})["amenity"="fast_food"]; + node(around:{radius},{lat},{lon})["amenity"="pub"]; + node(around:{radius},{lat},{lon})["amenity"="bar"]; + node(around:{radius},{lat},{lon})["amenity"="food_court"]; + node(around:{radius},{lat},{lon})["amenity"="ice_cream"]; + node(around:{radius},{lat},{lon})["amenity"="bakery"]; + node(around:{radius},{lat},{lon})["amenity"="confectionery"]; + ); + out; + """ + + # Validate required parameters + if not lat or not lon: + return Response( + {"error": "Latitude and longitude parameters are required."}, status=400 + ) + + data = self.make_overpass_query(query) + adventures = self.parse_overpass_response(data, request) + return Response(adventures) + + @action(detail=False, methods=['get'], url_path='search') + def search(self, request): + """ + Name-based search for nodes with the specified name. + """ + name = request.query_params.get('name') + + # Validate required parameter + if not name: + return Response({"error": "Name parameter is required."}, status=400) + + # Construct Overpass API query + query = f'[out:json];node["name"~"{name}",i];out;' + data = self.make_overpass_query(query) + + adventures = self.parse_overpass_response(data, request) + return Response(adventures) diff --git a/backend/server/requirements.txt b/backend/server/requirements.txt index 0e2ccbf8..80ba65b0 100644 --- a/backend/server/requirements.txt +++ b/backend/server/requirements.txt @@ -19,4 +19,5 @@ django-widget-tweaks==1.5.0 django-ical==1.9.2 icalendar==6.1.0 ijson==3.3.0 -tqdm==4.67.1 \ No newline at end of file +tqdm==4.67.1 +overpy==0.7 \ No newline at end of file diff --git a/frontend/src/lib/index.ts b/frontend/src/lib/index.ts index b476fe0e..17dcae42 100644 --- a/frontend/src/lib/index.ts +++ b/frontend/src/lib/index.ts @@ -347,3 +347,120 @@ export let themes = [ { name: 'aestheticDark', label: 'Aesthetic Dark' }, { name: 'northernLights', label: 'Northern Lights' } ]; + +export function osmTagToEmoji(tag: string) { + switch (tag) { + case 'camp_site': + return '🏕️'; + case 'slipway': + return '🛳️'; + case 'playground': + return '🛝'; + case 'viewpoint': + return '👀'; + case 'cape': + return '🏞️'; + case 'beach': + return '🏖️'; + case 'park': + return '🌳'; + case 'museum': + return '🏛️'; + case 'theme_park': + return '🎢'; + case 'nature_reserve': + return '🌲'; + case 'memorial': + return '🕊️'; + case 'monument': + return '🗿'; + case 'wood': + return '🌲'; + case 'zoo': + return '🦁'; + case 'attraction': + return '🎡'; + case 'ruins': + return '🏚️'; + case 'bay': + return '🌊'; + case 'hotel': + return '🏨'; + case 'motel': + return '🏩'; + case 'pub': + return '🍺'; + case 'restaurant': + return '🍽️'; + case 'cafe': + return '☕'; + case 'bakery': + return '🥐'; + case 'archaeological_site': + return '🏺'; + case 'lighthouse': + return '🗼'; + case 'tree': + return '🌳'; + case 'cliff': + return '⛰️'; + case 'water': + return '💧'; + case 'fishing': + return '🎣'; + case 'golf_course': + return '⛳'; + case 'swimming_pool': + return '🏊'; + case 'stadium': + return '🏟️'; + case 'cave_entrance': + return '🕳️'; + case 'anchor': + return '⚓'; + case 'garden': + return '🌼'; + case 'disc_golf_course': + return '🥏'; + case 'natural': + return '🌿'; + case 'ice_rink': + return '⛸️'; + case 'horse_riding': + return '🐎'; + case 'wreck': + return '🚢'; + case 'water_park': + return '💦'; + case 'picnic_site': + return '🧺'; + case 'axe_throwing': + return '🪓'; + case 'fort': + return '🏰'; + case 'amusement_arcade': + return '🕹️'; + case 'tepee': + return '🏕️'; + case 'track': + return '🏃'; + case 'trampoline_park': + return '🤸'; + case 'dojo': + return '🥋'; + case 'tree_stump': + return '🪵'; + case 'peak': + return '🏔️'; + case 'fitness_centre': + return '🏋️'; + case 'artwork': + return '🎨'; + case 'fast_food': + return '🍔'; + case 'ice_cream': + return '🍦'; + default: + return '📍'; // Default placeholder emoji for unknown tags + } +} diff --git a/frontend/src/routes/collections/[id]/+page.svelte b/frontend/src/routes/collections/[id]/+page.svelte index c4107b88..b72cf181 100644 --- a/frontend/src/routes/collections/[id]/+page.svelte +++ b/frontend/src/routes/collections/[id]/+page.svelte @@ -26,7 +26,8 @@ groupAdventuresByDate, groupNotesByDate, groupTransportationsByDate, - groupChecklistsByDate + groupChecklistsByDate, + osmTagToEmoji } from '$lib'; import ChecklistCard from '$lib/components/ChecklistCard.svelte'; import ChecklistModal from '$lib/components/ChecklistModal.svelte'; @@ -240,6 +241,42 @@ isAdventureModalOpen = false; } + let isPopupOpen = false; + + function togglePopup() { + isPopupOpen = !isPopupOpen; + } + + let recomendationsData: any; + let loadingRecomendations: boolean = false; + let recomendationsRange: number = 1600; + let recomendationType: string = 'tourism'; + let recomendationTags: string[] = []; + let selectedRecomendationTag: string = ''; + + async function getRecomendations(adventure: Adventure) { + recomendationsData = null; + loadingRecomendations = true; + let res = await fetch( + `/api/overpass/query/?lat=${adventure.latitude}&lon=${adventure.longitude}&radius=${recomendationsRange}&category=${recomendationType}` + ); + if (!res.ok) { + console.log('Error fetching recommendations'); + return; + } + let data = await res.json(); + recomendationsData = data; + + console.log(data); + if (recomendationsData) { + recomendationTags = [ + ...new Set(recomendationsData.map((r: any) => r.tag).filter(Boolean)) + ] as string[]; + } + loadingRecomendations = false; + console.log(recomendationTags); + } + function saveOrCreateTransportation(event: CustomEvent) { if (transportations.find((transportation) => transportation.id === event.detail.id)) { // Update existing transportation @@ -476,7 +513,7 @@ {#if collection.id} {/if} + {#if currentView == 'recommendations'} +
    +
    +

    Adventure Recommendations

    + {#each adventures as adventure} + + {/each} +
    + +
    + {Math.round(recomendationsRange / 1600)} mile ({( + (recomendationsRange / 1600) * + 1.6 + ).toFixed(1)} km) +
    +
    + (recomendationType = 'tourism')} + /> + (recomendationType = 'food')} + /> + (recomendationType = 'lodging')} + /> +
    + {#if recomendationTags.length > 0} + + {/if} +
    + + {#if recomendationsData} + + {#each recomendationsData as recomendation} + {#if recomendation.longitude && recomendation.latitude && recomendation.name} + + + {osmTagToEmoji(recomendation.tag)} + + {#if isPopupOpen} + (isPopupOpen = false)}> +
    {recomendation.name}
    + +

    + {`${recomendation.tag} ${osmTagToEmoji(recomendation.tag)}`} +

    + + +
    + {/if} +
    + {/if} + {/each} +
    + {#each recomendationsData as recomendation} + {#if recomendation.name && recomendation.longitude && recomendation.latitude} +
    +
    +

    + {recomendation.name || 'Recommendation'} +

    +
    {recomendation.tag}
    +

    {recomendation.description || 'No description available.'}

    + {#if recomendation.address} +

    + Address: + {recomendation.address.housenumber} + {recomendation.address.street}, {recomendation.address.city}, {recomendation + .address.state} + {recomendation.address.postcode} +

    + {/if} + {#if recomendation.contact} +

    + Contact: + {#if recomendation.contact.phone} + Phone: {recomendation.contact.phone} + {/if} + {#if recomendation.contact.email} + Email: {recomendation.contact.email} + {/if} + {#if recomendation.contact.website} + Website: {recomendation.contact.website} + {/if} +

    + {/if} +
    +
    + {/if} + {/each} + {/if} + {#if loadingRecomendations} +
    +
    +
    + +
    +

    + Discovering hidden gems for your next adventure... +

    +
    +
    +
    +
    + {/if} +
    +
    + {/if} {/if} diff --git a/frontend/src/routes/search/+page.svelte b/frontend/src/routes/search/+page.svelte index d85902f7..6c355303 100644 --- a/frontend/src/routes/search/+page.svelte +++ b/frontend/src/routes/search/+page.svelte @@ -130,7 +130,7 @@ class="join-item btn" type="radio" name="filter" - aria-label={$t('adventures.activity_types')} + aria-label={$t('adventures.tags')} id="activity_types" on:change={() => (property = 'activity_types')} /> From f56abd2312b7d7dac61f0e930e56135bd7310b8e Mon Sep 17 00:00:00 2001 From: Fabian Bucher Date: Thu, 16 Jan 2025 22:24:31 +0100 Subject: [PATCH 092/209] fix: container relations --- docker-compose-traefik.yaml | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/docker-compose-traefik.yaml b/docker-compose-traefik.yaml index 6fb2f032..507be5a0 100644 --- a/docker-compose-traefik.yaml +++ b/docker-compose-traefik.yaml @@ -41,7 +41,9 @@ services: - "traefik.http.routers.adventurelogweb.entrypoints=websecure" - "traefik.http.routers.adventurelogweb.rule=Host(`yourdomain.com`) && !(PathPrefix(`/media`) || PathPrefix(`/admin`) || PathPrefix(`/static`))" # Replace with your domain - "traefik.http.routers.adventurelogweb.tls=true" - - "traefik.http.routers.adventurelogweb.tls.certresolver=letsencrypt" + - "traefik.http.routers.adventurelogweb.tls.certresolver=letsencrypt" + depends_on: + - server server: image: ghcr.io/seanmorley15/adventurelog-backend:latest @@ -64,9 +66,11 @@ services: labels: - "traefik.enable=true" - "traefik.http.routers.adventurelogserver.entrypoints=websecure" - - "traefik.http.routers.adventurelogserver.rule=Host(`yourdomain.com`) && && (PathPrefix(`/media`) || PathPrefix(`/admin`) || PathPrefix(`/static`))" # Replace with your domain + - "traefik.http.routers.adventurelogserver.rule=Host(`yourdomain.com`) && (PathPrefix(`/media`) || PathPrefix(`/admin`) || PathPrefix(`/static`))" # Replace with your domain - "traefik.http.routers.adventurelogserver.tls=true" - - "traefik.http.routers.adventurelogserver.tls.certresolver=letsencrypt" + - "traefik.http.routers.adventurelogserver.tls.certresolver=letsencrypt" + depends_on: + - db volumes: postgres-data: From 2b7802115599036bdb0c7fcbb5bd57b1ce198cf2 Mon Sep 17 00:00:00 2001 From: Sean Morley Date: Thu, 16 Jan 2025 21:15:22 -0500 Subject: [PATCH 093/209] feat: update localization files and add adventure creation messages; refine adventure filtering logic --- backend/server/adventures/views.py | 11 +- documentation/package.json | 2 +- frontend/src/lib/types.ts | 1 - frontend/src/locales/de.json | 4 +- frontend/src/locales/en.json | 2 + frontend/src/locales/es.json | 4 +- frontend/src/locales/fr.json | 4 +- frontend/src/locales/it.json | 4 +- frontend/src/locales/nl.json | 4 +- frontend/src/locales/pl.json | 4 +- frontend/src/locales/sv.json | 4 +- .../src/routes/collections/[id]/+page.svelte | 103 +++++++++++++++--- 12 files changed, 117 insertions(+), 30 deletions(-) diff --git a/backend/server/adventures/views.py b/backend/server/adventures/views.py index 1961fdc6..ef41d7f8 100644 --- a/backend/server/adventures/views.py +++ b/backend/server/adventures/views.py @@ -1409,13 +1409,10 @@ class OverpassViewSet(viewsets.ViewSet): # "tags": tags, # Include all raw tags for future use } - # Filter out adventures with no meaningful data - if any([ - adventure["name"], - adventure["latitude"], - adventure["longitude"], - ] - ) or all: + # Filter out adventures with no name, latitude, or longitude + if (adventure["name"] and + adventure["latitude"] is not None and -90 <= adventure["latitude"] <= 90 and + adventure["longitude"] is not None and -180 <= adventure["longitude"] <= 180) or all: adventures.append(adventure) return adventures diff --git a/documentation/package.json b/documentation/package.json index 44e30080..c17d74f2 100644 --- a/documentation/package.json +++ b/documentation/package.json @@ -11,4 +11,4 @@ "prettier": "^3.3.3", "vue": "^3.5.13" } -} \ No newline at end of file +} diff --git a/frontend/src/lib/types.ts b/frontend/src/lib/types.ts index 4677658b..60191a96 100644 --- a/frontend/src/lib/types.ts +++ b/frontend/src/lib/types.ts @@ -15,7 +15,6 @@ export type User = { export type Adventure = { id: string; user_id: string | null; - type: string; name: string; location?: string | null; activity_types?: string[] | null; diff --git a/frontend/src/locales/de.json b/frontend/src/locales/de.json index 7bc5ba73..e9789697 100644 --- a/frontend/src/locales/de.json +++ b/frontend/src/locales/de.json @@ -218,7 +218,9 @@ "transportation_delete_confirm": "Sind Sie sicher, dass Sie diesen Transport löschen möchten? \nDiese Aktion kann nicht rückgängig gemacht werden.", "show_map": "Karte anzeigen", "will_be_marked": "wird als besucht markiert, sobald das Abenteuer gespeichert ist.", - "cities_updated": "Städte aktualisiert" + "cities_updated": "Städte aktualisiert", + "create_adventure": "Erstelle Abenteuer", + "no_adventures_to_recommendations": "Keine Abenteuer gefunden. \nFügen Sie mindestens ein Abenteuer hinzu, um Empfehlungen zu erhalten." }, "home": { "desc_1": "Entdecken, planen und erkunden Sie mit Leichtigkeit", diff --git a/frontend/src/locales/en.json b/frontend/src/locales/en.json index c924bd70..c5ff3c5b 100644 --- a/frontend/src/locales/en.json +++ b/frontend/src/locales/en.json @@ -185,12 +185,14 @@ "no_description_found": "No description found", "adventure_created": "Adventure created", "adventure_create_error": "Failed to create adventure", + "create_adventure": "Create Adventure", "adventure_updated": "Adventure updated", "adventure_update_error": "Failed to update adventure", "set_to_pin": "Set to Pin", "category_fetch_error": "Error fetching categories", "new_adventure": "New Adventure", "basic_information": "Basic Information", + "no_adventures_to_recommendations": "No adventures found. Add at leat one adventure to get recommendations.", "adventure_not_found": "There are no adventures to display. Add some using the plus button at the bottom right or try changing filters!", "no_adventures_found": "No adventures found", "mark_region_as_visited": "Mark region {region}, {country} as visited?", diff --git a/frontend/src/locales/es.json b/frontend/src/locales/es.json index fcf1572e..34402767 100644 --- a/frontend/src/locales/es.json +++ b/frontend/src/locales/es.json @@ -265,7 +265,9 @@ "transportation_delete_confirm": "¿Está seguro de que desea eliminar este transporte? \nEsta acción no se puede deshacer.", "show_map": "Mostrar mapa", "will_be_marked": "se marcará como visitado una vez guardada la aventura.", - "cities_updated": "ciudades actualizadas" + "cities_updated": "ciudades actualizadas", + "create_adventure": "Crear aventura", + "no_adventures_to_recommendations": "No se encontraron aventuras. \nAñade al menos una aventura para obtener recomendaciones." }, "worldtravel": { "all": "Todo", diff --git a/frontend/src/locales/fr.json b/frontend/src/locales/fr.json index a4206b3b..8cac395f 100644 --- a/frontend/src/locales/fr.json +++ b/frontend/src/locales/fr.json @@ -218,7 +218,9 @@ "transportation_delete_confirm": "Etes-vous sûr de vouloir supprimer ce transport ? \nCette action ne peut pas être annulée.", "show_map": "Afficher la carte", "will_be_marked": "sera marqué comme visité une fois l’aventure sauvegardée.", - "cities_updated": "villes mises à jour" + "cities_updated": "villes mises à jour", + "create_adventure": "Créer une aventure", + "no_adventures_to_recommendations": "Aucune aventure trouvée. \nAjoutez au moins une aventure pour obtenir des recommandations." }, "home": { "desc_1": "Découvrez, planifiez et explorez en toute simplicité", diff --git a/frontend/src/locales/it.json b/frontend/src/locales/it.json index b7abe9e1..2c43ba61 100644 --- a/frontend/src/locales/it.json +++ b/frontend/src/locales/it.json @@ -218,7 +218,9 @@ "transportation_delete_confirm": "Sei sicuro di voler eliminare questo trasporto? \nQuesta azione non può essere annullata.", "show_map": "Mostra mappa", "will_be_marked": "verrà contrassegnato come visitato una volta salvata l'avventura.", - "cities_updated": "città aggiornate" + "cities_updated": "città aggiornate", + "create_adventure": "Crea Avventura", + "no_adventures_to_recommendations": "Nessuna avventura trovata. \nAggiungi almeno un'avventura per ricevere consigli." }, "home": { "desc_1": "Scopri, pianifica ed esplora con facilità", diff --git a/frontend/src/locales/nl.json b/frontend/src/locales/nl.json index 5ea896d7..e230dd24 100644 --- a/frontend/src/locales/nl.json +++ b/frontend/src/locales/nl.json @@ -218,7 +218,9 @@ "ending_airport": "Einde luchthaven", "show_map": "Toon kaart", "will_be_marked": "wordt gemarkeerd als bezocht zodra het avontuur is opgeslagen.", - "cities_updated": "steden bijgewerkt" + "cities_updated": "steden bijgewerkt", + "create_adventure": "Creëer avontuur", + "no_adventures_to_recommendations": "Geen avonturen gevonden. \nVoeg ten minste één avontuur toe om aanbevelingen te krijgen." }, "home": { "desc_1": "Ontdek, plan en verken met gemak", diff --git a/frontend/src/locales/pl.json b/frontend/src/locales/pl.json index e6422d3b..a39baeb3 100644 --- a/frontend/src/locales/pl.json +++ b/frontend/src/locales/pl.json @@ -265,7 +265,9 @@ "transportation_delete_confirm": "Czy na pewno chcesz usunąć ten transport? \nTej akcji nie można cofnąć.", "show_map": "Pokaż mapę", "will_be_marked": "zostanie oznaczona jako odwiedzona po zapisaniu przygody.", - "cities_updated": "miasta zaktualizowane" + "cities_updated": "miasta zaktualizowane", + "create_adventure": "Stwórz przygodę", + "no_adventures_to_recommendations": "Nie znaleziono żadnych przygód. \nDodaj co najmniej jedną przygodę, aby uzyskać rekomendacje." }, "worldtravel": { "country_list": "Lista krajów", diff --git a/frontend/src/locales/sv.json b/frontend/src/locales/sv.json index e5367c5b..3dfcae1f 100644 --- a/frontend/src/locales/sv.json +++ b/frontend/src/locales/sv.json @@ -218,7 +218,9 @@ "transportation_delete_confirm": "Är du säker på att du vill ta bort denna transport? \nDenna åtgärd kan inte ångras.", "show_map": "Visa karta", "will_be_marked": "kommer att markeras som besökt när äventyret har sparats.", - "cities_updated": "städer uppdaterade" + "cities_updated": "städer uppdaterade", + "create_adventure": "Skapa äventyr", + "no_adventures_to_recommendations": "Inga äventyr hittades. \nLägg till minst ett äventyr för att få rekommendationer." }, "home": { "desc_1": "Upptäck, planera och utforska med lätthet", diff --git a/frontend/src/routes/collections/[id]/+page.svelte b/frontend/src/routes/collections/[id]/+page.svelte index b72cf181..1653708d 100644 --- a/frontend/src/routes/collections/[id]/+page.svelte +++ b/frontend/src/routes/collections/[id]/+page.svelte @@ -208,6 +208,30 @@ } } + function recomendationToAdventure(recomendation: any) { + adventureToEdit = { + id: '', + user_id: null, + name: recomendation.name, + latitude: recomendation.latitude, + longitude: recomendation.longitude, + images: [], + is_visited: false, + is_public: false, + visits: [], + category: { + display_name: recomendation.tag + .replace(/_/g, ' ') + .replace(/\b\w/g, (char: string) => char.toUpperCase()), + icon: osmTagToEmoji(recomendation.tag), + id: '', + name: recomendation.tag, + user_id: '' + } + }; + isAdventureModalOpen = true; + } + let adventureToEdit: Adventure | null = null; let transportationToEdit: Transportation | null = null; let isAdventureModalOpen: boolean = false; @@ -251,11 +275,24 @@ let loadingRecomendations: boolean = false; let recomendationsRange: number = 1600; let recomendationType: string = 'tourism'; - let recomendationTags: string[] = []; + let recomendationTags: { name: string; display_name: string }[] = []; let selectedRecomendationTag: string = ''; + let filteredRecomendations: any[] = []; + $: { + if (recomendationsData && selectedRecomendationTag) { + filteredRecomendations = recomendationsData.filter( + (r: any) => r.tag === selectedRecomendationTag + ); + } else { + filteredRecomendations = recomendationsData; + } + console.log(filteredRecomendations); + console.log(selectedRecomendationTag); + } async function getRecomendations(adventure: Adventure) { recomendationsData = null; + selectedRecomendationTag = ''; loadingRecomendations = true; let res = await fetch( `/api/overpass/query/?lat=${adventure.latitude}&lon=${adventure.longitude}&radius=${recomendationsRange}&category=${recomendationType}` @@ -267,11 +304,28 @@ let data = await res.json(); recomendationsData = data; - console.log(data); - if (recomendationsData) { - recomendationTags = [ - ...new Set(recomendationsData.map((r: any) => r.tag).filter(Boolean)) - ] as string[]; + if (recomendationsData && recomendationsData.some((r: any) => r.longitude && r.latitude)) { + const tagMap = new Map(); + recomendationsData.forEach((r: any) => { + const tag = formatTag(r.tag); + if (tag) { + tagMap.set(r.tag, { name: r.tag, display_name: tag }); + } + }); + recomendationTags = Array.from(tagMap.values()); + + function formatTag(tag: string): string { + if (tag) { + return ( + tag + .split('_') + .map((word) => word.charAt(0).toUpperCase() + word.slice(1)) + .join(' ') + osmTagToEmoji(tag) + ); + } else { + return ''; + } + } } loadingRecomendations = false; console.log(recomendationTags); @@ -850,10 +904,17 @@

    Adventure Recommendations

    {#each adventures as adventure} - + {#if adventure.longitude && adventure.latitude} + + {/if} {/each} + {#if adventures.length == 0} +
    +

    {$t('adventures.no_adventures_to_recommendations')}

    +
    + {/if}
    {#if recomendationTags.length > 0} - + {#each recomendationTags as tag} - + {/each} {/if} @@ -916,7 +980,7 @@ center={{ lng: recomendationsData[0].longitude, lat: recomendationsData[0].latitude }} zoom={12} > - {#each recomendationsData as recomendation} + {#each filteredRecomendations as recomendation} {#if recomendation.longitude && recomendation.latitude && recomendation.name} {$t('map.view_details')} + {/if} {/if} {/each} - {#each recomendationsData as recomendation} + {#each filteredRecomendations as recomendation} {#if recomendation.name && recomendation.longitude && recomendation.latitude}
    @@ -984,6 +1053,12 @@ {/if}

    {/if} +
    {/if} From 5b4092ba6c113efba612f6e25541ca17c0525b52 Mon Sep 17 00:00:00 2001 From: Sean Morley Date: Fri, 17 Jan 2025 16:50:01 -0500 Subject: [PATCH 094/209] feat: implement pagination, add activity types and stats views; update category management and localization --- backend/server/adventures/permissions.py | 26 +- backend/server/adventures/urls.py | 2 +- backend/server/adventures/utils/pagination.py | 6 + backend/server/adventures/views.py | 1510 ----------------- backend/server/adventures/views/__init__.py | 13 + .../adventures/views/activity_types_view.py | 32 + .../adventures/views/adventure_image_view.py | 128 ++ .../server/adventures/views/adventure_view.py | 314 ++++ .../server/adventures/views/category_view.py | 42 + .../server/adventures/views/checklist_view.py | 130 ++ .../adventures/views/collection_view.py | 219 +++ .../views/generate_description_view.py | 37 + .../adventures/views/ics_calendar_view.py | 67 + backend/server/adventures/views/note_view.py | 130 ++ .../server/adventures/views/overpass_view.py | 183 ++ .../adventures/views/reverse_geocode_view.py | 117 ++ backend/server/adventures/views/stats_view.py | 38 + .../adventures/views/transportation_view.py | 84 + frontend/src/locales/zh.json | 4 +- 19 files changed, 1548 insertions(+), 1534 deletions(-) create mode 100644 backend/server/adventures/utils/pagination.py create mode 100644 backend/server/adventures/views/__init__.py create mode 100644 backend/server/adventures/views/activity_types_view.py create mode 100644 backend/server/adventures/views/adventure_image_view.py create mode 100644 backend/server/adventures/views/adventure_view.py create mode 100644 backend/server/adventures/views/category_view.py create mode 100644 backend/server/adventures/views/checklist_view.py create mode 100644 backend/server/adventures/views/collection_view.py create mode 100644 backend/server/adventures/views/generate_description_view.py create mode 100644 backend/server/adventures/views/ics_calendar_view.py create mode 100644 backend/server/adventures/views/note_view.py create mode 100644 backend/server/adventures/views/overpass_view.py create mode 100644 backend/server/adventures/views/reverse_geocode_view.py create mode 100644 backend/server/adventures/views/stats_view.py create mode 100644 backend/server/adventures/views/transportation_view.py diff --git a/backend/server/adventures/permissions.py b/backend/server/adventures/permissions.py index 5f9a911c..941fbc01 100644 --- a/backend/server/adventures/permissions.py +++ b/backend/server/adventures/permissions.py @@ -61,6 +61,10 @@ class IsOwnerOrSharedWithFullAccess(permissions.BasePermission): """ def has_object_permission(self, request, view, obj): + + # Allow GET only for a public object + if request.method in permissions.SAFE_METHODS and obj.is_public: + return True # Check if the object has a collection if hasattr(obj, 'collection') and obj.collection: # Allow all actions for shared users @@ -71,27 +75,5 @@ class IsOwnerOrSharedWithFullAccess(permissions.BasePermission): if request.method in permissions.SAFE_METHODS: return True - # Allow all actions for the owner - return obj.user_id == request.user - -class IsPublicOrOwnerOrSharedWithFullAccess(permissions.BasePermission): - """ - Custom permission to allow: - - Read-only access for public objects - - Full access for shared users - - Full access for owners - """ - - def has_object_permission(self, request, view, obj): - # Allow read-only access for public objects - if obj.is_public and request.method in permissions.SAFE_METHODS: - return True - - # Check if the object has a collection - if hasattr(obj, 'collection') and obj.collection: - # Allow all actions for shared users - if request.user in obj.collection.shared_with.all(): - return True - # Allow all actions for the owner return obj.user_id == request.user \ No newline at end of file diff --git a/backend/server/adventures/urls.py b/backend/server/adventures/urls.py index 993ce853..c28478e1 100644 --- a/backend/server/adventures/urls.py +++ b/backend/server/adventures/urls.py @@ -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, IcsCalendarGeneratorViewSet, OverpassViewSet +from adventures.views import * router = DefaultRouter() router.register(r'adventures', AdventureViewSet, basename='adventures') diff --git a/backend/server/adventures/utils/pagination.py b/backend/server/adventures/utils/pagination.py new file mode 100644 index 00000000..43371904 --- /dev/null +++ b/backend/server/adventures/utils/pagination.py @@ -0,0 +1,6 @@ +from rest_framework.pagination import PageNumberPagination + +class StandardResultsSetPagination(PageNumberPagination): + page_size = 25 + page_size_query_param = 'page_size' + max_page_size = 1000 \ No newline at end of file diff --git a/backend/server/adventures/views.py b/backend/server/adventures/views.py index ef41d7f8..e69de29b 100644 --- a/backend/server/adventures/views.py +++ b/backend/server/adventures/views.py @@ -1,1510 +0,0 @@ -import json -import uuid -import requests -from django.db import transaction -from rest_framework.decorators import action -from rest_framework import viewsets -from django.db.models.functions import Lower -from rest_framework.response import Response -from .models import Adventure, Checklist, Collection, Transportation, Note, AdventureImage, Category -from django.core.exceptions import PermissionDenied -from worldtravel.models import VisitedCity, VisitedRegion, Region, Country, City -from .serializers import AdventureImageSerializer, AdventureSerializer, CategorySerializer, CollectionSerializer, NoteSerializer, TransportationSerializer, ChecklistSerializer -from rest_framework.permissions import IsAuthenticated -from django.db.models import Q -from .permissions import CollectionShared, IsOwnerOrSharedWithFullAccess, IsPublicOrOwnerOrSharedWithFullAccess -from rest_framework.pagination import PageNumberPagination -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 -from django.db.models import Max -from overpy import Overpass - -User = get_user_model() - -class StandardResultsSetPagination(PageNumberPagination): - page_size = 25 - page_size_query_param = 'page_size' - max_page_size = 1000 - -from rest_framework.pagination import PageNumberPagination - -from rest_framework.decorators import action -from rest_framework.response import Response -from django.db.models import Q - -class AdventureViewSet(viewsets.ModelViewSet): - serializer_class = AdventureSerializer - permission_classes = [IsOwnerOrSharedWithFullAccess, IsPublicOrOwnerOrSharedWithFullAccess] - pagination_class = StandardResultsSetPagination - - def apply_sorting(self, queryset): - order_by = self.request.query_params.get('order_by', 'updated_at') - order_direction = self.request.query_params.get('order_direction', 'asc') - include_collections = self.request.query_params.get('include_collections', 'true') - - valid_order_by = ['name', 'type', 'date', 'rating', 'updated_at'] - if order_by not in valid_order_by: - order_by = 'name' - - if order_direction not in ['asc', 'desc']: - order_direction = 'asc' - - if order_by == 'date': - # order by the earliest visit object associated with the adventure - queryset = queryset.annotate(latest_visit=Max('visits__start_date')) - queryset = queryset.filter(latest_visit__isnull=False) - ordering = 'latest_visit' - # Apply case-insensitive sorting for the 'name' field - elif order_by == 'name': - queryset = queryset.annotate(lower_name=Lower('name')) - ordering = 'lower_name' - elif order_by == 'rating': - queryset = queryset.filter(rating__isnull=False) - ordering = 'rating' - else: - ordering = order_by - - if order_direction == 'desc': - ordering = f'-{ordering}' - - # reverse ordering for updated_at field - if order_by == 'updated_at': - if order_direction == 'asc': - ordering = '-updated_at' - else: - ordering = 'updated_at' - - print(f"Ordering by: {ordering}") # For debugging - - if include_collections == 'false': - queryset = queryset.filter(collection = None) - - 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': - return Adventure.objects.filter(is_public=True).distinct().order_by('-updated_at') - return Adventure.objects.none() - - if self.action == 'retrieve': - # For individual adventure retrieval, include public adventures - return Adventure.objects.filter( - Q(is_public=True) | Q(user_id=self.request.user.id) | Q(collection__shared_with=self.request.user) - ).distinct().order_by('-updated_at') - else: - # For other actions, include user's own adventures and shared adventures - return Adventure.objects.filter( - Q(user_id=self.request.user.id) | Q(collection__shared_with=self.request.user) - ).distinct().order_by('-updated_at') - - def retrieve(self, request, *args, **kwargs): - queryset = self.get_queryset() - adventure = get_object_or_404(queryset, pk=kwargs['pk']) - serializer = self.get_serializer(adventure) - return Response(serializer.data) - - def perform_update(self, serializer): - adventure = serializer.save() - if adventure.collection: - adventure.is_public = adventure.collection.is_public - adventure.save() - - @action(detail=False, methods=['get']) - def filtered(self, request): - types = request.query_params.get('types', '').split(',') - is_visited = request.query_params.get('is_visited', 'all') - - # Handle case where types is all - if 'all' in types: - types = Category.objects.filter(user_id=request.user).values_list('name', flat=True) - - else: - for type in types: - if not Category.objects.filter(user_id=request.user, name=type).exists(): - return Response({"error": f"Category {type} does not exist"}, status=400) - - if not types: - return Response({"error": "At least one type must be provided"}, status=400) - - queryset = Adventure.objects.filter( - category__in=Category.objects.filter(name__in=types, user_id=request.user), - user_id=request.user.id - ) - - # Handle is_visited filtering - if is_visited.lower() == 'true': - serializer = self.get_serializer(queryset, many=True) - filtered_ids = [ - adventure.id for adventure, serialized_adventure in zip(queryset, serializer.data) - if serialized_adventure['is_visited'] - ] - queryset = queryset.filter(id__in=filtered_ids) - elif is_visited.lower() == 'false': - serializer = self.get_serializer(queryset, many=True) - filtered_ids = [ - adventure.id for adventure, serialized_adventure in zip(queryset, serializer.data) - if not serialized_adventure['is_visited'] - ] - queryset = queryset.filter(id__in=filtered_ids) - # If is_visited is 'all' or any other value, we don't apply additional filtering - - # Apply sorting - queryset = self.apply_sorting(queryset) - - # Paginate and respond - adventures = self.paginate_and_respond(queryset, request) - return adventures - - @action(detail=False, methods=['get']) - def all(self, request): - if not request.user.is_authenticated: - return Response({"error": "User is not authenticated"}, status=400) - include_collections = request.query_params.get('include_collections', 'false') - if include_collections not in ['true', 'false']: - include_collections = 'false' - - if include_collections == 'true': - queryset = Adventure.objects.filter( - Q(is_public=True) | Q(user_id=request.user.id) - ) - else: - queryset = Adventure.objects.filter( - Q(is_public=True) | Q(user_id=request.user.id), collection=None - ) - queryset = Adventure.objects.filter( - Q(user_id=request.user.id) - ) - queryset = self.apply_sorting(queryset) - serializer = self.get_serializer(queryset, many=True) - - return Response(serializer.data) - - @action(detail=False, methods=['get']) - def search(self, request): - query = self.request.query_params.get('query', '') - property = self.request.query_params.get('property', 'all') - if len(query) < 2: - return Response({"error": "Query must be at least 2 characters long"}, status=400) - - if property not in ['name', 'type', 'location', 'description', 'activity_types']: - property = 'all' - - queryset = Adventure.objects.none() - - if property == 'name': - queryset = Adventure.objects.filter( - (Q(name__icontains=query)) & - (Q(user_id=request.user.id) | Q(is_public=True)) - ) - elif property == 'type': - queryset = Adventure.objects.filter( - (Q(type__icontains=query)) & - (Q(user_id=request.user.id) | Q(is_public=True)) - ) - elif property == 'location': - queryset = Adventure.objects.filter( - (Q(location__icontains=query)) & - (Q(user_id=request.user.id) | Q(is_public=True)) - ) - elif property == 'description': - queryset = Adventure.objects.filter( - (Q(description__icontains=query)) & - (Q(user_id=request.user.id) | Q(is_public=True)) - ) - elif property == 'activity_types': - queryset = Adventure.objects.filter( - (Q(activity_types__icontains=query)) & - (Q(user_id=request.user.id) | Q(is_public=True)) - ) - else: - queryset = Adventure.objects.filter( - (Q(name__icontains=query) | Q(description__icontains=query) | Q(location__icontains=query) | Q(activity_types__icontains=query)) & - (Q(user_id=request.user.id) | Q(is_public=True)) - ) - - queryset = self.apply_sorting(queryset) - serializer = self.get_serializer(queryset, many=True) - return Response(serializer.data) - - def update(self, request, *args, **kwargs): - # Retrieve the current object - instance = self.get_object() - - # Partially update the instance with the request data - serializer = self.get_serializer(instance, data=request.data, partial=True) - serializer.is_valid(raise_exception=True) - - # if the adventure is trying to have is_public changed and its part of a collection return an error - if new_collection is not None: - serializer.validated_data['is_public'] = new_collection.is_public - elif instance.collection: - serializer.validated_data['is_public'] = instance.collection.is_public - - - # Retrieve the collection from the validated data - new_collection = serializer.validated_data.get('collection') - - user = request.user - print(new_collection) - - if new_collection is not None and new_collection!=instance.collection: - # Check if the user is the owner of the new collection - if new_collection.user_id != user or instance.user_id != user: - raise PermissionDenied("You do not have permission to use this collection.") - elif new_collection is None: - # Handle the case where the user is trying to set the collection to None - if instance.collection is not None and instance.collection.user_id != user: - raise PermissionDenied("You cannot remove the collection as you are not the owner.") - - # Perform the update - self.perform_update(serializer) - - # Return the updated instance - return Response(serializer.data) - - def partial_update(self, request, *args, **kwargs): - # Retrieve the current object - instance = self.get_object() - - # Partially update the instance with the request data - serializer = self.get_serializer(instance, data=request.data, partial=True) - serializer.is_valid(raise_exception=True) - - # Retrieve the collection from the validated data - new_collection = serializer.validated_data.get('collection') - - user = request.user - print(new_collection) - - # if the adventure is trying to have is_public changed and its part of a collection return an error - if new_collection is not None: - serializer.validated_data['is_public'] = new_collection.is_public - elif instance.collection: - serializer.validated_data['is_public'] = instance.collection.is_public - - if new_collection is not None and new_collection!=instance.collection: - # Check if the user is the owner of the new collection - if new_collection.user_id != user or instance.user_id != user: - raise PermissionDenied("You do not have permission to use this collection.") - elif new_collection is None: - # Handle the case where the user is trying to set the collection to None - if instance.collection is not None and instance.collection.user_id != user: - raise PermissionDenied("You cannot remove the collection as you are not the owner.") - - # Perform the update - self.perform_update(serializer) - - # Return the updated instance - return Response(serializer.data) - - def perform_update(self, serializer): - serializer.save() - - # when creating an adventure, make sure the user is the owner of the collection or shared with the collection - @transaction.atomic - def perform_create(self, serializer): - # Retrieve the collection from the validated data - collection = serializer.validated_data.get('collection') - - # Check if a collection is provided - if collection: - user = self.request.user - # Check if the user is the owner or is in the shared_with list - if collection.user_id != user and not collection.shared_with.filter(id=user.id).exists(): - # Return an error response if the user does not have permission - raise PermissionDenied("You do not have permission to use this collection.") - # if collection the owner of the adventure is the owner of the collection - # set the is_public field of the adventure to the is_public field of the collection - serializer.save(user_id=collection.user_id, is_public=collection.is_public) - return - - # Save the adventure with the current user as the owner - serializer.save(user_id=self.request.user) - - def paginate_and_respond(self, queryset, request): - paginator = self.pagination_class() - page = paginator.paginate_queryset(queryset, request) - if page is not None: - serializer = self.get_serializer(page, many=True) - return paginator.get_paginated_response(serializer.data) - serializer = self.get_serializer(queryset, many=True) - return Response(serializer.data) - -class CollectionViewSet(viewsets.ModelViewSet): - serializer_class = CollectionSerializer - permission_classes = [CollectionShared] - pagination_class = StandardResultsSetPagination - - # def get_queryset(self): - # return Collection.objects.filter(Q(user_id=self.request.user.id) & Q(is_archived=False)) - - def apply_sorting(self, queryset): - order_by = self.request.query_params.get('order_by', 'name') - order_direction = self.request.query_params.get('order_direction', 'asc') - - valid_order_by = ['name', 'upated_at'] - if order_by not in valid_order_by: - order_by = 'updated_at' - - if order_direction not in ['asc', 'desc']: - order_direction = 'asc' - - # Apply case-insensitive sorting for the 'name' field - if order_by == 'name': - queryset = queryset.annotate(lower_name=Lower('name')) - ordering = 'lower_name' - if order_direction == 'desc': - ordering = f'-{ordering}' - else: - order_by == 'updated_at' - ordering = 'updated_at' - if order_direction == 'asc': - ordering = '-updated_at' - - #print(f"Ordering by: {ordering}") # For debugging - - return queryset.order_by(ordering) - - def list(self, request, *args, **kwargs): - # make sure the user is authenticated - if not request.user.is_authenticated: - return Response({"error": "User is not authenticated"}, status=400) - queryset = Collection.objects.filter(user_id=request.user.id) - queryset = self.apply_sorting(queryset) - collections = self.paginate_and_respond(queryset, request) - return collections - - @action(detail=False, methods=['get']) - def all(self, request): - if not request.user.is_authenticated: - return Response({"error": "User is not authenticated"}, status=400) - - queryset = Collection.objects.filter( - Q(user_id=request.user.id) - ) - - queryset = self.apply_sorting(queryset) - serializer = self.get_serializer(queryset, many=True) - - return Response(serializer.data) - - @action(detail=False, methods=['get']) - def archived(self, request): - if not request.user.is_authenticated: - return Response({"error": "User is not authenticated"}, status=400) - - queryset = Collection.objects.filter( - Q(user_id=request.user.id) & Q(is_archived=True) - ) - - queryset = self.apply_sorting(queryset) - serializer = self.get_serializer(queryset, many=True) - - return Response(serializer.data) - - # this make the is_public field of the collection cascade to the adventures - @transaction.atomic - def update(self, request, *args, **kwargs): - partial = kwargs.pop('partial', False) - instance = self.get_object() - serializer = self.get_serializer(instance, data=request.data, partial=partial) - serializer.is_valid(raise_exception=True) - - if 'collection' in serializer.validated_data: - new_collection = serializer.validated_data['collection'] - # if the new collection is different from the old one and the user making the request is not the owner of the new collection return an error - if new_collection != instance.collection and new_collection.user_id != request.user: - return Response({"error": "User does not own the new collection"}, status=400) - - # Check if the 'is_public' field is present in the update data - if 'is_public' in serializer.validated_data: - new_public_status = serializer.validated_data['is_public'] - - # if is_publuc has changed and the user is not the owner of the collection return an error - if new_public_status != instance.is_public and instance.user_id != request.user: - print(f"User {request.user.id} does not own the collection {instance.id} that is owned by {instance.user_id}") - return Response({"error": "User does not own the collection"}, status=400) - - # Update associated adventures to match the collection's is_public status - Adventure.objects.filter(collection=instance).update(is_public=new_public_status) - - # do the same for transportations - Transportation.objects.filter(collection=instance).update(is_public=new_public_status) - - # do the same for notes - Note.objects.filter(collection=instance).update(is_public=new_public_status) - - # Log the action (optional) - action = "public" if new_public_status else "private" - print(f"Collection {instance.id} and its adventures were set to {action}") - - self.perform_update(serializer) - - if getattr(instance, '_prefetched_objects_cache', None): - # If 'prefetch_related' has been applied to a queryset, we need to - # forcibly invalidate the prefetch cache on the instance. - instance._prefetched_objects_cache = {} - - return Response(serializer.data) - - # make an action to retreive all adventures that are shared with the user - @action(detail=False, methods=['get']) - def shared(self, request): - if not request.user.is_authenticated: - return Response({"error": "User is not authenticated"}, status=400) - queryset = Collection.objects.filter( - shared_with=request.user - ) - queryset = self.apply_sorting(queryset) - serializer = self.get_serializer(queryset, many=True) - return Response(serializer.data) - - # Adds a new user to the shared_with field of an adventure - @action(detail=True, methods=['post'], url_path='share/(?P[^/.]+)') - def share(self, request, pk=None, uuid=None): - collection = self.get_object() - if not uuid: - return Response({"error": "User UUID is required"}, status=400) - try: - user = User.objects.get(uuid=uuid, public_profile=True) - except User.DoesNotExist: - return Response({"error": "User not found"}, status=404) - - if user == request.user: - return Response({"error": "Cannot share with yourself"}, status=400) - - if collection.shared_with.filter(id=user.id).exists(): - return Response({"error": "Adventure is already shared with this user"}, status=400) - - collection.shared_with.add(user) - collection.save() - return Response({"success": f"Shared with {user.username}"}) - - @action(detail=True, methods=['post'], url_path='unshare/(?P[^/.]+)') - def unshare(self, request, pk=None, uuid=None): - if not request.user.is_authenticated: - return Response({"error": "User is not authenticated"}, status=400) - collection = self.get_object() - if not uuid: - return Response({"error": "User UUID is required"}, status=400) - try: - user = User.objects.get(uuid=uuid, public_profile=True) - except User.DoesNotExist: - return Response({"error": "User not found"}, status=404) - - if user == request.user: - return Response({"error": "Cannot unshare with yourself"}, status=400) - - if not collection.shared_with.filter(id=user.id).exists(): - return Response({"error": "Collection is not shared with this user"}, status=400) - - collection.shared_with.remove(user) - collection.save() - return Response({"success": f"Unshared with {user.username}"}) - - def get_queryset(self): - if self.action == 'destroy': - return Collection.objects.filter(user_id=self.request.user.id) - - if self.action in ['update', 'partial_update']: - return Collection.objects.filter( - Q(user_id=self.request.user.id) | Q(shared_with=self.request.user) - ).distinct() - - if self.action == 'retrieve': - if not self.request.user.is_authenticated: - return Collection.objects.filter(is_public=True) - return Collection.objects.filter( - Q(is_public=True) | Q(user_id=self.request.user.id) | Q(shared_with=self.request.user) - ).distinct() - - # For list action, include collections owned by the user or shared with the user, that are not archived - return Collection.objects.filter( - (Q(user_id=self.request.user.id) | Q(shared_with=self.request.user)) & Q(is_archived=False) - ).distinct() - - - def perform_create(self, serializer): - # This is ok because you cannot share a collection when creating it - serializer.save(user_id=self.request.user) - - def paginate_and_respond(self, queryset, request): - paginator = self.pagination_class() - page = paginator.paginate_queryset(queryset, request) - if page is not None: - serializer = self.get_serializer(page, many=True) - return paginator.get_paginated_response(serializer.data) - serializer = self.get_serializer(queryset, many=True) - return Response(serializer.data) - -class StatsViewSet(viewsets.ViewSet): - permission_classes = [IsAuthenticated] - - @action(detail=False, methods=['get']) - def counts(self, request): - adventure_count = Adventure.objects.filter( - user_id=request.user.id).count() - trips_count = Collection.objects.filter( - user_id=request.user.id).count() - visited_city_count = VisitedCity.objects.filter( - user_id=request.user.id).count() - total_cities = City.objects.count() - visited_region_count = VisitedRegion.objects.filter( - user_id=request.user.id).count() - total_regions = Region.objects.count() - visited_country_count = VisitedRegion.objects.filter( - user_id=request.user.id).values('region__country').distinct().count() - total_countries = Country.objects.count() - return Response({ - 'adventure_count': adventure_count, - 'trips_count': trips_count, - 'visited_city_count': visited_city_count, - 'total_cities': total_cities, - 'visited_region_count': visited_region_count, - 'total_regions': total_regions, - 'visited_country_count': visited_country_count, - 'total_countries': total_countries - }) - -class GenerateDescription(viewsets.ViewSet): - permission_classes = [IsAuthenticated] - - @action(detail=False, methods=['get'],) - def desc(self, request): - name = self.request.query_params.get('name', '') - # un url encode the name - name = name.replace('%20', ' ') - print(name) - url = 'https://en.wikipedia.org/w/api.php?origin=*&action=query&prop=extracts&exintro&explaintext&format=json&titles=%s' % name - response = requests.get(url) - data = response.json() - data = response.json() - page_id = next(iter(data["query"]["pages"])) - extract = data["query"]["pages"][page_id] - if extract.get('extract') is None: - return Response({"error": "No description found"}, status=400) - return Response(extract) - @action(detail=False, methods=['get'],) - def img(self, request): - name = self.request.query_params.get('name', '') - # un url encode the name - name = name.replace('%20', ' ') - url = 'https://en.wikipedia.org/w/api.php?origin=*&action=query&prop=pageimages&format=json&piprop=original&titles=%s' % name - response = requests.get(url) - data = response.json() - page_id = next(iter(data["query"]["pages"])) - extract = data["query"]["pages"][page_id] - if extract.get('original') is None: - return Response({"error": "No image found"}, status=400) - return Response(extract["original"]) - - -class ActivityTypesView(viewsets.ViewSet): - permission_classes = [IsAuthenticated] - - @action(detail=False, methods=['get']) - def types(self, request): - """ - Retrieve a list of distinct activity types for adventures associated with the current user. - - Args: - request (HttpRequest): The HTTP request object. - - Returns: - Response: A response containing a list of distinct activity types. - """ - types = Adventure.objects.filter(user_id=request.user.id).values_list('activity_types', flat=True).distinct() - - allTypes = [] - - for i in types: - if not i: - continue - for x in i: - if x and x not in allTypes: - allTypes.append(x) - - return Response(allTypes) - -class CategoryViewSet(viewsets.ModelViewSet): - queryset = Category.objects.all() - serializer_class = CategorySerializer - permission_classes = [IsAuthenticated] - - def get_queryset(self): - return Category.objects.filter(user_id=self.request.user) - - @action(detail=False, methods=['get']) - def categories(self, request): - """ - Retrieve a list of distinct categories for adventures associated with the current user. - """ - categories = self.get_queryset().distinct() - serializer = self.get_serializer(categories, many=True) - return Response(serializer.data) - - def destroy(self, request, *args, **kwargs): - instance = self.get_object() - if instance.user_id != request.user: - return Response({"error": "User does not own this category"}, status - =400) - - if instance.name == 'general': - return Response({"error": "Cannot delete the general category"}, status=400) - - # set any adventures with this category to a default category called general before deleting the category, if general does not exist create it for the user - general_category = Category.objects.filter(user_id=request.user, name='general').first() - - if not general_category: - general_category = Category.objects.create(user_id=request.user, name='general', icon='🌍', display_name='General') - - Adventure.objects.filter(category=instance).update(category=general_category) - - return super().destroy(request, *args, **kwargs) - - -class TransportationViewSet(viewsets.ModelViewSet): - queryset = Transportation.objects.all() - serializer_class = TransportationSerializer - permission_classes = [IsOwnerOrSharedWithFullAccess, IsPublicOrOwnerOrSharedWithFullAccess] - filterset_fields = ['type', 'is_public', 'collection'] - - # return error message if user is not authenticated on the root endpoint - def list(self, request, *args, **kwargs): - # Prevent listing all adventures - return Response({"detail": "Listing all transportations is not allowed."}, - status=status.HTTP_403_FORBIDDEN) - - @action(detail=False, methods=['get']) - def all(self, request): - if not request.user.is_authenticated: - return Response({"error": "User is not authenticated"}, status=400) - queryset = Transportation.objects.filter( - Q(user_id=request.user.id) - ) - serializer = self.get_serializer(queryset, many=True) - return Response(serializer.data) - - - def get_queryset(self): - # if the user is not authenticated return only public transportations for retrieve action - if not self.request.user.is_authenticated: - if self.action == 'retrieve': - return Transportation.objects.filter(is_public=True).distinct().order_by('-updated_at') - return Transportation.objects.none() - - - if self.action == 'retrieve': - # For individual adventure retrieval, include public adventures - return Transportation.objects.filter( - Q(is_public=True) | Q(user_id=self.request.user.id) | Q(collection__shared_with=self.request.user) - ).distinct().order_by('-updated_at') - else: - # For other actions, include user's own adventures and shared adventures - return Transportation.objects.filter( - Q(user_id=self.request.user.id) | Q(collection__shared_with=self.request.user) - ).distinct().order_by('-updated_at') - - def partial_update(self, request, *args, **kwargs): - # Retrieve the current object - instance = self.get_object() - - # Partially update the instance with the request data - serializer = self.get_serializer(instance, data=request.data, partial=True) - serializer.is_valid(raise_exception=True) - - # Retrieve the collection from the validated data - new_collection = serializer.validated_data.get('collection') - - user = request.user - print(new_collection) - - if new_collection is not None and new_collection!=instance.collection: - # Check if the user is the owner of the new collection - if new_collection.user_id != user or instance.user_id != user: - raise PermissionDenied("You do not have permission to use this collection.") - elif new_collection is None: - # Handle the case where the user is trying to set the collection to None - if instance.collection is not None and instance.collection.user_id != user: - raise PermissionDenied("You cannot remove the collection as you are not the owner.") - - # Perform the update - self.perform_update(serializer) - - # Return the updated instance - return Response(serializer.data) - - def partial_update(self, request, *args, **kwargs): - # Retrieve the current object - instance = self.get_object() - - # Partially update the instance with the request data - serializer = self.get_serializer(instance, data=request.data, partial=True) - serializer.is_valid(raise_exception=True) - - # Retrieve the collection from the validated data - new_collection = serializer.validated_data.get('collection') - - user = request.user - print(new_collection) - - if new_collection is not None and new_collection!=instance.collection: - # Check if the user is the owner of the new collection - if new_collection.user_id != user or instance.user_id != user: - raise PermissionDenied("You do not have permission to use this collection.") - elif new_collection is None: - # Handle the case where the user is trying to set the collection to None - if instance.collection is not None and instance.collection.user_id != user: - raise PermissionDenied("You cannot remove the collection as you are not the owner.") - - # Perform the update - self.perform_update(serializer) - - # Return the updated instance - return Response(serializer.data) - - def perform_update(self, serializer): - serializer.save() - - # when creating an adventure, make sure the user is the owner of the collection or shared with the collection - def perform_create(self, serializer): - # Retrieve the collection from the validated data - collection = serializer.validated_data.get('collection') - - # Check if a collection is provided - if collection: - user = self.request.user - # Check if the user is the owner or is in the shared_with list - if collection.user_id != user and not collection.shared_with.filter(id=user.id).exists(): - # Return an error response if the user does not have permission - raise PermissionDenied("You do not have permission to use this collection.") - # if collection the owner of the adventure is the owner of the collection - serializer.save(user_id=collection.user_id) - return - - # Save the adventure with the current user as the owner - serializer.save(user_id=self.request.user) - -class NoteViewSet(viewsets.ModelViewSet): - queryset = Note.objects.all() - serializer_class = NoteSerializer - permission_classes = [IsOwnerOrSharedWithFullAccess, IsPublicOrOwnerOrSharedWithFullAccess] - filterset_fields = ['is_public', 'collection'] - - # return error message if user is not authenticated on the root endpoint - def list(self, request, *args, **kwargs): - # Prevent listing all adventures - return Response({"detail": "Listing all notes is not allowed."}, - status=status.HTTP_403_FORBIDDEN) - - @action(detail=False, methods=['get']) - def all(self, request): - if not request.user.is_authenticated: - return Response({"error": "User is not authenticated"}, status=400) - queryset = Note.objects.filter( - Q(user_id=request.user.id) - ) - serializer = self.get_serializer(queryset, many=True) - return Response(serializer.data) - - - def get_queryset(self): - # if the user is not authenticated return only public transportations for retrieve action - if not self.request.user.is_authenticated: - if self.action == 'retrieve': - return Note.objects.filter(is_public=True).distinct().order_by('-updated_at') - return Note.objects.none() - - - if self.action == 'retrieve': - # For individual adventure retrieval, include public adventures - return Note.objects.filter( - Q(is_public=True) | Q(user_id=self.request.user.id) | Q(collection__shared_with=self.request.user) - ).distinct().order_by('-updated_at') - else: - # For other actions, include user's own adventures and shared adventures - return Note.objects.filter( - Q(user_id=self.request.user.id) | Q(collection__shared_with=self.request.user) - ).distinct().order_by('-updated_at') - - def partial_update(self, request, *args, **kwargs): - # Retrieve the current object - instance = self.get_object() - - # Partially update the instance with the request data - serializer = self.get_serializer(instance, data=request.data, partial=True) - serializer.is_valid(raise_exception=True) - - # Retrieve the collection from the validated data - new_collection = serializer.validated_data.get('collection') - - user = request.user - print(new_collection) - - if new_collection is not None and new_collection!=instance.collection: - # Check if the user is the owner of the new collection - if new_collection.user_id != user or instance.user_id != user: - raise PermissionDenied("You do not have permission to use this collection.") - elif new_collection is None: - # Handle the case where the user is trying to set the collection to None - if instance.collection is not None and instance.collection.user_id != user: - raise PermissionDenied("You cannot remove the collection as you are not the owner.") - - # Perform the update - self.perform_update(serializer) - - # Return the updated instance - return Response(serializer.data) - - def partial_update(self, request, *args, **kwargs): - # Retrieve the current object - instance = self.get_object() - - # Partially update the instance with the request data - serializer = self.get_serializer(instance, data=request.data, partial=True) - serializer.is_valid(raise_exception=True) - - # Retrieve the collection from the validated data - new_collection = serializer.validated_data.get('collection') - - user = request.user - print(new_collection) - - if new_collection is not None and new_collection!=instance.collection: - # Check if the user is the owner of the new collection - if new_collection.user_id != user or instance.user_id != user: - raise PermissionDenied("You do not have permission to use this collection.") - elif new_collection is None: - # Handle the case where the user is trying to set the collection to None - if instance.collection is not None and instance.collection.user_id != user: - raise PermissionDenied("You cannot remove the collection as you are not the owner.") - - # Perform the update - self.perform_update(serializer) - - # Return the updated instance - return Response(serializer.data) - - def perform_update(self, serializer): - serializer.save() - - # when creating an adventure, make sure the user is the owner of the collection or shared with the collection - def perform_create(self, serializer): - # Retrieve the collection from the validated data - collection = serializer.validated_data.get('collection') - - # Check if a collection is provided - if collection: - user = self.request.user - # Check if the user is the owner or is in the shared_with list - if collection.user_id != user and not collection.shared_with.filter(id=user.id).exists(): - # Return an error response if the user does not have permission - raise PermissionDenied("You do not have permission to use this collection.") - # if collection the owner of the adventure is the owner of the collection - serializer.save(user_id=collection.user_id) - return - - # Save the adventure with the current user as the owner - serializer.save(user_id=self.request.user) - -class ChecklistViewSet(viewsets.ModelViewSet): - queryset = Checklist.objects.all() - serializer_class = ChecklistSerializer - permission_classes = [IsOwnerOrSharedWithFullAccess, IsPublicOrOwnerOrSharedWithFullAccess] - filterset_fields = ['is_public', 'collection'] - - # return error message if user is not authenticated on the root endpoint - def list(self, request, *args, **kwargs): - # Prevent listing all adventures - return Response({"detail": "Listing all checklists is not allowed."}, - status=status.HTTP_403_FORBIDDEN) - - @action(detail=False, methods=['get']) - def all(self, request): - if not request.user.is_authenticated: - return Response({"error": "User is not authenticated"}, status=400) - queryset = Checklist.objects.filter( - Q(user_id=request.user.id) - ) - serializer = self.get_serializer(queryset, many=True) - return Response(serializer.data) - - - def get_queryset(self): - # if the user is not authenticated return only public transportations for retrieve action - if not self.request.user.is_authenticated: - if self.action == 'retrieve': - return Checklist.objects.filter(is_public=True).distinct().order_by('-updated_at') - return Checklist.objects.none() - - - if self.action == 'retrieve': - # For individual adventure retrieval, include public adventures - return Checklist.objects.filter( - Q(is_public=True) | Q(user_id=self.request.user.id) | Q(collection__shared_with=self.request.user) - ).distinct().order_by('-updated_at') - else: - # For other actions, include user's own adventures and shared adventures - return Checklist.objects.filter( - Q(user_id=self.request.user.id) | Q(collection__shared_with=self.request.user) - ).distinct().order_by('-updated_at') - - def partial_update(self, request, *args, **kwargs): - # Retrieve the current object - instance = self.get_object() - - # Partially update the instance with the request data - serializer = self.get_serializer(instance, data=request.data, partial=True) - serializer.is_valid(raise_exception=True) - - # Retrieve the collection from the validated data - new_collection = serializer.validated_data.get('collection') - - user = request.user - print(new_collection) - - if new_collection is not None and new_collection!=instance.collection: - # Check if the user is the owner of the new collection - if new_collection.user_id != user or instance.user_id != user: - raise PermissionDenied("You do not have permission to use this collection.") - elif new_collection is None: - # Handle the case where the user is trying to set the collection to None - if instance.collection is not None and instance.collection.user_id != user: - raise PermissionDenied("You cannot remove the collection as you are not the owner.") - - # Perform the update - self.perform_update(serializer) - - # Return the updated instance - return Response(serializer.data) - - def partial_update(self, request, *args, **kwargs): - # Retrieve the current object - instance = self.get_object() - - # Partially update the instance with the request data - serializer = self.get_serializer(instance, data=request.data, partial=True) - serializer.is_valid(raise_exception=True) - - # Retrieve the collection from the validated data - new_collection = serializer.validated_data.get('collection') - - user = request.user - print(new_collection) - - if new_collection is not None and new_collection!=instance.collection: - # Check if the user is the owner of the new collection - if new_collection.user_id != user or instance.user_id != user: - raise PermissionDenied("You do not have permission to use this collection.") - elif new_collection is None: - # Handle the case where the user is trying to set the collection to None - if instance.collection is not None and instance.collection.user_id != user: - raise PermissionDenied("You cannot remove the collection as you are not the owner.") - - # Perform the update - self.perform_update(serializer) - - # Return the updated instance - return Response(serializer.data) - - def perform_update(self, serializer): - serializer.save() - - # when creating an adventure, make sure the user is the owner of the collection or shared with the collection - def perform_create(self, serializer): - # Retrieve the collection from the validated data - collection = serializer.validated_data.get('collection') - - # Check if a collection is provided - if collection: - user = self.request.user - # Check if the user is the owner or is in the shared_with list - if collection.user_id != user and not collection.shared_with.filter(id=user.id).exists(): - # Return an error response if the user does not have permission - raise PermissionDenied("You do not have permission to use this collection.") - # if collection the owner of the adventure is the owner of the collection - serializer.save(user_id=collection.user_id) - return - - # Save the adventure with the current user as the owner - serializer.save(user_id=self.request.user) - -class AdventureImageViewSet(viewsets.ModelViewSet): - serializer_class = AdventureImageSerializer - permission_classes = [IsAuthenticated] - - def dispatch(self, request, *args, **kwargs): - print(f"Method: {request.method}") - return super().dispatch(request, *args, **kwargs) - - @action(detail=True, methods=['post']) - def image_delete(self, request, *args, **kwargs): - return self.destroy(request, *args, **kwargs) - - @action(detail=True, methods=['post']) - def toggle_primary(self, request, *args, **kwargs): - # Makes the image the primary image for the adventure, if there is already a primary image linked to the adventure, it is set to false and the new image is set to true. make sure that the permission is set to the owner of the adventure - if not request.user.is_authenticated: - return Response({"error": "User is not authenticated"}, status=status.HTTP_401_UNAUTHORIZED) - - instance = self.get_object() - adventure = instance.adventure - if adventure.user_id != request.user: - return Response({"error": "User does not own this adventure"}, status=status.HTTP_403_FORBIDDEN) - - # Check if the image is already the primary image - if instance.is_primary: - return Response({"error": "Image is already the primary image"}, status=status.HTTP_400_BAD_REQUEST) - - # Set the current primary image to false - AdventureImage.objects.filter(adventure=adventure, is_primary=True).update(is_primary=False) - - # Set the new image to true - instance.is_primary = True - instance.save() - return Response({"success": "Image set as primary image"}) - - def create(self, request, *args, **kwargs): - if not request.user.is_authenticated: - return Response({"error": "User is not authenticated"}, status=status.HTTP_401_UNAUTHORIZED) - adventure_id = request.data.get('adventure') - try: - adventure = Adventure.objects.get(id=adventure_id) - except Adventure.DoesNotExist: - return Response({"error": "Adventure not found"}, status=status.HTTP_404_NOT_FOUND) - - if adventure.user_id != request.user: - # Check if the adventure has a collection - if adventure.collection: - # Check if the user is in the collection's shared_with list - if not adventure.collection.shared_with.filter(id=request.user.id).exists(): - return Response({"error": "User does not have permission to access this adventure"}, status=status.HTTP_403_FORBIDDEN) - else: - return Response({"error": "User does not own this adventure"}, status=status.HTTP_403_FORBIDDEN) - - return super().create(request, *args, **kwargs) - - def update(self, request, *args, **kwargs): - if not request.user.is_authenticated: - return Response({"error": "User is not authenticated"}, status=status.HTTP_401_UNAUTHORIZED) - - adventure_id = request.data.get('adventure') - try: - adventure = Adventure.objects.get(id=adventure_id) - except Adventure.DoesNotExist: - return Response({"error": "Adventure not found"}, status=status.HTTP_404_NOT_FOUND) - - if adventure.user_id != request.user: - return Response({"error": "User does not own this adventure"}, status=status.HTTP_403_FORBIDDEN) - - return super().update(request, *args, **kwargs) - - def perform_destroy(self, instance): - print("perform_destroy") - return super().perform_destroy(instance) - - def destroy(self, request, *args, **kwargs): - print("destroy") - if not request.user.is_authenticated: - return Response({"error": "User is not authenticated"}, status=status.HTTP_401_UNAUTHORIZED) - - instance = self.get_object() - adventure = instance.adventure - if adventure.user_id != request.user: - return Response({"error": "User does not own this adventure"}, status=status.HTTP_403_FORBIDDEN) - - return super().destroy(request, *args, **kwargs) - - def partial_update(self, request, *args, **kwargs): - if not request.user.is_authenticated: - return Response({"error": "User is not authenticated"}, status=status.HTTP_401_UNAUTHORIZED) - - instance = self.get_object() - adventure = instance.adventure - if adventure.user_id != request.user: - return Response({"error": "User does not own this adventure"}, status=status.HTTP_403_FORBIDDEN) - - return super().partial_update(request, *args, **kwargs) - - @action(detail=False, methods=['GET'], url_path='(?P[0-9a-f-]+)') - def adventure_images(self, request, adventure_id=None, *args, **kwargs): - if not request.user.is_authenticated: - return Response({"error": "User is not authenticated"}, status=status.HTTP_401_UNAUTHORIZED) - - try: - adventure_uuid = uuid.UUID(adventure_id) - except ValueError: - return Response({"error": "Invalid adventure ID"}, status=status.HTTP_400_BAD_REQUEST) - - queryset = AdventureImage.objects.filter( - Q(adventure__id=adventure_uuid) & Q(user_id=request.user) - ) - - serializer = self.get_serializer(queryset, many=True, context={'request': request}) - return Response(serializer.data) - - def get_queryset(self): - return AdventureImage.objects.filter(user_id=self.request.user) - - def perform_create(self, serializer): - serializer.save(user_id=self.request.user) - -class ReverseGeocodeViewSet(viewsets.ViewSet): - permission_classes = [IsAuthenticated] - - def extractIsoCode(self, data): - """ - Extract the ISO code from the response data. - Returns a dictionary containing the region name, country name, and ISO code if found. - """ - iso_code = None - town_city_or_county = None - display_name = None - country_code = None - city = None - visited_city = None - - # town = None - # city = None - # county = None - - if 'address' in data.keys(): - keys = data['address'].keys() - for key in keys: - if key.find("ISO") != -1: - iso_code = data['address'][key] - if 'town' in keys: - town_city_or_county = data['address']['town'] - if 'county' in keys: - town_city_or_county = data['address']['county'] - if 'city' in keys: - town_city_or_county = data['address']['city'] - if not iso_code: - return {"error": "No region found"} - - region = Region.objects.filter(id=iso_code).first() - visited_region = VisitedRegion.objects.filter(region=region, user_id=self.request.user).first() - - region_visited = False - city_visited = False - country_code = iso_code[:2] - - if region: - if town_city_or_county: - display_name = f"{town_city_or_county}, {region.name}, {country_code}" - city = City.objects.filter(name__contains=town_city_or_county, region=region).first() - visited_city = VisitedCity.objects.filter(city=city, user_id=self.request.user).first() - - if visited_region: - region_visited = True - if visited_city: - city_visited = True - if region: - return {"region_id": iso_code, "region": region.name, "country": region.country.name, "region_visited": region_visited, "display_name": display_name, "city": city.name if city else None, "city_id": city.id if city else None, "city_visited": city_visited} - return {"error": "No region found"} - - @action(detail=False, methods=['get']) - def reverse_geocode(self, request): - lat = request.query_params.get('lat', '') - lon = request.query_params.get('lon', '') - url = f"https://nominatim.openstreetmap.org/reverse?format=jsonv2&lat={lat}&lon={lon}" - headers = {'User-Agent': 'AdventureLog Server'} - response = requests.get(url, headers=headers) - try: - data = response.json() - except requests.exceptions.JSONDecodeError: - return Response({"error": "Invalid response from geocoding service"}, status=400) - return Response(self.extractIsoCode(data)) - - @action(detail=False, methods=['post']) - def mark_visited_region(self, request): - # searches through all of the users adventures, if the serialized data is_visited, is true, runs reverse geocode on the adventures and if a region is found, marks it as visited. Use the extractIsoCode function to get the region - new_region_count = 0 - new_regions = {} - new_city_count = 0 - new_cities = {} - adventures = Adventure.objects.filter(user_id=self.request.user) - serializer = AdventureSerializer(adventures, many=True) - for adventure, serialized_adventure in zip(adventures, serializer.data): - if serialized_adventure['is_visited'] == True: - lat = adventure.latitude - lon = adventure.longitude - if not lat or not lon: - continue - url = f"https://nominatim.openstreetmap.org/reverse?format=jsonv2&lat={lat}&lon={lon}" - headers = {'User-Agent': 'AdventureLog Server'} - response = requests.get(url, headers=headers) - try: - data = response.json() - except requests.exceptions.JSONDecodeError: - return Response({"error": "Invalid response from geocoding service"}, status=400) - extracted_region = self.extractIsoCode(data) - if 'error' not in extracted_region: - region = Region.objects.filter(id=extracted_region['region_id']).first() - visited_region = VisitedRegion.objects.filter(region=region, user_id=self.request.user).first() - if not visited_region: - visited_region = VisitedRegion(region=region, user_id=self.request.user) - visited_region.save() - new_region_count += 1 - new_regions[region.id] = region.name - - if extracted_region['city_id'] is not None: - city = City.objects.filter(id=extracted_region['city_id']).first() - visited_city = VisitedCity.objects.filter(city=city, user_id=self.request.user).first() - if not visited_city: - visited_city = VisitedCity(city=city, user_id=self.request.user) - visited_city.save() - new_city_count += 1 - new_cities[city.id] = city.name - return Response({"new_regions": new_region_count, "regions": new_regions, "new_cities": new_city_count, "cities": new_cities}) - -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']: - # Skip if start_date is missing - if not visit.get('start_date'): - continue - - # Parse start_date and handle end_date - try: - start_date = datetime.strptime(visit['start_date'], '%Y-%m-%d').date() - except ValueError: - continue # Skip if the start_date is invalid - - end_date = ( - datetime.strptime(visit['end_date'], '%Y-%m-%d').date() + timedelta(days=1) - if visit.get('end_date') else start_date + timedelta(days=1) - ) - - # Create event - event = Event() - event.add('summary', adventure['name']) - 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 - -class OverpassViewSet(viewsets.ViewSet): - permission_classes = [IsAuthenticated] - BASE_URL = "https://overpass-api.de/api/interpreter" - HEADERS = {'User-Agent': 'AdventureLog Server'} - - def make_overpass_query(self, query): - """ - Sends a query to the Overpass API and returns the response data. - Args: - query (str): The Overpass QL query string. - Returns: - dict: Parsed JSON response from the Overpass API. - Raises: - Response: DRF Response object with an error message in case of failure. - """ - url = f"{self.BASE_URL}?data={query}" - try: - response = requests.get(url, headers=self.HEADERS) - response.raise_for_status() # Raise an exception for HTTP errors - return response.json() - except requests.exceptions.RequestException as e: - return Response({"error": str(e)}, status=500) - except requests.exceptions.JSONDecodeError: - return Response({"error": "Invalid response from Overpass API"}, status=400) - - def parse_overpass_response(self, data, request): - """ - Parses the JSON response from the Overpass API and extracts relevant data, - turning it into an adventure-structured object. - - Args: - response (dict): The JSON response from the Overpass API. - - Returns: - list: A list of adventure objects with structured data. - """ - # Extract elements (nodes/ways/relations) from the response - nodes = data.get('elements', []) - adventures = [] - - # include all entries, even the ones that do not have lat long - all = request.query_params.get('all', False) - - for node in nodes: - # Ensure we are working with a "node" type (can also handle "way" or "relation" if needed) - if node.get('type') not in ['node', 'way', 'relation']: - continue - - # Extract tags and general data - tags = node.get('tags', {}) - adventure = { - "id": node.get('id'), # Include the unique OSM ID - "type": node.get('type'), # Type of element (node, way, relation) - "name": tags.get('name', tags.get('official_name', '')), # Fallback to 'official_name' - "description": tags.get('description', None), # Additional descriptive information - "latitude": node.get('lat', None), # Use None for consistency with missing values - "longitude": node.get('lon', None), - "address": { - "city": tags.get('addr:city', None), - "housenumber": tags.get('addr:housenumber', None), - "postcode": tags.get('addr:postcode', None), - "state": tags.get('addr:state', None), - "street": tags.get('addr:street', None), - "country": tags.get('addr:country', None), # Add 'country' if available - "suburb": tags.get('addr:suburb', None), # Add 'suburb' for more granularity - }, - "feature_id": tags.get('gnis:feature_id', None), - "tag": next((tags.get(key, None) for key in ['leisure', 'tourism', 'natural', 'historic', 'amenity'] if key in tags), None), - "contact": { - "phone": tags.get('phone', None), - "email": tags.get('contact:email', None), - "website": tags.get('website', None), - "facebook": tags.get('contact:facebook', None), # Social media links - "twitter": tags.get('contact:twitter', None), - }, - # "tags": tags, # Include all raw tags for future use - } - - # Filter out adventures with no name, latitude, or longitude - if (adventure["name"] and - adventure["latitude"] is not None and -90 <= adventure["latitude"] <= 90 and - adventure["longitude"] is not None and -180 <= adventure["longitude"] <= 180) or all: - adventures.append(adventure) - - return adventures - - - @action(detail=False, methods=['get']) - def query(self, request): - """ - Radius-based search for tourism-related locations around given coordinates. - """ - lat = request.query_params.get('lat') - lon = request.query_params.get('lon') - radius = request.query_params.get('radius', '1000') # Default radius: 1000 meters - - valid_categories = ['lodging', 'food', 'tourism'] - category = request.query_params.get('category', 'all') - if category not in valid_categories: - return Response({"error": f"Invalid category. Valid categories: {', '.join(valid_categories)}"}, status=400) - - if category == 'tourism': - query = f""" - [out:json]; - ( - node(around:{radius},{lat},{lon})["tourism"]; - node(around:{radius},{lat},{lon})["leisure"]; - node(around:{radius},{lat},{lon})["historic"]; - node(around:{radius},{lat},{lon})["sport"]; - node(around:{radius},{lat},{lon})["natural"]; - node(around:{radius},{lat},{lon})["attraction"]; - node(around:{radius},{lat},{lon})["museum"]; - node(around:{radius},{lat},{lon})["zoo"]; - node(around:{radius},{lat},{lon})["aquarium"]; - ); - out; - """ - if category == 'lodging': - query = f""" - [out:json]; - ( - node(around:{radius},{lat},{lon})["tourism"="hotel"]; - node(around:{radius},{lat},{lon})["tourism"="motel"]; - node(around:{radius},{lat},{lon})["tourism"="guest_house"]; - node(around:{radius},{lat},{lon})["tourism"="hostel"]; - node(around:{radius},{lat},{lon})["tourism"="camp_site"]; - node(around:{radius},{lat},{lon})["tourism"="caravan_site"]; - node(around:{radius},{lat},{lon})["tourism"="chalet"]; - node(around:{radius},{lat},{lon})["tourism"="alpine_hut"]; - node(around:{radius},{lat},{lon})["tourism"="apartment"]; - ); - out; - """ - if category == 'food': - query = f""" - [out:json]; - ( - node(around:{radius},{lat},{lon})["amenity"="restaurant"]; - node(around:{radius},{lat},{lon})["amenity"="cafe"]; - node(around:{radius},{lat},{lon})["amenity"="fast_food"]; - node(around:{radius},{lat},{lon})["amenity"="pub"]; - node(around:{radius},{lat},{lon})["amenity"="bar"]; - node(around:{radius},{lat},{lon})["amenity"="food_court"]; - node(around:{radius},{lat},{lon})["amenity"="ice_cream"]; - node(around:{radius},{lat},{lon})["amenity"="bakery"]; - node(around:{radius},{lat},{lon})["amenity"="confectionery"]; - ); - out; - """ - - # Validate required parameters - if not lat or not lon: - return Response( - {"error": "Latitude and longitude parameters are required."}, status=400 - ) - - data = self.make_overpass_query(query) - adventures = self.parse_overpass_response(data, request) - return Response(adventures) - - @action(detail=False, methods=['get'], url_path='search') - def search(self, request): - """ - Name-based search for nodes with the specified name. - """ - name = request.query_params.get('name') - - # Validate required parameter - if not name: - return Response({"error": "Name parameter is required."}, status=400) - - # Construct Overpass API query - query = f'[out:json];node["name"~"{name}",i];out;' - data = self.make_overpass_query(query) - - adventures = self.parse_overpass_response(data, request) - return Response(adventures) diff --git a/backend/server/adventures/views/__init__.py b/backend/server/adventures/views/__init__.py new file mode 100644 index 00000000..7b0d3359 --- /dev/null +++ b/backend/server/adventures/views/__init__.py @@ -0,0 +1,13 @@ +from .activity_types_view import * +from .adventure_image_view import * +from .adventure_view import * +from .category_view import * +from .checklist_view import * +from .collection_view import * +from .generate_description_view import * +from .ics_calendar_view import * +from .note_view import * +from .overpass_view import * +from .reverse_geocode_view import * +from .stats_view import * +from .transportation_view import * \ No newline at end of file diff --git a/backend/server/adventures/views/activity_types_view.py b/backend/server/adventures/views/activity_types_view.py new file mode 100644 index 00000000..438c0d09 --- /dev/null +++ b/backend/server/adventures/views/activity_types_view.py @@ -0,0 +1,32 @@ +from rest_framework import viewsets +from rest_framework.decorators import action +from rest_framework.permissions import IsAuthenticated +from rest_framework.response import Response +from adventures.models import Adventure + +class ActivityTypesView(viewsets.ViewSet): + permission_classes = [IsAuthenticated] + + @action(detail=False, methods=['get']) + def types(self, request): + """ + Retrieve a list of distinct activity types for adventures associated with the current user. + + Args: + request (HttpRequest): The HTTP request object. + + Returns: + Response: A response containing a list of distinct activity types. + """ + types = Adventure.objects.filter(user_id=request.user.id).values_list('activity_types', flat=True).distinct() + + allTypes = [] + + for i in types: + if not i: + continue + for x in i: + if x and x not in allTypes: + allTypes.append(x) + + return Response(allTypes) \ No newline at end of file diff --git a/backend/server/adventures/views/adventure_image_view.py b/backend/server/adventures/views/adventure_image_view.py new file mode 100644 index 00000000..b4a3ce41 --- /dev/null +++ b/backend/server/adventures/views/adventure_image_view.py @@ -0,0 +1,128 @@ +from rest_framework import viewsets, status +from rest_framework.decorators import action +from rest_framework.permissions import IsAuthenticated +from rest_framework.response import Response +from django.db.models import Q +from adventures.models import Adventure, AdventureImage +from adventures.serializers import AdventureImageSerializer +import uuid + +class AdventureImageViewSet(viewsets.ModelViewSet): + serializer_class = AdventureImageSerializer + permission_classes = [IsAuthenticated] + + def dispatch(self, request, *args, **kwargs): + print(f"Method: {request.method}") + return super().dispatch(request, *args, **kwargs) + + @action(detail=True, methods=['post']) + def image_delete(self, request, *args, **kwargs): + return self.destroy(request, *args, **kwargs) + + @action(detail=True, methods=['post']) + def toggle_primary(self, request, *args, **kwargs): + # Makes the image the primary image for the adventure, if there is already a primary image linked to the adventure, it is set to false and the new image is set to true. make sure that the permission is set to the owner of the adventure + if not request.user.is_authenticated: + return Response({"error": "User is not authenticated"}, status=status.HTTP_401_UNAUTHORIZED) + + instance = self.get_object() + adventure = instance.adventure + if adventure.user_id != request.user: + return Response({"error": "User does not own this adventure"}, status=status.HTTP_403_FORBIDDEN) + + # Check if the image is already the primary image + if instance.is_primary: + return Response({"error": "Image is already the primary image"}, status=status.HTTP_400_BAD_REQUEST) + + # Set the current primary image to false + AdventureImage.objects.filter(adventure=adventure, is_primary=True).update(is_primary=False) + + # Set the new image to true + instance.is_primary = True + instance.save() + return Response({"success": "Image set as primary image"}) + + def create(self, request, *args, **kwargs): + if not request.user.is_authenticated: + return Response({"error": "User is not authenticated"}, status=status.HTTP_401_UNAUTHORIZED) + adventure_id = request.data.get('adventure') + try: + adventure = Adventure.objects.get(id=adventure_id) + except Adventure.DoesNotExist: + return Response({"error": "Adventure not found"}, status=status.HTTP_404_NOT_FOUND) + + if adventure.user_id != request.user: + # Check if the adventure has a collection + if adventure.collection: + # Check if the user is in the collection's shared_with list + if not adventure.collection.shared_with.filter(id=request.user.id).exists(): + return Response({"error": "User does not have permission to access this adventure"}, status=status.HTTP_403_FORBIDDEN) + else: + return Response({"error": "User does not own this adventure"}, status=status.HTTP_403_FORBIDDEN) + + return super().create(request, *args, **kwargs) + + def update(self, request, *args, **kwargs): + if not request.user.is_authenticated: + return Response({"error": "User is not authenticated"}, status=status.HTTP_401_UNAUTHORIZED) + + adventure_id = request.data.get('adventure') + try: + adventure = Adventure.objects.get(id=adventure_id) + except Adventure.DoesNotExist: + return Response({"error": "Adventure not found"}, status=status.HTTP_404_NOT_FOUND) + + if adventure.user_id != request.user: + return Response({"error": "User does not own this adventure"}, status=status.HTTP_403_FORBIDDEN) + + return super().update(request, *args, **kwargs) + + def perform_destroy(self, instance): + print("perform_destroy") + return super().perform_destroy(instance) + + def destroy(self, request, *args, **kwargs): + print("destroy") + if not request.user.is_authenticated: + return Response({"error": "User is not authenticated"}, status=status.HTTP_401_UNAUTHORIZED) + + instance = self.get_object() + adventure = instance.adventure + if adventure.user_id != request.user: + return Response({"error": "User does not own this adventure"}, status=status.HTTP_403_FORBIDDEN) + + return super().destroy(request, *args, **kwargs) + + def partial_update(self, request, *args, **kwargs): + if not request.user.is_authenticated: + return Response({"error": "User is not authenticated"}, status=status.HTTP_401_UNAUTHORIZED) + + instance = self.get_object() + adventure = instance.adventure + if adventure.user_id != request.user: + return Response({"error": "User does not own this adventure"}, status=status.HTTP_403_FORBIDDEN) + + return super().partial_update(request, *args, **kwargs) + + @action(detail=False, methods=['GET'], url_path='(?P[0-9a-f-]+)') + def adventure_images(self, request, adventure_id=None, *args, **kwargs): + if not request.user.is_authenticated: + return Response({"error": "User is not authenticated"}, status=status.HTTP_401_UNAUTHORIZED) + + try: + adventure_uuid = uuid.UUID(adventure_id) + except ValueError: + return Response({"error": "Invalid adventure ID"}, status=status.HTTP_400_BAD_REQUEST) + + queryset = AdventureImage.objects.filter( + Q(adventure__id=adventure_uuid) & Q(user_id=request.user) + ) + + serializer = self.get_serializer(queryset, many=True, context={'request': request}) + return Response(serializer.data) + + def get_queryset(self): + return AdventureImage.objects.filter(user_id=self.request.user) + + def perform_create(self, serializer): + serializer.save(user_id=self.request.user) \ No newline at end of file diff --git a/backend/server/adventures/views/adventure_view.py b/backend/server/adventures/views/adventure_view.py new file mode 100644 index 00000000..be86ff3e --- /dev/null +++ b/backend/server/adventures/views/adventure_view.py @@ -0,0 +1,314 @@ +from django.db import transaction +from rest_framework.decorators import action +from rest_framework import viewsets +from django.db.models.functions import Lower +from rest_framework.response import Response +from adventures.models import Adventure, Category +from django.core.exceptions import PermissionDenied +from adventures.serializers import AdventureSerializer +from django.db.models import Q +from adventures.permissions import IsOwnerOrSharedWithFullAccess +from django.shortcuts import get_object_or_404 +from django.db.models import Max +from adventures.utils import pagination + +class AdventureViewSet(viewsets.ModelViewSet): + serializer_class = AdventureSerializer + permission_classes = [IsOwnerOrSharedWithFullAccess] + pagination_class = pagination.StandardResultsSetPagination + + def apply_sorting(self, queryset): + order_by = self.request.query_params.get('order_by', 'updated_at') + order_direction = self.request.query_params.get('order_direction', 'asc') + include_collections = self.request.query_params.get('include_collections', 'true') + + valid_order_by = ['name', 'type', 'date', 'rating', 'updated_at'] + if order_by not in valid_order_by: + order_by = 'name' + + if order_direction not in ['asc', 'desc']: + order_direction = 'asc' + + if order_by == 'date': + # order by the earliest visit object associated with the adventure + queryset = queryset.annotate(latest_visit=Max('visits__start_date')) + queryset = queryset.filter(latest_visit__isnull=False) + ordering = 'latest_visit' + # Apply case-insensitive sorting for the 'name' field + elif order_by == 'name': + queryset = queryset.annotate(lower_name=Lower('name')) + ordering = 'lower_name' + elif order_by == 'rating': + queryset = queryset.filter(rating__isnull=False) + ordering = 'rating' + else: + ordering = order_by + + if order_direction == 'desc': + ordering = f'-{ordering}' + + # reverse ordering for updated_at field + if order_by == 'updated_at': + if order_direction == 'asc': + ordering = '-updated_at' + else: + ordering = 'updated_at' + + print(f"Ordering by: {ordering}") # For debugging + + if include_collections == 'false': + queryset = queryset.filter(collection = None) + + 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': + return Adventure.objects.filter(is_public=True).distinct().order_by('-updated_at') + return Adventure.objects.none() + + if self.action == 'retrieve': + # For individual adventure retrieval, include public adventures + return Adventure.objects.filter( + Q(is_public=True) | Q(user_id=self.request.user.id) | Q(collection__shared_with=self.request.user) + ).distinct().order_by('-updated_at') + else: + # For other actions, include user's own adventures and shared adventures + return Adventure.objects.filter( + Q(user_id=self.request.user.id) | Q(collection__shared_with=self.request.user) + ).distinct().order_by('-updated_at') + + def retrieve(self, request, *args, **kwargs): + queryset = self.get_queryset() + adventure = get_object_or_404(queryset, pk=kwargs['pk']) + serializer = self.get_serializer(adventure) + return Response(serializer.data) + + def perform_update(self, serializer): + adventure = serializer.save() + if adventure.collection: + adventure.is_public = adventure.collection.is_public + adventure.save() + + @action(detail=False, methods=['get']) + def filtered(self, request): + types = request.query_params.get('types', '').split(',') + is_visited = request.query_params.get('is_visited', 'all') + + # Handle case where types is all + if 'all' in types: + types = Category.objects.filter(user_id=request.user).values_list('name', flat=True) + + else: + for type in types: + if not Category.objects.filter(user_id=request.user, name=type).exists(): + return Response({"error": f"Category {type} does not exist"}, status=400) + + if not types: + return Response({"error": "At least one type must be provided"}, status=400) + + queryset = Adventure.objects.filter( + category__in=Category.objects.filter(name__in=types, user_id=request.user), + user_id=request.user.id + ) + + # Handle is_visited filtering + if is_visited.lower() == 'true': + serializer = self.get_serializer(queryset, many=True) + filtered_ids = [ + adventure.id for adventure, serialized_adventure in zip(queryset, serializer.data) + if serialized_adventure['is_visited'] + ] + queryset = queryset.filter(id__in=filtered_ids) + elif is_visited.lower() == 'false': + serializer = self.get_serializer(queryset, many=True) + filtered_ids = [ + adventure.id for adventure, serialized_adventure in zip(queryset, serializer.data) + if not serialized_adventure['is_visited'] + ] + queryset = queryset.filter(id__in=filtered_ids) + # If is_visited is 'all' or any other value, we don't apply additional filtering + + # Apply sorting + queryset = self.apply_sorting(queryset) + + # Paginate and respond + adventures = self.paginate_and_respond(queryset, request) + return adventures + + @action(detail=False, methods=['get']) + def all(self, request): + if not request.user.is_authenticated: + return Response({"error": "User is not authenticated"}, status=400) + include_collections = request.query_params.get('include_collections', 'false') + if include_collections not in ['true', 'false']: + include_collections = 'false' + + if include_collections == 'true': + queryset = Adventure.objects.filter( + Q(is_public=True) | Q(user_id=request.user.id) + ) + else: + queryset = Adventure.objects.filter( + Q(is_public=True) | Q(user_id=request.user.id), collection=None + ) + queryset = Adventure.objects.filter( + Q(user_id=request.user.id) + ) + queryset = self.apply_sorting(queryset) + serializer = self.get_serializer(queryset, many=True) + + return Response(serializer.data) + + @action(detail=False, methods=['get']) + def search(self, request): + query = self.request.query_params.get('query', '') + property = self.request.query_params.get('property', 'all') + if len(query) < 2: + return Response({"error": "Query must be at least 2 characters long"}, status=400) + + if property not in ['name', 'type', 'location', 'description', 'activity_types']: + property = 'all' + + queryset = Adventure.objects.none() + + if property == 'name': + queryset = Adventure.objects.filter( + (Q(name__icontains=query)) & + (Q(user_id=request.user.id) | Q(is_public=True)) + ) + elif property == 'type': + queryset = Adventure.objects.filter( + (Q(type__icontains=query)) & + (Q(user_id=request.user.id) | Q(is_public=True)) + ) + elif property == 'location': + queryset = Adventure.objects.filter( + (Q(location__icontains=query)) & + (Q(user_id=request.user.id) | Q(is_public=True)) + ) + elif property == 'description': + queryset = Adventure.objects.filter( + (Q(description__icontains=query)) & + (Q(user_id=request.user.id) | Q(is_public=True)) + ) + elif property == 'activity_types': + queryset = Adventure.objects.filter( + (Q(activity_types__icontains=query)) & + (Q(user_id=request.user.id) | Q(is_public=True)) + ) + else: + queryset = Adventure.objects.filter( + (Q(name__icontains=query) | Q(description__icontains=query) | Q(location__icontains=query) | Q(activity_types__icontains=query)) & + (Q(user_id=request.user.id) | Q(is_public=True)) + ) + + queryset = self.apply_sorting(queryset) + serializer = self.get_serializer(queryset, many=True) + return Response(serializer.data) + + def update(self, request, *args, **kwargs): + # Retrieve the current object + instance = self.get_object() + + # Partially update the instance with the request data + serializer = self.get_serializer(instance, data=request.data, partial=True) + serializer.is_valid(raise_exception=True) + + # if the adventure is trying to have is_public changed and its part of a collection return an error + if new_collection is not None: + serializer.validated_data['is_public'] = new_collection.is_public + elif instance.collection: + serializer.validated_data['is_public'] = instance.collection.is_public + + + # Retrieve the collection from the validated data + new_collection = serializer.validated_data.get('collection') + + user = request.user + print(new_collection) + + if new_collection is not None and new_collection!=instance.collection: + # Check if the user is the owner of the new collection + if new_collection.user_id != user or instance.user_id != user: + raise PermissionDenied("You do not have permission to use this collection.") + elif new_collection is None: + # Handle the case where the user is trying to set the collection to None + if instance.collection is not None and instance.collection.user_id != user: + raise PermissionDenied("You cannot remove the collection as you are not the owner.") + + # Perform the update + self.perform_update(serializer) + + # Return the updated instance + return Response(serializer.data) + + def partial_update(self, request, *args, **kwargs): + # Retrieve the current object + instance = self.get_object() + + # Partially update the instance with the request data + serializer = self.get_serializer(instance, data=request.data, partial=True) + serializer.is_valid(raise_exception=True) + + # Retrieve the collection from the validated data + new_collection = serializer.validated_data.get('collection') + + user = request.user + print(new_collection) + + # if the adventure is trying to have is_public changed and its part of a collection return an error + if new_collection is not None: + serializer.validated_data['is_public'] = new_collection.is_public + elif instance.collection: + serializer.validated_data['is_public'] = instance.collection.is_public + + if new_collection is not None and new_collection!=instance.collection: + # Check if the user is the owner of the new collection + if new_collection.user_id != user or instance.user_id != user: + raise PermissionDenied("You do not have permission to use this collection.") + elif new_collection is None: + # Handle the case where the user is trying to set the collection to None + if instance.collection is not None and instance.collection.user_id != user: + raise PermissionDenied("You cannot remove the collection as you are not the owner.") + + # Perform the update + self.perform_update(serializer) + + # Return the updated instance + return Response(serializer.data) + + def perform_update(self, serializer): + serializer.save() + + # when creating an adventure, make sure the user is the owner of the collection or shared with the collection + @transaction.atomic + def perform_create(self, serializer): + # Retrieve the collection from the validated data + collection = serializer.validated_data.get('collection') + + # Check if a collection is provided + if collection: + user = self.request.user + # Check if the user is the owner or is in the shared_with list + if collection.user_id != user and not collection.shared_with.filter(id=user.id).exists(): + # Return an error response if the user does not have permission + raise PermissionDenied("You do not have permission to use this collection.") + # if collection the owner of the adventure is the owner of the collection + # set the is_public field of the adventure to the is_public field of the collection + serializer.save(user_id=collection.user_id, is_public=collection.is_public) + return + + # Save the adventure with the current user as the owner + serializer.save(user_id=self.request.user) + + def paginate_and_respond(self, queryset, request): + paginator = self.pagination_class() + page = paginator.paginate_queryset(queryset, request) + if page is not None: + serializer = self.get_serializer(page, many=True) + return paginator.get_paginated_response(serializer.data) + serializer = self.get_serializer(queryset, many=True) + return Response(serializer.data) \ No newline at end of file diff --git a/backend/server/adventures/views/category_view.py b/backend/server/adventures/views/category_view.py new file mode 100644 index 00000000..4bde278c --- /dev/null +++ b/backend/server/adventures/views/category_view.py @@ -0,0 +1,42 @@ +from rest_framework import viewsets +from rest_framework.decorators import action +from rest_framework.permissions import IsAuthenticated +from rest_framework.response import Response +from adventures.models import Category, Adventure +from adventures.serializers import CategorySerializer + +class CategoryViewSet(viewsets.ModelViewSet): + queryset = Category.objects.all() + serializer_class = CategorySerializer + permission_classes = [IsAuthenticated] + + def get_queryset(self): + return Category.objects.filter(user_id=self.request.user) + + @action(detail=False, methods=['get']) + def categories(self, request): + """ + Retrieve a list of distinct categories for adventures associated with the current user. + """ + categories = self.get_queryset().distinct() + serializer = self.get_serializer(categories, many=True) + return Response(serializer.data) + + def destroy(self, request, *args, **kwargs): + instance = self.get_object() + if instance.user_id != request.user: + return Response({"error": "User does not own this category"}, status + =400) + + if instance.name == 'general': + return Response({"error": "Cannot delete the general category"}, status=400) + + # set any adventures with this category to a default category called general before deleting the category, if general does not exist create it for the user + general_category = Category.objects.filter(user_id=request.user, name='general').first() + + if not general_category: + general_category = Category.objects.create(user_id=request.user, name='general', icon='🌍', display_name='General') + + Adventure.objects.filter(category=instance).update(category=general_category) + + return super().destroy(request, *args, **kwargs) \ No newline at end of file diff --git a/backend/server/adventures/views/checklist_view.py b/backend/server/adventures/views/checklist_view.py new file mode 100644 index 00000000..6824f03d --- /dev/null +++ b/backend/server/adventures/views/checklist_view.py @@ -0,0 +1,130 @@ +from rest_framework import viewsets, status +from rest_framework.decorators import action +from rest_framework.response import Response +from django.db.models import Q +from adventures.models import Checklist +from adventures.serializers import ChecklistSerializer +from rest_framework.exceptions import PermissionDenied +from adventures.permissions import IsOwnerOrSharedWithFullAccess + +class ChecklistViewSet(viewsets.ModelViewSet): + queryset = Checklist.objects.all() + serializer_class = ChecklistSerializer + permission_classes = [IsOwnerOrSharedWithFullAccess] + filterset_fields = ['is_public', 'collection'] + + # return error message if user is not authenticated on the root endpoint + def list(self, request, *args, **kwargs): + # Prevent listing all adventures + return Response({"detail": "Listing all checklists is not allowed."}, + status=status.HTTP_403_FORBIDDEN) + + @action(detail=False, methods=['get']) + def all(self, request): + if not request.user.is_authenticated: + return Response({"error": "User is not authenticated"}, status=400) + queryset = Checklist.objects.filter( + Q(user_id=request.user.id) + ) + serializer = self.get_serializer(queryset, many=True) + return Response(serializer.data) + + + def get_queryset(self): + # if the user is not authenticated return only public transportations for retrieve action + if not self.request.user.is_authenticated: + if self.action == 'retrieve': + return Checklist.objects.filter(is_public=True).distinct().order_by('-updated_at') + return Checklist.objects.none() + + + if self.action == 'retrieve': + # For individual adventure retrieval, include public adventures + return Checklist.objects.filter( + Q(is_public=True) | Q(user_id=self.request.user.id) | Q(collection__shared_with=self.request.user) + ).distinct().order_by('-updated_at') + else: + # For other actions, include user's own adventures and shared adventures + return Checklist.objects.filter( + Q(user_id=self.request.user.id) | Q(collection__shared_with=self.request.user) + ).distinct().order_by('-updated_at') + + def partial_update(self, request, *args, **kwargs): + # Retrieve the current object + instance = self.get_object() + + # Partially update the instance with the request data + serializer = self.get_serializer(instance, data=request.data, partial=True) + serializer.is_valid(raise_exception=True) + + # Retrieve the collection from the validated data + new_collection = serializer.validated_data.get('collection') + + user = request.user + print(new_collection) + + if new_collection is not None and new_collection!=instance.collection: + # Check if the user is the owner of the new collection + if new_collection.user_id != user or instance.user_id != user: + raise PermissionDenied("You do not have permission to use this collection.") + elif new_collection is None: + # Handle the case where the user is trying to set the collection to None + if instance.collection is not None and instance.collection.user_id != user: + raise PermissionDenied("You cannot remove the collection as you are not the owner.") + + # Perform the update + self.perform_update(serializer) + + # Return the updated instance + return Response(serializer.data) + + def partial_update(self, request, *args, **kwargs): + # Retrieve the current object + instance = self.get_object() + + # Partially update the instance with the request data + serializer = self.get_serializer(instance, data=request.data, partial=True) + serializer.is_valid(raise_exception=True) + + # Retrieve the collection from the validated data + new_collection = serializer.validated_data.get('collection') + + user = request.user + print(new_collection) + + if new_collection is not None and new_collection!=instance.collection: + # Check if the user is the owner of the new collection + if new_collection.user_id != user or instance.user_id != user: + raise PermissionDenied("You do not have permission to use this collection.") + elif new_collection is None: + # Handle the case where the user is trying to set the collection to None + if instance.collection is not None and instance.collection.user_id != user: + raise PermissionDenied("You cannot remove the collection as you are not the owner.") + + # Perform the update + self.perform_update(serializer) + + # Return the updated instance + return Response(serializer.data) + + def perform_update(self, serializer): + serializer.save() + + # when creating an adventure, make sure the user is the owner of the collection or shared with the collection + def perform_create(self, serializer): + # Retrieve the collection from the validated data + collection = serializer.validated_data.get('collection') + + # Check if a collection is provided + if collection: + user = self.request.user + # Check if the user is the owner or is in the shared_with list + if collection.user_id != user and not collection.shared_with.filter(id=user.id).exists(): + # Return an error response if the user does not have permission + raise PermissionDenied("You do not have permission to use this collection.") + # if collection the owner of the adventure is the owner of the collection + serializer.save(user_id=collection.user_id) + return + + # Save the adventure with the current user as the owner + serializer.save(user_id=self.request.user) \ No newline at end of file diff --git a/backend/server/adventures/views/collection_view.py b/backend/server/adventures/views/collection_view.py new file mode 100644 index 00000000..7e35d640 --- /dev/null +++ b/backend/server/adventures/views/collection_view.py @@ -0,0 +1,219 @@ +from django.db.models import Q +from django.db.models.functions import Lower +from django.db import transaction +from rest_framework import viewsets +from rest_framework.decorators import action +from rest_framework.response import Response +from adventures.models import Collection, Adventure, Transportation, Note +from adventures.permissions import CollectionShared +from adventures.serializers import CollectionSerializer +from users.models import CustomUser as User +from adventures.utils import pagination + +class CollectionViewSet(viewsets.ModelViewSet): + serializer_class = CollectionSerializer + permission_classes = [CollectionShared] + pagination_class = pagination.StandardResultsSetPagination + + # def get_queryset(self): + # return Collection.objects.filter(Q(user_id=self.request.user.id) & Q(is_archived=False)) + + def apply_sorting(self, queryset): + order_by = self.request.query_params.get('order_by', 'name') + order_direction = self.request.query_params.get('order_direction', 'asc') + + valid_order_by = ['name', 'upated_at'] + if order_by not in valid_order_by: + order_by = 'updated_at' + + if order_direction not in ['asc', 'desc']: + order_direction = 'asc' + + # Apply case-insensitive sorting for the 'name' field + if order_by == 'name': + queryset = queryset.annotate(lower_name=Lower('name')) + ordering = 'lower_name' + if order_direction == 'desc': + ordering = f'-{ordering}' + else: + order_by == 'updated_at' + ordering = 'updated_at' + if order_direction == 'asc': + ordering = '-updated_at' + + #print(f"Ordering by: {ordering}") # For debugging + + return queryset.order_by(ordering) + + def list(self, request, *args, **kwargs): + # make sure the user is authenticated + if not request.user.is_authenticated: + return Response({"error": "User is not authenticated"}, status=400) + queryset = Collection.objects.filter(user_id=request.user.id) + queryset = self.apply_sorting(queryset) + collections = self.paginate_and_respond(queryset, request) + return collections + + @action(detail=False, methods=['get']) + def all(self, request): + if not request.user.is_authenticated: + return Response({"error": "User is not authenticated"}, status=400) + + queryset = Collection.objects.filter( + Q(user_id=request.user.id) + ) + + queryset = self.apply_sorting(queryset) + serializer = self.get_serializer(queryset, many=True) + + return Response(serializer.data) + + @action(detail=False, methods=['get']) + def archived(self, request): + if not request.user.is_authenticated: + return Response({"error": "User is not authenticated"}, status=400) + + queryset = Collection.objects.filter( + Q(user_id=request.user.id) & Q(is_archived=True) + ) + + queryset = self.apply_sorting(queryset) + serializer = self.get_serializer(queryset, many=True) + + return Response(serializer.data) + + # this make the is_public field of the collection cascade to the adventures + @transaction.atomic + def update(self, request, *args, **kwargs): + partial = kwargs.pop('partial', False) + instance = self.get_object() + serializer = self.get_serializer(instance, data=request.data, partial=partial) + serializer.is_valid(raise_exception=True) + + if 'collection' in serializer.validated_data: + new_collection = serializer.validated_data['collection'] + # if the new collection is different from the old one and the user making the request is not the owner of the new collection return an error + if new_collection != instance.collection and new_collection.user_id != request.user: + return Response({"error": "User does not own the new collection"}, status=400) + + # Check if the 'is_public' field is present in the update data + if 'is_public' in serializer.validated_data: + new_public_status = serializer.validated_data['is_public'] + + # if is_publuc has changed and the user is not the owner of the collection return an error + if new_public_status != instance.is_public and instance.user_id != request.user: + print(f"User {request.user.id} does not own the collection {instance.id} that is owned by {instance.user_id}") + return Response({"error": "User does not own the collection"}, status=400) + + # Update associated adventures to match the collection's is_public status + Adventure.objects.filter(collection=instance).update(is_public=new_public_status) + + # do the same for transportations + Transportation.objects.filter(collection=instance).update(is_public=new_public_status) + + # do the same for notes + Note.objects.filter(collection=instance).update(is_public=new_public_status) + + # Log the action (optional) + action = "public" if new_public_status else "private" + print(f"Collection {instance.id} and its adventures were set to {action}") + + self.perform_update(serializer) + + if getattr(instance, '_prefetched_objects_cache', None): + # If 'prefetch_related' has been applied to a queryset, we need to + # forcibly invalidate the prefetch cache on the instance. + instance._prefetched_objects_cache = {} + + return Response(serializer.data) + + # make an action to retreive all adventures that are shared with the user + @action(detail=False, methods=['get']) + def shared(self, request): + if not request.user.is_authenticated: + return Response({"error": "User is not authenticated"}, status=400) + queryset = Collection.objects.filter( + shared_with=request.user + ) + queryset = self.apply_sorting(queryset) + serializer = self.get_serializer(queryset, many=True) + return Response(serializer.data) + + # Adds a new user to the shared_with field of an adventure + @action(detail=True, methods=['post'], url_path='share/(?P[^/.]+)') + def share(self, request, pk=None, uuid=None): + collection = self.get_object() + if not uuid: + return Response({"error": "User UUID is required"}, status=400) + try: + user = User.objects.get(uuid=uuid, public_profile=True) + except User.DoesNotExist: + return Response({"error": "User not found"}, status=404) + + if user == request.user: + return Response({"error": "Cannot share with yourself"}, status=400) + + if collection.shared_with.filter(id=user.id).exists(): + return Response({"error": "Adventure is already shared with this user"}, status=400) + + collection.shared_with.add(user) + collection.save() + return Response({"success": f"Shared with {user.username}"}) + + @action(detail=True, methods=['post'], url_path='unshare/(?P[^/.]+)') + def unshare(self, request, pk=None, uuid=None): + if not request.user.is_authenticated: + return Response({"error": "User is not authenticated"}, status=400) + collection = self.get_object() + if not uuid: + return Response({"error": "User UUID is required"}, status=400) + try: + user = User.objects.get(uuid=uuid, public_profile=True) + except User.DoesNotExist: + return Response({"error": "User not found"}, status=404) + + if user == request.user: + return Response({"error": "Cannot unshare with yourself"}, status=400) + + if not collection.shared_with.filter(id=user.id).exists(): + return Response({"error": "Collection is not shared with this user"}, status=400) + + collection.shared_with.remove(user) + collection.save() + return Response({"success": f"Unshared with {user.username}"}) + + def get_queryset(self): + if self.action == 'destroy': + return Collection.objects.filter(user_id=self.request.user.id) + + if self.action in ['update', 'partial_update']: + return Collection.objects.filter( + Q(user_id=self.request.user.id) | Q(shared_with=self.request.user) + ).distinct() + + if self.action == 'retrieve': + if not self.request.user.is_authenticated: + return Collection.objects.filter(is_public=True) + return Collection.objects.filter( + Q(is_public=True) | Q(user_id=self.request.user.id) | Q(shared_with=self.request.user) + ).distinct() + + # For list action, include collections owned by the user or shared with the user, that are not archived + return Collection.objects.filter( + (Q(user_id=self.request.user.id) | Q(shared_with=self.request.user)) & Q(is_archived=False) + ).distinct() + + + def perform_create(self, serializer): + # This is ok because you cannot share a collection when creating it + serializer.save(user_id=self.request.user) + + def paginate_and_respond(self, queryset, request): + paginator = self.pagination_class() + page = paginator.paginate_queryset(queryset, request) + if page is not None: + serializer = self.get_serializer(page, many=True) + return paginator.get_paginated_response(serializer.data) + serializer = self.get_serializer(queryset, many=True) + return Response(serializer.data) + diff --git a/backend/server/adventures/views/generate_description_view.py b/backend/server/adventures/views/generate_description_view.py new file mode 100644 index 00000000..154bdc1f --- /dev/null +++ b/backend/server/adventures/views/generate_description_view.py @@ -0,0 +1,37 @@ +from rest_framework import viewsets +from rest_framework.decorators import action +from rest_framework.permissions import IsAuthenticated +from rest_framework.response import Response +import requests + +class GenerateDescription(viewsets.ViewSet): + permission_classes = [IsAuthenticated] + + @action(detail=False, methods=['get'],) + def desc(self, request): + name = self.request.query_params.get('name', '') + # un url encode the name + name = name.replace('%20', ' ') + print(name) + url = 'https://en.wikipedia.org/w/api.php?origin=*&action=query&prop=extracts&exintro&explaintext&format=json&titles=%s' % name + response = requests.get(url) + data = response.json() + data = response.json() + page_id = next(iter(data["query"]["pages"])) + extract = data["query"]["pages"][page_id] + if extract.get('extract') is None: + return Response({"error": "No description found"}, status=400) + return Response(extract) + @action(detail=False, methods=['get'],) + def img(self, request): + name = self.request.query_params.get('name', '') + # un url encode the name + name = name.replace('%20', ' ') + url = 'https://en.wikipedia.org/w/api.php?origin=*&action=query&prop=pageimages&format=json&piprop=original&titles=%s' % name + response = requests.get(url) + data = response.json() + page_id = next(iter(data["query"]["pages"])) + extract = data["query"]["pages"][page_id] + if extract.get('original') is None: + return Response({"error": "No image found"}, status=400) + return Response(extract["original"]) \ No newline at end of file diff --git a/backend/server/adventures/views/ics_calendar_view.py b/backend/server/adventures/views/ics_calendar_view.py new file mode 100644 index 00000000..9d120e47 --- /dev/null +++ b/backend/server/adventures/views/ics_calendar_view.py @@ -0,0 +1,67 @@ +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 +from adventures.models import Adventure +from adventures.serializers import AdventureSerializer + +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']: + # Skip if start_date is missing + if not visit.get('start_date'): + continue + + # Parse start_date and handle end_date + try: + start_date = datetime.strptime(visit['start_date'], '%Y-%m-%d').date() + except ValueError: + continue # Skip if the start_date is invalid + + end_date = ( + datetime.strptime(visit['end_date'], '%Y-%m-%d').date() + timedelta(days=1) + if visit.get('end_date') else start_date + timedelta(days=1) + ) + + # Create event + event = Event() + event.add('summary', adventure['name']) + 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 \ No newline at end of file diff --git a/backend/server/adventures/views/note_view.py b/backend/server/adventures/views/note_view.py new file mode 100644 index 00000000..5f5f314e --- /dev/null +++ b/backend/server/adventures/views/note_view.py @@ -0,0 +1,130 @@ +from rest_framework import viewsets, status +from rest_framework.response import Response +from django.db.models import Q +from adventures.models import Note +from adventures.serializers import NoteSerializer +from rest_framework.exceptions import PermissionDenied +from adventures.permissions import IsOwnerOrSharedWithFullAccess +from rest_framework.decorators import action + +class NoteViewSet(viewsets.ModelViewSet): + queryset = Note.objects.all() + serializer_class = NoteSerializer + permission_classes = [IsOwnerOrSharedWithFullAccess] + filterset_fields = ['is_public', 'collection'] + + # return error message if user is not authenticated on the root endpoint + def list(self, request, *args, **kwargs): + # Prevent listing all adventures + return Response({"detail": "Listing all notes is not allowed."}, + status=status.HTTP_403_FORBIDDEN) + + @action(detail=False, methods=['get']) + def all(self, request): + if not request.user.is_authenticated: + return Response({"error": "User is not authenticated"}, status=400) + queryset = Note.objects.filter( + Q(user_id=request.user.id) + ) + serializer = self.get_serializer(queryset, many=True) + return Response(serializer.data) + + + def get_queryset(self): + # if the user is not authenticated return only public transportations for retrieve action + if not self.request.user.is_authenticated: + if self.action == 'retrieve': + return Note.objects.filter(is_public=True).distinct().order_by('-updated_at') + return Note.objects.none() + + + if self.action == 'retrieve': + # For individual adventure retrieval, include public adventures + return Note.objects.filter( + Q(is_public=True) | Q(user_id=self.request.user.id) | Q(collection__shared_with=self.request.user) + ).distinct().order_by('-updated_at') + else: + # For other actions, include user's own adventures and shared adventures + return Note.objects.filter( + Q(user_id=self.request.user.id) | Q(collection__shared_with=self.request.user) + ).distinct().order_by('-updated_at') + + def partial_update(self, request, *args, **kwargs): + # Retrieve the current object + instance = self.get_object() + + # Partially update the instance with the request data + serializer = self.get_serializer(instance, data=request.data, partial=True) + serializer.is_valid(raise_exception=True) + + # Retrieve the collection from the validated data + new_collection = serializer.validated_data.get('collection') + + user = request.user + print(new_collection) + + if new_collection is not None and new_collection!=instance.collection: + # Check if the user is the owner of the new collection + if new_collection.user_id != user or instance.user_id != user: + raise PermissionDenied("You do not have permission to use this collection.") + elif new_collection is None: + # Handle the case where the user is trying to set the collection to None + if instance.collection is not None and instance.collection.user_id != user: + raise PermissionDenied("You cannot remove the collection as you are not the owner.") + + # Perform the update + self.perform_update(serializer) + + # Return the updated instance + return Response(serializer.data) + + def partial_update(self, request, *args, **kwargs): + # Retrieve the current object + instance = self.get_object() + + # Partially update the instance with the request data + serializer = self.get_serializer(instance, data=request.data, partial=True) + serializer.is_valid(raise_exception=True) + + # Retrieve the collection from the validated data + new_collection = serializer.validated_data.get('collection') + + user = request.user + print(new_collection) + + if new_collection is not None and new_collection!=instance.collection: + # Check if the user is the owner of the new collection + if new_collection.user_id != user or instance.user_id != user: + raise PermissionDenied("You do not have permission to use this collection.") + elif new_collection is None: + # Handle the case where the user is trying to set the collection to None + if instance.collection is not None and instance.collection.user_id != user: + raise PermissionDenied("You cannot remove the collection as you are not the owner.") + + # Perform the update + self.perform_update(serializer) + + # Return the updated instance + return Response(serializer.data) + + def perform_update(self, serializer): + serializer.save() + + # when creating an adventure, make sure the user is the owner of the collection or shared with the collection + def perform_create(self, serializer): + # Retrieve the collection from the validated data + collection = serializer.validated_data.get('collection') + + # Check if a collection is provided + if collection: + user = self.request.user + # Check if the user is the owner or is in the shared_with list + if collection.user_id != user and not collection.shared_with.filter(id=user.id).exists(): + # Return an error response if the user does not have permission + raise PermissionDenied("You do not have permission to use this collection.") + # if collection the owner of the adventure is the owner of the collection + serializer.save(user_id=collection.user_id) + return + + # Save the adventure with the current user as the owner + serializer.save(user_id=self.request.user) diff --git a/backend/server/adventures/views/overpass_view.py b/backend/server/adventures/views/overpass_view.py new file mode 100644 index 00000000..a64e55b5 --- /dev/null +++ b/backend/server/adventures/views/overpass_view.py @@ -0,0 +1,183 @@ +from rest_framework import viewsets +from rest_framework.decorators import action +from rest_framework.permissions import IsAuthenticated +from rest_framework.response import Response +import requests + +class OverpassViewSet(viewsets.ViewSet): + permission_classes = [IsAuthenticated] + BASE_URL = "https://overpass-api.de/api/interpreter" + HEADERS = {'User-Agent': 'AdventureLog Server'} + + def make_overpass_query(self, query): + """ + Sends a query to the Overpass API and returns the response data. + Args: + query (str): The Overpass QL query string. + Returns: + dict: Parsed JSON response from the Overpass API. + Raises: + Response: DRF Response object with an error message in case of failure. + """ + url = f"{self.BASE_URL}?data={query}" + try: + response = requests.get(url, headers=self.HEADERS) + response.raise_for_status() # Raise an exception for HTTP errors + return response.json() + except requests.exceptions.RequestException as e: + return Response({"error": str(e)}, status=500) + except requests.exceptions.JSONDecodeError: + return Response({"error": "Invalid response from Overpass API"}, status=400) + + def parse_overpass_response(self, data, request): + """ + Parses the JSON response from the Overpass API and extracts relevant data, + turning it into an adventure-structured object. + + Args: + response (dict): The JSON response from the Overpass API. + + Returns: + list: A list of adventure objects with structured data. + """ + # Extract elements (nodes/ways/relations) from the response + nodes = data.get('elements', []) + adventures = [] + + # include all entries, even the ones that do not have lat long + all = request.query_params.get('all', False) + + for node in nodes: + # Ensure we are working with a "node" type (can also handle "way" or "relation" if needed) + if node.get('type') not in ['node', 'way', 'relation']: + continue + + # Extract tags and general data + tags = node.get('tags', {}) + adventure = { + "id": node.get('id'), # Include the unique OSM ID + "type": node.get('type'), # Type of element (node, way, relation) + "name": tags.get('name', tags.get('official_name', '')), # Fallback to 'official_name' + "description": tags.get('description', None), # Additional descriptive information + "latitude": node.get('lat', None), # Use None for consistency with missing values + "longitude": node.get('lon', None), + "address": { + "city": tags.get('addr:city', None), + "housenumber": tags.get('addr:housenumber', None), + "postcode": tags.get('addr:postcode', None), + "state": tags.get('addr:state', None), + "street": tags.get('addr:street', None), + "country": tags.get('addr:country', None), # Add 'country' if available + "suburb": tags.get('addr:suburb', None), # Add 'suburb' for more granularity + }, + "feature_id": tags.get('gnis:feature_id', None), + "tag": next((tags.get(key, None) for key in ['leisure', 'tourism', 'natural', 'historic', 'amenity'] if key in tags), None), + "contact": { + "phone": tags.get('phone', None), + "email": tags.get('contact:email', None), + "website": tags.get('website', None), + "facebook": tags.get('contact:facebook', None), # Social media links + "twitter": tags.get('contact:twitter', None), + }, + # "tags": tags, # Include all raw tags for future use + } + + # Filter out adventures with no name, latitude, or longitude + if (adventure["name"] and + adventure["latitude"] is not None and -90 <= adventure["latitude"] <= 90 and + adventure["longitude"] is not None and -180 <= adventure["longitude"] <= 180) or all: + adventures.append(adventure) + + return adventures + + + @action(detail=False, methods=['get']) + def query(self, request): + """ + Radius-based search for tourism-related locations around given coordinates. + """ + lat = request.query_params.get('lat') + lon = request.query_params.get('lon') + radius = request.query_params.get('radius', '1000') # Default radius: 1000 meters + + valid_categories = ['lodging', 'food', 'tourism'] + category = request.query_params.get('category', 'all') + if category not in valid_categories: + return Response({"error": f"Invalid category. Valid categories: {', '.join(valid_categories)}"}, status=400) + + if category == 'tourism': + query = f""" + [out:json]; + ( + node(around:{radius},{lat},{lon})["tourism"]; + node(around:{radius},{lat},{lon})["leisure"]; + node(around:{radius},{lat},{lon})["historic"]; + node(around:{radius},{lat},{lon})["sport"]; + node(around:{radius},{lat},{lon})["natural"]; + node(around:{radius},{lat},{lon})["attraction"]; + node(around:{radius},{lat},{lon})["museum"]; + node(around:{radius},{lat},{lon})["zoo"]; + node(around:{radius},{lat},{lon})["aquarium"]; + ); + out; + """ + if category == 'lodging': + query = f""" + [out:json]; + ( + node(around:{radius},{lat},{lon})["tourism"="hotel"]; + node(around:{radius},{lat},{lon})["tourism"="motel"]; + node(around:{radius},{lat},{lon})["tourism"="guest_house"]; + node(around:{radius},{lat},{lon})["tourism"="hostel"]; + node(around:{radius},{lat},{lon})["tourism"="camp_site"]; + node(around:{radius},{lat},{lon})["tourism"="caravan_site"]; + node(around:{radius},{lat},{lon})["tourism"="chalet"]; + node(around:{radius},{lat},{lon})["tourism"="alpine_hut"]; + node(around:{radius},{lat},{lon})["tourism"="apartment"]; + ); + out; + """ + if category == 'food': + query = f""" + [out:json]; + ( + node(around:{radius},{lat},{lon})["amenity"="restaurant"]; + node(around:{radius},{lat},{lon})["amenity"="cafe"]; + node(around:{radius},{lat},{lon})["amenity"="fast_food"]; + node(around:{radius},{lat},{lon})["amenity"="pub"]; + node(around:{radius},{lat},{lon})["amenity"="bar"]; + node(around:{radius},{lat},{lon})["amenity"="food_court"]; + node(around:{radius},{lat},{lon})["amenity"="ice_cream"]; + node(around:{radius},{lat},{lon})["amenity"="bakery"]; + node(around:{radius},{lat},{lon})["amenity"="confectionery"]; + ); + out; + """ + + # Validate required parameters + if not lat or not lon: + return Response( + {"error": "Latitude and longitude parameters are required."}, status=400 + ) + + data = self.make_overpass_query(query) + adventures = self.parse_overpass_response(data, request) + return Response(adventures) + + @action(detail=False, methods=['get'], url_path='search') + def search(self, request): + """ + Name-based search for nodes with the specified name. + """ + name = request.query_params.get('name') + + # Validate required parameter + if not name: + return Response({"error": "Name parameter is required."}, status=400) + + # Construct Overpass API query + query = f'[out:json];node["name"~"{name}",i];out;' + data = self.make_overpass_query(query) + + adventures = self.parse_overpass_response(data, request) + return Response(adventures) diff --git a/backend/server/adventures/views/reverse_geocode_view.py b/backend/server/adventures/views/reverse_geocode_view.py new file mode 100644 index 00000000..dbbd026d --- /dev/null +++ b/backend/server/adventures/views/reverse_geocode_view.py @@ -0,0 +1,117 @@ +from rest_framework import viewsets +from rest_framework.decorators import action +from rest_framework.permissions import IsAuthenticated +from rest_framework.response import Response +from worldtravel.models import Region, City, VisitedRegion, VisitedCity +from adventures.models import Adventure +from adventures.serializers import AdventureSerializer +import requests + +class ReverseGeocodeViewSet(viewsets.ViewSet): + permission_classes = [IsAuthenticated] + + def extractIsoCode(self, data): + """ + Extract the ISO code from the response data. + Returns a dictionary containing the region name, country name, and ISO code if found. + """ + iso_code = None + town_city_or_county = None + display_name = None + country_code = None + city = None + visited_city = None + + # town = None + # city = None + # county = None + + if 'address' in data.keys(): + keys = data['address'].keys() + for key in keys: + if key.find("ISO") != -1: + iso_code = data['address'][key] + if 'town' in keys: + town_city_or_county = data['address']['town'] + if 'county' in keys: + town_city_or_county = data['address']['county'] + if 'city' in keys: + town_city_or_county = data['address']['city'] + if not iso_code: + return {"error": "No region found"} + + region = Region.objects.filter(id=iso_code).first() + visited_region = VisitedRegion.objects.filter(region=region, user_id=self.request.user).first() + + region_visited = False + city_visited = False + country_code = iso_code[:2] + + if region: + if town_city_or_county: + display_name = f"{town_city_or_county}, {region.name}, {country_code}" + city = City.objects.filter(name__contains=town_city_or_county, region=region).first() + visited_city = VisitedCity.objects.filter(city=city, user_id=self.request.user).first() + + if visited_region: + region_visited = True + if visited_city: + city_visited = True + if region: + return {"region_id": iso_code, "region": region.name, "country": region.country.name, "region_visited": region_visited, "display_name": display_name, "city": city.name if city else None, "city_id": city.id if city else None, "city_visited": city_visited} + return {"error": "No region found"} + + @action(detail=False, methods=['get']) + def reverse_geocode(self, request): + lat = request.query_params.get('lat', '') + lon = request.query_params.get('lon', '') + url = f"https://nominatim.openstreetmap.org/reverse?format=jsonv2&lat={lat}&lon={lon}" + headers = {'User-Agent': 'AdventureLog Server'} + response = requests.get(url, headers=headers) + try: + data = response.json() + except requests.exceptions.JSONDecodeError: + return Response({"error": "Invalid response from geocoding service"}, status=400) + return Response(self.extractIsoCode(data)) + + @action(detail=False, methods=['post']) + def mark_visited_region(self, request): + # searches through all of the users adventures, if the serialized data is_visited, is true, runs reverse geocode on the adventures and if a region is found, marks it as visited. Use the extractIsoCode function to get the region + new_region_count = 0 + new_regions = {} + new_city_count = 0 + new_cities = {} + adventures = Adventure.objects.filter(user_id=self.request.user) + serializer = AdventureSerializer(adventures, many=True) + for adventure, serialized_adventure in zip(adventures, serializer.data): + if serialized_adventure['is_visited'] == True: + lat = adventure.latitude + lon = adventure.longitude + if not lat or not lon: + continue + url = f"https://nominatim.openstreetmap.org/reverse?format=jsonv2&lat={lat}&lon={lon}" + headers = {'User-Agent': 'AdventureLog Server'} + response = requests.get(url, headers=headers) + try: + data = response.json() + except requests.exceptions.JSONDecodeError: + return Response({"error": "Invalid response from geocoding service"}, status=400) + extracted_region = self.extractIsoCode(data) + if 'error' not in extracted_region: + region = Region.objects.filter(id=extracted_region['region_id']).first() + visited_region = VisitedRegion.objects.filter(region=region, user_id=self.request.user).first() + if not visited_region: + visited_region = VisitedRegion(region=region, user_id=self.request.user) + visited_region.save() + new_region_count += 1 + new_regions[region.id] = region.name + + if extracted_region['city_id'] is not None: + city = City.objects.filter(id=extracted_region['city_id']).first() + visited_city = VisitedCity.objects.filter(city=city, user_id=self.request.user).first() + if not visited_city: + visited_city = VisitedCity(city=city, user_id=self.request.user) + visited_city.save() + new_city_count += 1 + new_cities[city.id] = city.name + return Response({"new_regions": new_region_count, "regions": new_regions, "new_cities": new_city_count, "cities": new_cities}) \ No newline at end of file diff --git a/backend/server/adventures/views/stats_view.py b/backend/server/adventures/views/stats_view.py new file mode 100644 index 00000000..23733a5f --- /dev/null +++ b/backend/server/adventures/views/stats_view.py @@ -0,0 +1,38 @@ +from rest_framework import viewsets +from rest_framework.permissions import IsAuthenticated +from rest_framework.response import Response +from rest_framework.decorators import action +from worldtravel.models import City, Region, Country, VisitedCity, VisitedRegion +from adventures.models import Adventure, Collection + +class StatsViewSet(viewsets.ViewSet): + """ + A simple ViewSet for listing the stats of a user. + """ + permission_classes = [IsAuthenticated] + + @action(detail=False, methods=['get']) + def counts(self, request): + adventure_count = Adventure.objects.filter( + user_id=request.user.id).count() + trips_count = Collection.objects.filter( + user_id=request.user.id).count() + visited_city_count = VisitedCity.objects.filter( + user_id=request.user.id).count() + total_cities = City.objects.count() + visited_region_count = VisitedRegion.objects.filter( + user_id=request.user.id).count() + total_regions = Region.objects.count() + visited_country_count = VisitedRegion.objects.filter( + user_id=request.user.id).values('region__country').distinct().count() + total_countries = Country.objects.count() + return Response({ + 'adventure_count': adventure_count, + 'trips_count': trips_count, + 'visited_city_count': visited_city_count, + 'total_cities': total_cities, + 'visited_region_count': visited_region_count, + 'total_regions': total_regions, + 'visited_country_count': visited_country_count, + 'total_countries': total_countries + }) \ No newline at end of file diff --git a/backend/server/adventures/views/transportation_view.py b/backend/server/adventures/views/transportation_view.py new file mode 100644 index 00000000..2bd1e8cf --- /dev/null +++ b/backend/server/adventures/views/transportation_view.py @@ -0,0 +1,84 @@ +from rest_framework import viewsets, status +from rest_framework.decorators import action +from rest_framework.response import Response +from django.db.models import Q +from adventures.models import Transportation +from adventures.serializers import TransportationSerializer +from rest_framework.exceptions import PermissionDenied +from adventures.permissions import IsOwnerOrSharedWithFullAccess +from rest_framework.permissions import IsAuthenticated + +class TransportationViewSet(viewsets.ModelViewSet): + queryset = Transportation.objects.all() + serializer_class = TransportationSerializer + permission_classes = [IsOwnerOrSharedWithFullAccess] + + def list(self, request, *args, **kwargs): + if not request.user.is_authenticated: + return Response(status=status.HTTP_403_FORBIDDEN) + queryset = Transportation.objects.filter( + Q(user_id=request.user.id) + ) + serializer = self.get_serializer(queryset, many=True) + return Response(serializer.data) + + def get_queryset(self): + user = self.request.user + if self.action == 'retrieve': + # For individual adventure retrieval, include public adventures, user's own adventures and shared adventures + return Transportation.objects.filter( + Q(is_public=True) | Q(user_id=user.id) | Q(collection__shared_with=user.id) + ).distinct().order_by('-updated_at') + # For other actions, include user's own adventures and shared adventures + return Transportation.objects.filter( + Q(user_id=user.id) | Q(collection__shared_with=user.id) + ).distinct().order_by('-updated_at') + + def partial_update(self, request, *args, **kwargs): + # Retrieve the current object + instance = self.get_object() + user = request.user + + # Partially update the instance with the request data + serializer = self.get_serializer(instance, data=request.data, partial=True) + serializer.is_valid(raise_exception=True) + + # Retrieve the collection from the validated data + new_collection = serializer.validated_data.get('collection') + + if new_collection is not None and new_collection != instance.collection: + # Check if the user is the owner of the new collection + if new_collection.user_id != user or instance.user_id != user: + raise PermissionDenied("You do not have permission to use this collection.") + elif new_collection is None: + # Handle the case where the user is trying to set the collection to None + if instance.collection is not None and instance.collection.user_id != user: + raise PermissionDenied("You cannot remove the collection as you are not the owner.") + + # Perform the update + self.perform_update(serializer) + + # Return the updated instance + return Response(serializer.data) + + def perform_update(self, serializer): + serializer.save() + + # when creating an adventure, make sure the user is the owner of the collection or shared with the collection + def perform_create(self, serializer): + # Retrieve the collection from the validated data + collection = serializer.validated_data.get('collection') + + # Check if a collection is provided + if collection: + user = self.request.user + # Check if the user is the owner or is in the shared_with list + if collection.user_id != user and not collection.shared_with.filter(id=user.id).exists(): + # Return an error response if the user does not have permission + raise PermissionDenied("You do not have permission to use this collection.") + # if collection the owner of the adventure is the owner of the collection + serializer.save(user_id=collection.user_id) + return + + # Save the adventure with the current user as the owner + serializer.save(user_id=self.request.user) \ No newline at end of file diff --git a/frontend/src/locales/zh.json b/frontend/src/locales/zh.json index d98383b6..6a43eb22 100644 --- a/frontend/src/locales/zh.json +++ b/frontend/src/locales/zh.json @@ -218,7 +218,9 @@ "transportation_delete_confirm": "您确定要删除此交通工具吗?\n此操作无法撤消。", "show_map": "显示地图", "will_be_marked": "保存冒险后将被标记为已访问。", - "cities_updated": "城市已更新" + "cities_updated": "城市已更新", + "create_adventure": "创造冒险", + "no_adventures_to_recommendations": "没有发现任何冒险。\n至少添加一次冒险以获得推荐。" }, "home": { "desc_1": "轻松发现、规划和探索", From f4450b6a3844a41014afede4175a528c37a001d2 Mon Sep 17 00:00:00 2001 From: Sean Morley Date: Fri, 17 Jan 2025 16:58:08 -0500 Subject: [PATCH 095/209] fix: include Referer header in API requests --- frontend/src/routes/activities/+page.server.ts | 3 ++- frontend/src/routes/adventures/+page.server.ts | 3 ++- frontend/src/routes/adventures/[id]/+page.server.ts | 4 +++- frontend/src/routes/collections/+page.server.ts | 5 ++++- frontend/src/routes/collections/[id]/+page.server.ts | 3 ++- frontend/src/routes/login/+page.server.ts | 6 ++++-- frontend/src/routes/signup/+page.server.ts | 3 ++- frontend/src/routes/user/reset-password/+page.server.ts | 3 ++- .../src/routes/user/reset-password/[key]/+page.server.ts | 3 ++- 9 files changed, 23 insertions(+), 10 deletions(-) diff --git a/frontend/src/routes/activities/+page.server.ts b/frontend/src/routes/activities/+page.server.ts index 238e6b48..626bef81 100644 --- a/frontend/src/routes/activities/+page.server.ts +++ b/frontend/src/routes/activities/+page.server.ts @@ -37,7 +37,8 @@ export const actions: Actions = { headers: { 'X-CSRFToken': csrfToken, 'Content-Type': 'application/json', - Cookie: `csrftoken=${csrfToken}` + Cookie: `csrftoken=${csrfToken}`, + Referer: event.url.origin // Include Referer header } }); console.log(res); diff --git a/frontend/src/routes/adventures/+page.server.ts b/frontend/src/routes/adventures/+page.server.ts index da69fdf5..a2d7ab69 100644 --- a/frontend/src/routes/adventures/+page.server.ts +++ b/frontend/src/routes/adventures/+page.server.ts @@ -69,7 +69,8 @@ export const actions: Actions = { method: 'POST', headers: { Cookie: `csrftoken=${csrfToken}; sessionid=${sessionId}`, - 'X-CSRFToken': csrfToken + 'X-CSRFToken': csrfToken, + Referer: event.url.origin // Include Referer header }, body: formData }); diff --git a/frontend/src/routes/adventures/[id]/+page.server.ts b/frontend/src/routes/adventures/[id]/+page.server.ts index bba55aa1..eed47ba5 100644 --- a/frontend/src/routes/adventures/[id]/+page.server.ts +++ b/frontend/src/routes/adventures/[id]/+page.server.ts @@ -66,7 +66,9 @@ export const actions: Actions = { let res = await fetch(`${serverEndpoint}/api/adventures/${event.params.id}`, { method: 'DELETE', headers: { - Cookie: `sessionid=${event.cookies.get('sessionid')}; csrftoken=${csrfToken}`, + Referer: event.url.origin, // Include Referer header + Cookie: `sessionid=${event.cookies.get('sessionid')}; + csrftoken=${csrfToken}`, 'X-CSRFToken': csrfToken }, credentials: 'include' diff --git a/frontend/src/routes/collections/+page.server.ts b/frontend/src/routes/collections/+page.server.ts index f88e5ee4..20e2c401 100644 --- a/frontend/src/routes/collections/+page.server.ts +++ b/frontend/src/routes/collections/+page.server.ts @@ -96,6 +96,7 @@ export const actions: Actions = { method: 'POST', headers: { 'X-CSRFToken': csrfToken, + Referer: event.url.origin, // Include Referer header Cookie: `sessionid=${sessionid}; csrftoken=${csrfToken}` }, body: formDataToSend @@ -174,9 +175,11 @@ export const actions: Actions = { method: 'PATCH', headers: { 'X-CSRFToken': csrfToken, - Cookie: `sessionid=${sessionId}; csrftoken=${csrfToken}` + Cookie: `sessionid=${sessionId}; csrftoken=${csrfToken}`, + Referer: event.url.origin // Include Referer header }, body: formDataToSend, + credentials: 'include' }); diff --git a/frontend/src/routes/collections/[id]/+page.server.ts b/frontend/src/routes/collections/[id]/+page.server.ts index bf54a5b7..f672eedb 100644 --- a/frontend/src/routes/collections/[id]/+page.server.ts +++ b/frontend/src/routes/collections/[id]/+page.server.ts @@ -63,7 +63,8 @@ export const actions: Actions = { headers: { Cookie: `sessionid=${sessionId}; csrftoken=${csrfToken}`, 'Content-Type': 'application/json', - 'X-CSRFToken': csrfToken + 'X-CSRFToken': csrfToken, + Referer: event.url.origin // Include Referer header }, credentials: 'include' }); diff --git a/frontend/src/routes/login/+page.server.ts b/frontend/src/routes/login/+page.server.ts index f8723ba4..b2571a18 100644 --- a/frontend/src/routes/login/+page.server.ts +++ b/frontend/src/routes/login/+page.server.ts @@ -46,7 +46,8 @@ export const actions: Actions = { headers: { 'X-CSRFToken': csrfToken, 'Content-Type': 'application/json', - Cookie: `csrftoken=${csrfToken}` + Cookie: `csrftoken=${csrfToken}`, + Referer: event.url.origin // Include Referer header }, body: JSON.stringify({ username, password }), credentials: 'include' @@ -73,7 +74,8 @@ export const actions: Actions = { headers: { 'X-CSRFToken': csrfToken, 'Content-Type': 'application/json', - Cookie: `csrftoken=${csrfToken}; sessionid=${sessionId}` + Cookie: `csrftoken=${csrfToken}; sessionid=${sessionId}`, + Referer: event.url.origin // Include Referer header }, body: JSON.stringify({ code: totp }), credentials: 'include' diff --git a/frontend/src/routes/signup/+page.server.ts b/frontend/src/routes/signup/+page.server.ts index 813b4710..f9a96dc0 100644 --- a/frontend/src/routes/signup/+page.server.ts +++ b/frontend/src/routes/signup/+page.server.ts @@ -56,7 +56,8 @@ export const actions: Actions = { headers: { 'X-CSRFToken': csrfToken, 'Content-Type': 'application/json', - Cookie: `csrftoken=${csrfToken}` + Cookie: `csrftoken=${csrfToken}`, + Referer: event.url.origin // Include Referer header }, body: JSON.stringify({ username: username, diff --git a/frontend/src/routes/user/reset-password/+page.server.ts b/frontend/src/routes/user/reset-password/+page.server.ts index f91db59c..39f8232f 100644 --- a/frontend/src/routes/user/reset-password/+page.server.ts +++ b/frontend/src/routes/user/reset-password/+page.server.ts @@ -21,7 +21,8 @@ export const actions: Actions = { headers: { 'Content-Type': 'application/json', 'X-CSRFToken': csrfToken, - Cookie: `csrftoken=${csrfToken}` + Cookie: `csrftoken=${csrfToken}`, + Referer: event.url.origin // Include Referer header }, body: JSON.stringify({ email diff --git a/frontend/src/routes/user/reset-password/[key]/+page.server.ts b/frontend/src/routes/user/reset-password/[key]/+page.server.ts index 2db51f61..e2f92b7d 100644 --- a/frontend/src/routes/user/reset-password/[key]/+page.server.ts +++ b/frontend/src/routes/user/reset-password/[key]/+page.server.ts @@ -35,7 +35,8 @@ export const actions: Actions = { headers: { 'Content-Type': 'application/json', Cookie: `csrftoken=${csrfToken}`, - 'X-CSRFToken': csrfToken + 'X-CSRFToken': csrfToken, + Referer: event.url.origin // Include Referer header }, method: 'POST', credentials: 'include', From 0cee4c386d4903f305ab59d0023065213297feb0 Mon Sep 17 00:00:00 2001 From: Sean Morley Date: Fri, 17 Jan 2025 17:15:18 -0500 Subject: [PATCH 096/209] fix: update deleteAdventure function to use DELETE method for API call --- frontend/src/lib/components/AdventureCard.svelte | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/frontend/src/lib/components/AdventureCard.svelte b/frontend/src/lib/components/AdventureCard.svelte index b77b8eaa..2e9dd610 100644 --- a/frontend/src/lib/components/AdventureCard.svelte +++ b/frontend/src/lib/components/AdventureCard.svelte @@ -60,11 +60,8 @@ } async function deleteAdventure() { - let res = await fetch(`/adventures/${adventure.id}?/delete`, { - method: 'POST', - headers: { - 'Content-Type': 'application/x-www-form-urlencoded' - } + let res = await fetch(`/api/adventures/${adventure.id}`, { + method: 'DELETE' }); if (res.ok) { addToast('info', $t('adventures.adventure_delete_success')); From e9084db832cc732603f3a527a5c16ba0b65a08b9 Mon Sep 17 00:00:00 2001 From: Sean Morley Date: Fri, 17 Jan 2025 17:33:16 -0500 Subject: [PATCH 097/209] fix: remove default adventure type and streamline image upload form --- frontend/src/lib/components/AdventureModal.svelte | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/frontend/src/lib/components/AdventureModal.svelte b/frontend/src/lib/components/AdventureModal.svelte index 322e67fa..1e9b1b41 100644 --- a/frontend/src/lib/components/AdventureModal.svelte +++ b/frontend/src/lib/components/AdventureModal.svelte @@ -48,7 +48,6 @@ let adventure: Adventure = { id: '', name: '', - type: 'visited', visits: [], link: null, description: null, @@ -75,7 +74,6 @@ adventure = { id: adventureToEdit?.id || '', name: adventureToEdit?.name || '', - type: adventureToEdit?.type || 'general', link: adventureToEdit?.link || null, description: adventureToEdit?.description || null, activity_types: adventureToEdit?.activity_types || [], @@ -1045,13 +1043,7 @@ it would also work to just use on:click on the MapLibre component itself. --> -
    + Date: Fri, 17 Jan 2025 15:47:48 -0700 Subject: [PATCH 098/209] updated traefik config to work with oidc --- docker-compose-traefik.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docker-compose-traefik.yaml b/docker-compose-traefik.yaml index 507be5a0..16a1805c 100644 --- a/docker-compose-traefik.yaml +++ b/docker-compose-traefik.yaml @@ -39,7 +39,7 @@ services: labels: - "traefik.enable=true" - "traefik.http.routers.adventurelogweb.entrypoints=websecure" - - "traefik.http.routers.adventurelogweb.rule=Host(`yourdomain.com`) && !(PathPrefix(`/media`) || PathPrefix(`/admin`) || PathPrefix(`/static`))" # Replace with your domain + - "traefik.http.routers.adventurelogweb.rule=Host(`yourdomain.com`) && !(PathPrefix(`/media`) || PathPrefix(`/admin`) || PathPrefix(`/static`) || PathPrefix(`/accounts`))" # Replace with your domain - "traefik.http.routers.adventurelogweb.tls=true" - "traefik.http.routers.adventurelogweb.tls.certresolver=letsencrypt" depends_on: @@ -66,7 +66,7 @@ services: labels: - "traefik.enable=true" - "traefik.http.routers.adventurelogserver.entrypoints=websecure" - - "traefik.http.routers.adventurelogserver.rule=Host(`yourdomain.com`) && (PathPrefix(`/media`) || PathPrefix(`/admin`) || PathPrefix(`/static`))" # Replace with your domain + - "traefik.http.routers.adventurelogserver.rule=Host(`yourdomain.com`) && (PathPrefix(`/media`) || PathPrefix(`/admin`) || PathPrefix(`/static`) || PathPrefix(`/accounts`))" # Replace with your domain - "traefik.http.routers.adventurelogserver.tls=true" - "traefik.http.routers.adventurelogserver.tls.certresolver=letsencrypt" depends_on: From a508dd8d9600e73bfed63c7053978b1ecc0bbad2 Mon Sep 17 00:00:00 2001 From: Corbin Whitton Date: Fri, 17 Jan 2025 15:55:31 -0700 Subject: [PATCH 099/209] update documentation for authentik --- .../docs/configuration/social_auth/authentik.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/documentation/docs/configuration/social_auth/authentik.md b/documentation/docs/configuration/social_auth/authentik.md index 6bc62d9a..6d86ed8a 100644 --- a/documentation/docs/configuration/social_auth/authentik.md +++ b/documentation/docs/configuration/social_auth/authentik.md @@ -54,3 +54,13 @@ This configuration is done in the [Admin Panel](../../guides/admin_panel.md). Yo 4. Save the configuration. Ensure that the Authentik server is running and accessible by AdventureLog. Users should now be able to log in to AdventureLog using their Authentik account. + +## Troubleshooting + +### 404 error when logging in. + +Ensure the /accounts path is routed to the backend, as it shouldn't hit the frontend when it's properly configured. + +### Authentik - No Permission + +In the Authentik instance, check access to the AdventureLog application from a specific user by using the Check Access/Test button on the Application dashboard. If the user doesn't have access, you can add an existing user/group policy to give your specific user/group access to the AdventureLog application. From ae8617a9fbfb2d6f607977fffb294588b36c1266 Mon Sep 17 00:00:00 2001 From: Sean Morley Date: Fri, 17 Jan 2025 18:15:26 -0500 Subject: [PATCH 100/209] fix: update formatting in Authentik documentation for clarity --- documentation/docs/configuration/social_auth/authentik.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/docs/configuration/social_auth/authentik.md b/documentation/docs/configuration/social_auth/authentik.md index 6d86ed8a..82a8d55f 100644 --- a/documentation/docs/configuration/social_auth/authentik.md +++ b/documentation/docs/configuration/social_auth/authentik.md @@ -59,7 +59,7 @@ Ensure that the Authentik server is running and accessible by AdventureLog. User ### 404 error when logging in. -Ensure the /accounts path is routed to the backend, as it shouldn't hit the frontend when it's properly configured. +Ensure the `/accounts` path is routed to the backend, as it shouldn't hit the frontend when it's properly configured. ### Authentik - No Permission From 75162bbf7ba07210409a8a1bdf97d1edb37b283d Mon Sep 17 00:00:00 2001 From: Sean Morley Date: Fri, 17 Jan 2025 18:22:03 -0500 Subject: [PATCH 101/209] fix: improve error handling for Overpass API connection failures --- backend/server/adventures/views/overpass_view.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/server/adventures/views/overpass_view.py b/backend/server/adventures/views/overpass_view.py index a64e55b5..a72b4a79 100644 --- a/backend/server/adventures/views/overpass_view.py +++ b/backend/server/adventures/views/overpass_view.py @@ -24,8 +24,8 @@ class OverpassViewSet(viewsets.ViewSet): response = requests.get(url, headers=self.HEADERS) response.raise_for_status() # Raise an exception for HTTP errors return response.json() - except requests.exceptions.RequestException as e: - return Response({"error": str(e)}, status=500) + except requests.exceptions.RequestException: + return Response({"error": "Failed to connect to Overpass API"}, status=500) except requests.exceptions.JSONDecodeError: return Response({"error": "Invalid response from Overpass API"}, status=400) From fcec4a420cb623a27450440da818dbf3515014a8 Mon Sep 17 00:00:00 2001 From: Sean Morley <98704938+seanmorley15@users.noreply.github.com> Date: Fri, 17 Jan 2025 19:38:18 -0500 Subject: [PATCH 102/209] Update README.md --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index a9102863..f130b323 100644 --- a/README.md +++ b/README.md @@ -142,3 +142,8 @@ Hi! I'm Sean, the creator of AdventureLog. I'm a college student and software de - Logo Design by [nordtechtiger](https://github.com/nordtechtiger) - WorldTravel Dataset [dr5hn/countries-states-cities-database](https://github.com/dr5hn/countries-states-cities-database) + +### Top Supporters 💖 +- [Veymax](https://x.com/veymax) +- nebriv +- [Victor Butler](https://x.com/victor_butler) From 1a7643b8a718583b31b58bb9b2fdd0f22439631e Mon Sep 17 00:00:00 2001 From: Sean Morley Date: Fri, 17 Jan 2025 20:20:56 -0500 Subject: [PATCH 103/209] fix: include Referer header in user-related API requests --- frontend/src/routes/settings/+page.server.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/frontend/src/routes/settings/+page.server.ts b/frontend/src/routes/settings/+page.server.ts index 44bfe970..f85ad0bd 100644 --- a/frontend/src/routes/settings/+page.server.ts +++ b/frontend/src/routes/settings/+page.server.ts @@ -107,7 +107,8 @@ export const actions: Actions = { const resCurrent = await fetch(`${endpoint}/auth/user-metadata/`, { headers: { - Cookie: `sessionid=${sessionId}` + Cookie: `sessionid=${sessionId}`, + Referer: event.url.origin // Include Referer header } }); @@ -158,6 +159,7 @@ export const actions: Actions = { let res = await fetch(`${endpoint}/auth/update-user/`, { method: 'PATCH', headers: { + Referer: event.url.origin, // Include Referer header Cookie: `sessionid=${sessionId}; csrftoken=${csrfToken}`, 'X-CSRFToken': csrfToken }, @@ -209,6 +211,7 @@ export const actions: Actions = { let res = await fetch(`${endpoint}/_allauth/browser/v1/account/password/change`, { method: 'POST', headers: { + Referer: event.url.origin, // Include Referer header Cookie: `sessionid=${sessionId}; csrftoken=${csrfToken}`, 'X-CSRFToken': csrfToken, 'Content-Type': 'application/json' @@ -226,6 +229,7 @@ export const actions: Actions = { let res = await fetch(`${endpoint}/_allauth/browser/v1/account/password/change`, { method: 'POST', headers: { + Referer: event.url.origin, // Include Referer header Cookie: `sessionid=${sessionId}; csrftoken=${csrfToken}`, 'X-CSRFToken': csrfToken, 'Content-Type': 'application/json' @@ -258,6 +262,7 @@ export const actions: Actions = { let res = await fetch(`${endpoint}/auth/change-email/`, { method: 'POST', headers: { + Referer: event.url.origin, // Include Referer header Cookie: `sessionid=${sessionId}; csrftoken=${csrfToken}`, 'Content-Type': 'application/json', 'X-CSRFToken': csrfToken From 9132ef39ece7deb3715b80755622afd62451e888 Mon Sep 17 00:00:00 2001 From: Sean Morley Date: Sat, 18 Jan 2025 09:19:21 -0500 Subject: [PATCH 104/209] fix: add 'finding_recommendations' key and update 'no_adventures_to_recommendations' in multiple locale files --- frontend/src/locales/de.json | 10 +++++++++- frontend/src/locales/en.json | 8 ++++++++ frontend/src/locales/es.json | 10 +++++++++- frontend/src/locales/fr.json | 10 +++++++++- frontend/src/locales/it.json | 10 +++++++++- frontend/src/locales/nl.json | 10 +++++++++- frontend/src/locales/pl.json | 10 +++++++++- frontend/src/locales/sv.json | 10 +++++++++- frontend/src/locales/zh.json | 10 +++++++++- .../src/routes/collections/[id]/+page.svelte | 16 ++++++++-------- 10 files changed, 88 insertions(+), 16 deletions(-) diff --git a/frontend/src/locales/de.json b/frontend/src/locales/de.json index e9789697..0508d185 100644 --- a/frontend/src/locales/de.json +++ b/frontend/src/locales/de.json @@ -220,7 +220,8 @@ "will_be_marked": "wird als besucht markiert, sobald das Abenteuer gespeichert ist.", "cities_updated": "Städte aktualisiert", "create_adventure": "Erstelle Abenteuer", - "no_adventures_to_recommendations": "Keine Abenteuer gefunden. \nFügen Sie mindestens ein Abenteuer hinzu, um Empfehlungen zu erhalten." + "no_adventures_to_recommendations": "Keine Abenteuer gefunden. \nFügen Sie mindestens ein Abenteuer hinzu, um Empfehlungen zu erhalten.", + "finding_recommendations": "Entdecken Sie verborgene Schätze für Ihr nächstes Abenteuer" }, "home": { "desc_1": "Entdecken, planen und erkunden Sie mit Leichtigkeit", @@ -546,5 +547,12 @@ "immich_integration": "Immich-Integration", "documentation": "Immich-Integrationsdokumentation", "localhost_note": "Hinweis: localhost wird höchstwahrscheinlich nicht funktionieren, es sei denn, Sie haben Docker-Netzwerke entsprechend eingerichtet. \nEs wird empfohlen, die IP-Adresse des Servers oder den Domänennamen zu verwenden." + }, + "recomendations": { + "address": "Adresse", + "contact": "Kontakt", + "phone": "Telefon", + "recommendation": "Empfehlung", + "website": "Webseite" } } diff --git a/frontend/src/locales/en.json b/frontend/src/locales/en.json index c5ff3c5b..a1383925 100644 --- a/frontend/src/locales/en.json +++ b/frontend/src/locales/en.json @@ -242,6 +242,7 @@ "flight_information": "Flight Information", "out_of_range": "Not in itinerary date range", "preview": "Preview", + "finding_recommendations": "Discovering hidden gems for your next adventure", "md_instructions": "Write your markdown here...", "days": "days", "activities": { @@ -546,5 +547,12 @@ "immich_integration": "Immich Integration", "localhost_note": "Note: localhost will most likely not work unless you have setup docker networks accordingly. It is recommended to use the IP address of the server or the domain name.", "documentation": "Immich Integration Documentation" + }, + "recomendations": { + "address": "Address", + "phone": "Phone", + "contact": "Contact", + "website": "Website", + "recommendation": "Recommendation" } } diff --git a/frontend/src/locales/es.json b/frontend/src/locales/es.json index 34402767..79e2f7d1 100644 --- a/frontend/src/locales/es.json +++ b/frontend/src/locales/es.json @@ -267,7 +267,8 @@ "will_be_marked": "se marcará como visitado una vez guardada la aventura.", "cities_updated": "ciudades actualizadas", "create_adventure": "Crear aventura", - "no_adventures_to_recommendations": "No se encontraron aventuras. \nAñade al menos una aventura para obtener recomendaciones." + "no_adventures_to_recommendations": "No se encontraron aventuras. \nAñade al menos una aventura para obtener recomendaciones.", + "finding_recommendations": "Descubriendo gemas ocultas para tu próxima aventura" }, "worldtravel": { "all": "Todo", @@ -546,5 +547,12 @@ "immich_integration": "Integración Immich", "documentation": "Documentación de integración de Immich", "localhost_note": "Nota: lo más probable es que localhost no funcione a menos que haya configurado las redes acoplables en consecuencia. \nSe recomienda utilizar la dirección IP del servidor o el nombre de dominio." + }, + "recomendations": { + "address": "DIRECCIÓN", + "contact": "Contacto", + "phone": "Teléfono", + "recommendation": "Recomendación", + "website": "Sitio web" } } diff --git a/frontend/src/locales/fr.json b/frontend/src/locales/fr.json index 8cac395f..8f10eb48 100644 --- a/frontend/src/locales/fr.json +++ b/frontend/src/locales/fr.json @@ -220,7 +220,8 @@ "will_be_marked": "sera marqué comme visité une fois l’aventure sauvegardée.", "cities_updated": "villes mises à jour", "create_adventure": "Créer une aventure", - "no_adventures_to_recommendations": "Aucune aventure trouvée. \nAjoutez au moins une aventure pour obtenir des recommandations." + "no_adventures_to_recommendations": "Aucune aventure trouvée. \nAjoutez au moins une aventure pour obtenir des recommandations.", + "finding_recommendations": "Découvrir des trésors cachés pour votre prochaine aventure" }, "home": { "desc_1": "Découvrez, planifiez et explorez en toute simplicité", @@ -546,5 +547,12 @@ "update_integration": "Intégration des mises à jour", "documentation": "Documentation d'intégration Immich", "localhost_note": "Remarque : localhost ne fonctionnera probablement pas à moins que vous n'ayez configuré les réseaux Docker en conséquence. \nIl est recommandé d'utiliser l'adresse IP du serveur ou le nom de domaine." + }, + "recomendations": { + "address": "Adresse", + "contact": "Contact", + "phone": "Téléphone", + "recommendation": "Recommandation", + "website": "Site web" } } diff --git a/frontend/src/locales/it.json b/frontend/src/locales/it.json index 2c43ba61..a885cf90 100644 --- a/frontend/src/locales/it.json +++ b/frontend/src/locales/it.json @@ -220,7 +220,8 @@ "will_be_marked": "verrà contrassegnato come visitato una volta salvata l'avventura.", "cities_updated": "città aggiornate", "create_adventure": "Crea Avventura", - "no_adventures_to_recommendations": "Nessuna avventura trovata. \nAggiungi almeno un'avventura per ricevere consigli." + "no_adventures_to_recommendations": "Nessuna avventura trovata. \nAggiungi almeno un'avventura per ricevere consigli.", + "finding_recommendations": "Alla scoperta di gemme nascoste per la tua prossima avventura" }, "home": { "desc_1": "Scopri, pianifica ed esplora con facilità", @@ -546,5 +547,12 @@ "update_integration": "Aggiorna integrazione", "documentation": "Documentazione sull'integrazione di Immich", "localhost_note": "Nota: molto probabilmente localhost non funzionerà a meno che tu non abbia configurato le reti docker di conseguenza. \nSi consiglia di utilizzare l'indirizzo IP del server o il nome del dominio." + }, + "recomendations": { + "address": "Indirizzo", + "contact": "Contatto", + "phone": "Telefono", + "recommendation": "Raccomandazione", + "website": "Sito web" } } diff --git a/frontend/src/locales/nl.json b/frontend/src/locales/nl.json index e230dd24..71d3506b 100644 --- a/frontend/src/locales/nl.json +++ b/frontend/src/locales/nl.json @@ -220,7 +220,8 @@ "will_be_marked": "wordt gemarkeerd als bezocht zodra het avontuur is opgeslagen.", "cities_updated": "steden bijgewerkt", "create_adventure": "Creëer avontuur", - "no_adventures_to_recommendations": "Geen avonturen gevonden. \nVoeg ten minste één avontuur toe om aanbevelingen te krijgen." + "no_adventures_to_recommendations": "Geen avonturen gevonden. \nVoeg ten minste één avontuur toe om aanbevelingen te krijgen.", + "finding_recommendations": "Ontdek verborgen juweeltjes voor je volgende avontuur" }, "home": { "desc_1": "Ontdek, plan en verken met gemak", @@ -546,5 +547,12 @@ "update_integration": "Integratie bijwerken", "documentation": "Immich-integratiedocumentatie", "localhost_note": "Opmerking: localhost zal hoogstwaarschijnlijk niet werken tenzij u de docker-netwerken dienovereenkomstig hebt ingesteld. \nHet wordt aanbevolen om het IP-adres van de server of de domeinnaam te gebruiken." + }, + "recomendations": { + "address": "Adres", + "contact": "Contact", + "phone": "Telefoon", + "recommendation": "Aanbeveling", + "website": "Website" } } diff --git a/frontend/src/locales/pl.json b/frontend/src/locales/pl.json index a39baeb3..c14b61a3 100644 --- a/frontend/src/locales/pl.json +++ b/frontend/src/locales/pl.json @@ -267,7 +267,8 @@ "will_be_marked": "zostanie oznaczona jako odwiedzona po zapisaniu przygody.", "cities_updated": "miasta zaktualizowane", "create_adventure": "Stwórz przygodę", - "no_adventures_to_recommendations": "Nie znaleziono żadnych przygód. \nDodaj co najmniej jedną przygodę, aby uzyskać rekomendacje." + "no_adventures_to_recommendations": "Nie znaleziono żadnych przygód. \nDodaj co najmniej jedną przygodę, aby uzyskać rekomendacje.", + "finding_recommendations": "Odkrywanie ukrytych klejnotów na następną przygodę" }, "worldtravel": { "country_list": "Lista krajów", @@ -546,5 +547,12 @@ "immich_disabled": "Integracja z Immich została pomyślnie wyłączona!", "documentation": "Dokumentacja integracji Immicha", "localhost_note": "Uwaga: localhost najprawdopodobniej nie będzie działać, jeśli nie skonfigurujesz odpowiednio sieci dokerów. \nZalecane jest użycie adresu IP serwera lub nazwy domeny." + }, + "recomendations": { + "address": "Adres", + "contact": "Kontakt", + "phone": "Telefon", + "recommendation": "Zalecenie", + "website": "Strona internetowa" } } diff --git a/frontend/src/locales/sv.json b/frontend/src/locales/sv.json index 3dfcae1f..c60bb309 100644 --- a/frontend/src/locales/sv.json +++ b/frontend/src/locales/sv.json @@ -220,7 +220,8 @@ "will_be_marked": "kommer att markeras som besökt när äventyret har sparats.", "cities_updated": "städer uppdaterade", "create_adventure": "Skapa äventyr", - "no_adventures_to_recommendations": "Inga äventyr hittades. \nLägg till minst ett äventyr för att få rekommendationer." + "no_adventures_to_recommendations": "Inga äventyr hittades. \nLägg till minst ett äventyr för att få rekommendationer.", + "finding_recommendations": "Upptäck dolda pärlor för ditt nästa äventyr" }, "home": { "desc_1": "Upptäck, planera och utforska med lätthet", @@ -546,5 +547,12 @@ "update_integration": "Uppdatera integration", "documentation": "Immich Integrationsdokumentation", "localhost_note": "Obs: localhost kommer sannolikt inte att fungera om du inte har konfigurerat docker-nätverk i enlighet med detta. \nDet rekommenderas att använda serverns IP-adress eller domännamnet." + }, + "recomendations": { + "address": "Adress", + "contact": "Kontakta", + "phone": "Telefon", + "recommendation": "Rekommendation", + "website": "Webbplats" } } diff --git a/frontend/src/locales/zh.json b/frontend/src/locales/zh.json index 6a43eb22..3161fe82 100644 --- a/frontend/src/locales/zh.json +++ b/frontend/src/locales/zh.json @@ -220,7 +220,8 @@ "will_be_marked": "保存冒险后将被标记为已访问。", "cities_updated": "城市已更新", "create_adventure": "创造冒险", - "no_adventures_to_recommendations": "没有发现任何冒险。\n至少添加一次冒险以获得推荐。" + "no_adventures_to_recommendations": "没有发现任何冒险。\n至少添加一次冒险以获得推荐。", + "finding_recommendations": "为您的下一次冒险发现隐藏的宝石" }, "home": { "desc_1": "轻松发现、规划和探索", @@ -546,5 +547,12 @@ "update_integration": "更新集成", "documentation": "Immich 集成文档", "localhost_note": "注意:除非您相应地设置了 docker 网络,否则 localhost 很可能无法工作。\n建议使用服务器的IP地址或域名。" + }, + "recomendations": { + "address": "地址", + "contact": "接触", + "phone": "电话", + "recommendation": "推荐", + "website": "网站" } } diff --git a/frontend/src/routes/collections/[id]/+page.svelte b/frontend/src/routes/collections/[id]/+page.svelte index 1653708d..3a4c1f7e 100644 --- a/frontend/src/routes/collections/[id]/+page.svelte +++ b/frontend/src/routes/collections/[id]/+page.svelte @@ -1022,13 +1022,12 @@

    - {recomendation.name || 'Recommendation'} + {recomendation.name || $t('recomendations.recommendation')}

    {recomendation.tag}
    -

    {recomendation.description || 'No description available.'}

    {#if recomendation.address}

    - Address: + {$t('recomendations.address')}: {recomendation.address.housenumber} {recomendation.address.street}, {recomendation.address.city}, {recomendation .address.state} @@ -1037,15 +1036,16 @@ {/if} {#if recomendation.contact}

    - Contact: + {$t('recomendations.contact')}: {#if recomendation.contact.phone} - Phone: {recomendation.contact.phone} + {$t('recomendations.phone')}: {recomendation.contact.phone} {/if} {#if recomendation.contact.email} - Email: {recomendation.contact.email} + {$t('auth.email')}: {recomendation.contact.email} {/if} {#if recomendation.contact.website} - Website: {recomendation.contact.website}

    - Discovering hidden gems for your next adventure... + {$t('adventures.finding_recommendations')}...

    From d60945d5b70dcf5acfd9545f9d1d0af8eb639621 Mon Sep 17 00:00:00 2001 From: Sean Morley Date: Sat, 18 Jan 2025 12:28:14 -0500 Subject: [PATCH 105/209] feat: implement global search functionality for adventures, collections, users, and locations --- backend/server/adventures/managers.py | 22 ++ backend/server/adventures/models.py | 5 +- backend/server/adventures/urls.py | 1 + backend/server/adventures/views/__init__.py | 3 +- .../adventures/views/adventure_image_view.py | 4 - .../server/adventures/views/adventure_view.py | 273 +++++------------- .../adventures/views/collection_view.py | 1 - .../views/generate_description_view.py | 11 +- .../adventures/views/global_search_view.py | 71 +++++ .../src/lib/components/AdventureModal.svelte | 4 + 10 files changed, 186 insertions(+), 209 deletions(-) create mode 100644 backend/server/adventures/managers.py create mode 100644 backend/server/adventures/views/global_search_view.py diff --git a/backend/server/adventures/managers.py b/backend/server/adventures/managers.py new file mode 100644 index 00000000..6d8d43c5 --- /dev/null +++ b/backend/server/adventures/managers.py @@ -0,0 +1,22 @@ +from django.db import models +from django.db.models import Q + +class AdventureManager(models.Manager): + def retrieve_adventures(self, user, include_owned=False, include_shared=False, include_public=False): + # Initialize the query with an empty Q object + query = Q() + + # Add owned adventures to the query if included + if include_owned: + query |= Q(user_id=user.id) + + # Add shared adventures to the query if included + if include_shared: + query |= Q(collection__shared_with=user.id) + + # Add public adventures to the query if included + if include_public: + query |= Q(is_public=True) + + # Perform the query with the final Q object and remove duplicates + return self.filter(query).distinct() diff --git a/backend/server/adventures/models.py b/backend/server/adventures/models.py index c77bc4dd..779d7f67 100644 --- a/backend/server/adventures/models.py +++ b/backend/server/adventures/models.py @@ -4,12 +4,13 @@ from typing import Iterable import uuid from django.db import models from django.utils.deconstruct import deconstructible - +from adventures.managers import AdventureManager from django.contrib.auth import get_user_model from django.contrib.postgres.fields import ArrayField from django.forms import ValidationError from django_resized import ResizedImageField + ADVENTURE_TYPES = [ ('general', 'General 🌍'), ('outdoor', 'Outdoor 🏞️'), @@ -88,6 +89,8 @@ class Adventure(models.Model): created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) + objects = AdventureManager() + # DEPRECATED FIELDS - TO BE REMOVED IN FUTURE VERSIONS # Migrations performed in this version will remove these fields # image = ResizedImageField(force_format="WEBP", quality=75, null=True, blank=True, upload_to='images/') diff --git a/backend/server/adventures/urls.py b/backend/server/adventures/urls.py index c28478e1..cc596706 100644 --- a/backend/server/adventures/urls.py +++ b/backend/server/adventures/urls.py @@ -16,6 +16,7 @@ router.register(r'reverse-geocode', ReverseGeocodeViewSet, basename='reverse-geo router.register(r'categories', CategoryViewSet, basename='categories') router.register(r'ics-calendar', IcsCalendarGeneratorViewSet, basename='ics-calendar') router.register(r'overpass', OverpassViewSet, basename='overpass') +router.register(r'search', GlobalSearchView, basename='search') urlpatterns = [ diff --git a/backend/server/adventures/views/__init__.py b/backend/server/adventures/views/__init__.py index 7b0d3359..59206198 100644 --- a/backend/server/adventures/views/__init__.py +++ b/backend/server/adventures/views/__init__.py @@ -10,4 +10,5 @@ from .note_view import * from .overpass_view import * from .reverse_geocode_view import * from .stats_view import * -from .transportation_view import * \ No newline at end of file +from .transportation_view import * +from .global_search_view import * \ No newline at end of file diff --git a/backend/server/adventures/views/adventure_image_view.py b/backend/server/adventures/views/adventure_image_view.py index b4a3ce41..d76f6a56 100644 --- a/backend/server/adventures/views/adventure_image_view.py +++ b/backend/server/adventures/views/adventure_image_view.py @@ -11,10 +11,6 @@ class AdventureImageViewSet(viewsets.ModelViewSet): serializer_class = AdventureImageSerializer permission_classes = [IsAuthenticated] - def dispatch(self, request, *args, **kwargs): - print(f"Method: {request.method}") - return super().dispatch(request, *args, **kwargs) - @action(detail=True, methods=['post']) def image_delete(self, request, *args, **kwargs): return self.destroy(request, *args, **kwargs) diff --git a/backend/server/adventures/views/adventure_view.py b/backend/server/adventures/views/adventure_view.py index be86ff3e..5249e008 100644 --- a/backend/server/adventures/views/adventure_view.py +++ b/backend/server/adventures/views/adventure_view.py @@ -1,15 +1,14 @@ from django.db import transaction -from rest_framework.decorators import action -from rest_framework import viewsets -from django.db.models.functions import Lower -from rest_framework.response import Response -from adventures.models import Adventure, Category from django.core.exceptions import PermissionDenied -from adventures.serializers import AdventureSerializer -from django.db.models import Q +from django.db.models import Q, Max +from django.db.models.functions import Lower +from rest_framework import viewsets +from rest_framework.decorators import action +from rest_framework.response import Response + +from adventures.models import Adventure, Category from adventures.permissions import IsOwnerOrSharedWithFullAccess -from django.shortcuts import get_object_or_404 -from django.db.models import Max +from adventures.serializers import AdventureSerializer from adventures.utils import pagination class AdventureViewSet(viewsets.ModelViewSet): @@ -30,11 +29,8 @@ class AdventureViewSet(viewsets.ModelViewSet): order_direction = 'asc' if order_by == 'date': - # order by the earliest visit object associated with the adventure - queryset = queryset.annotate(latest_visit=Max('visits__start_date')) - queryset = queryset.filter(latest_visit__isnull=False) + queryset = queryset.annotate(latest_visit=Max('visits__start_date')).filter(latest_visit__isnull=False) ordering = 'latest_visit' - # Apply case-insensitive sorting for the 'name' field elif order_by == 'name': queryset = queryset.annotate(lower_name=Lower('name')) ordering = 'lower_name' @@ -47,262 +43,138 @@ class AdventureViewSet(viewsets.ModelViewSet): if order_direction == 'desc': ordering = f'-{ordering}' - # reverse ordering for updated_at field if order_by == 'updated_at': - if order_direction == 'asc': - ordering = '-updated_at' - else: - ordering = 'updated_at' - - print(f"Ordering by: {ordering}") # For debugging + ordering = '-updated_at' if order_direction == 'asc' else 'updated_at' if include_collections == 'false': - queryset = queryset.filter(collection = None) + queryset = queryset.filter(collection=None) 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: + """ + Returns the queryset for the AdventureViewSet. Unauthenticated users can only + retrieve public adventures, while authenticated users can access their own, + shared, and public adventures depending on the action. + """ + user = self.request.user + + if not user.is_authenticated: + # Unauthenticated users can only access public adventures for retrieval if self.action == 'retrieve': - return Adventure.objects.filter(is_public=True).distinct().order_by('-updated_at') + return Adventure.objects.retrieve_adventures(user, include_public=True).order_by('-updated_at') return Adventure.objects.none() - if self.action == 'retrieve': - # For individual adventure retrieval, include public adventures - return Adventure.objects.filter( - Q(is_public=True) | Q(user_id=self.request.user.id) | Q(collection__shared_with=self.request.user) - ).distinct().order_by('-updated_at') - else: - # For other actions, include user's own adventures and shared adventures - return Adventure.objects.filter( - Q(user_id=self.request.user.id) | Q(collection__shared_with=self.request.user) - ).distinct().order_by('-updated_at') - - def retrieve(self, request, *args, **kwargs): - queryset = self.get_queryset() - adventure = get_object_or_404(queryset, pk=kwargs['pk']) - serializer = self.get_serializer(adventure) - return Response(serializer.data) + # Authenticated users: Handle retrieval separately + include_public = self.action == 'retrieve' + return Adventure.objects.retrieve_adventures( + user, + include_public=include_public, + include_owned=True, + include_shared=True + ).order_by('-updated_at') def perform_update(self, serializer): adventure = serializer.save() if adventure.collection: adventure.is_public = adventure.collection.is_public adventure.save() - + @action(detail=False, methods=['get']) def filtered(self, request): types = request.query_params.get('types', '').split(',') is_visited = request.query_params.get('is_visited', 'all') - # Handle case where types is all if 'all' in types: types = Category.objects.filter(user_id=request.user).values_list('name', flat=True) - else: - for type in types: - if not Category.objects.filter(user_id=request.user, name=type).exists(): - return Response({"error": f"Category {type} does not exist"}, status=400) - - if not types: - return Response({"error": "At least one type must be provided"}, status=400) + if not types or not all( + Category.objects.filter(user_id=request.user, name=type).exists() for type in types + ): + return Response({"error": "Invalid category or no types provided"}, status=400) queryset = Adventure.objects.filter( category__in=Category.objects.filter(name__in=types, user_id=request.user), user_id=request.user.id ) - # Handle is_visited filtering - if is_visited.lower() == 'true': - serializer = self.get_serializer(queryset, many=True) - filtered_ids = [ - adventure.id for adventure, serialized_adventure in zip(queryset, serializer.data) - if serialized_adventure['is_visited'] - ] - queryset = queryset.filter(id__in=filtered_ids) - elif is_visited.lower() == 'false': - serializer = self.get_serializer(queryset, many=True) - filtered_ids = [ - adventure.id for adventure, serialized_adventure in zip(queryset, serializer.data) - if not serialized_adventure['is_visited'] - ] - queryset = queryset.filter(id__in=filtered_ids) - # If is_visited is 'all' or any other value, we don't apply additional filtering + if is_visited.lower() in ['true', 'false']: + is_visited_bool = is_visited.lower() == 'true' + queryset = queryset.filter(is_visited=is_visited_bool) - # Apply sorting queryset = self.apply_sorting(queryset) + return self.paginate_and_respond(queryset, request) - # Paginate and respond - adventures = self.paginate_and_respond(queryset, request) - return adventures - @action(detail=False, methods=['get']) def all(self, request): if not request.user.is_authenticated: return Response({"error": "User is not authenticated"}, status=400) - include_collections = request.query_params.get('include_collections', 'false') - if include_collections not in ['true', 'false']: - include_collections = 'false' - if include_collections == 'true': - queryset = Adventure.objects.filter( - Q(is_public=True) | Q(user_id=request.user.id) - ) - else: - queryset = Adventure.objects.filter( - Q(is_public=True) | Q(user_id=request.user.id), collection=None - ) + include_collections = request.query_params.get('include_collections', 'false') == 'true' queryset = Adventure.objects.filter( - Q(user_id=request.user.id) + Q(is_public=True) | Q(user_id=request.user.id), + collection=None if not include_collections else Q() ) + queryset = self.apply_sorting(queryset) serializer = self.get_serializer(queryset, many=True) - return Response(serializer.data) - + @action(detail=False, methods=['get']) def search(self, request): - query = self.request.query_params.get('query', '') - property = self.request.query_params.get('property', 'all') + query = request.query_params.get('query', '') + property = request.query_params.get('property', 'all') + if len(query) < 2: return Response({"error": "Query must be at least 2 characters long"}, status=400) - - if property not in ['name', 'type', 'location', 'description', 'activity_types']: + + valid_properties = ['name', 'location', 'description', 'activity_types'] + if property not in valid_properties: property = 'all' - queryset = Adventure.objects.none() + filters = { + 'name': Q(name__icontains=query), + 'location': Q(location__icontains=query), + 'description': Q(description__icontains=query), + 'activity_types': Q(activity_types__icontains=query), + 'all': Q(name__icontains=query) | Q(description__icontains=query) | + Q(location__icontains=query) | Q(activity_types__icontains=query) + } - if property == 'name': - queryset = Adventure.objects.filter( - (Q(name__icontains=query)) & - (Q(user_id=request.user.id) | Q(is_public=True)) - ) - elif property == 'type': - queryset = Adventure.objects.filter( - (Q(type__icontains=query)) & - (Q(user_id=request.user.id) | Q(is_public=True)) - ) - elif property == 'location': - queryset = Adventure.objects.filter( - (Q(location__icontains=query)) & - (Q(user_id=request.user.id) | Q(is_public=True)) - ) - elif property == 'description': - queryset = Adventure.objects.filter( - (Q(description__icontains=query)) & - (Q(user_id=request.user.id) | Q(is_public=True)) - ) - elif property == 'activity_types': - queryset = Adventure.objects.filter( - (Q(activity_types__icontains=query)) & - (Q(user_id=request.user.id) | Q(is_public=True)) - ) - else: - queryset = Adventure.objects.filter( - (Q(name__icontains=query) | Q(description__icontains=query) | Q(location__icontains=query) | Q(activity_types__icontains=query)) & - (Q(user_id=request.user.id) | Q(is_public=True)) + queryset = Adventure.objects.filter( + filters[property] & (Q(user_id=request.user.id) | Q(is_public=True)) ) - + queryset = self.apply_sorting(queryset) serializer = self.get_serializer(queryset, many=True) return Response(serializer.data) - + def update(self, request, *args, **kwargs): - # Retrieve the current object instance = self.get_object() - - # Partially update the instance with the request data serializer = self.get_serializer(instance, data=request.data, partial=True) serializer.is_valid(raise_exception=True) - # if the adventure is trying to have is_public changed and its part of a collection return an error - if new_collection is not None: - serializer.validated_data['is_public'] = new_collection.is_public - elif instance.collection: - serializer.validated_data['is_public'] = instance.collection.is_public - - - # Retrieve the collection from the validated data new_collection = serializer.validated_data.get('collection') - - user = request.user - print(new_collection) - - if new_collection is not None and new_collection!=instance.collection: - # Check if the user is the owner of the new collection - if new_collection.user_id != user or instance.user_id != user: + if new_collection and new_collection!=instance.collection: + if new_collection.user_id != request.user or instance.user_id != request.user: raise PermissionDenied("You do not have permission to use this collection.") - elif new_collection is None: - # Handle the case where the user is trying to set the collection to None - if instance.collection is not None and instance.collection.user_id != user: - raise PermissionDenied("You cannot remove the collection as you are not the owner.") - - # Perform the update + elif new_collection is None and instance.collection and instance.collection.user_id != request.user: + raise PermissionDenied("You cannot remove the collection as you are not the owner.") + self.perform_update(serializer) - - # Return the updated instance - return Response(serializer.data) - - def partial_update(self, request, *args, **kwargs): - # Retrieve the current object - instance = self.get_object() - - # Partially update the instance with the request data - serializer = self.get_serializer(instance, data=request.data, partial=True) - serializer.is_valid(raise_exception=True) - - # Retrieve the collection from the validated data - new_collection = serializer.validated_data.get('collection') - - user = request.user - print(new_collection) - - # if the adventure is trying to have is_public changed and its part of a collection return an error - if new_collection is not None: - serializer.validated_data['is_public'] = new_collection.is_public - elif instance.collection: - serializer.validated_data['is_public'] = instance.collection.is_public - - if new_collection is not None and new_collection!=instance.collection: - # Check if the user is the owner of the new collection - if new_collection.user_id != user or instance.user_id != user: - raise PermissionDenied("You do not have permission to use this collection.") - elif new_collection is None: - # Handle the case where the user is trying to set the collection to None - if instance.collection is not None and instance.collection.user_id != user: - raise PermissionDenied("You cannot remove the collection as you are not the owner.") - - # Perform the update - self.perform_update(serializer) - - # Return the updated instance return Response(serializer.data) - def perform_update(self, serializer): - serializer.save() - - # when creating an adventure, make sure the user is the owner of the collection or shared with the collection @transaction.atomic def perform_create(self, serializer): - # Retrieve the collection from the validated data collection = serializer.validated_data.get('collection') - - # Check if a collection is provided - if collection: - user = self.request.user - # Check if the user is the owner or is in the shared_with list - if collection.user_id != user and not collection.shared_with.filter(id=user.id).exists(): - # Return an error response if the user does not have permission - raise PermissionDenied("You do not have permission to use this collection.") - # if collection the owner of the adventure is the owner of the collection - # set the is_public field of the adventure to the is_public field of the collection + + if collection and not (collection.user_id == self.request.user or collection.shared_with.filter(id=self.request.user.id).exists()): + raise PermissionDenied("You do not have permission to use this collection.") + elif collection: serializer.save(user_id=collection.user_id, is_public=collection.is_public) return - # Save the adventure with the current user as the owner - serializer.save(user_id=self.request.user) + serializer.save(user_id=self.request.user, is_public=collection.is_public if collection else False) def paginate_and_respond(self, queryset, request): paginator = self.pagination_class() @@ -310,5 +182,6 @@ class AdventureViewSet(viewsets.ModelViewSet): if page is not None: serializer = self.get_serializer(page, many=True) return paginator.get_paginated_response(serializer.data) + serializer = self.get_serializer(queryset, many=True) - return Response(serializer.data) \ No newline at end of file + return Response(serializer.data) diff --git a/backend/server/adventures/views/collection_view.py b/backend/server/adventures/views/collection_view.py index 7e35d640..f0529ee6 100644 --- a/backend/server/adventures/views/collection_view.py +++ b/backend/server/adventures/views/collection_view.py @@ -216,4 +216,3 @@ class CollectionViewSet(viewsets.ModelViewSet): return paginator.get_paginated_response(serializer.data) serializer = self.get_serializer(queryset, many=True) return Response(serializer.data) - diff --git a/backend/server/adventures/views/generate_description_view.py b/backend/server/adventures/views/generate_description_view.py index 154bdc1f..988773af 100644 --- a/backend/server/adventures/views/generate_description_view.py +++ b/backend/server/adventures/views/generate_description_view.py @@ -12,7 +12,7 @@ class GenerateDescription(viewsets.ViewSet): name = self.request.query_params.get('name', '') # un url encode the name name = name.replace('%20', ' ') - print(name) + name = self.get_search_term(name) url = 'https://en.wikipedia.org/w/api.php?origin=*&action=query&prop=extracts&exintro&explaintext&format=json&titles=%s' % name response = requests.get(url) data = response.json() @@ -27,6 +27,7 @@ class GenerateDescription(viewsets.ViewSet): name = self.request.query_params.get('name', '') # un url encode the name name = name.replace('%20', ' ') + name = self.get_search_term(name) url = 'https://en.wikipedia.org/w/api.php?origin=*&action=query&prop=pageimages&format=json&piprop=original&titles=%s' % name response = requests.get(url) data = response.json() @@ -34,4 +35,10 @@ class GenerateDescription(viewsets.ViewSet): extract = data["query"]["pages"][page_id] if extract.get('original') is None: return Response({"error": "No image found"}, status=400) - return Response(extract["original"]) \ No newline at end of file + return Response(extract["original"]) + + def get_search_term(self, term): + response = requests.get(f'https://en.wikipedia.org/w/api.php?action=opensearch&search={term}&limit=10&namespace=0&format=json') + data = response.json() + if data[1] and len(data[1]) > 0: + return data[1][0] \ No newline at end of file diff --git a/backend/server/adventures/views/global_search_view.py b/backend/server/adventures/views/global_search_view.py new file mode 100644 index 00000000..37806297 --- /dev/null +++ b/backend/server/adventures/views/global_search_view.py @@ -0,0 +1,71 @@ + +from rest_framework import viewsets +from rest_framework.response import Response +from rest_framework.permissions import IsAuthenticated +from adventures.models import Adventure, Collection +from adventures.serializers import AdventureSerializer, CollectionSerializer +from django.db.models import Q +from adventures.utils import pagination +from worldtravel.models import Country, Region, City +from worldtravel.serializers import CountrySerializer, RegionSerializer, CitySerializer +from users.models import CustomUser as User +from users.serializers import CustomUserDetailsSerializer as UserSerializer + +class GlobalSearchView(viewsets.ViewSet): + permission_classes = [IsAuthenticated] + pagination_class = pagination.StandardResultsSetPagination + + def list(self, request): + search_term = request.query_params.get('query', '') + # print(f"Searching for: {search_term}") # For debugging + + if not search_term: + return Response({"error": "Search query is required"}, status=400) + + # Search for adventures + adventures = Adventure.objects.filter( + (Q(name__icontains=search_term) | Q(description__icontains=search_term) | Q(location__icontains=search_term)) & Q(user_id=request.user.id) + ) + + # Search for collections + collections = Collection.objects.filter( + Q(name__icontains=search_term) & Q(user_id=request.user.id) + ) + + # Search for users + users = User.objects.filter( + (Q(username__icontains=search_term) | Q(first_name__icontains=search_term) | Q(last_name__icontains=search_term)) & Q(public_profile=True) + ) + + # Search for countries + countries = Country.objects.filter( + Q(name__icontains=search_term) | Q(country_code__icontains=search_term) + ) + + # Search for regions + regions = Region.objects.filter( + Q(name__icontains=search_term) | Q(country__name__icontains=search_term) + ) + + # Search for cities + cities = City.objects.filter( + Q(name__icontains=search_term) | Q(region__name__icontains=search_term) | Q(region__country__name__icontains=search_term) + ) + + # Serialize the results + adventure_serializer = AdventureSerializer(adventures, many=True) + collection_serializer = CollectionSerializer(collections, many=True) + user_serializer = UserSerializer(users, many=True) + country_serializer = CountrySerializer(countries, many=True) + region_serializer = RegionSerializer(regions, many=True) + city_serializer = CitySerializer(cities, many=True) + + return Response({ + "adventures": adventure_serializer.data, + "collections": collection_serializer.data, + "users": user_serializer.data, + "countries": country_serializer.data, + "regions": region_serializer.data, + "cities": city_serializer.data + }) + \ No newline at end of file diff --git a/frontend/src/lib/components/AdventureModal.svelte b/frontend/src/lib/components/AdventureModal.svelte index 1e9b1b41..5b0ce949 100644 --- a/frontend/src/lib/components/AdventureModal.svelte +++ b/frontend/src/lib/components/AdventureModal.svelte @@ -289,6 +289,7 @@ let res = await fetch(imageUrl); let blob = await res.blob(); let file = new File([blob], `${imageSearch}.jpg`, { type: 'image/jpeg' }); + wikiImageError = ''; let formData = new FormData(); formData.append('image', file); formData.append('adventure', adventure.id); @@ -1097,6 +1098,9 @@ it would also work to just use on:click on the MapLibre component itself. --> {$t('adventures.fetch_image')}
    + {#if wikiImageError} +

    {$t('adventures.wiki_image_error')}

    + {/if}
    {#if immichIntegration} From f10e171a8ec9cd2a848866ea6c1aed305dd21b8f Mon Sep 17 00:00:00 2001 From: Sean Morley Date: Sat, 18 Jan 2025 12:57:56 -0500 Subject: [PATCH 106/209] fix: update RegionCard component to handle undefined visited state and refactor global search API to return structured results --- .../adventures/views/global_search_view.py | 92 ++++---- frontend/src/lib/components/RegionCard.svelte | 6 +- frontend/src/routes/search/+page.server.ts | 43 ++-- frontend/src/routes/search/+page.svelte | 212 ++++++------------ 4 files changed, 129 insertions(+), 224 deletions(-) diff --git a/backend/server/adventures/views/global_search_view.py b/backend/server/adventures/views/global_search_view.py index 37806297..d2fa5d34 100644 --- a/backend/server/adventures/views/global_search_view.py +++ b/backend/server/adventures/views/global_search_view.py @@ -1,71 +1,73 @@ - from rest_framework import viewsets from rest_framework.response import Response from rest_framework.permissions import IsAuthenticated +from django.db.models import Q +from django.contrib.postgres.search import SearchVector, SearchQuery from adventures.models import Adventure, Collection from adventures.serializers import AdventureSerializer, CollectionSerializer -from django.db.models import Q -from adventures.utils import pagination -from worldtravel.models import Country, Region, City -from worldtravel.serializers import CountrySerializer, RegionSerializer, CitySerializer +from worldtravel.models import Country, Region, City, VisitedCity, VisitedRegion +from worldtravel.serializers import CountrySerializer, RegionSerializer, CitySerializer, VisitedCitySerializer, VisitedRegionSerializer from users.models import CustomUser as User from users.serializers import CustomUserDetailsSerializer as UserSerializer class GlobalSearchView(viewsets.ViewSet): permission_classes = [IsAuthenticated] - pagination_class = pagination.StandardResultsSetPagination def list(self, request): - search_term = request.query_params.get('query', '') - # print(f"Searching for: {search_term}") # For debugging - + search_term = request.query_params.get('query', '').strip() if not search_term: return Response({"error": "Search query is required"}, status=400) - # Search for adventures - adventures = Adventure.objects.filter( - (Q(name__icontains=search_term) | Q(description__icontains=search_term) | Q(location__icontains=search_term)) & Q(user_id=request.user.id) - ) + # Initialize empty results + results = { + "adventures": [], + "collections": [], + "users": [], + "countries": [], + "regions": [], + "cities": [], + "visited_regions": [], + "visited_cities": [] + } - # Search for collections + # Adventures: Full-Text Search + adventures = Adventure.objects.annotate( + search=SearchVector('name', 'description', 'location') + ).filter(search=SearchQuery(search_term), user_id=request.user) + results["adventures"] = AdventureSerializer(adventures, many=True).data + + # Collections: Partial Match Search collections = Collection.objects.filter( - Q(name__icontains=search_term) & Q(user_id=request.user.id) + Q(name__icontains=search_term) & Q(user_id=request.user) ) + results["collections"] = CollectionSerializer(collections, many=True).data - # Search for users + # Users: Public Profiles Only users = User.objects.filter( - (Q(username__icontains=search_term) | Q(first_name__icontains=search_term) | Q(last_name__icontains=search_term)) & Q(public_profile=True) + (Q(username__icontains=search_term) | + Q(first_name__icontains=search_term) | + Q(last_name__icontains=search_term)) & Q(public_profile=True) ) + results["users"] = UserSerializer(users, many=True).data - # Search for countries - countries = Country.objects.filter( - Q(name__icontains=search_term) | Q(country_code__icontains=search_term) - ) + # Countries: Full-Text Search + countries = Country.objects.annotate( + search=SearchVector('name', 'country_code') + ).filter(search=SearchQuery(search_term)) + results["countries"] = CountrySerializer(countries, many=True).data - # Search for regions - regions = Region.objects.filter( - Q(name__icontains=search_term) | Q(country__name__icontains=search_term) - ) + # Regions and Cities: Partial Match Search + regions = Region.objects.filter(Q(name__icontains=search_term)) + results["regions"] = RegionSerializer(regions, many=True).data - # Search for cities - cities = City.objects.filter( - Q(name__icontains=search_term) | Q(region__name__icontains=search_term) | Q(region__country__name__icontains=search_term) - ) + cities = City.objects.filter(Q(name__icontains=search_term)) + results["cities"] = CitySerializer(cities, many=True).data - # Serialize the results - adventure_serializer = AdventureSerializer(adventures, many=True) - collection_serializer = CollectionSerializer(collections, many=True) - user_serializer = UserSerializer(users, many=True) - country_serializer = CountrySerializer(countries, many=True) - region_serializer = RegionSerializer(regions, many=True) - city_serializer = CitySerializer(cities, many=True) + # Visited Regions and Cities + visited_regions = VisitedRegion.objects.filter(user_id=request.user) + results["visited_regions"] = VisitedRegionSerializer(visited_regions, many=True).data - return Response({ - "adventures": adventure_serializer.data, - "collections": collection_serializer.data, - "users": user_serializer.data, - "countries": country_serializer.data, - "regions": region_serializer.data, - "cities": city_serializer.data - }) - \ No newline at end of file + visited_cities = VisitedCity.objects.filter(user_id=request.user) + results["visited_cities"] = VisitedCitySerializer(visited_cities, many=True).data + + return Response(results) diff --git a/frontend/src/lib/components/RegionCard.svelte b/frontend/src/lib/components/RegionCard.svelte index 5acd3a9a..a5fc538a 100644 --- a/frontend/src/lib/components/RegionCard.svelte +++ b/frontend/src/lib/components/RegionCard.svelte @@ -7,7 +7,7 @@ import { t } from 'svelte-i18n'; export let region: Region; - export let visited: boolean; + export let visited: boolean | undefined; function goToCity() { console.log(region); @@ -64,12 +64,12 @@
    - {#if !visited} + {#if !visited && visited !== undefined} {/if} - {#if visited} + {#if visited && visited !== undefined} {/if} {#if region.num_cities > 0} diff --git a/frontend/src/routes/search/+page.server.ts b/frontend/src/routes/search/+page.server.ts index 8ff7e237..250bc967 100644 --- a/frontend/src/routes/search/+page.server.ts +++ b/frontend/src/routes/search/+page.server.ts @@ -8,7 +8,6 @@ const serverEndpoint = PUBLIC_SERVER_URL || 'http://localhost:8000'; export const load = (async (event) => { const query = event.url.searchParams.get('query'); - const property = event.url.searchParams.get('property') || 'all'; if (!query) { return { data: [] }; @@ -16,15 +15,12 @@ export const load = (async (event) => { let sessionId = event.cookies.get('sessionid'); - let res = await fetch( - `${serverEndpoint}/api/adventures/search/?query=${query}&property=${property}`, - { - headers: { - 'Content-Type': 'application/json', - Cookie: `sessionid=${sessionId}` - } + let res = await fetch(`${serverEndpoint}/api/search/?query=${query}`, { + headers: { + 'Content-Type': 'application/json', + Cookie: `sessionid=${sessionId}` } - ); + }); if (!res.ok) { console.error('Failed to fetch search data'); @@ -32,27 +28,16 @@ export const load = (async (event) => { return { error: error.error }; } - let adventures: Adventure[] = await res.json(); - - let osmRes = await fetch(`https://nominatim.openstreetmap.org/search?q=${query}&format=jsonv2`, { - headers: { - 'User-Agent': `AdventureLog / ${appVersion} ` - } - }); - - if (!osmRes.ok) { - console.error('Failed to fetch OSM data'); - let error = await res.json(); - return { error: error.error }; - } - - let osmData = (await osmRes.json()) as OpenStreetMapPlace[]; + let data = await res.json(); return { - props: { - adventures, - query, - osmData - } + adventures: data.adventures, + collections: data.collections, + users: data.users, + countries: data.countries, + regions: data.regions, + cities: data.cities, + visited_cities: data.visited_cities, + visited_regions: data.visited_regions }; }) satisfies PageServerLoad; diff --git a/frontend/src/routes/search/+page.svelte b/frontend/src/routes/search/+page.svelte index 6c355303..f45e6be8 100644 --- a/frontend/src/routes/search/+page.svelte +++ b/frontend/src/routes/search/+page.svelte @@ -1,184 +1,102 @@ -{#if isAdventureModalOpen} - (isAdventureModalOpen = false)} - on:save={filterByProperty} - /> -{/if} +

    Search{query ? `: ${query}` : ''}

    -{#if myAdventures.length === 0 && osmResults.length === 0} - -{/if} - -{#if myAdventures.length !== 0} -

    {$t('search.adventurelog_results')}

    -
    -
    - (property = 'all')} - /> - (property = 'name')} - /> - (property = 'location')} - /> - (property = 'description')} - /> - (property = 'activity_types')} - /> -
    - +{#if adventures.length > 0} +

    Adventures

    +
    + {#each adventures as adventure} + + {/each}
    {/if} -{#if myAdventures.length > 0} -

    {$t('adventures.my_adventures')}

    -
    - {#each myAdventures as adventure} - 0} +

    Collections

    +
    + {#each collections as collection} + + {/each} +
    +{/if} + +{#if countries.length > 0} +

    Countries

    +
    + {#each countries as country} + + {/each} +
    +{/if} + +{#if regions.length > 0} +

    Regions

    +
    + {#each regions as region} + visitedRegion.region === region.id)} /> {/each}
    {/if} -{#if publicAdventures.length > 0} -

    {$t('search.public_adventures')}

    -
    - {#each publicAdventures as adventure} - +{#if cities.length > 0} +

    Cities

    +
    + {#each cities as city} + visitedCity.city === city.id)} + /> {/each}
    {/if} -{#if myAdventures.length > 0 && osmResults.length > 0 && publicAdventures.length > 0} -
    -{/if} -{#if osmResults.length > 0} -

    {$t('search.online_results')}

    -
    - {#each osmResults as result} -
    -

    {result.display_name}

    -

    {result.type}

    -

    {result.lat}, {result.lon}

    -
    + +{#if users.length > 0} +

    Users

    +
    + {#each users as user} + {/each}
    {/if} +{#if adventures.length === 0 && regions.length === 0 && cities.length === 0 && countries.length === 0 && collections.length === 0 && users.length === 0} +

    + {$t('adventures.no_results')} +

    +{/if} + Search{query ? `: ${query}` : ''} From f7e183f9a36130decc6a2f3b5af51213fd153f02 Mon Sep 17 00:00:00 2001 From: Sean Morley <98704938+seanmorley15@users.noreply.github.com> Date: Sat, 18 Jan 2025 14:30:05 -0500 Subject: [PATCH 107/209] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f130b323..4df60b02 100644 --- a/README.md +++ b/README.md @@ -145,5 +145,5 @@ Hi! I'm Sean, the creator of AdventureLog. I'm a college student and software de ### Top Supporters 💖 - [Veymax](https://x.com/veymax) -- nebriv +- [nebriv](https://github.com/nebriv) - [Victor Butler](https://x.com/victor_butler) From 433599dc20625b84af9d792f5e720a79d1df1bf0 Mon Sep 17 00:00:00 2001 From: Sean Morley Date: Sat, 18 Jan 2025 17:03:03 -0500 Subject: [PATCH 108/209] feat: implement protected media serving and permission checks for adventure images --- backend/nginx.conf | 12 ++- .../utils/check_adventure_image_permisison.py | 33 +++++++ backend/server/main/settings.py | 2 +- backend/server/main/urls.py | 13 ++- backend/server/main/views.py | 30 ++++++- frontend/src/lib/components/Navbar.svelte | 87 ++++++++++--------- frontend/src/routes/search/+page.server.ts | 8 +- 7 files changed, 130 insertions(+), 55 deletions(-) create mode 100644 backend/server/adventures/utils/check_adventure_image_permisison.py diff --git a/backend/nginx.conf b/backend/nginx.conf index b4bad7df..8074aa69 100644 --- a/backend/nginx.conf +++ b/backend/nginx.conf @@ -19,7 +19,7 @@ http { } server { - listen 80; # NGINX listens on port 80 inside the container + listen 80; server_name localhost; location / { @@ -34,8 +34,12 @@ http { alias /code/staticfiles/; # Serve static files directly } - location /media/ { - alias /code/media/; # Serve media files directly + # Serve protected media files with X-Accel-Redirect + location /protectedMedia/ { + internal; # Only internal requests are allowed + alias /code/media/; # This should match Django MEDIA_ROOT + try_files $uri =404; # Return a 404 if the file doesn't exist } + } -} \ No newline at end of file +} diff --git a/backend/server/adventures/utils/check_adventure_image_permisison.py b/backend/server/adventures/utils/check_adventure_image_permisison.py new file mode 100644 index 00000000..ad7df574 --- /dev/null +++ b/backend/server/adventures/utils/check_adventure_image_permisison.py @@ -0,0 +1,33 @@ +from adventures.models import AdventureImage + +def checkAdventureImagePermission(imageId, user): + """ + Checks if the given user has permission to access the specified adventure image. + + Args: + imageId (str): The ID of the image to check permissions for. + user (User): The user object to check permissions against. + + Returns: + bool: True if the user has permission to access the image, False otherwise. + + Raises: + AdventureImage.DoesNotExist: If the image with the specified ID does not exist. + """ + try: + # Construct the full relative path to match the database field + image_path = f"images/{imageId}" + # Fetch the AdventureImage object + adventure = AdventureImage.objects.get(image=image_path).adventure + if adventure.is_public: + return True + elif adventure.user_id == user: + return True + elif adventure.collection: + if adventure.collection.shared_with.filter(id=user.id).exists(): + return True + else: + return False + except AdventureImage.DoesNotExist: + print('No image') + return False diff --git a/backend/server/main/settings.py b/backend/server/main/settings.py index 208b2a15..83ab09a1 100644 --- a/backend/server/main/settings.py +++ b/backend/server/main/settings.py @@ -157,7 +157,7 @@ STATIC_ROOT = BASE_DIR / "staticfiles" STATIC_URL = '/static/' MEDIA_URL = '/media/' -MEDIA_ROOT = BASE_DIR / 'media' +MEDIA_ROOT = BASE_DIR / 'media' # This path must match the NGINX root STATICFILES_DIRS = [BASE_DIR / 'static'] STORAGES = { diff --git a/backend/server/main/urls.py b/backend/server/main/urls.py index 571946e2..b7bb2a18 100644 --- a/backend/server/main/urls.py +++ b/backend/server/main/urls.py @@ -1,12 +1,9 @@ from django.urls import include, re_path, path 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 users.views import IsRegistrationDisabled, PublicUserListView, PublicUserDetailView, UserMetadataView, UpdateUserMetadataView, EnabledSocialProvidersView -from .views import get_csrf_token, get_public_url +from .views import get_csrf_token, get_public_url, serve_protected_media from drf_yasg.views import get_schema_view - from drf_yasg import openapi schema_view = get_schema_view( @@ -20,6 +17,9 @@ urlpatterns = [ path('api/', include('worldtravel.urls')), path("_allauth/", include("allauth.headless.urls")), + # Serve protected media files + re_path(r'^media/(?P.*)$', serve_protected_media, name='serve-protected-media'), + 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//', PublicUserDetailView.as_view(), name='public-user-detail'), @@ -44,6 +44,5 @@ urlpatterns = [ path("api/integrations/", include("integrations.urls")), - # Include the API endpoints: - -] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) + # Include the API endpoints: +] \ No newline at end of file diff --git a/backend/server/main/views.py b/backend/server/main/views.py index f21082b2..594017b5 100644 --- a/backend/server/main/views.py +++ b/backend/server/main/views.py @@ -1,10 +1,38 @@ from django.http import JsonResponse from django.middleware.csrf import get_token from os import getenv +from django.conf import settings +from django.http import HttpResponse, HttpResponseForbidden +from django.contrib.auth.decorators import login_required +from django.views.static import serve +from adventures.utils.check_adventure_image_permisison import checkAdventureImagePermission def get_csrf_token(request): csrf_token = get_token(request) return JsonResponse({'csrfToken': csrf_token}) def get_public_url(request): - return JsonResponse({'PUBLIC_URL': getenv('PUBLIC_URL')}) \ No newline at end of file + return JsonResponse({'PUBLIC_URL': getenv('PUBLIC_URL')}) + +def serve_protected_media(request, path): + if path.startswith('images/'): + image_id = path.split('/')[1] + user = request.user + if checkAdventureImagePermission(image_id, user): + if settings.DEBUG: + # In debug mode, serve the file directly + return serve(request, path, document_root=settings.MEDIA_ROOT) + else: + # In production, use X-Accel-Redirect + response = HttpResponse() + response['Content-Type'] = '' + response['X-Accel-Redirect'] = '/protectedMedia/' + path + return response + else: + return HttpResponseForbidden() + else: + response = HttpResponse() + response['Content-Type'] = '' + response['X-Accel-Redirect'] = '/protectedMedia/' + path + return response + \ No newline at end of file diff --git a/frontend/src/lib/components/Navbar.svelte b/frontend/src/lib/components/Navbar.svelte index bbfbaf5f..13a2e85f 100644 --- a/frontend/src/lib/components/Navbar.svelte +++ b/frontend/src/lib/components/Navbar.svelte @@ -120,27 +120,34 @@ {/if} - - - - + + + + + + + + {/if}
    {/if} -
    - - -
    + + {/if}
    +
    + +
    + {$t('adventures.attachments')} ({adventure.attachments?.length || 0}) +
    +
    +
    + {#each adventure.attachments as attachment} + (attachmentToEdit = e.detail)} + /> + {/each} +
    +
    { + e.preventDefault(); + uploadAttachment(e); + }} + > +
    + + + + +
    +
    + {#if attachmentToEdit} +
    { + e.preventDefault(); + editAttachment(); + }} + > +
    + + +
    +
    + {/if} +
    +
    diff --git a/frontend/src/lib/components/AttachmentCard.svelte b/frontend/src/lib/components/AttachmentCard.svelte new file mode 100644 index 00000000..7d90f2f4 --- /dev/null +++ b/frontend/src/lib/components/AttachmentCard.svelte @@ -0,0 +1,99 @@ + + +
    + + +
    window.open(attachment.file, '_blank')} + role="button" + tabindex="0" + aria-label={attachment.file.split('/').pop()} + > + {#if !['.jpg', '.jpeg', '.png', '.gif', '.webp'].some((ext) => attachment.file.endsWith(ext))} +
    +

    + {attachment.name}
    + {attachment.extension.toUpperCase()} +

    +
    + + {/if} +
    + + +
    + {$t('adventures.attachment')} +
    +
    + {attachment.extension} +
    + + +
    + + {attachment.name} + +
    + + {#if allowEdit} + + {/if} + +
    +
    +
    diff --git a/frontend/src/lib/types.ts b/frontend/src/lib/types.ts index 60191a96..c97c43d9 100644 --- a/frontend/src/lib/types.ts +++ b/frontend/src/lib/types.ts @@ -40,6 +40,7 @@ export type Adventure = { updated_at?: string | null; is_visited?: boolean; category: Category | null; + attachments: Attachment[]; }; export type Country = { @@ -252,3 +253,12 @@ export type ImmichAlbum = { order: string; lastModifiedAssetTimestamp: string; }; + +export type Attachment = { + id: string; + file: string; + adventure: string; + extension: string; + user_id: string; + name: string; +}; diff --git a/frontend/src/locales/en.json b/frontend/src/locales/en.json index a1383925..a8198492 100644 --- a/frontend/src/locales/en.json +++ b/frontend/src/locales/en.json @@ -129,6 +129,11 @@ "search_results": "Searh results", "no_results": "No results found", "wiki_desc": "Pulls excerpt from Wikipedia article matching the name of the adventure.", + "attachments": "Attachments", + "attachment": "Attachment", + "images": "Images", + "primary": "Primary", + "view_attachment": "View Attachment", "generate_desc": "Generate Description", "public_adventure": "Public Adventure", "location_information": "Location Information", @@ -245,6 +250,12 @@ "finding_recommendations": "Discovering hidden gems for your next adventure", "md_instructions": "Write your markdown here...", "days": "days", + "attachment_upload_success": "Attachment uploaded successfully!", + "attachment_upload_error": "Error uploading attachment", + "upload": "Upload", + "attachment_delete_success": "Attachment deleted successfully!", + "attachment_update_success": "Attachment updated successfully!", + "attachment_update_error": "Error updating attachment", "activities": { "general": "General 🌍", "outdoor": "Outdoor 🏞️", diff --git a/frontend/src/routes/adventures/+page.server.ts b/frontend/src/routes/adventures/+page.server.ts index a2d7ab69..89887f30 100644 --- a/frontend/src/routes/adventures/+page.server.ts +++ b/frontend/src/routes/adventures/+page.server.ts @@ -76,5 +76,21 @@ export const actions: Actions = { }); let data = await res.json(); return data; + }, + attachment: async (event) => { + let formData = await event.request.formData(); + let csrfToken = await fetchCSRFToken(); + let sessionId = event.cookies.get('sessionid'); + let res = await fetch(`${serverEndpoint}/api/attachments/`, { + method: 'POST', + headers: { + Cookie: `csrftoken=${csrfToken}; sessionid=${sessionId}`, + 'X-CSRFToken': csrfToken, + Referer: event.url.origin // Include Referer header + }, + body: formData + }); + let data = await res.json(); + return data; } }; diff --git a/frontend/src/routes/adventures/[id]/+page.svelte b/frontend/src/routes/adventures/[id]/+page.svelte index 21b622f4..96b7cbbf 100644 --- a/frontend/src/routes/adventures/[id]/+page.svelte +++ b/frontend/src/routes/adventures/[id]/+page.svelte @@ -12,6 +12,12 @@ return marked(markdown); }; + function deleteAttachment(event: CustomEvent) { + adventure.attachments = adventure.attachments.filter( + (attachment) => attachment.id !== event.detail + ); + } + export let data: PageData; console.log(data); @@ -30,6 +36,7 @@ import ClipboardList from '~icons/mdi/clipboard-list'; import AdventureModal from '$lib/components/AdventureModal.svelte'; import ImageDisplayModal from '$lib/components/ImageDisplayModal.svelte'; + import AttachmentCard from '$lib/components/AttachmentCard.svelte'; onMount(() => { if (data.props.adventure) { @@ -380,6 +387,52 @@ {/if}
    + {#if adventure.attachments && adventure.attachments.length > 0} +
    + +

    {$t('adventures.attachments')}

    +
    + {#if adventure.attachments && adventure.attachments.length > 0} +
    + {#each adventure.attachments as attachment} + + {/each} +
    + {/if} +
    +
    + {/if} + {#if adventure.images && adventure.images.length > 0} +
    +

    {$t('adventures.images')}

    +
    + {#if adventure.images && adventure.images.length > 0} +
    + {#each adventure.images as image} +
    + + + + +
    (image_url = image.image)} + >
    + {#if image.is_primary} +
    + {$t('adventures.primary')} +
    + {/if} +
    + {/each} +
    + {/if} +
    +
    + {/if}
    diff --git a/frontend/src/routes/dashboard/+page.svelte b/frontend/src/routes/dashboard/+page.svelte index fd3ee82e..76c77bbf 100644 --- a/frontend/src/routes/dashboard/+page.svelte +++ b/frontend/src/routes/dashboard/+page.svelte @@ -21,7 +21,7 @@ x: -50, // Smaller movement for quicker animation duration: 0.6, // Quicker animation duration stagger: 0.1, // Faster staggering - ease: 'power2.out' + ease: 'power2.out' // Slightly sharper easing for quicker feel }); // Stat values with faster reveal and snappier effect @@ -30,8 +30,8 @@ scale: 0.8, // Slightly less scaling for a snappier effect duration: 1, // Shorter duration stagger: 0.2, // Faster staggering - ease: 'power2.out', // Snappier easing - delay: 0.3 // Faster delay for quicker sequencing + ease: 'elastic.out(0.75, 0.5)', // Slightly snappier bounce + delay: 0 // Faster delay for quicker sequencing }); // Adventure card animations with quicker reveal @@ -41,7 +41,7 @@ duration: 0.8, // Quicker duration stagger: 0.1, // Faster staggering ease: 'power2.out', - delay: 0.6 // Shorter delay for quicker appearance + delay: 0 // Shorter delay for quicker appearance }); // Inspiration section with faster bounce effect @@ -50,7 +50,7 @@ scale: 0.7, // Less scale for snappier effect duration: 1, // Slightly quicker duration ease: 'elastic.out(0.75, 0.5)', // Snappier bounce - delay: 1 // Reduced delay for quicker animation + delay: 0 // Reduced delay for quicker animation }); }); From 1f3abf7f3281046fb077ef89adc3dd651f08f8f2 Mon Sep 17 00:00:00 2001 From: Sean Morley Date: Sun, 19 Jan 2025 22:33:35 -0500 Subject: [PATCH 115/209] feat: replace placeholder image with gradient background and text for no image available state in CardCarousel component --- frontend/src/lib/components/CardCarousel.svelte | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/frontend/src/lib/components/CardCarousel.svelte b/frontend/src/lib/components/CardCarousel.svelte index edbf4697..cb238d02 100644 --- a/frontend/src/lib/components/CardCarousel.svelte +++ b/frontend/src/lib/components/CardCarousel.svelte @@ -95,11 +95,14 @@
    {:else} - - No image available + +
    + +
    + {$t('adventures.no_image_found')} +
    +
    {/if} From 25edec460b19c8ce0c57a542934368d3e1e79ab2 Mon Sep 17 00:00:00 2001 From: Sean Morley Date: Sun, 19 Jan 2025 23:09:28 -0500 Subject: [PATCH 116/209] feat: enhance search page with reactive data updates and simplify component usage --- frontend/src/routes/search/+page.svelte | 60 ++++++++++++------------- 1 file changed, 28 insertions(+), 32 deletions(-) diff --git a/frontend/src/routes/search/+page.svelte b/frontend/src/routes/search/+page.svelte index f45e6be8..d498aa19 100644 --- a/frontend/src/routes/search/+page.svelte +++ b/frontend/src/routes/search/+page.svelte @@ -1,32 +1,39 @@

    Search{query ? `: ${query}` : ''}

    @@ -44,7 +51,7 @@

    Collections

    {#each collections as collection} - + {/each}
    {/if} @@ -62,10 +69,7 @@

    Regions

    {#each regions as region} - visitedRegion.region === region.id)} - /> + vr.region === region.id)} /> {/each}
    {/if} @@ -74,10 +78,7 @@

    Cities

    {#each cities as city} - visitedCity.city === city.id)} - /> + vc.city === city.id)} /> {/each}
    {/if} @@ -96,8 +97,3 @@ {$t('adventures.no_results')}

    {/if} - - - Search{query ? `: ${query}` : ''} - - From 30c58ca1183de42940465d37bcb86dba2453121e Mon Sep 17 00:00:00 2001 From: Sean Morley Date: Mon, 20 Jan 2025 09:01:11 -0500 Subject: [PATCH 117/209] feat: refactor AttachmentCard component to handle delete action locally and simplify adventure page logic --- frontend/src/lib/components/AttachmentCard.svelte | 6 +++--- frontend/src/routes/adventures/[id]/+page.svelte | 8 +------- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/frontend/src/lib/components/AttachmentCard.svelte b/frontend/src/lib/components/AttachmentCard.svelte index 7d90f2f4..1de97e80 100644 --- a/frontend/src/lib/components/AttachmentCard.svelte +++ b/frontend/src/lib/components/AttachmentCard.svelte @@ -90,10 +90,10 @@ > {$t('transportation.edit')} + {/if} - diff --git a/frontend/src/routes/adventures/[id]/+page.svelte b/frontend/src/routes/adventures/[id]/+page.svelte index 96b7cbbf..a4715720 100644 --- a/frontend/src/routes/adventures/[id]/+page.svelte +++ b/frontend/src/routes/adventures/[id]/+page.svelte @@ -12,12 +12,6 @@ return marked(markdown); }; - function deleteAttachment(event: CustomEvent) { - adventure.attachments = adventure.attachments.filter( - (attachment) => attachment.id !== event.detail - ); - } - export let data: PageData; console.log(data); @@ -395,7 +389,7 @@ {#if adventure.attachments && adventure.attachments.length > 0}
    {#each adventure.attachments as attachment} - + {/each}
    {/if} From 64d2bdebcea09c1f654764c5baa2a82307290df3 Mon Sep 17 00:00:00 2001 From: Sean Morley Date: Mon, 20 Jan 2025 20:03:00 -0500 Subject: [PATCH 118/209] feat: add GPX file handling and GeoJSON integration in adventure page; update dependencies and AttachmentCard component --- .../adventures/views/attachment_view.py | 2 - frontend/package.json | 1 + frontend/pnpm-lock.yaml | 40 +++++++++++ .../src/lib/components/AttachmentCard.svelte | 2 +- .../src/routes/adventures/[id]/+page.svelte | 69 ++++++++++++++++++- frontend/src/routes/gpx/[file]/+server.ts | 22 ++++++ 6 files changed, 131 insertions(+), 5 deletions(-) create mode 100644 frontend/src/routes/gpx/[file]/+server.ts diff --git a/backend/server/adventures/views/attachment_view.py b/backend/server/adventures/views/attachment_view.py index 47ed3285..e83bdeab 100644 --- a/backend/server/adventures/views/attachment_view.py +++ b/backend/server/adventures/views/attachment_view.py @@ -2,10 +2,8 @@ from rest_framework import viewsets, status from rest_framework.decorators import action from rest_framework.permissions import IsAuthenticated from rest_framework.response import Response -from django.db.models import Q from adventures.models import Adventure, Attachment from adventures.serializers import AttachmentSerializer -import uuid class AttachmentViewSet(viewsets.ModelViewSet): serializer_class = AttachmentSerializer diff --git a/frontend/package.json b/frontend/package.json index f8abe55a..5b53d58d 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -40,6 +40,7 @@ "type": "module", "dependencies": { "@lukulent/svelte-umami": "^0.0.3", + "@mapbox/togeojson": "^0.16.2", "emoji-picker-element": "^1.26.0", "gsap": "^3.12.7", "marked": "^15.0.4", diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index 3e72f679..7a021cb6 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -11,6 +11,9 @@ importers: '@lukulent/svelte-umami': specifier: ^0.0.3 version: 0.0.3(svelte@4.2.19) + '@mapbox/togeojson': + specifier: ^0.16.2 + version: 0.16.2 emoji-picker-element: specifier: ^1.26.0 version: 1.26.0 @@ -483,6 +486,10 @@ packages: '@mapbox/tiny-sdf@2.0.6': resolution: {integrity: sha512-qMqa27TLw+ZQz5Jk+RcwZGH7BQf5G/TrutJhspsca/3SHwmgKQ1iq+d3Jxz5oysPVYTGP6aXxCo5Lk9Er6YBAA==} + '@mapbox/togeojson@0.16.2': + resolution: {integrity: sha512-DcApudmw4g/grOrpM5gYPZfts6Kr8litBESN6n/27sDsjR2f+iJhx4BA0J2B+XrLlnHyJkKztYApe6oCUZpzFA==} + hasBin: true + '@mapbox/unitbezier@0.0.1': resolution: {integrity: sha512-nMkuDXFv60aBr9soUG5q+GvZYL+2KZHVvsqFCzqnkGEf46U2fvmytHaEVc1/YZbiLn8X+eR3QzX1+dwDO1lxlw==} @@ -868,6 +875,10 @@ packages: engines: {node: '>=16'} hasBin: true + '@xmldom/xmldom@0.8.10': + resolution: {integrity: sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==} + engines: {node: '>=10.0.0'} + abbrev@1.1.1: resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==} @@ -972,6 +983,9 @@ packages: resolution: {integrity: sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==} engines: {node: '>=8.0.0'} + buffer-from@1.1.2: + resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + builtin-modules@3.3.0: resolution: {integrity: sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==} engines: {node: '>=6'} @@ -1039,6 +1053,10 @@ packages: concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + concat-stream@2.0.0: + resolution: {integrity: sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==} + engines: {'0': node >= 6.0} + confbox@0.1.7: resolution: {integrity: sha512-uJcB/FKZtBMCJpK8MQji6bJHgu1tixKPxRLeGkNzBoOZzpnZUJm0jm2/sBDWcuBx1dYgxV4JU+g5hmNxCyAmdA==} @@ -2116,6 +2134,9 @@ packages: type@2.7.3: resolution: {integrity: sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ==} + typedarray@0.0.6: + resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==} + typescript@5.5.2: resolution: {integrity: sha512-NcRtPEOsPFFWjobJEtfihkLCZCXZt/os3zf8nTxjVH3RvTSxjrCamJpbExGvYOF+tFHc3pA65qpdwPbzjohhew==} engines: {node: '>=14.17'} @@ -2553,6 +2574,12 @@ snapshots: '@mapbox/tiny-sdf@2.0.6': {} + '@mapbox/togeojson@0.16.2': + dependencies: + '@xmldom/xmldom': 0.8.10 + concat-stream: 2.0.0 + minimist: 1.2.8 + '@mapbox/unitbezier@0.0.1': {} '@mapbox/vector-tile@1.3.1': @@ -3031,6 +3058,8 @@ snapshots: - encoding - supports-color + '@xmldom/xmldom@0.8.10': {} + abbrev@1.1.1: {} acorn-import-attributes@1.9.5(acorn@8.12.0): @@ -3125,6 +3154,8 @@ snapshots: buffer-crc32@1.0.0: {} + buffer-from@1.1.2: {} + builtin-modules@3.3.0: {} bytewise-core@1.2.3: @@ -3196,6 +3227,13 @@ snapshots: concat-map@0.0.1: {} + concat-stream@2.0.0: + dependencies: + buffer-from: 1.1.2 + inherits: 2.0.4 + readable-stream: 3.6.2 + typedarray: 0.0.6 + confbox@0.1.7: {} console-control-strings@1.1.0: {} @@ -4320,6 +4358,8 @@ snapshots: type@2.7.3: {} + typedarray@0.0.6: {} + typescript@5.5.2: {} typewise-core@1.2.0: {} diff --git a/frontend/src/lib/components/AttachmentCard.svelte b/frontend/src/lib/components/AttachmentCard.svelte index 1de97e80..226b0521 100644 --- a/frontend/src/lib/components/AttachmentCard.svelte +++ b/frontend/src/lib/components/AttachmentCard.svelte @@ -27,7 +27,7 @@ const isImage = ['.jpg', '.jpeg', '.png', '.gif', '.webp'].some((ext) => attachment.file.endsWith(ext) ); - return isImage ? `url(${attachment.file})` : 'url(/path/to/default-placeholder.png)'; + return isImage ? `url(${attachment.file})` : ''; } diff --git a/frontend/src/routes/adventures/[id]/+page.svelte b/frontend/src/routes/adventures/[id]/+page.svelte index a4715720..90fc2839 100644 --- a/frontend/src/routes/adventures/[id]/+page.svelte +++ b/frontend/src/routes/adventures/[id]/+page.svelte @@ -4,14 +4,65 @@ import type { PageData } from './$types'; import { goto } from '$app/navigation'; import Lost from '$lib/assets/undraw_lost.svg'; - import { DefaultMarker, MapLibre, Popup } from 'svelte-maplibre'; + import { DefaultMarker, MapLibre, Popup, GeoJSON, LineLayer } from 'svelte-maplibre'; import { t } from 'svelte-i18n'; import { marked } from 'marked'; // Import the markdown parser + // @ts-ignore + import toGeoJSON from '@mapbox/togeojson'; + + let geojson: any; + const renderMarkdown = (markdown: string) => { return marked(markdown); }; + async function getGpxFiles() { + let gpxfiles: string[] = []; + + // Collect all GPX file attachments + if (adventure.attachments && adventure.attachments.length > 0) { + adventure.attachments + .filter((attachment) => attachment.extension === 'gpx') + .forEach((attachment) => gpxfiles.push(attachment.file)); + } + + // Initialize a collection GeoJSON object + geojson = { + type: 'FeatureCollection', + features: [] + }; + + // Process each GPX file + if (gpxfiles.length > 0) { + for (const gpxfile of gpxfiles) { + try { + let gpxFileName = gpxfile.split('/').pop(); + let res = await fetch('/gpx/' + gpxFileName); + + if (!res.ok) { + console.error(`Failed to fetch GPX file: ${gpxFileName}`); + continue; + } + + let gpxData = await res.text(); + let parser = new DOMParser(); + let gpx = parser.parseFromString(gpxData, 'text/xml'); + + // Convert GPX to GeoJSON and merge features + let convertedGeoJSON = toGeoJSON.gpx(gpx); + if (convertedGeoJSON.features) { + geojson.features.push(...convertedGeoJSON.features); + } + } catch (error) { + console.error(`Error processing GPX file ${gpxfile}:`, error); + } + } + + // Log the final GeoJSON for debugging + } + } + export let data: PageData; console.log(data); @@ -32,7 +83,7 @@ import ImageDisplayModal from '$lib/components/ImageDisplayModal.svelte'; import AttachmentCard from '$lib/components/AttachmentCard.svelte'; - onMount(() => { + onMount(async () => { if (data.props.adventure) { adventure = data.props.adventure; // sort so that any image in adventure_images .is_primary is first @@ -48,6 +99,7 @@ } else { notFound = true; } + getGpxFiles(); }); function saveEdit(event: CustomEvent) { @@ -345,6 +397,19 @@ center={{ lng: adventure.longitude, lat: adventure.latitude }} zoom={12} > + + {#if geojson} + + + + + {/if} + diff --git a/frontend/src/routes/gpx/[file]/+server.ts b/frontend/src/routes/gpx/[file]/+server.ts new file mode 100644 index 00000000..9edaa38a --- /dev/null +++ b/frontend/src/routes/gpx/[file]/+server.ts @@ -0,0 +1,22 @@ +const PUBLIC_SERVER_URL = process.env['PUBLIC_SERVER_URL']; +const endpoint = PUBLIC_SERVER_URL || 'http://localhost:8000'; + +/** @type {import('./$types').RequestHandler} */ +export async function GET(event) { + let sessionid = event.cookies.get('sessionid'); + let fileName = event.params.file; + let res = await fetch(`${endpoint}/media/attachments/${fileName}`, { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + Cookie: `sessionid=${sessionid}` + } + }); + let data = await res.text(); + return new Response(data, { + status: res.status, + headers: { + 'Content-Type': 'application/xml' + } + }); +} From f9cf92041dc19635078d93012a07e9a3f1c3b43c Mon Sep 17 00:00:00 2001 From: Sean Morley Date: Tue, 21 Jan 2025 19:01:45 -0500 Subject: [PATCH 119/209] feat: enhance CategoryModal with loading state and remove unused activities route --- .../src/lib/components/CategoryModal.svelte | 43 +++++++++------- .../src/routes/activities/+page.server.ts | 51 ------------------- frontend/src/routes/activities/+page.svelte | 38 -------------- .../src/routes/adventures/[id]/+page.svelte | 2 - 4 files changed, 25 insertions(+), 109 deletions(-) delete mode 100644 frontend/src/routes/activities/+page.server.ts delete mode 100644 frontend/src/routes/activities/+page.svelte diff --git a/frontend/src/lib/components/CategoryModal.svelte b/frontend/src/lib/components/CategoryModal.svelte index 687e6c7e..e28706fe 100644 --- a/frontend/src/lib/components/CategoryModal.svelte +++ b/frontend/src/lib/components/CategoryModal.svelte @@ -6,14 +6,14 @@ let modal: HTMLDialogElement; import { t } from 'svelte-i18n'; - import InformationSlabCircle from '~icons/mdi/information-slab-circle'; - export let categories: Category[] = []; let category_to_edit: Category | null = null; let is_changed: boolean = false; + let has_loaded: boolean = false; + onMount(async () => { modal = document.getElementById('my_modal_1') as HTMLDialogElement; if (modal) { @@ -21,6 +21,7 @@ } let category_fetch = await fetch('/api/categories/categories'); categories = await category_fetch.json(); + has_loaded = true; // remove the general category if it exists // categories = categories.filter((c) => c.name !== 'general'); }); @@ -77,25 +78,31 @@ - -
    - -
    - {$t('adventures.attachments')} ({adventure.attachments?.length || 0}) -
    -
    -
    - {#each adventure.attachments as attachment} - (attachmentToEdit = e.detail)} - /> - {/each} -
    - { - e.preventDefault(); - uploadAttachment(e); - }} - > -
    - - - - -
    - - {#if attachmentToEdit} -
    { - e.preventDefault(); - editAttachment(); - }} - > -
    - - -
    -
    - {/if} -
    -
    @@ -1185,122 +1133,180 @@ it would also work to just use on:click on the MapLibre component itself. -->
    {:else} -

    {$t('adventures.upload_images_here')}

    - -
    - -
    - - - -
    -
    - -
    - -
    - - -
    -
    - -
    - -
    - - -
    - {#if wikiImageError} -

    {$t('adventures.wiki_image_error')}

    - {/if} -
    - - {#if immichIntegration} - { - url = e.detail; - fetchImage(); - }} - /> - {/if} - -
    - - {#if images.length > 0} -

    {$t('adventures.my_images')}

    -
    - {#each images as image} -
    - - {#if !image.is_primary} - - {:else} - - -
    - -
    - {/if} - {image.id} + +
    + +
    + {$t('adventures.images')} ({adventure.images?.length || 0}) +
    +
    + +
    + + +
    + +
    + +
    + + +
    +
    + +
    + +
    + + +
    + {#if wikiImageError} +

    {$t('adventures.wiki_image_error')}

    + {/if} +
    + + {#if immichIntegration} + { + url = e.detail; + fetchImage(); + }} + /> + {/if} + +
    + + {#if images.length > 0} +

    {$t('adventures.my_images')}

    +
    + {#each images as image} +
    + + {#if !image.is_primary} + + {:else} + + +
    + +
    + {/if} + {image.id} +
    + {/each} +
    + {:else} +

    {$t('adventures.no_images')}

    + {/if} +
    +
    +
    diff --git a/frontend/src/lib/components/Avatar.svelte b/frontend/src/lib/components/Avatar.svelte index a94a8eba..01eb0681 100644 --- a/frontend/src/lib/components/Avatar.svelte +++ b/frontend/src/lib/components/Avatar.svelte @@ -36,7 +36,6 @@

  • -
  • diff --git a/frontend/src/routes/adventures/+page.server.ts b/frontend/src/routes/adventures/+page.server.ts index 89887f30..98753999 100644 --- a/frontend/src/routes/adventures/+page.server.ts +++ b/frontend/src/routes/adventures/+page.server.ts @@ -91,6 +91,9 @@ export const actions: Actions = { body: formData }); let data = await res.json(); + + console.log(res); + console.log(data); return data; } }; diff --git a/frontend/src/routes/adventures/[id]/+page.svelte b/frontend/src/routes/adventures/[id]/+page.svelte index fb0528f9..35cd73ca 100644 --- a/frontend/src/routes/adventures/[id]/+page.svelte +++ b/frontend/src/routes/adventures/[id]/+page.svelte @@ -102,9 +102,11 @@ await getGpxFiles(); }); - function saveEdit(event: CustomEvent) { + async function saveEdit(event: CustomEvent) { adventure = event.detail; isEditModalOpen = false; + geojson = null; + await getGpxFiles(); } From 9e66c67c623bf0b1d73f0efa303fb03f7596762d Mon Sep 17 00:00:00 2001 From: Lars Lehmann Date: Thu, 23 Jan 2025 20:07:16 +0100 Subject: [PATCH 126/209] fix: Display adventure category icon also on collection maps --- frontend/package-lock.json | 4499 +++++++++++++++++ .../src/routes/collections/[id]/+page.svelte | 7 +- 2 files changed, 4504 insertions(+), 2 deletions(-) create mode 100644 frontend/package-lock.json diff --git a/frontend/package-lock.json b/frontend/package-lock.json new file mode 100644 index 00000000..3748c971 --- /dev/null +++ b/frontend/package-lock.json @@ -0,0 +1,4499 @@ +{ + "name": "adventurelog-frontend", + "version": "0.8.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "adventurelog-frontend", + "version": "0.8.0", + "dependencies": { + "@lukulent/svelte-umami": "^0.0.3", + "emoji-picker-element": "^1.26.0", + "gsap": "^3.12.7", + "marked": "^15.0.4", + "qrcode": "^1.5.4", + "svelte-i18n": "^4.0.1", + "svelte-maplibre": "^0.9.8", + "tsparticles": "^3.7.1" + }, + "devDependencies": { + "@event-calendar/core": "^3.7.1", + "@event-calendar/day-grid": "^3.7.1", + "@event-calendar/time-grid": "^3.7.1", + "@iconify-json/mdi": "^1.1.67", + "@sveltejs/adapter-auto": "^3.2.2", + "@sveltejs/adapter-node": "^5.2.0", + "@sveltejs/adapter-vercel": "^5.4.1", + "@sveltejs/kit": "^2.8.3", + "@sveltejs/vite-plugin-svelte": "^3.1.1", + "@tailwindcss/typography": "^0.5.13", + "@types/node": "^22.5.4", + "@types/qrcode": "^1.5.5", + "autoprefixer": "^10.4.19", + "daisyui": "^4.12.6", + "postcss": "^8.4.38", + "prettier": "^3.3.2", + "prettier-plugin-svelte": "^3.2.5", + "svelte": "^4.2.19", + "svelte-check": "^3.8.1", + "tailwindcss": "^3.4.4", + "tslib": "^2.6.3", + "typescript": "^5.5.2", + "unplugin-icons": "^0.19.0", + "vite": "^5.3.6" + } + }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "license": "Apache-2.0", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@antfu/install-pkg": { + "version": "0.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "package-manager-detector": "^0.2.0", + "tinyexec": "^0.3.0" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@antfu/utils": { + "version": "0.7.10", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.24.2", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@event-calendar/core": { + "version": "3.8.0", + "dev": true, + "license": "MIT", + "dependencies": { + "svelte": "^4.2.19" + } + }, + "node_modules/@event-calendar/day-grid": { + "version": "3.8.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@event-calendar/core": "~3.8.0", + "svelte": "^4.2.19" + } + }, + "node_modules/@event-calendar/time-grid": { + "version": "3.8.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@event-calendar/core": "~3.8.0", + "svelte": "^4.2.19" + } + }, + "node_modules/@formatjs/ecma402-abstract": { + "version": "2.3.2", + "license": "MIT", + "dependencies": { + "@formatjs/fast-memoize": "2.2.6", + "@formatjs/intl-localematcher": "0.5.10", + "decimal.js": "10", + "tslib": "2" + } + }, + "node_modules/@formatjs/fast-memoize": { + "version": "2.2.6", + "license": "MIT", + "dependencies": { + "tslib": "2" + } + }, + "node_modules/@formatjs/icu-messageformat-parser": { + "version": "2.11.0", + "license": "MIT", + "dependencies": { + "@formatjs/ecma402-abstract": "2.3.2", + "@formatjs/icu-skeleton-parser": "1.8.12", + "tslib": "2" + } + }, + "node_modules/@formatjs/icu-skeleton-parser": { + "version": "1.8.12", + "license": "MIT", + "dependencies": { + "@formatjs/ecma402-abstract": "2.3.2", + "tslib": "2" + } + }, + "node_modules/@formatjs/intl-localematcher": { + "version": "0.5.10", + "license": "MIT", + "dependencies": { + "tslib": "2" + } + }, + "node_modules/@iconify-json/mdi": { + "version": "1.2.3", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@iconify/types": "*" + } + }, + "node_modules/@iconify/types": { + "version": "2.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/@iconify/utils": { + "version": "2.2.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@antfu/install-pkg": "^0.4.1", + "@antfu/utils": "^0.7.10", + "@iconify/types": "^2.0.0", + "debug": "^4.4.0", + "globals": "^15.13.0", + "kolorist": "^1.8.0", + "local-pkg": "^0.5.1", + "mlly": "^1.7.3" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "dev": true, + "license": "MIT" + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/fs-minipass": { + "version": "4.0.1", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.8", + "license": "MIT", + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@lukulent/svelte-umami": { + "version": "0.0.3", + "license": "MIT", + "peerDependencies": { + "svelte": "^4.0.0" + } + }, + "node_modules/@mapbox/geojson-rewind": { + "version": "0.5.2", + "license": "ISC", + "dependencies": { + "get-stream": "^6.0.1", + "minimist": "^1.2.6" + }, + "bin": { + "geojson-rewind": "geojson-rewind" + } + }, + "node_modules/@mapbox/jsonlint-lines-primitives": { + "version": "2.0.2", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@mapbox/node-pre-gyp": { + "version": "2.0.0-rc.0", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "consola": "^3.2.3", + "detect-libc": "^2.0.0", + "https-proxy-agent": "^7.0.5", + "node-fetch": "^2.6.7", + "nopt": "^8.0.0", + "semver": "^7.5.3", + "tar": "^7.4.0" + }, + "bin": { + "node-pre-gyp": "bin/node-pre-gyp" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@mapbox/point-geometry": { + "version": "0.1.0", + "license": "ISC" + }, + "node_modules/@mapbox/tiny-sdf": { + "version": "2.0.6", + "license": "BSD-2-Clause" + }, + "node_modules/@mapbox/unitbezier": { + "version": "0.0.1", + "license": "BSD-2-Clause" + }, + "node_modules/@mapbox/vector-tile": { + "version": "1.3.1", + "license": "BSD-3-Clause", + "dependencies": { + "@mapbox/point-geometry": "~0.1.0" + } + }, + "node_modules/@mapbox/whoots-js": { + "version": "3.1.0", + "license": "ISC", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@maplibre/maplibre-gl-style-spec": { + "version": "20.4.0", + "license": "ISC", + "dependencies": { + "@mapbox/jsonlint-lines-primitives": "~2.0.2", + "@mapbox/unitbezier": "^0.0.1", + "json-stringify-pretty-compact": "^4.0.0", + "minimist": "^1.2.8", + "quickselect": "^2.0.0", + "rw": "^1.3.3", + "tinyqueue": "^3.0.0" + }, + "bin": { + "gl-style-format": "dist/gl-style-format.mjs", + "gl-style-migrate": "dist/gl-style-migrate.mjs", + "gl-style-validate": "dist/gl-style-validate.mjs" + } + }, + "node_modules/@maplibre/maplibre-gl-style-spec/node_modules/quickselect": { + "version": "2.0.0", + "license": "ISC" + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@polka/url": { + "version": "1.0.0-next.28", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/plugin-commonjs": { + "version": "28.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^5.0.1", + "commondir": "^1.0.1", + "estree-walker": "^2.0.2", + "fdir": "^6.2.0", + "is-reference": "1.2.1", + "magic-string": "^0.30.3", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=16.0.0 || 14 >= 14.17" + }, + "peerDependencies": { + "rollup": "^2.68.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-json": { + "version": "6.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^5.1.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-node-resolve": { + "version": "16.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^5.0.1", + "@types/resolve": "1.20.2", + "deepmerge": "^4.2.2", + "is-module": "^1.0.0", + "resolve": "^1.22.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^2.78.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/pluginutils": { + "version": "5.1.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.31.0", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@sveltejs/adapter-auto": { + "version": "3.3.1", + "dev": true, + "license": "MIT", + "dependencies": { + "import-meta-resolve": "^4.1.0" + }, + "peerDependencies": { + "@sveltejs/kit": "^2.0.0" + } + }, + "node_modules/@sveltejs/adapter-node": { + "version": "5.2.12", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/plugin-commonjs": "^28.0.1", + "@rollup/plugin-json": "^6.1.0", + "@rollup/plugin-node-resolve": "^16.0.0", + "rollup": "^4.9.5" + }, + "peerDependencies": { + "@sveltejs/kit": "^2.4.0" + } + }, + "node_modules/@sveltejs/adapter-vercel": { + "version": "5.5.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@vercel/nft": "^0.29.0", + "esbuild": "^0.24.0" + }, + "peerDependencies": { + "@sveltejs/kit": "^2.4.0" + } + }, + "node_modules/@sveltejs/kit": { + "version": "2.16.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/cookie": "^0.6.0", + "cookie": "^0.6.0", + "devalue": "^5.1.0", + "esm-env": "^1.2.2", + "import-meta-resolve": "^4.1.0", + "kleur": "^4.1.5", + "magic-string": "^0.30.5", + "mrmime": "^2.0.0", + "sade": "^1.8.1", + "set-cookie-parser": "^2.6.0", + "sirv": "^3.0.0" + }, + "bin": { + "svelte-kit": "svelte-kit.js" + }, + "engines": { + "node": ">=18.13" + }, + "peerDependencies": { + "@sveltejs/vite-plugin-svelte": "^3.0.0 || ^4.0.0-next.1 || ^5.0.0", + "svelte": "^4.0.0 || ^5.0.0-next.0", + "vite": "^5.0.3 || ^6.0.0" + } + }, + "node_modules/@sveltejs/vite-plugin-svelte": { + "version": "3.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@sveltejs/vite-plugin-svelte-inspector": "^2.1.0", + "debug": "^4.3.4", + "deepmerge": "^4.3.1", + "kleur": "^4.1.5", + "magic-string": "^0.30.10", + "svelte-hmr": "^0.16.0", + "vitefu": "^0.2.5" + }, + "engines": { + "node": "^18.0.0 || >=20" + }, + "peerDependencies": { + "svelte": "^4.0.0 || ^5.0.0-next.0", + "vite": "^5.0.0" + } + }, + "node_modules/@sveltejs/vite-plugin-svelte-inspector": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.0.0 || >=20" + }, + "peerDependencies": { + "@sveltejs/vite-plugin-svelte": "^3.0.0", + "svelte": "^4.0.0 || ^5.0.0-next.0", + "vite": "^5.0.0" + } + }, + "node_modules/@tailwindcss/typography": { + "version": "0.5.16", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash.castarray": "^4.4.0", + "lodash.isplainobject": "^4.0.6", + "lodash.merge": "^4.6.2", + "postcss-selector-parser": "6.0.10" + }, + "peerDependencies": { + "tailwindcss": ">=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1" + } + }, + "node_modules/@tsparticles/basic": { + "version": "3.8.0", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/matteobruni" + }, + { + "type": "github", + "url": "https://github.com/sponsors/tsparticles" + }, + { + "type": "buymeacoffee", + "url": "https://www.buymeacoffee.com/matteobruni" + } + ], + "license": "MIT", + "dependencies": { + "@tsparticles/engine": "3.8.0", + "@tsparticles/move-base": "3.8.0", + "@tsparticles/plugin-hex-color": "3.8.0", + "@tsparticles/plugin-hsl-color": "3.8.0", + "@tsparticles/plugin-rgb-color": "3.8.0", + "@tsparticles/shape-circle": "3.8.0", + "@tsparticles/updater-color": "3.8.0", + "@tsparticles/updater-opacity": "3.8.0", + "@tsparticles/updater-out-modes": "3.8.0", + "@tsparticles/updater-size": "3.8.0" + } + }, + "node_modules/@tsparticles/engine": { + "version": "3.8.0", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/matteobruni" + }, + { + "type": "github", + "url": "https://github.com/sponsors/tsparticles" + }, + { + "type": "buymeacoffee", + "url": "https://www.buymeacoffee.com/matteobruni" + } + ], + "hasInstallScript": true, + "license": "MIT" + }, + "node_modules/@tsparticles/interaction-external-attract": { + "version": "3.8.0", + "license": "MIT", + "dependencies": { + "@tsparticles/engine": "3.8.0" + } + }, + "node_modules/@tsparticles/interaction-external-bounce": { + "version": "3.8.0", + "license": "MIT", + "dependencies": { + "@tsparticles/engine": "3.8.0" + } + }, + "node_modules/@tsparticles/interaction-external-bubble": { + "version": "3.8.0", + "license": "MIT", + "dependencies": { + "@tsparticles/engine": "3.8.0" + } + }, + "node_modules/@tsparticles/interaction-external-connect": { + "version": "3.8.0", + "license": "MIT", + "dependencies": { + "@tsparticles/engine": "3.8.0" + } + }, + "node_modules/@tsparticles/interaction-external-grab": { + "version": "3.8.0", + "license": "MIT", + "dependencies": { + "@tsparticles/engine": "3.8.0" + } + }, + "node_modules/@tsparticles/interaction-external-pause": { + "version": "3.8.0", + "license": "MIT", + "dependencies": { + "@tsparticles/engine": "3.8.0" + } + }, + "node_modules/@tsparticles/interaction-external-push": { + "version": "3.8.0", + "license": "MIT", + "dependencies": { + "@tsparticles/engine": "3.8.0" + } + }, + "node_modules/@tsparticles/interaction-external-remove": { + "version": "3.8.0", + "license": "MIT", + "dependencies": { + "@tsparticles/engine": "3.8.0" + } + }, + "node_modules/@tsparticles/interaction-external-repulse": { + "version": "3.8.0", + "license": "MIT", + "dependencies": { + "@tsparticles/engine": "3.8.0" + } + }, + "node_modules/@tsparticles/interaction-external-slow": { + "version": "3.8.0", + "license": "MIT", + "dependencies": { + "@tsparticles/engine": "3.8.0" + } + }, + "node_modules/@tsparticles/interaction-external-trail": { + "version": "3.8.0", + "license": "MIT", + "dependencies": { + "@tsparticles/engine": "3.8.0" + } + }, + "node_modules/@tsparticles/interaction-particles-attract": { + "version": "3.8.0", + "license": "MIT", + "dependencies": { + "@tsparticles/engine": "3.8.0" + } + }, + "node_modules/@tsparticles/interaction-particles-collisions": { + "version": "3.8.0", + "license": "MIT", + "dependencies": { + "@tsparticles/engine": "3.8.0" + } + }, + "node_modules/@tsparticles/interaction-particles-links": { + "version": "3.8.0", + "license": "MIT", + "dependencies": { + "@tsparticles/engine": "3.8.0" + } + }, + "node_modules/@tsparticles/move-base": { + "version": "3.8.0", + "license": "MIT", + "dependencies": { + "@tsparticles/engine": "3.8.0" + } + }, + "node_modules/@tsparticles/move-parallax": { + "version": "3.8.0", + "license": "MIT", + "dependencies": { + "@tsparticles/engine": "3.8.0" + } + }, + "node_modules/@tsparticles/plugin-absorbers": { + "version": "3.8.0", + "license": "MIT", + "dependencies": { + "@tsparticles/engine": "3.8.0" + } + }, + "node_modules/@tsparticles/plugin-easing-quad": { + "version": "3.8.0", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/matteobruni" + }, + { + "type": "github", + "url": "https://github.com/sponsors/tsparticles" + }, + { + "type": "buymeacoffee", + "url": "https://www.buymeacoffee.com/matteobruni" + } + ], + "license": "MIT", + "dependencies": { + "@tsparticles/engine": "3.8.0" + } + }, + "node_modules/@tsparticles/plugin-emitters": { + "version": "3.8.0", + "license": "MIT", + "dependencies": { + "@tsparticles/engine": "3.8.0" + } + }, + "node_modules/@tsparticles/plugin-emitters-shape-circle": { + "version": "3.8.0", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/matteobruni" + }, + { + "type": "github", + "url": "https://github.com/sponsors/tsparticles" + }, + { + "type": "buymeacoffee", + "url": "https://www.buymeacoffee.com/matteobruni" + } + ], + "license": "MIT", + "dependencies": { + "@tsparticles/engine": "3.8.0", + "@tsparticles/plugin-emitters": "3.8.0" + } + }, + "node_modules/@tsparticles/plugin-emitters-shape-square": { + "version": "3.8.0", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/matteobruni" + }, + { + "type": "github", + "url": "https://github.com/sponsors/tsparticles" + }, + { + "type": "buymeacoffee", + "url": "https://www.buymeacoffee.com/matteobruni" + } + ], + "license": "MIT", + "dependencies": { + "@tsparticles/engine": "3.8.0", + "@tsparticles/plugin-emitters": "3.8.0" + } + }, + "node_modules/@tsparticles/plugin-hex-color": { + "version": "3.8.0", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/matteobruni" + }, + { + "type": "github", + "url": "https://github.com/sponsors/tsparticles" + }, + { + "type": "buymeacoffee", + "url": "https://www.buymeacoffee.com/matteobruni" + } + ], + "license": "MIT", + "dependencies": { + "@tsparticles/engine": "3.8.0" + } + }, + "node_modules/@tsparticles/plugin-hsl-color": { + "version": "3.8.0", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/matteobruni" + }, + { + "type": "github", + "url": "https://github.com/sponsors/tsparticles" + }, + { + "type": "buymeacoffee", + "url": "https://www.buymeacoffee.com/matteobruni" + } + ], + "license": "MIT", + "dependencies": { + "@tsparticles/engine": "3.8.0" + } + }, + "node_modules/@tsparticles/plugin-rgb-color": { + "version": "3.8.0", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/matteobruni" + }, + { + "type": "github", + "url": "https://github.com/sponsors/tsparticles" + }, + { + "type": "buymeacoffee", + "url": "https://www.buymeacoffee.com/matteobruni" + } + ], + "license": "MIT", + "dependencies": { + "@tsparticles/engine": "3.8.0" + } + }, + "node_modules/@tsparticles/shape-circle": { + "version": "3.8.0", + "license": "MIT", + "dependencies": { + "@tsparticles/engine": "3.8.0" + } + }, + "node_modules/@tsparticles/shape-emoji": { + "version": "3.8.0", + "license": "MIT", + "dependencies": { + "@tsparticles/engine": "3.8.0" + } + }, + "node_modules/@tsparticles/shape-image": { + "version": "3.8.0", + "license": "MIT", + "dependencies": { + "@tsparticles/engine": "3.8.0" + } + }, + "node_modules/@tsparticles/shape-line": { + "version": "3.8.0", + "license": "MIT", + "dependencies": { + "@tsparticles/engine": "3.8.0" + } + }, + "node_modules/@tsparticles/shape-polygon": { + "version": "3.8.0", + "license": "MIT", + "dependencies": { + "@tsparticles/engine": "3.8.0" + } + }, + "node_modules/@tsparticles/shape-square": { + "version": "3.8.0", + "license": "MIT", + "dependencies": { + "@tsparticles/engine": "3.8.0" + } + }, + "node_modules/@tsparticles/shape-star": { + "version": "3.8.0", + "license": "MIT", + "dependencies": { + "@tsparticles/engine": "3.8.0" + } + }, + "node_modules/@tsparticles/shape-text": { + "version": "3.8.0", + "license": "MIT", + "dependencies": { + "@tsparticles/engine": "3.8.0" + } + }, + "node_modules/@tsparticles/slim": { + "version": "3.8.0", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/matteobruni" + }, + { + "type": "github", + "url": "https://github.com/sponsors/tsparticles" + }, + { + "type": "buymeacoffee", + "url": "https://www.buymeacoffee.com/matteobruni" + } + ], + "license": "MIT", + "dependencies": { + "@tsparticles/basic": "3.8.0", + "@tsparticles/engine": "3.8.0", + "@tsparticles/interaction-external-attract": "3.8.0", + "@tsparticles/interaction-external-bounce": "3.8.0", + "@tsparticles/interaction-external-bubble": "3.8.0", + "@tsparticles/interaction-external-connect": "3.8.0", + "@tsparticles/interaction-external-grab": "3.8.0", + "@tsparticles/interaction-external-pause": "3.8.0", + "@tsparticles/interaction-external-push": "3.8.0", + "@tsparticles/interaction-external-remove": "3.8.0", + "@tsparticles/interaction-external-repulse": "3.8.0", + "@tsparticles/interaction-external-slow": "3.8.0", + "@tsparticles/interaction-particles-attract": "3.8.0", + "@tsparticles/interaction-particles-collisions": "3.8.0", + "@tsparticles/interaction-particles-links": "3.8.0", + "@tsparticles/move-parallax": "3.8.0", + "@tsparticles/plugin-easing-quad": "3.8.0", + "@tsparticles/shape-emoji": "3.8.0", + "@tsparticles/shape-image": "3.8.0", + "@tsparticles/shape-line": "3.8.0", + "@tsparticles/shape-polygon": "3.8.0", + "@tsparticles/shape-square": "3.8.0", + "@tsparticles/shape-star": "3.8.0", + "@tsparticles/updater-life": "3.8.0", + "@tsparticles/updater-rotate": "3.8.0", + "@tsparticles/updater-stroke-color": "3.8.0" + } + }, + "node_modules/@tsparticles/updater-color": { + "version": "3.8.0", + "license": "MIT", + "dependencies": { + "@tsparticles/engine": "3.8.0" + } + }, + "node_modules/@tsparticles/updater-destroy": { + "version": "3.8.0", + "license": "MIT", + "dependencies": { + "@tsparticles/engine": "3.8.0" + } + }, + "node_modules/@tsparticles/updater-life": { + "version": "3.8.0", + "license": "MIT", + "dependencies": { + "@tsparticles/engine": "3.8.0" + } + }, + "node_modules/@tsparticles/updater-opacity": { + "version": "3.8.0", + "license": "MIT", + "dependencies": { + "@tsparticles/engine": "3.8.0" + } + }, + "node_modules/@tsparticles/updater-out-modes": { + "version": "3.8.0", + "license": "MIT", + "dependencies": { + "@tsparticles/engine": "3.8.0" + } + }, + "node_modules/@tsparticles/updater-roll": { + "version": "3.8.0", + "license": "MIT", + "dependencies": { + "@tsparticles/engine": "3.8.0" + } + }, + "node_modules/@tsparticles/updater-rotate": { + "version": "3.8.0", + "license": "MIT", + "dependencies": { + "@tsparticles/engine": "3.8.0" + } + }, + "node_modules/@tsparticles/updater-size": { + "version": "3.8.0", + "license": "MIT", + "dependencies": { + "@tsparticles/engine": "3.8.0" + } + }, + "node_modules/@tsparticles/updater-stroke-color": { + "version": "3.8.0", + "license": "MIT", + "dependencies": { + "@tsparticles/engine": "3.8.0" + } + }, + "node_modules/@tsparticles/updater-tilt": { + "version": "3.8.0", + "license": "MIT", + "dependencies": { + "@tsparticles/engine": "3.8.0" + } + }, + "node_modules/@tsparticles/updater-twinkle": { + "version": "3.8.0", + "license": "MIT", + "dependencies": { + "@tsparticles/engine": "3.8.0" + } + }, + "node_modules/@tsparticles/updater-wobble": { + "version": "3.8.0", + "license": "MIT", + "dependencies": { + "@tsparticles/engine": "3.8.0" + } + }, + "node_modules/@types/cookie": { + "version": "0.6.0", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.6", + "license": "MIT" + }, + "node_modules/@types/geojson": { + "version": "7946.0.15", + "license": "MIT" + }, + "node_modules/@types/geojson-vt": { + "version": "3.2.5", + "license": "MIT", + "dependencies": { + "@types/geojson": "*" + } + }, + "node_modules/@types/leaflet": { + "version": "1.9.16", + "license": "MIT", + "dependencies": { + "@types/geojson": "*" + } + }, + "node_modules/@types/mapbox__point-geometry": { + "version": "0.1.4", + "license": "MIT" + }, + "node_modules/@types/mapbox__vector-tile": { + "version": "1.3.4", + "license": "MIT", + "dependencies": { + "@types/geojson": "*", + "@types/mapbox__point-geometry": "*", + "@types/pbf": "*" + } + }, + "node_modules/@types/node": { + "version": "22.10.9", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.20.0" + } + }, + "node_modules/@types/pbf": { + "version": "3.0.5", + "license": "MIT" + }, + "node_modules/@types/pug": { + "version": "2.0.10", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/qrcode": { + "version": "1.5.5", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/resolve": { + "version": "1.20.2", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/supercluster": { + "version": "7.1.3", + "license": "MIT", + "dependencies": { + "@types/geojson": "*" + } + }, + "node_modules/@vercel/nft": { + "version": "0.29.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@mapbox/node-pre-gyp": "^2.0.0-rc.0", + "@rollup/pluginutils": "^5.1.3", + "acorn": "^8.6.0", + "acorn-import-attributes": "^1.9.5", + "async-sema": "^3.1.1", + "bindings": "^1.4.0", + "estree-walker": "2.0.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "node-gyp-build": "^4.2.2", + "picomatch": "^4.0.2", + "resolve-from": "^5.0.0" + }, + "bin": { + "nft": "out/cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/abbrev": { + "version": "3.0.0", + "dev": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/acorn": { + "version": "8.14.0", + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-import-attributes": { + "version": "1.9.5", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^8" + } + }, + "node_modules/agent-base": { + "version": "7.1.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/any-promise": { + "version": "1.3.0", + "dev": true, + "license": "MIT" + }, + "node_modules/anymatch": { + "version": "3.1.3", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/anymatch/node_modules/picomatch": { + "version": "2.3.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/arg": { + "version": "5.0.2", + "dev": true, + "license": "MIT" + }, + "node_modules/aria-query": { + "version": "5.3.2", + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/async-sema": { + "version": "3.1.1", + "dev": true, + "license": "MIT" + }, + "node_modules/autoprefixer": { + "version": "10.4.20", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "browserslist": "^4.23.3", + "caniuse-lite": "^1.0.30001646", + "fraction.js": "^4.3.7", + "normalize-range": "^0.1.2", + "picocolors": "^1.0.1", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/axobject-query": { + "version": "4.1.0", + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "dev": true, + "license": "MIT" + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bindings": { + "version": "1.5.0", + "dev": true, + "license": "MIT", + "dependencies": { + "file-uri-to-path": "1.0.0" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.24.4", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "caniuse-lite": "^1.0.30001688", + "electron-to-chromium": "^1.5.73", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.1" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer-crc32": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase-css": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001695", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chokidar": { + "version": "3.6.0", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chownr": { + "version": "3.0.0", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/cli-color": { + "version": "2.0.4", + "license": "ISC", + "dependencies": { + "d": "^1.0.1", + "es5-ext": "^0.10.64", + "es6-iterator": "^2.0.3", + "memoizee": "^0.4.15", + "timers-ext": "^0.1.7" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/cliui": { + "version": "6.0.0", + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "node_modules/code-red": { + "version": "1.0.4", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.4.15", + "@types/estree": "^1.0.1", + "acorn": "^8.10.0", + "estree-walker": "^3.0.3", + "periscopic": "^3.1.0" + } + }, + "node_modules/code-red/node_modules/estree-walker": { + "version": "3.0.3", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "license": "MIT" + }, + "node_modules/commander": { + "version": "4.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/commondir": { + "version": "1.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/confbox": { + "version": "0.1.8", + "dev": true, + "license": "MIT" + }, + "node_modules/consola": { + "version": "3.4.0", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.18.0 || >=16.10.0" + } + }, + "node_modules/cookie": { + "version": "0.6.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/cross-spawn/node_modules/isexe": { + "version": "2.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/cross-spawn/node_modules/which": { + "version": "2.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/css-selector-tokenizer": { + "version": "0.8.0", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "fastparse": "^1.1.2" + } + }, + "node_modules/css-tree": { + "version": "2.3.1", + "license": "MIT", + "dependencies": { + "mdn-data": "2.0.30", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/culori": { + "version": "3.3.0", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/d": { + "version": "1.0.2", + "license": "ISC", + "dependencies": { + "es5-ext": "^0.10.64", + "type": "^2.7.2" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/d3-array": { + "version": "3.2.4", + "license": "ISC", + "dependencies": { + "internmap": "1 - 2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-geo": { + "version": "3.1.1", + "license": "ISC", + "dependencies": { + "d3-array": "2.5.0 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/daisyui": { + "version": "4.12.23", + "dev": true, + "license": "MIT", + "dependencies": { + "css-selector-tokenizer": "^0.8", + "culori": "^3", + "picocolors": "^1", + "postcss-js": "^4" + }, + "engines": { + "node": ">=16.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/daisyui" + } + }, + "node_modules/debug": { + "version": "4.4.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decamelize": { + "version": "1.2.0", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decimal.js": { + "version": "10.4.3", + "license": "MIT" + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/dequal": { + "version": "2.0.3", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/detect-indent": { + "version": "6.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/detect-libc": { + "version": "2.0.3", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/devalue": { + "version": "5.1.1", + "dev": true, + "license": "MIT" + }, + "node_modules/didyoumean": { + "version": "1.2.2", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/dijkstrajs": { + "version": "1.0.3", + "license": "MIT" + }, + "node_modules/dlv": { + "version": "1.1.3", + "dev": true, + "license": "MIT" + }, + "node_modules/earcut": { + "version": "3.0.1", + "license": "ISC" + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "dev": true, + "license": "MIT" + }, + "node_modules/electron-to-chromium": { + "version": "1.5.86", + "dev": true, + "license": "ISC" + }, + "node_modules/emoji-picker-element": { + "version": "1.26.0", + "license": "Apache-2.0" + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "license": "MIT" + }, + "node_modules/es5-ext": { + "version": "0.10.64", + "hasInstallScript": true, + "license": "ISC", + "dependencies": { + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.3", + "esniff": "^2.0.1", + "next-tick": "^1.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/es6-iterator": { + "version": "2.0.3", + "license": "MIT", + "dependencies": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "node_modules/es6-promise": { + "version": "3.3.1", + "dev": true, + "license": "MIT" + }, + "node_modules/es6-symbol": { + "version": "3.1.4", + "license": "ISC", + "dependencies": { + "d": "^1.0.2", + "ext": "^1.7.0" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/es6-weak-map": { + "version": "2.0.3", + "license": "ISC", + "dependencies": { + "d": "1", + "es5-ext": "^0.10.46", + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.1" + } + }, + "node_modules/esbuild": { + "version": "0.24.2", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.24.2", + "@esbuild/android-arm": "0.24.2", + "@esbuild/android-arm64": "0.24.2", + "@esbuild/android-x64": "0.24.2", + "@esbuild/darwin-arm64": "0.24.2", + "@esbuild/darwin-x64": "0.24.2", + "@esbuild/freebsd-arm64": "0.24.2", + "@esbuild/freebsd-x64": "0.24.2", + "@esbuild/linux-arm": "0.24.2", + "@esbuild/linux-arm64": "0.24.2", + "@esbuild/linux-ia32": "0.24.2", + "@esbuild/linux-loong64": "0.24.2", + "@esbuild/linux-mips64el": "0.24.2", + "@esbuild/linux-ppc64": "0.24.2", + "@esbuild/linux-riscv64": "0.24.2", + "@esbuild/linux-s390x": "0.24.2", + "@esbuild/linux-x64": "0.24.2", + "@esbuild/netbsd-arm64": "0.24.2", + "@esbuild/netbsd-x64": "0.24.2", + "@esbuild/openbsd-arm64": "0.24.2", + "@esbuild/openbsd-x64": "0.24.2", + "@esbuild/sunos-x64": "0.24.2", + "@esbuild/win32-arm64": "0.24.2", + "@esbuild/win32-ia32": "0.24.2", + "@esbuild/win32-x64": "0.24.2" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/esm-env": { + "version": "1.2.2", + "dev": true, + "license": "MIT" + }, + "node_modules/esniff": { + "version": "2.0.1", + "license": "ISC", + "dependencies": { + "d": "^1.0.1", + "es5-ext": "^0.10.62", + "event-emitter": "^0.3.5", + "type": "^2.7.2" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "license": "MIT" + }, + "node_modules/event-emitter": { + "version": "0.3.5", + "license": "MIT", + "dependencies": { + "d": "1", + "es5-ext": "~0.10.14" + } + }, + "node_modules/ext": { + "version": "1.7.0", + "license": "ISC", + "dependencies": { + "type": "^2.7.2" + } + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fastparse": { + "version": "1.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/fastq": { + "version": "1.18.0", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fdir": { + "version": "6.4.3", + "dev": true, + "license": "MIT", + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fflate": { + "version": "0.8.2", + "license": "MIT" + }, + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/fill-range": { + "version": "7.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "4.1.0", + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/foreground-child": { + "version": "3.3.0", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/fraction.js": { + "version": "4.3.7", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + }, + "funding": { + "type": "patreon", + "url": "https://github.com/sponsors/rawify" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/function-bind": { + "version": "1.1.2", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/geojson-vt": { + "version": "4.0.2", + "license": "ISC" + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gl-matrix": { + "version": "3.4.3", + "license": "MIT" + }, + "node_modules/glob": { + "version": "7.2.3", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/global-prefix": { + "version": "4.0.0", + "license": "MIT", + "dependencies": { + "ini": "^4.1.3", + "kind-of": "^6.0.3", + "which": "^4.0.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/globals": { + "version": "15.14.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globalyzer": { + "version": "0.1.0", + "license": "MIT" + }, + "node_modules/globrex": { + "version": "0.1.2", + "license": "MIT" + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "dev": true, + "license": "ISC" + }, + "node_modules/gsap": { + "version": "3.12.7", + "license": "Standard 'no charge' license: https://gsap.com/standard-license. Club GSAP members get more: https://gsap.com/licensing/. Why GreenSock doesn't employ an MIT license: https://gsap.com/why-license/" + }, + "node_modules/hasown": { + "version": "2.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/import-meta-resolve": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "dev": true, + "license": "ISC" + }, + "node_modules/ini": { + "version": "4.1.3", + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/internmap": { + "version": "2.0.3", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/intl-messageformat": { + "version": "10.7.14", + "license": "BSD-3-Clause", + "dependencies": { + "@formatjs/ecma402-abstract": "2.3.2", + "@formatjs/fast-memoize": "2.2.6", + "@formatjs/icu-messageformat-parser": "2.11.0", + "tslib": "2" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-module": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/is-number": { + "version": "7.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-promise": { + "version": "2.2.2", + "license": "MIT" + }, + "node_modules/is-reference": { + "version": "1.2.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "*" + } + }, + "node_modules/isexe": { + "version": "3.1.1", + "license": "ISC", + "engines": { + "node": ">=16" + } + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jiti": { + "version": "1.21.7", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "bin/jiti.js" + } + }, + "node_modules/json-stringify-pretty-compact": { + "version": "4.0.0", + "license": "MIT" + }, + "node_modules/just-compare": { + "version": "2.3.0", + "license": "MIT" + }, + "node_modules/just-flush": { + "version": "2.3.0", + "license": "MIT" + }, + "node_modules/kdbush": { + "version": "4.0.2", + "license": "ISC" + }, + "node_modules/kind-of": { + "version": "6.0.3", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/kleur": { + "version": "4.1.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/kolorist": { + "version": "1.8.0", + "dev": true, + "license": "MIT" + }, + "node_modules/lilconfig": { + "version": "3.1.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "dev": true, + "license": "MIT" + }, + "node_modules/local-pkg": { + "version": "0.5.1", + "dev": true, + "license": "MIT", + "dependencies": { + "mlly": "^1.7.3", + "pkg-types": "^1.2.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/locate-character": { + "version": "3.0.0", + "license": "MIT" + }, + "node_modules/locate-path": { + "version": "5.0.0", + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lodash.castarray": { + "version": "4.4.0", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "dev": true, + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "10.4.3", + "dev": true, + "license": "ISC" + }, + "node_modules/lru-queue": { + "version": "0.1.0", + "license": "MIT", + "dependencies": { + "es5-ext": "~0.10.2" + } + }, + "node_modules/magic-string": { + "version": "0.30.17", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0" + } + }, + "node_modules/maplibre-gl": { + "version": "4.7.1", + "license": "BSD-3-Clause", + "dependencies": { + "@mapbox/geojson-rewind": "^0.5.2", + "@mapbox/jsonlint-lines-primitives": "^2.0.2", + "@mapbox/point-geometry": "^0.1.0", + "@mapbox/tiny-sdf": "^2.0.6", + "@mapbox/unitbezier": "^0.0.1", + "@mapbox/vector-tile": "^1.3.1", + "@mapbox/whoots-js": "^3.1.0", + "@maplibre/maplibre-gl-style-spec": "^20.3.1", + "@types/geojson": "^7946.0.14", + "@types/geojson-vt": "3.2.5", + "@types/mapbox__point-geometry": "^0.1.4", + "@types/mapbox__vector-tile": "^1.3.4", + "@types/pbf": "^3.0.5", + "@types/supercluster": "^7.1.3", + "earcut": "^3.0.0", + "geojson-vt": "^4.0.2", + "gl-matrix": "^3.4.3", + "global-prefix": "^4.0.0", + "kdbush": "^4.0.2", + "murmurhash-js": "^1.0.0", + "pbf": "^3.3.0", + "potpack": "^2.0.0", + "quickselect": "^3.0.0", + "supercluster": "^8.0.1", + "tinyqueue": "^3.0.0", + "vt-pbf": "^3.1.3" + }, + "engines": { + "node": ">=16.14.0", + "npm": ">=8.1.0" + }, + "funding": { + "url": "https://github.com/maplibre/maplibre-gl-js?sponsor=1" + } + }, + "node_modules/marked": { + "version": "15.0.6", + "license": "MIT", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/mdn-data": { + "version": "2.0.30", + "license": "CC0-1.0" + }, + "node_modules/memoizee": { + "version": "0.4.17", + "license": "ISC", + "dependencies": { + "d": "^1.0.2", + "es5-ext": "^0.10.64", + "es6-weak-map": "^2.0.3", + "event-emitter": "^0.3.5", + "is-promise": "^2.2.2", + "lru-queue": "^0.1.0", + "next-tick": "^1.1.0", + "timers-ext": "^0.1.7" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/min-indent": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/minizlib": { + "version": "3.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.0.4", + "rimraf": "^5.0.5" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/minizlib/node_modules/brace-expansion": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/minizlib/node_modules/glob": { + "version": "10.4.5", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minizlib/node_modules/minimatch": { + "version": "9.0.5", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minizlib/node_modules/rimraf": { + "version": "5.0.10", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^10.3.7" + }, + "bin": { + "rimraf": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mkdirp": { + "version": "0.5.6", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/mlly": { + "version": "1.7.4", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.14.0", + "pathe": "^2.0.1", + "pkg-types": "^1.3.0", + "ufo": "^1.5.4" + } + }, + "node_modules/mri": { + "version": "1.2.0", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/mrmime": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "dev": true, + "license": "MIT" + }, + "node_modules/murmurhash-js": { + "version": "1.0.0", + "license": "MIT" + }, + "node_modules/mz": { + "version": "2.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.8", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/next-tick": { + "version": "1.1.0", + "license": "ISC" + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-gyp-build": { + "version": "4.8.4", + "dev": true, + "license": "MIT", + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, + "node_modules/node-releases": { + "version": "2.0.19", + "dev": true, + "license": "MIT" + }, + "node_modules/nopt": { + "version": "8.1.0", + "dev": true, + "license": "ISC", + "dependencies": { + "abbrev": "^3.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-range": { + "version": "0.1.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/once": { + "version": "1.4.0", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/p-limit": { + "version": "2.3.0", + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "dev": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/package-manager-detector": { + "version": "0.2.8", + "dev": true, + "license": "MIT" + }, + "node_modules/path-exists": { + "version": "4.0.0", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "dev": true, + "license": "MIT" + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/pathe": { + "version": "2.0.2", + "dev": true, + "license": "MIT" + }, + "node_modules/pbf": { + "version": "3.3.0", + "license": "BSD-3-Clause", + "dependencies": { + "ieee754": "^1.1.12", + "resolve-protobuf-schema": "^2.1.0" + }, + "bin": { + "pbf": "bin/pbf" + } + }, + "node_modules/periscopic": { + "version": "3.1.0", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^3.0.0", + "is-reference": "^3.0.0" + } + }, + "node_modules/periscopic/node_modules/estree-walker": { + "version": "3.0.3", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/periscopic/node_modules/is-reference": { + "version": "3.0.3", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.6" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "2.3.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pirates": { + "version": "4.0.6", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-types": { + "version": "1.3.1", + "dev": true, + "license": "MIT", + "dependencies": { + "confbox": "^0.1.8", + "mlly": "^1.7.4", + "pathe": "^2.0.1" + } + }, + "node_modules/pmtiles": { + "version": "3.2.1", + "license": "BSD-3-Clause", + "dependencies": { + "@types/leaflet": "^1.9.8", + "fflate": "^0.8.0" + } + }, + "node_modules/pngjs": { + "version": "5.0.0", + "license": "MIT", + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/postcss": { + "version": "8.5.1", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.8", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-import": { + "version": "15.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-js": { + "version": "4.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "camelcase-css": "^2.0.1" + }, + "engines": { + "node": "^12 || ^14 || >= 16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.4.21" + } + }, + "node_modules/postcss-load-config": { + "version": "4.0.2", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "lilconfig": "^3.0.0", + "yaml": "^2.3.4" + }, + "engines": { + "node": ">= 14" + }, + "peerDependencies": { + "postcss": ">=8.0.9", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "postcss": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/postcss-nested": { + "version": "6.2.0", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "^6.1.1" + }, + "engines": { + "node": ">=12.0" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/postcss-nested/node_modules/postcss-selector-parser": { + "version": "6.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.0.10", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "dev": true, + "license": "MIT" + }, + "node_modules/potpack": { + "version": "2.0.0", + "license": "ISC" + }, + "node_modules/prettier": { + "version": "3.4.2", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-plugin-svelte": { + "version": "3.3.3", + "dev": true, + "license": "MIT", + "peerDependencies": { + "prettier": "^3.0.0", + "svelte": "^3.2.0 || ^4.0.0-next.0 || ^5.0.0-next.0" + } + }, + "node_modules/protocol-buffers-schema": { + "version": "3.6.0", + "license": "MIT" + }, + "node_modules/qrcode": { + "version": "1.5.4", + "license": "MIT", + "dependencies": { + "dijkstrajs": "^1.0.1", + "pngjs": "^5.0.0", + "yargs": "^15.3.1" + }, + "bin": { + "qrcode": "bin/qrcode" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/quickselect": { + "version": "3.0.0", + "license": "ISC" + }, + "node_modules/read-cache": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "pify": "^2.3.0" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/readdirp/node_modules/picomatch": { + "version": "2.3.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-main-filename": { + "version": "2.0.0", + "license": "ISC" + }, + "node_modules/resolve": { + "version": "1.22.10", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-protobuf-schema": { + "version": "2.1.0", + "license": "MIT", + "dependencies": { + "protocol-buffers-schema": "^3.3.1" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "2.7.1", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/rollup": { + "version": "4.31.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.6" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.31.0", + "@rollup/rollup-android-arm64": "4.31.0", + "@rollup/rollup-darwin-arm64": "4.31.0", + "@rollup/rollup-darwin-x64": "4.31.0", + "@rollup/rollup-freebsd-arm64": "4.31.0", + "@rollup/rollup-freebsd-x64": "4.31.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.31.0", + "@rollup/rollup-linux-arm-musleabihf": "4.31.0", + "@rollup/rollup-linux-arm64-gnu": "4.31.0", + "@rollup/rollup-linux-arm64-musl": "4.31.0", + "@rollup/rollup-linux-loongarch64-gnu": "4.31.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.31.0", + "@rollup/rollup-linux-riscv64-gnu": "4.31.0", + "@rollup/rollup-linux-s390x-gnu": "4.31.0", + "@rollup/rollup-linux-x64-gnu": "4.31.0", + "@rollup/rollup-linux-x64-musl": "4.31.0", + "@rollup/rollup-win32-arm64-msvc": "4.31.0", + "@rollup/rollup-win32-ia32-msvc": "4.31.0", + "@rollup/rollup-win32-x64-msvc": "4.31.0", + "fsevents": "~2.3.2" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/rw": { + "version": "1.3.3", + "license": "BSD-3-Clause" + }, + "node_modules/sade": { + "version": "1.8.1", + "license": "MIT", + "dependencies": { + "mri": "^1.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/sander": { + "version": "0.5.1", + "dev": true, + "license": "MIT", + "dependencies": { + "es6-promise": "^3.1.2", + "graceful-fs": "^4.1.3", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.2" + } + }, + "node_modules/semver": { + "version": "7.6.3", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "license": "ISC" + }, + "node_modules/set-cookie-parser": { + "version": "2.7.1", + "dev": true, + "license": "MIT" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/sirv": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@polka/url": "^1.0.0-next.24", + "mrmime": "^2.0.0", + "totalist": "^3.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/sorcery": { + "version": "0.11.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.4.14", + "buffer-crc32": "^1.0.0", + "minimist": "^1.2.0", + "sander": "^0.5.0" + }, + "bin": { + "sorcery": "bin/sorcery" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-indent": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "min-indent": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/sucrase": { + "version": "3.35.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "glob": "^10.3.10", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/sucrase/node_modules/brace-expansion": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/sucrase/node_modules/glob": { + "version": "10.4.5", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/sucrase/node_modules/minimatch": { + "version": "9.0.5", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/supercluster": { + "version": "8.0.1", + "license": "ISC", + "dependencies": { + "kdbush": "^4.0.2" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/svelte": { + "version": "4.2.19", + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.2.1", + "@jridgewell/sourcemap-codec": "^1.4.15", + "@jridgewell/trace-mapping": "^0.3.18", + "@types/estree": "^1.0.1", + "acorn": "^8.9.0", + "aria-query": "^5.3.0", + "axobject-query": "^4.0.0", + "code-red": "^1.0.3", + "css-tree": "^2.3.1", + "estree-walker": "^3.0.3", + "is-reference": "^3.0.1", + "locate-character": "^3.0.0", + "magic-string": "^0.30.4", + "periscopic": "^3.1.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/svelte-check": { + "version": "3.8.6", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.17", + "chokidar": "^3.4.1", + "picocolors": "^1.0.0", + "sade": "^1.7.4", + "svelte-preprocess": "^5.1.3", + "typescript": "^5.0.3" + }, + "bin": { + "svelte-check": "bin/svelte-check" + }, + "peerDependencies": { + "svelte": "^3.55.0 || ^4.0.0-next.0 || ^4.0.0 || ^5.0.0-next.0" + } + }, + "node_modules/svelte-hmr": { + "version": "0.16.0", + "dev": true, + "license": "ISC", + "engines": { + "node": "^12.20 || ^14.13.1 || >= 16" + }, + "peerDependencies": { + "svelte": "^3.19.0 || ^4.0.0" + } + }, + "node_modules/svelte-i18n": { + "version": "4.0.1", + "license": "MIT", + "dependencies": { + "cli-color": "^2.0.3", + "deepmerge": "^4.2.2", + "esbuild": "^0.19.2", + "estree-walker": "^2", + "intl-messageformat": "^10.5.3", + "sade": "^1.8.1", + "tiny-glob": "^0.2.9" + }, + "bin": { + "svelte-i18n": "dist/cli.js" + }, + "engines": { + "node": ">= 16" + }, + "peerDependencies": { + "svelte": "^3 || ^4 || ^5" + } + }, + "node_modules/svelte-i18n/node_modules/@esbuild/linux-x64": { + "version": "0.19.12", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/svelte-i18n/node_modules/esbuild": { + "version": "0.19.12", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.19.12", + "@esbuild/android-arm": "0.19.12", + "@esbuild/android-arm64": "0.19.12", + "@esbuild/android-x64": "0.19.12", + "@esbuild/darwin-arm64": "0.19.12", + "@esbuild/darwin-x64": "0.19.12", + "@esbuild/freebsd-arm64": "0.19.12", + "@esbuild/freebsd-x64": "0.19.12", + "@esbuild/linux-arm": "0.19.12", + "@esbuild/linux-arm64": "0.19.12", + "@esbuild/linux-ia32": "0.19.12", + "@esbuild/linux-loong64": "0.19.12", + "@esbuild/linux-mips64el": "0.19.12", + "@esbuild/linux-ppc64": "0.19.12", + "@esbuild/linux-riscv64": "0.19.12", + "@esbuild/linux-s390x": "0.19.12", + "@esbuild/linux-x64": "0.19.12", + "@esbuild/netbsd-x64": "0.19.12", + "@esbuild/openbsd-x64": "0.19.12", + "@esbuild/sunos-x64": "0.19.12", + "@esbuild/win32-arm64": "0.19.12", + "@esbuild/win32-ia32": "0.19.12", + "@esbuild/win32-x64": "0.19.12" + } + }, + "node_modules/svelte-maplibre": { + "version": "0.9.14", + "license": "MIT", + "dependencies": { + "d3-geo": "^3.1.0", + "dequal": "^2.0.3", + "just-compare": "^2.3.0", + "just-flush": "^2.3.0", + "maplibre-gl": "^4.0.0", + "pmtiles": "^3.0.3" + }, + "peerDependencies": { + "@deck.gl/core": "^8.8.0", + "@deck.gl/layers": "^8.8.0", + "@deck.gl/mapbox": "^8.8.0", + "svelte": "^3.54.0 || ^4.0.0 || ^5.0.0" + }, + "peerDependenciesMeta": { + "@deck.gl/core": { + "optional": true + }, + "@deck.gl/layers": { + "optional": true + }, + "@deck.gl/mapbox": { + "optional": true + } + } + }, + "node_modules/svelte-preprocess": { + "version": "5.1.4", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "@types/pug": "^2.0.6", + "detect-indent": "^6.1.0", + "magic-string": "^0.30.5", + "sorcery": "^0.11.0", + "strip-indent": "^3.0.0" + }, + "engines": { + "node": ">= 16.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.10.2", + "coffeescript": "^2.5.1", + "less": "^3.11.3 || ^4.0.0", + "postcss": "^7 || ^8", + "postcss-load-config": "^2.1.0 || ^3.0.0 || ^4.0.0 || ^5.0.0", + "pug": "^3.0.0", + "sass": "^1.26.8", + "stylus": "^0.55.0", + "sugarss": "^2.0.0 || ^3.0.0 || ^4.0.0", + "svelte": "^3.23.0 || ^4.0.0-next.0 || ^4.0.0 || ^5.0.0-next.0", + "typescript": ">=3.9.5 || ^4.0.0 || ^5.0.0" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "coffeescript": { + "optional": true + }, + "less": { + "optional": true + }, + "postcss": { + "optional": true + }, + "postcss-load-config": { + "optional": true + }, + "pug": { + "optional": true + }, + "sass": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/svelte/node_modules/estree-walker": { + "version": "3.0.3", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/svelte/node_modules/is-reference": { + "version": "3.0.3", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.6" + } + }, + "node_modules/tailwindcss": { + "version": "3.4.17", + "dev": true, + "license": "MIT", + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "arg": "^5.0.2", + "chokidar": "^3.6.0", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.3.2", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "jiti": "^1.21.6", + "lilconfig": "^3.1.3", + "micromatch": "^4.0.8", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.1.1", + "postcss": "^8.4.47", + "postcss-import": "^15.1.0", + "postcss-js": "^4.0.1", + "postcss-load-config": "^4.0.2", + "postcss-nested": "^6.2.0", + "postcss-selector-parser": "^6.1.2", + "resolve": "^1.22.8", + "sucrase": "^3.35.0" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tailwindcss/node_modules/glob-parent": { + "version": "6.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/tailwindcss/node_modules/postcss-selector-parser": { + "version": "6.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/tar": { + "version": "7.4.3", + "dev": true, + "license": "ISC", + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.0.1", + "mkdirp": "^3.0.1", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/tar/node_modules/mkdirp": { + "version": "3.0.1", + "dev": true, + "license": "MIT", + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/thenify": { + "version": "3.3.1", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "dev": true, + "license": "MIT", + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/timers-ext": { + "version": "0.1.8", + "license": "ISC", + "dependencies": { + "es5-ext": "^0.10.64", + "next-tick": "^1.1.0" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/tiny-glob": { + "version": "0.2.9", + "license": "MIT", + "dependencies": { + "globalyzer": "0.1.0", + "globrex": "^0.1.2" + } + }, + "node_modules/tinyexec": { + "version": "0.3.2", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyqueue": { + "version": "3.0.0", + "license": "ISC" + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/totalist": { + "version": "3.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "dev": true, + "license": "MIT" + }, + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/tslib": { + "version": "2.8.1", + "license": "0BSD" + }, + "node_modules/tsparticles": { + "version": "3.8.0", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/matteobruni" + }, + { + "type": "github", + "url": "https://github.com/sponsors/tsparticles" + }, + { + "type": "buymeacoffee", + "url": "https://www.buymeacoffee.com/matteobruni" + } + ], + "license": "MIT", + "dependencies": { + "@tsparticles/engine": "3.8.0", + "@tsparticles/interaction-external-trail": "3.8.0", + "@tsparticles/plugin-absorbers": "3.8.0", + "@tsparticles/plugin-emitters": "3.8.0", + "@tsparticles/plugin-emitters-shape-circle": "3.8.0", + "@tsparticles/plugin-emitters-shape-square": "3.8.0", + "@tsparticles/shape-text": "3.8.0", + "@tsparticles/slim": "3.8.0", + "@tsparticles/updater-destroy": "3.8.0", + "@tsparticles/updater-roll": "3.8.0", + "@tsparticles/updater-tilt": "3.8.0", + "@tsparticles/updater-twinkle": "3.8.0", + "@tsparticles/updater-wobble": "3.8.0" + } + }, + "node_modules/type": { + "version": "2.7.3", + "license": "ISC" + }, + "node_modules/typescript": { + "version": "5.7.3", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/ufo": { + "version": "1.5.4", + "dev": true, + "license": "MIT" + }, + "node_modules/undici-types": { + "version": "6.20.0", + "dev": true, + "license": "MIT" + }, + "node_modules/unplugin": { + "version": "1.16.1", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.14.0", + "webpack-virtual-modules": "^0.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/unplugin-icons": { + "version": "0.19.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@antfu/install-pkg": "^0.4.1", + "@antfu/utils": "^0.7.10", + "@iconify/utils": "^2.1.29", + "debug": "^4.3.6", + "kolorist": "^1.8.0", + "local-pkg": "^0.5.0", + "unplugin": "^1.12.0" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@svgr/core": ">=7.0.0", + "@svgx/core": "^1.0.1", + "@vue/compiler-sfc": "^3.0.2 || ^2.7.0", + "vue-template-compiler": "^2.6.12", + "vue-template-es2015-compiler": "^1.9.0" + }, + "peerDependenciesMeta": { + "@svgr/core": { + "optional": true + }, + "@svgx/core": { + "optional": true + }, + "@vue/compiler-sfc": { + "optional": true + }, + "vue-template-compiler": { + "optional": true + }, + "vue-template-es2015-compiler": { + "optional": true + } + } + }, + "node_modules/update-browserslist-db": { + "version": "1.1.2", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "dev": true, + "license": "MIT" + }, + "node_modules/vite": { + "version": "5.4.14", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/esbuild": { + "version": "0.21.5", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/vitefu": { + "version": "0.2.5", + "dev": true, + "license": "MIT", + "peerDependencies": { + "vite": "^3.0.0 || ^4.0.0 || ^5.0.0" + }, + "peerDependenciesMeta": { + "vite": { + "optional": true + } + } + }, + "node_modules/vt-pbf": { + "version": "3.1.3", + "license": "MIT", + "dependencies": { + "@mapbox/point-geometry": "0.1.0", + "@mapbox/vector-tile": "^1.3.1", + "pbf": "^3.2.1" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/webpack-virtual-modules": { + "version": "0.6.2", + "dev": true, + "license": "MIT" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/which": { + "version": "4.0.0", + "license": "ISC", + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^16.13.0 || >=18.0.0" + } + }, + "node_modules/which-module": { + "version": "2.0.1", + "license": "ISC" + }, + "node_modules/wrap-ansi": { + "version": "6.2.0", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "dev": true, + "license": "ISC" + }, + "node_modules/y18n": { + "version": "4.0.3", + "license": "ISC" + }, + "node_modules/yallist": { + "version": "5.0.0", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/yaml": { + "version": "2.7.0", + "dev": true, + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/yargs": { + "version": "15.4.1", + "license": "MIT", + "dependencies": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs-parser": { + "version": "18.1.3", + "license": "ISC", + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "engines": { + "node": ">=6" + } + } + } +} diff --git a/frontend/src/routes/collections/[id]/+page.svelte b/frontend/src/routes/collections/[id]/+page.svelte index 3a4c1f7e..01874545 100644 --- a/frontend/src/routes/collections/[id]/+page.svelte +++ b/frontend/src/routes/collections/[id]/+page.svelte @@ -833,14 +833,17 @@ > {#each adventures as adventure} {#if adventure.longitude && adventure.latitude} - + + + {adventure.category?.icon} +
    {adventure.name}

    {adventure.category?.display_name + ' ' + adventure.category?.icon}

    -
    + {/if} {/each} {#each transportations as transportation} From f04a6e30a7b05e12dc83979923b418a69ffdf405 Mon Sep 17 00:00:00 2001 From: Lars Lehmann Date: Thu, 23 Jan 2025 20:14:10 +0100 Subject: [PATCH 127/209] Remove package-lock.json --- frontend/package-lock.json | 4499 ------------------------------------ 1 file changed, 4499 deletions(-) delete mode 100644 frontend/package-lock.json diff --git a/frontend/package-lock.json b/frontend/package-lock.json deleted file mode 100644 index 3748c971..00000000 --- a/frontend/package-lock.json +++ /dev/null @@ -1,4499 +0,0 @@ -{ - "name": "adventurelog-frontend", - "version": "0.8.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "adventurelog-frontend", - "version": "0.8.0", - "dependencies": { - "@lukulent/svelte-umami": "^0.0.3", - "emoji-picker-element": "^1.26.0", - "gsap": "^3.12.7", - "marked": "^15.0.4", - "qrcode": "^1.5.4", - "svelte-i18n": "^4.0.1", - "svelte-maplibre": "^0.9.8", - "tsparticles": "^3.7.1" - }, - "devDependencies": { - "@event-calendar/core": "^3.7.1", - "@event-calendar/day-grid": "^3.7.1", - "@event-calendar/time-grid": "^3.7.1", - "@iconify-json/mdi": "^1.1.67", - "@sveltejs/adapter-auto": "^3.2.2", - "@sveltejs/adapter-node": "^5.2.0", - "@sveltejs/adapter-vercel": "^5.4.1", - "@sveltejs/kit": "^2.8.3", - "@sveltejs/vite-plugin-svelte": "^3.1.1", - "@tailwindcss/typography": "^0.5.13", - "@types/node": "^22.5.4", - "@types/qrcode": "^1.5.5", - "autoprefixer": "^10.4.19", - "daisyui": "^4.12.6", - "postcss": "^8.4.38", - "prettier": "^3.3.2", - "prettier-plugin-svelte": "^3.2.5", - "svelte": "^4.2.19", - "svelte-check": "^3.8.1", - "tailwindcss": "^3.4.4", - "tslib": "^2.6.3", - "typescript": "^5.5.2", - "unplugin-icons": "^0.19.0", - "vite": "^5.3.6" - } - }, - "node_modules/@alloc/quick-lru": { - "version": "5.2.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@ampproject/remapping": { - "version": "2.3.0", - "license": "Apache-2.0", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@antfu/install-pkg": { - "version": "0.4.1", - "dev": true, - "license": "MIT", - "dependencies": { - "package-manager-detector": "^0.2.0", - "tinyexec": "^0.3.0" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" - } - }, - "node_modules/@antfu/utils": { - "version": "0.7.10", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/antfu" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.24.2", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@event-calendar/core": { - "version": "3.8.0", - "dev": true, - "license": "MIT", - "dependencies": { - "svelte": "^4.2.19" - } - }, - "node_modules/@event-calendar/day-grid": { - "version": "3.8.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@event-calendar/core": "~3.8.0", - "svelte": "^4.2.19" - } - }, - "node_modules/@event-calendar/time-grid": { - "version": "3.8.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@event-calendar/core": "~3.8.0", - "svelte": "^4.2.19" - } - }, - "node_modules/@formatjs/ecma402-abstract": { - "version": "2.3.2", - "license": "MIT", - "dependencies": { - "@formatjs/fast-memoize": "2.2.6", - "@formatjs/intl-localematcher": "0.5.10", - "decimal.js": "10", - "tslib": "2" - } - }, - "node_modules/@formatjs/fast-memoize": { - "version": "2.2.6", - "license": "MIT", - "dependencies": { - "tslib": "2" - } - }, - "node_modules/@formatjs/icu-messageformat-parser": { - "version": "2.11.0", - "license": "MIT", - "dependencies": { - "@formatjs/ecma402-abstract": "2.3.2", - "@formatjs/icu-skeleton-parser": "1.8.12", - "tslib": "2" - } - }, - "node_modules/@formatjs/icu-skeleton-parser": { - "version": "1.8.12", - "license": "MIT", - "dependencies": { - "@formatjs/ecma402-abstract": "2.3.2", - "tslib": "2" - } - }, - "node_modules/@formatjs/intl-localematcher": { - "version": "0.5.10", - "license": "MIT", - "dependencies": { - "tslib": "2" - } - }, - "node_modules/@iconify-json/mdi": { - "version": "1.2.3", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@iconify/types": "*" - } - }, - "node_modules/@iconify/types": { - "version": "2.0.0", - "dev": true, - "license": "MIT" - }, - "node_modules/@iconify/utils": { - "version": "2.2.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@antfu/install-pkg": "^0.4.1", - "@antfu/utils": "^0.7.10", - "@iconify/types": "^2.0.0", - "debug": "^4.4.0", - "globals": "^15.13.0", - "kolorist": "^1.8.0", - "local-pkg": "^0.5.1", - "mlly": "^1.7.3" - } - }, - "node_modules/@isaacs/cliui": { - "version": "8.0.2", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@isaacs/cliui/node_modules/ansi-regex": { - "version": "6.1.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/ansi-styles": { - "version": "6.2.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/emoji-regex": { - "version": "9.2.2", - "dev": true, - "license": "MIT" - }, - "node_modules/@isaacs/cliui/node_modules/string-width": { - "version": "5.1.2", - "dev": true, - "license": "MIT", - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@isaacs/cliui/node_modules/strip-ansi": { - "version": "7.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { - "version": "8.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/@isaacs/fs-minipass": { - "version": "4.0.1", - "dev": true, - "license": "ISC", - "dependencies": { - "minipass": "^7.0.4" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.8", - "license": "MIT", - "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.0", - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@lukulent/svelte-umami": { - "version": "0.0.3", - "license": "MIT", - "peerDependencies": { - "svelte": "^4.0.0" - } - }, - "node_modules/@mapbox/geojson-rewind": { - "version": "0.5.2", - "license": "ISC", - "dependencies": { - "get-stream": "^6.0.1", - "minimist": "^1.2.6" - }, - "bin": { - "geojson-rewind": "geojson-rewind" - } - }, - "node_modules/@mapbox/jsonlint-lines-primitives": { - "version": "2.0.2", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/@mapbox/node-pre-gyp": { - "version": "2.0.0-rc.0", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "consola": "^3.2.3", - "detect-libc": "^2.0.0", - "https-proxy-agent": "^7.0.5", - "node-fetch": "^2.6.7", - "nopt": "^8.0.0", - "semver": "^7.5.3", - "tar": "^7.4.0" - }, - "bin": { - "node-pre-gyp": "bin/node-pre-gyp" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@mapbox/point-geometry": { - "version": "0.1.0", - "license": "ISC" - }, - "node_modules/@mapbox/tiny-sdf": { - "version": "2.0.6", - "license": "BSD-2-Clause" - }, - "node_modules/@mapbox/unitbezier": { - "version": "0.0.1", - "license": "BSD-2-Clause" - }, - "node_modules/@mapbox/vector-tile": { - "version": "1.3.1", - "license": "BSD-3-Clause", - "dependencies": { - "@mapbox/point-geometry": "~0.1.0" - } - }, - "node_modules/@mapbox/whoots-js": { - "version": "3.1.0", - "license": "ISC", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@maplibre/maplibre-gl-style-spec": { - "version": "20.4.0", - "license": "ISC", - "dependencies": { - "@mapbox/jsonlint-lines-primitives": "~2.0.2", - "@mapbox/unitbezier": "^0.0.1", - "json-stringify-pretty-compact": "^4.0.0", - "minimist": "^1.2.8", - "quickselect": "^2.0.0", - "rw": "^1.3.3", - "tinyqueue": "^3.0.0" - }, - "bin": { - "gl-style-format": "dist/gl-style-format.mjs", - "gl-style-migrate": "dist/gl-style-migrate.mjs", - "gl-style-validate": "dist/gl-style-validate.mjs" - } - }, - "node_modules/@maplibre/maplibre-gl-style-spec/node_modules/quickselect": { - "version": "2.0.0", - "license": "ISC" - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">=14" - } - }, - "node_modules/@polka/url": { - "version": "1.0.0-next.28", - "dev": true, - "license": "MIT" - }, - "node_modules/@rollup/plugin-commonjs": { - "version": "28.0.2", - "dev": true, - "license": "MIT", - "dependencies": { - "@rollup/pluginutils": "^5.0.1", - "commondir": "^1.0.1", - "estree-walker": "^2.0.2", - "fdir": "^6.2.0", - "is-reference": "1.2.1", - "magic-string": "^0.30.3", - "picomatch": "^4.0.2" - }, - "engines": { - "node": ">=16.0.0 || 14 >= 14.17" - }, - "peerDependencies": { - "rollup": "^2.68.0||^3.0.0||^4.0.0" - }, - "peerDependenciesMeta": { - "rollup": { - "optional": true - } - } - }, - "node_modules/@rollup/plugin-json": { - "version": "6.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@rollup/pluginutils": "^5.1.0" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" - }, - "peerDependenciesMeta": { - "rollup": { - "optional": true - } - } - }, - "node_modules/@rollup/plugin-node-resolve": { - "version": "16.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@rollup/pluginutils": "^5.0.1", - "@types/resolve": "1.20.2", - "deepmerge": "^4.2.2", - "is-module": "^1.0.0", - "resolve": "^1.22.1" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "rollup": "^2.78.0||^3.0.0||^4.0.0" - }, - "peerDependenciesMeta": { - "rollup": { - "optional": true - } - } - }, - "node_modules/@rollup/pluginutils": { - "version": "5.1.4", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "estree-walker": "^2.0.2", - "picomatch": "^4.0.2" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" - }, - "peerDependenciesMeta": { - "rollup": { - "optional": true - } - } - }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.31.0", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@sveltejs/adapter-auto": { - "version": "3.3.1", - "dev": true, - "license": "MIT", - "dependencies": { - "import-meta-resolve": "^4.1.0" - }, - "peerDependencies": { - "@sveltejs/kit": "^2.0.0" - } - }, - "node_modules/@sveltejs/adapter-node": { - "version": "5.2.12", - "dev": true, - "license": "MIT", - "dependencies": { - "@rollup/plugin-commonjs": "^28.0.1", - "@rollup/plugin-json": "^6.1.0", - "@rollup/plugin-node-resolve": "^16.0.0", - "rollup": "^4.9.5" - }, - "peerDependencies": { - "@sveltejs/kit": "^2.4.0" - } - }, - "node_modules/@sveltejs/adapter-vercel": { - "version": "5.5.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@vercel/nft": "^0.29.0", - "esbuild": "^0.24.0" - }, - "peerDependencies": { - "@sveltejs/kit": "^2.4.0" - } - }, - "node_modules/@sveltejs/kit": { - "version": "2.16.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/cookie": "^0.6.0", - "cookie": "^0.6.0", - "devalue": "^5.1.0", - "esm-env": "^1.2.2", - "import-meta-resolve": "^4.1.0", - "kleur": "^4.1.5", - "magic-string": "^0.30.5", - "mrmime": "^2.0.0", - "sade": "^1.8.1", - "set-cookie-parser": "^2.6.0", - "sirv": "^3.0.0" - }, - "bin": { - "svelte-kit": "svelte-kit.js" - }, - "engines": { - "node": ">=18.13" - }, - "peerDependencies": { - "@sveltejs/vite-plugin-svelte": "^3.0.0 || ^4.0.0-next.1 || ^5.0.0", - "svelte": "^4.0.0 || ^5.0.0-next.0", - "vite": "^5.0.3 || ^6.0.0" - } - }, - "node_modules/@sveltejs/vite-plugin-svelte": { - "version": "3.1.2", - "dev": true, - "license": "MIT", - "dependencies": { - "@sveltejs/vite-plugin-svelte-inspector": "^2.1.0", - "debug": "^4.3.4", - "deepmerge": "^4.3.1", - "kleur": "^4.1.5", - "magic-string": "^0.30.10", - "svelte-hmr": "^0.16.0", - "vitefu": "^0.2.5" - }, - "engines": { - "node": "^18.0.0 || >=20" - }, - "peerDependencies": { - "svelte": "^4.0.0 || ^5.0.0-next.0", - "vite": "^5.0.0" - } - }, - "node_modules/@sveltejs/vite-plugin-svelte-inspector": { - "version": "2.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^4.3.4" - }, - "engines": { - "node": "^18.0.0 || >=20" - }, - "peerDependencies": { - "@sveltejs/vite-plugin-svelte": "^3.0.0", - "svelte": "^4.0.0 || ^5.0.0-next.0", - "vite": "^5.0.0" - } - }, - "node_modules/@tailwindcss/typography": { - "version": "0.5.16", - "dev": true, - "license": "MIT", - "dependencies": { - "lodash.castarray": "^4.4.0", - "lodash.isplainobject": "^4.0.6", - "lodash.merge": "^4.6.2", - "postcss-selector-parser": "6.0.10" - }, - "peerDependencies": { - "tailwindcss": ">=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1" - } - }, - "node_modules/@tsparticles/basic": { - "version": "3.8.0", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/matteobruni" - }, - { - "type": "github", - "url": "https://github.com/sponsors/tsparticles" - }, - { - "type": "buymeacoffee", - "url": "https://www.buymeacoffee.com/matteobruni" - } - ], - "license": "MIT", - "dependencies": { - "@tsparticles/engine": "3.8.0", - "@tsparticles/move-base": "3.8.0", - "@tsparticles/plugin-hex-color": "3.8.0", - "@tsparticles/plugin-hsl-color": "3.8.0", - "@tsparticles/plugin-rgb-color": "3.8.0", - "@tsparticles/shape-circle": "3.8.0", - "@tsparticles/updater-color": "3.8.0", - "@tsparticles/updater-opacity": "3.8.0", - "@tsparticles/updater-out-modes": "3.8.0", - "@tsparticles/updater-size": "3.8.0" - } - }, - "node_modules/@tsparticles/engine": { - "version": "3.8.0", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/matteobruni" - }, - { - "type": "github", - "url": "https://github.com/sponsors/tsparticles" - }, - { - "type": "buymeacoffee", - "url": "https://www.buymeacoffee.com/matteobruni" - } - ], - "hasInstallScript": true, - "license": "MIT" - }, - "node_modules/@tsparticles/interaction-external-attract": { - "version": "3.8.0", - "license": "MIT", - "dependencies": { - "@tsparticles/engine": "3.8.0" - } - }, - "node_modules/@tsparticles/interaction-external-bounce": { - "version": "3.8.0", - "license": "MIT", - "dependencies": { - "@tsparticles/engine": "3.8.0" - } - }, - "node_modules/@tsparticles/interaction-external-bubble": { - "version": "3.8.0", - "license": "MIT", - "dependencies": { - "@tsparticles/engine": "3.8.0" - } - }, - "node_modules/@tsparticles/interaction-external-connect": { - "version": "3.8.0", - "license": "MIT", - "dependencies": { - "@tsparticles/engine": "3.8.0" - } - }, - "node_modules/@tsparticles/interaction-external-grab": { - "version": "3.8.0", - "license": "MIT", - "dependencies": { - "@tsparticles/engine": "3.8.0" - } - }, - "node_modules/@tsparticles/interaction-external-pause": { - "version": "3.8.0", - "license": "MIT", - "dependencies": { - "@tsparticles/engine": "3.8.0" - } - }, - "node_modules/@tsparticles/interaction-external-push": { - "version": "3.8.0", - "license": "MIT", - "dependencies": { - "@tsparticles/engine": "3.8.0" - } - }, - "node_modules/@tsparticles/interaction-external-remove": { - "version": "3.8.0", - "license": "MIT", - "dependencies": { - "@tsparticles/engine": "3.8.0" - } - }, - "node_modules/@tsparticles/interaction-external-repulse": { - "version": "3.8.0", - "license": "MIT", - "dependencies": { - "@tsparticles/engine": "3.8.0" - } - }, - "node_modules/@tsparticles/interaction-external-slow": { - "version": "3.8.0", - "license": "MIT", - "dependencies": { - "@tsparticles/engine": "3.8.0" - } - }, - "node_modules/@tsparticles/interaction-external-trail": { - "version": "3.8.0", - "license": "MIT", - "dependencies": { - "@tsparticles/engine": "3.8.0" - } - }, - "node_modules/@tsparticles/interaction-particles-attract": { - "version": "3.8.0", - "license": "MIT", - "dependencies": { - "@tsparticles/engine": "3.8.0" - } - }, - "node_modules/@tsparticles/interaction-particles-collisions": { - "version": "3.8.0", - "license": "MIT", - "dependencies": { - "@tsparticles/engine": "3.8.0" - } - }, - "node_modules/@tsparticles/interaction-particles-links": { - "version": "3.8.0", - "license": "MIT", - "dependencies": { - "@tsparticles/engine": "3.8.0" - } - }, - "node_modules/@tsparticles/move-base": { - "version": "3.8.0", - "license": "MIT", - "dependencies": { - "@tsparticles/engine": "3.8.0" - } - }, - "node_modules/@tsparticles/move-parallax": { - "version": "3.8.0", - "license": "MIT", - "dependencies": { - "@tsparticles/engine": "3.8.0" - } - }, - "node_modules/@tsparticles/plugin-absorbers": { - "version": "3.8.0", - "license": "MIT", - "dependencies": { - "@tsparticles/engine": "3.8.0" - } - }, - "node_modules/@tsparticles/plugin-easing-quad": { - "version": "3.8.0", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/matteobruni" - }, - { - "type": "github", - "url": "https://github.com/sponsors/tsparticles" - }, - { - "type": "buymeacoffee", - "url": "https://www.buymeacoffee.com/matteobruni" - } - ], - "license": "MIT", - "dependencies": { - "@tsparticles/engine": "3.8.0" - } - }, - "node_modules/@tsparticles/plugin-emitters": { - "version": "3.8.0", - "license": "MIT", - "dependencies": { - "@tsparticles/engine": "3.8.0" - } - }, - "node_modules/@tsparticles/plugin-emitters-shape-circle": { - "version": "3.8.0", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/matteobruni" - }, - { - "type": "github", - "url": "https://github.com/sponsors/tsparticles" - }, - { - "type": "buymeacoffee", - "url": "https://www.buymeacoffee.com/matteobruni" - } - ], - "license": "MIT", - "dependencies": { - "@tsparticles/engine": "3.8.0", - "@tsparticles/plugin-emitters": "3.8.0" - } - }, - "node_modules/@tsparticles/plugin-emitters-shape-square": { - "version": "3.8.0", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/matteobruni" - }, - { - "type": "github", - "url": "https://github.com/sponsors/tsparticles" - }, - { - "type": "buymeacoffee", - "url": "https://www.buymeacoffee.com/matteobruni" - } - ], - "license": "MIT", - "dependencies": { - "@tsparticles/engine": "3.8.0", - "@tsparticles/plugin-emitters": "3.8.0" - } - }, - "node_modules/@tsparticles/plugin-hex-color": { - "version": "3.8.0", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/matteobruni" - }, - { - "type": "github", - "url": "https://github.com/sponsors/tsparticles" - }, - { - "type": "buymeacoffee", - "url": "https://www.buymeacoffee.com/matteobruni" - } - ], - "license": "MIT", - "dependencies": { - "@tsparticles/engine": "3.8.0" - } - }, - "node_modules/@tsparticles/plugin-hsl-color": { - "version": "3.8.0", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/matteobruni" - }, - { - "type": "github", - "url": "https://github.com/sponsors/tsparticles" - }, - { - "type": "buymeacoffee", - "url": "https://www.buymeacoffee.com/matteobruni" - } - ], - "license": "MIT", - "dependencies": { - "@tsparticles/engine": "3.8.0" - } - }, - "node_modules/@tsparticles/plugin-rgb-color": { - "version": "3.8.0", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/matteobruni" - }, - { - "type": "github", - "url": "https://github.com/sponsors/tsparticles" - }, - { - "type": "buymeacoffee", - "url": "https://www.buymeacoffee.com/matteobruni" - } - ], - "license": "MIT", - "dependencies": { - "@tsparticles/engine": "3.8.0" - } - }, - "node_modules/@tsparticles/shape-circle": { - "version": "3.8.0", - "license": "MIT", - "dependencies": { - "@tsparticles/engine": "3.8.0" - } - }, - "node_modules/@tsparticles/shape-emoji": { - "version": "3.8.0", - "license": "MIT", - "dependencies": { - "@tsparticles/engine": "3.8.0" - } - }, - "node_modules/@tsparticles/shape-image": { - "version": "3.8.0", - "license": "MIT", - "dependencies": { - "@tsparticles/engine": "3.8.0" - } - }, - "node_modules/@tsparticles/shape-line": { - "version": "3.8.0", - "license": "MIT", - "dependencies": { - "@tsparticles/engine": "3.8.0" - } - }, - "node_modules/@tsparticles/shape-polygon": { - "version": "3.8.0", - "license": "MIT", - "dependencies": { - "@tsparticles/engine": "3.8.0" - } - }, - "node_modules/@tsparticles/shape-square": { - "version": "3.8.0", - "license": "MIT", - "dependencies": { - "@tsparticles/engine": "3.8.0" - } - }, - "node_modules/@tsparticles/shape-star": { - "version": "3.8.0", - "license": "MIT", - "dependencies": { - "@tsparticles/engine": "3.8.0" - } - }, - "node_modules/@tsparticles/shape-text": { - "version": "3.8.0", - "license": "MIT", - "dependencies": { - "@tsparticles/engine": "3.8.0" - } - }, - "node_modules/@tsparticles/slim": { - "version": "3.8.0", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/matteobruni" - }, - { - "type": "github", - "url": "https://github.com/sponsors/tsparticles" - }, - { - "type": "buymeacoffee", - "url": "https://www.buymeacoffee.com/matteobruni" - } - ], - "license": "MIT", - "dependencies": { - "@tsparticles/basic": "3.8.0", - "@tsparticles/engine": "3.8.0", - "@tsparticles/interaction-external-attract": "3.8.0", - "@tsparticles/interaction-external-bounce": "3.8.0", - "@tsparticles/interaction-external-bubble": "3.8.0", - "@tsparticles/interaction-external-connect": "3.8.0", - "@tsparticles/interaction-external-grab": "3.8.0", - "@tsparticles/interaction-external-pause": "3.8.0", - "@tsparticles/interaction-external-push": "3.8.0", - "@tsparticles/interaction-external-remove": "3.8.0", - "@tsparticles/interaction-external-repulse": "3.8.0", - "@tsparticles/interaction-external-slow": "3.8.0", - "@tsparticles/interaction-particles-attract": "3.8.0", - "@tsparticles/interaction-particles-collisions": "3.8.0", - "@tsparticles/interaction-particles-links": "3.8.0", - "@tsparticles/move-parallax": "3.8.0", - "@tsparticles/plugin-easing-quad": "3.8.0", - "@tsparticles/shape-emoji": "3.8.0", - "@tsparticles/shape-image": "3.8.0", - "@tsparticles/shape-line": "3.8.0", - "@tsparticles/shape-polygon": "3.8.0", - "@tsparticles/shape-square": "3.8.0", - "@tsparticles/shape-star": "3.8.0", - "@tsparticles/updater-life": "3.8.0", - "@tsparticles/updater-rotate": "3.8.0", - "@tsparticles/updater-stroke-color": "3.8.0" - } - }, - "node_modules/@tsparticles/updater-color": { - "version": "3.8.0", - "license": "MIT", - "dependencies": { - "@tsparticles/engine": "3.8.0" - } - }, - "node_modules/@tsparticles/updater-destroy": { - "version": "3.8.0", - "license": "MIT", - "dependencies": { - "@tsparticles/engine": "3.8.0" - } - }, - "node_modules/@tsparticles/updater-life": { - "version": "3.8.0", - "license": "MIT", - "dependencies": { - "@tsparticles/engine": "3.8.0" - } - }, - "node_modules/@tsparticles/updater-opacity": { - "version": "3.8.0", - "license": "MIT", - "dependencies": { - "@tsparticles/engine": "3.8.0" - } - }, - "node_modules/@tsparticles/updater-out-modes": { - "version": "3.8.0", - "license": "MIT", - "dependencies": { - "@tsparticles/engine": "3.8.0" - } - }, - "node_modules/@tsparticles/updater-roll": { - "version": "3.8.0", - "license": "MIT", - "dependencies": { - "@tsparticles/engine": "3.8.0" - } - }, - "node_modules/@tsparticles/updater-rotate": { - "version": "3.8.0", - "license": "MIT", - "dependencies": { - "@tsparticles/engine": "3.8.0" - } - }, - "node_modules/@tsparticles/updater-size": { - "version": "3.8.0", - "license": "MIT", - "dependencies": { - "@tsparticles/engine": "3.8.0" - } - }, - "node_modules/@tsparticles/updater-stroke-color": { - "version": "3.8.0", - "license": "MIT", - "dependencies": { - "@tsparticles/engine": "3.8.0" - } - }, - "node_modules/@tsparticles/updater-tilt": { - "version": "3.8.0", - "license": "MIT", - "dependencies": { - "@tsparticles/engine": "3.8.0" - } - }, - "node_modules/@tsparticles/updater-twinkle": { - "version": "3.8.0", - "license": "MIT", - "dependencies": { - "@tsparticles/engine": "3.8.0" - } - }, - "node_modules/@tsparticles/updater-wobble": { - "version": "3.8.0", - "license": "MIT", - "dependencies": { - "@tsparticles/engine": "3.8.0" - } - }, - "node_modules/@types/cookie": { - "version": "0.6.0", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/estree": { - "version": "1.0.6", - "license": "MIT" - }, - "node_modules/@types/geojson": { - "version": "7946.0.15", - "license": "MIT" - }, - "node_modules/@types/geojson-vt": { - "version": "3.2.5", - "license": "MIT", - "dependencies": { - "@types/geojson": "*" - } - }, - "node_modules/@types/leaflet": { - "version": "1.9.16", - "license": "MIT", - "dependencies": { - "@types/geojson": "*" - } - }, - "node_modules/@types/mapbox__point-geometry": { - "version": "0.1.4", - "license": "MIT" - }, - "node_modules/@types/mapbox__vector-tile": { - "version": "1.3.4", - "license": "MIT", - "dependencies": { - "@types/geojson": "*", - "@types/mapbox__point-geometry": "*", - "@types/pbf": "*" - } - }, - "node_modules/@types/node": { - "version": "22.10.9", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.20.0" - } - }, - "node_modules/@types/pbf": { - "version": "3.0.5", - "license": "MIT" - }, - "node_modules/@types/pug": { - "version": "2.0.10", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/qrcode": { - "version": "1.5.5", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/resolve": { - "version": "1.20.2", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/supercluster": { - "version": "7.1.3", - "license": "MIT", - "dependencies": { - "@types/geojson": "*" - } - }, - "node_modules/@vercel/nft": { - "version": "0.29.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@mapbox/node-pre-gyp": "^2.0.0-rc.0", - "@rollup/pluginutils": "^5.1.3", - "acorn": "^8.6.0", - "acorn-import-attributes": "^1.9.5", - "async-sema": "^3.1.1", - "bindings": "^1.4.0", - "estree-walker": "2.0.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "node-gyp-build": "^4.2.2", - "picomatch": "^4.0.2", - "resolve-from": "^5.0.0" - }, - "bin": { - "nft": "out/cli.js" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/abbrev": { - "version": "3.0.0", - "dev": true, - "license": "ISC", - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/acorn": { - "version": "8.14.0", - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-import-attributes": { - "version": "1.9.5", - "dev": true, - "license": "MIT", - "peerDependencies": { - "acorn": "^8" - } - }, - "node_modules/agent-base": { - "version": "7.1.3", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 14" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/any-promise": { - "version": "1.3.0", - "dev": true, - "license": "MIT" - }, - "node_modules/anymatch": { - "version": "3.1.3", - "dev": true, - "license": "ISC", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/anymatch/node_modules/picomatch": { - "version": "2.3.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/arg": { - "version": "5.0.2", - "dev": true, - "license": "MIT" - }, - "node_modules/aria-query": { - "version": "5.3.2", - "license": "Apache-2.0", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/async-sema": { - "version": "3.1.1", - "dev": true, - "license": "MIT" - }, - "node_modules/autoprefixer": { - "version": "10.4.20", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/autoprefixer" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "browserslist": "^4.23.3", - "caniuse-lite": "^1.0.30001646", - "fraction.js": "^4.3.7", - "normalize-range": "^0.1.2", - "picocolors": "^1.0.1", - "postcss-value-parser": "^4.2.0" - }, - "bin": { - "autoprefixer": "bin/autoprefixer" - }, - "engines": { - "node": "^10 || ^12 || >=14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/axobject-query": { - "version": "4.1.0", - "license": "Apache-2.0", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "dev": true, - "license": "MIT" - }, - "node_modules/binary-extensions": { - "version": "2.3.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/bindings": { - "version": "1.5.0", - "dev": true, - "license": "MIT", - "dependencies": { - "file-uri-to-path": "1.0.0" - } - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "3.0.3", - "dev": true, - "license": "MIT", - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browserslist": { - "version": "4.24.4", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "caniuse-lite": "^1.0.30001688", - "electron-to-chromium": "^1.5.73", - "node-releases": "^2.0.19", - "update-browserslist-db": "^1.1.1" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/buffer-crc32": { - "version": "1.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/camelcase": { - "version": "5.3.1", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/camelcase-css": { - "version": "2.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001695", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "CC-BY-4.0" - }, - "node_modules/chokidar": { - "version": "3.6.0", - "dev": true, - "license": "MIT", - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/chownr": { - "version": "3.0.0", - "dev": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=18" - } - }, - "node_modules/cli-color": { - "version": "2.0.4", - "license": "ISC", - "dependencies": { - "d": "^1.0.1", - "es5-ext": "^0.10.64", - "es6-iterator": "^2.0.3", - "memoizee": "^0.4.15", - "timers-ext": "^0.1.7" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/cliui": { - "version": "6.0.0", - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^6.2.0" - } - }, - "node_modules/code-red": { - "version": "1.0.4", - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.4.15", - "@types/estree": "^1.0.1", - "acorn": "^8.10.0", - "estree-walker": "^3.0.3", - "periscopic": "^3.1.0" - } - }, - "node_modules/code-red/node_modules/estree-walker": { - "version": "3.0.3", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "license": "MIT" - }, - "node_modules/commander": { - "version": "4.1.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/commondir": { - "version": "1.0.1", - "dev": true, - "license": "MIT" - }, - "node_modules/concat-map": { - "version": "0.0.1", - "dev": true, - "license": "MIT" - }, - "node_modules/confbox": { - "version": "0.1.8", - "dev": true, - "license": "MIT" - }, - "node_modules/consola": { - "version": "3.4.0", - "dev": true, - "license": "MIT", - "engines": { - "node": "^14.18.0 || >=16.10.0" - } - }, - "node_modules/cookie": { - "version": "0.6.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/cross-spawn/node_modules/isexe": { - "version": "2.0.0", - "dev": true, - "license": "ISC" - }, - "node_modules/cross-spawn/node_modules/which": { - "version": "2.0.2", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/css-selector-tokenizer": { - "version": "0.8.0", - "dev": true, - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "fastparse": "^1.1.2" - } - }, - "node_modules/css-tree": { - "version": "2.3.1", - "license": "MIT", - "dependencies": { - "mdn-data": "2.0.30", - "source-map-js": "^1.0.1" - }, - "engines": { - "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" - } - }, - "node_modules/cssesc": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "bin": { - "cssesc": "bin/cssesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/culori": { - "version": "3.3.0", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - } - }, - "node_modules/d": { - "version": "1.0.2", - "license": "ISC", - "dependencies": { - "es5-ext": "^0.10.64", - "type": "^2.7.2" - }, - "engines": { - "node": ">=0.12" - } - }, - "node_modules/d3-array": { - "version": "3.2.4", - "license": "ISC", - "dependencies": { - "internmap": "1 - 2" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-geo": { - "version": "3.1.1", - "license": "ISC", - "dependencies": { - "d3-array": "2.5.0 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/daisyui": { - "version": "4.12.23", - "dev": true, - "license": "MIT", - "dependencies": { - "css-selector-tokenizer": "^0.8", - "culori": "^3", - "picocolors": "^1", - "postcss-js": "^4" - }, - "engines": { - "node": ">=16.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/daisyui" - } - }, - "node_modules/debug": { - "version": "4.4.0", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/decamelize": { - "version": "1.2.0", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/decimal.js": { - "version": "10.4.3", - "license": "MIT" - }, - "node_modules/deepmerge": { - "version": "4.3.1", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/dequal": { - "version": "2.0.3", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/detect-indent": { - "version": "6.1.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/detect-libc": { - "version": "2.0.3", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=8" - } - }, - "node_modules/devalue": { - "version": "5.1.1", - "dev": true, - "license": "MIT" - }, - "node_modules/didyoumean": { - "version": "1.2.2", - "dev": true, - "license": "Apache-2.0" - }, - "node_modules/dijkstrajs": { - "version": "1.0.3", - "license": "MIT" - }, - "node_modules/dlv": { - "version": "1.1.3", - "dev": true, - "license": "MIT" - }, - "node_modules/earcut": { - "version": "3.0.1", - "license": "ISC" - }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "dev": true, - "license": "MIT" - }, - "node_modules/electron-to-chromium": { - "version": "1.5.86", - "dev": true, - "license": "ISC" - }, - "node_modules/emoji-picker-element": { - "version": "1.26.0", - "license": "Apache-2.0" - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "license": "MIT" - }, - "node_modules/es5-ext": { - "version": "0.10.64", - "hasInstallScript": true, - "license": "ISC", - "dependencies": { - "es6-iterator": "^2.0.3", - "es6-symbol": "^3.1.3", - "esniff": "^2.0.1", - "next-tick": "^1.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/es6-iterator": { - "version": "2.0.3", - "license": "MIT", - "dependencies": { - "d": "1", - "es5-ext": "^0.10.35", - "es6-symbol": "^3.1.1" - } - }, - "node_modules/es6-promise": { - "version": "3.3.1", - "dev": true, - "license": "MIT" - }, - "node_modules/es6-symbol": { - "version": "3.1.4", - "license": "ISC", - "dependencies": { - "d": "^1.0.2", - "ext": "^1.7.0" - }, - "engines": { - "node": ">=0.12" - } - }, - "node_modules/es6-weak-map": { - "version": "2.0.3", - "license": "ISC", - "dependencies": { - "d": "1", - "es5-ext": "^0.10.46", - "es6-iterator": "^2.0.3", - "es6-symbol": "^3.1.1" - } - }, - "node_modules/esbuild": { - "version": "0.24.2", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.24.2", - "@esbuild/android-arm": "0.24.2", - "@esbuild/android-arm64": "0.24.2", - "@esbuild/android-x64": "0.24.2", - "@esbuild/darwin-arm64": "0.24.2", - "@esbuild/darwin-x64": "0.24.2", - "@esbuild/freebsd-arm64": "0.24.2", - "@esbuild/freebsd-x64": "0.24.2", - "@esbuild/linux-arm": "0.24.2", - "@esbuild/linux-arm64": "0.24.2", - "@esbuild/linux-ia32": "0.24.2", - "@esbuild/linux-loong64": "0.24.2", - "@esbuild/linux-mips64el": "0.24.2", - "@esbuild/linux-ppc64": "0.24.2", - "@esbuild/linux-riscv64": "0.24.2", - "@esbuild/linux-s390x": "0.24.2", - "@esbuild/linux-x64": "0.24.2", - "@esbuild/netbsd-arm64": "0.24.2", - "@esbuild/netbsd-x64": "0.24.2", - "@esbuild/openbsd-arm64": "0.24.2", - "@esbuild/openbsd-x64": "0.24.2", - "@esbuild/sunos-x64": "0.24.2", - "@esbuild/win32-arm64": "0.24.2", - "@esbuild/win32-ia32": "0.24.2", - "@esbuild/win32-x64": "0.24.2" - } - }, - "node_modules/escalade": { - "version": "3.2.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/esm-env": { - "version": "1.2.2", - "dev": true, - "license": "MIT" - }, - "node_modules/esniff": { - "version": "2.0.1", - "license": "ISC", - "dependencies": { - "d": "^1.0.1", - "es5-ext": "^0.10.62", - "event-emitter": "^0.3.5", - "type": "^2.7.2" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/estree-walker": { - "version": "2.0.2", - "license": "MIT" - }, - "node_modules/event-emitter": { - "version": "0.3.5", - "license": "MIT", - "dependencies": { - "d": "1", - "es5-ext": "~0.10.14" - } - }, - "node_modules/ext": { - "version": "1.7.0", - "license": "ISC", - "dependencies": { - "type": "^2.7.2" - } - }, - "node_modules/fast-glob": { - "version": "3.3.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.8" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fastparse": { - "version": "1.1.2", - "dev": true, - "license": "MIT" - }, - "node_modules/fastq": { - "version": "1.18.0", - "dev": true, - "license": "ISC", - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/fdir": { - "version": "6.4.3", - "dev": true, - "license": "MIT", - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, - "node_modules/fflate": { - "version": "0.8.2", - "license": "MIT" - }, - "node_modules/file-uri-to-path": { - "version": "1.0.0", - "dev": true, - "license": "MIT" - }, - "node_modules/fill-range": { - "version": "7.1.1", - "dev": true, - "license": "MIT", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-up": { - "version": "4.1.0", - "license": "MIT", - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/foreground-child": { - "version": "3.3.0", - "dev": true, - "license": "ISC", - "dependencies": { - "cross-spawn": "^7.0.0", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/fraction.js": { - "version": "4.3.7", - "dev": true, - "license": "MIT", - "engines": { - "node": "*" - }, - "funding": { - "type": "patreon", - "url": "https://github.com/sponsors/rawify" - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "dev": true, - "license": "ISC" - }, - "node_modules/function-bind": { - "version": "1.1.2", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/geojson-vt": { - "version": "4.0.2", - "license": "ISC" - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "license": "ISC", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-stream": { - "version": "6.0.1", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/gl-matrix": { - "version": "3.4.3", - "license": "MIT" - }, - "node_modules/glob": { - "version": "7.2.3", - "dev": true, - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob-parent": { - "version": "5.1.2", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/global-prefix": { - "version": "4.0.0", - "license": "MIT", - "dependencies": { - "ini": "^4.1.3", - "kind-of": "^6.0.3", - "which": "^4.0.0" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/globals": { - "version": "15.14.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/globalyzer": { - "version": "0.1.0", - "license": "MIT" - }, - "node_modules/globrex": { - "version": "0.1.2", - "license": "MIT" - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "dev": true, - "license": "ISC" - }, - "node_modules/gsap": { - "version": "3.12.7", - "license": "Standard 'no charge' license: https://gsap.com/standard-license. Club GSAP members get more: https://gsap.com/licensing/. Why GreenSock doesn't employ an MIT license: https://gsap.com/why-license/" - }, - "node_modules/hasown": { - "version": "2.0.2", - "dev": true, - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/https-proxy-agent": { - "version": "7.0.6", - "dev": true, - "license": "MIT", - "dependencies": { - "agent-base": "^7.1.2", - "debug": "4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/ieee754": { - "version": "1.2.1", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "BSD-3-Clause" - }, - "node_modules/import-meta-resolve": { - "version": "4.1.0", - "dev": true, - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "dev": true, - "license": "ISC", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "dev": true, - "license": "ISC" - }, - "node_modules/ini": { - "version": "4.1.3", - "license": "ISC", - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/internmap": { - "version": "2.0.3", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/intl-messageformat": { - "version": "10.7.14", - "license": "BSD-3-Clause", - "dependencies": { - "@formatjs/ecma402-abstract": "2.3.2", - "@formatjs/fast-memoize": "2.2.6", - "@formatjs/icu-messageformat-parser": "2.11.0", - "tslib": "2" - } - }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-core-module": { - "version": "2.16.1", - "dev": true, - "license": "MIT", - "dependencies": { - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "dev": true, - "license": "MIT", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-module": { - "version": "1.0.0", - "dev": true, - "license": "MIT" - }, - "node_modules/is-number": { - "version": "7.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-promise": { - "version": "2.2.2", - "license": "MIT" - }, - "node_modules/is-reference": { - "version": "1.2.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "*" - } - }, - "node_modules/isexe": { - "version": "3.1.1", - "license": "ISC", - "engines": { - "node": ">=16" - } - }, - "node_modules/jackspeak": { - "version": "3.4.3", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" - } - }, - "node_modules/jiti": { - "version": "1.21.7", - "dev": true, - "license": "MIT", - "bin": { - "jiti": "bin/jiti.js" - } - }, - "node_modules/json-stringify-pretty-compact": { - "version": "4.0.0", - "license": "MIT" - }, - "node_modules/just-compare": { - "version": "2.3.0", - "license": "MIT" - }, - "node_modules/just-flush": { - "version": "2.3.0", - "license": "MIT" - }, - "node_modules/kdbush": { - "version": "4.0.2", - "license": "ISC" - }, - "node_modules/kind-of": { - "version": "6.0.3", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/kleur": { - "version": "4.1.5", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/kolorist": { - "version": "1.8.0", - "dev": true, - "license": "MIT" - }, - "node_modules/lilconfig": { - "version": "3.1.3", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/antonk52" - } - }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "dev": true, - "license": "MIT" - }, - "node_modules/local-pkg": { - "version": "0.5.1", - "dev": true, - "license": "MIT", - "dependencies": { - "mlly": "^1.7.3", - "pkg-types": "^1.2.1" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" - } - }, - "node_modules/locate-character": { - "version": "3.0.0", - "license": "MIT" - }, - "node_modules/locate-path": { - "version": "5.0.0", - "license": "MIT", - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/lodash.castarray": { - "version": "4.4.0", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.isplainobject": { - "version": "4.0.6", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "dev": true, - "license": "MIT" - }, - "node_modules/lru-cache": { - "version": "10.4.3", - "dev": true, - "license": "ISC" - }, - "node_modules/lru-queue": { - "version": "0.1.0", - "license": "MIT", - "dependencies": { - "es5-ext": "~0.10.2" - } - }, - "node_modules/magic-string": { - "version": "0.30.17", - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0" - } - }, - "node_modules/maplibre-gl": { - "version": "4.7.1", - "license": "BSD-3-Clause", - "dependencies": { - "@mapbox/geojson-rewind": "^0.5.2", - "@mapbox/jsonlint-lines-primitives": "^2.0.2", - "@mapbox/point-geometry": "^0.1.0", - "@mapbox/tiny-sdf": "^2.0.6", - "@mapbox/unitbezier": "^0.0.1", - "@mapbox/vector-tile": "^1.3.1", - "@mapbox/whoots-js": "^3.1.0", - "@maplibre/maplibre-gl-style-spec": "^20.3.1", - "@types/geojson": "^7946.0.14", - "@types/geojson-vt": "3.2.5", - "@types/mapbox__point-geometry": "^0.1.4", - "@types/mapbox__vector-tile": "^1.3.4", - "@types/pbf": "^3.0.5", - "@types/supercluster": "^7.1.3", - "earcut": "^3.0.0", - "geojson-vt": "^4.0.2", - "gl-matrix": "^3.4.3", - "global-prefix": "^4.0.0", - "kdbush": "^4.0.2", - "murmurhash-js": "^1.0.0", - "pbf": "^3.3.0", - "potpack": "^2.0.0", - "quickselect": "^3.0.0", - "supercluster": "^8.0.1", - "tinyqueue": "^3.0.0", - "vt-pbf": "^3.1.3" - }, - "engines": { - "node": ">=16.14.0", - "npm": ">=8.1.0" - }, - "funding": { - "url": "https://github.com/maplibre/maplibre-gl-js?sponsor=1" - } - }, - "node_modules/marked": { - "version": "15.0.6", - "license": "MIT", - "bin": { - "marked": "bin/marked.js" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/mdn-data": { - "version": "2.0.30", - "license": "CC0-1.0" - }, - "node_modules/memoizee": { - "version": "0.4.17", - "license": "ISC", - "dependencies": { - "d": "^1.0.2", - "es5-ext": "^0.10.64", - "es6-weak-map": "^2.0.3", - "event-emitter": "^0.3.5", - "is-promise": "^2.2.2", - "lru-queue": "^0.1.0", - "next-tick": "^1.1.0", - "timers-ext": "^0.1.7" - }, - "engines": { - "node": ">=0.12" - } - }, - "node_modules/merge2": { - "version": "1.4.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/micromatch": { - "version": "4.0.8", - "dev": true, - "license": "MIT", - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/micromatch/node_modules/picomatch": { - "version": "2.3.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/min-indent": { - "version": "1.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/minimatch": { - "version": "3.1.2", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minimist": { - "version": "1.2.8", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/minipass": { - "version": "7.1.2", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/minizlib": { - "version": "3.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "minipass": "^7.0.4", - "rimraf": "^5.0.5" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/minizlib/node_modules/brace-expansion": { - "version": "2.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/minizlib/node_modules/glob": { - "version": "10.4.5", - "dev": true, - "license": "ISC", - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/minizlib/node_modules/minimatch": { - "version": "9.0.5", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/minizlib/node_modules/rimraf": { - "version": "5.0.10", - "dev": true, - "license": "ISC", - "dependencies": { - "glob": "^10.3.7" - }, - "bin": { - "rimraf": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/mkdirp": { - "version": "0.5.6", - "dev": true, - "license": "MIT", - "dependencies": { - "minimist": "^1.2.6" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "node_modules/mlly": { - "version": "1.7.4", - "dev": true, - "license": "MIT", - "dependencies": { - "acorn": "^8.14.0", - "pathe": "^2.0.1", - "pkg-types": "^1.3.0", - "ufo": "^1.5.4" - } - }, - "node_modules/mri": { - "version": "1.2.0", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/mrmime": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "dev": true, - "license": "MIT" - }, - "node_modules/murmurhash-js": { - "version": "1.0.0", - "license": "MIT" - }, - "node_modules/mz": { - "version": "2.7.0", - "dev": true, - "license": "MIT", - "dependencies": { - "any-promise": "^1.0.0", - "object-assign": "^4.0.1", - "thenify-all": "^1.0.0" - } - }, - "node_modules/nanoid": { - "version": "3.3.8", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/next-tick": { - "version": "1.1.0", - "license": "ISC" - }, - "node_modules/node-fetch": { - "version": "2.7.0", - "dev": true, - "license": "MIT", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "node_modules/node-gyp-build": { - "version": "4.8.4", - "dev": true, - "license": "MIT", - "bin": { - "node-gyp-build": "bin.js", - "node-gyp-build-optional": "optional.js", - "node-gyp-build-test": "build-test.js" - } - }, - "node_modules/node-releases": { - "version": "2.0.19", - "dev": true, - "license": "MIT" - }, - "node_modules/nopt": { - "version": "8.1.0", - "dev": true, - "license": "ISC", - "dependencies": { - "abbrev": "^3.0.0" - }, - "bin": { - "nopt": "bin/nopt.js" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/normalize-range": { - "version": "0.1.2", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-assign": { - "version": "4.1.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-hash": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/once": { - "version": "1.4.0", - "dev": true, - "license": "ISC", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/p-limit": { - "version": "2.3.0", - "license": "MIT", - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "4.1.0", - "license": "MIT", - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/p-try": { - "version": "2.2.0", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/package-json-from-dist": { - "version": "1.0.1", - "dev": true, - "license": "BlueOak-1.0.0" - }, - "node_modules/package-manager-detector": { - "version": "0.2.8", - "dev": true, - "license": "MIT" - }, - "node_modules/path-exists": { - "version": "4.0.0", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "dev": true, - "license": "MIT" - }, - "node_modules/path-scurry": { - "version": "1.11.1", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" - }, - "engines": { - "node": ">=16 || 14 >=14.18" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/pathe": { - "version": "2.0.2", - "dev": true, - "license": "MIT" - }, - "node_modules/pbf": { - "version": "3.3.0", - "license": "BSD-3-Clause", - "dependencies": { - "ieee754": "^1.1.12", - "resolve-protobuf-schema": "^2.1.0" - }, - "bin": { - "pbf": "bin/pbf" - } - }, - "node_modules/periscopic": { - "version": "3.1.0", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "estree-walker": "^3.0.0", - "is-reference": "^3.0.0" - } - }, - "node_modules/periscopic/node_modules/estree-walker": { - "version": "3.0.3", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0" - } - }, - "node_modules/periscopic/node_modules/is-reference": { - "version": "3.0.3", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.6" - } - }, - "node_modules/picocolors": { - "version": "1.1.1", - "dev": true, - "license": "ISC" - }, - "node_modules/picomatch": { - "version": "4.0.2", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pify": { - "version": "2.3.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/pirates": { - "version": "4.0.6", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/pkg-types": { - "version": "1.3.1", - "dev": true, - "license": "MIT", - "dependencies": { - "confbox": "^0.1.8", - "mlly": "^1.7.4", - "pathe": "^2.0.1" - } - }, - "node_modules/pmtiles": { - "version": "3.2.1", - "license": "BSD-3-Clause", - "dependencies": { - "@types/leaflet": "^1.9.8", - "fflate": "^0.8.0" - } - }, - "node_modules/pngjs": { - "version": "5.0.0", - "license": "MIT", - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/postcss": { - "version": "8.5.1", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "nanoid": "^3.3.8", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/postcss-import": { - "version": "15.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "postcss-value-parser": "^4.0.0", - "read-cache": "^1.0.0", - "resolve": "^1.1.7" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "postcss": "^8.0.0" - } - }, - "node_modules/postcss-js": { - "version": "4.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "camelcase-css": "^2.0.1" - }, - "engines": { - "node": "^12 || ^14 || >= 16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - "peerDependencies": { - "postcss": "^8.4.21" - } - }, - "node_modules/postcss-load-config": { - "version": "4.0.2", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "lilconfig": "^3.0.0", - "yaml": "^2.3.4" - }, - "engines": { - "node": ">= 14" - }, - "peerDependencies": { - "postcss": ">=8.0.9", - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "postcss": { - "optional": true - }, - "ts-node": { - "optional": true - } - } - }, - "node_modules/postcss-nested": { - "version": "6.2.0", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "postcss-selector-parser": "^6.1.1" - }, - "engines": { - "node": ">=12.0" - }, - "peerDependencies": { - "postcss": "^8.2.14" - } - }, - "node_modules/postcss-nested/node_modules/postcss-selector-parser": { - "version": "6.1.2", - "dev": true, - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/postcss-selector-parser": { - "version": "6.0.10", - "dev": true, - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/postcss-value-parser": { - "version": "4.2.0", - "dev": true, - "license": "MIT" - }, - "node_modules/potpack": { - "version": "2.0.0", - "license": "ISC" - }, - "node_modules/prettier": { - "version": "3.4.2", - "dev": true, - "license": "MIT", - "bin": { - "prettier": "bin/prettier.cjs" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" - } - }, - "node_modules/prettier-plugin-svelte": { - "version": "3.3.3", - "dev": true, - "license": "MIT", - "peerDependencies": { - "prettier": "^3.0.0", - "svelte": "^3.2.0 || ^4.0.0-next.0 || ^5.0.0-next.0" - } - }, - "node_modules/protocol-buffers-schema": { - "version": "3.6.0", - "license": "MIT" - }, - "node_modules/qrcode": { - "version": "1.5.4", - "license": "MIT", - "dependencies": { - "dijkstrajs": "^1.0.1", - "pngjs": "^5.0.0", - "yargs": "^15.3.1" - }, - "bin": { - "qrcode": "bin/qrcode" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/quickselect": { - "version": "3.0.0", - "license": "ISC" - }, - "node_modules/read-cache": { - "version": "1.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "pify": "^2.3.0" - } - }, - "node_modules/readdirp": { - "version": "3.6.0", - "dev": true, - "license": "MIT", - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/readdirp/node_modules/picomatch": { - "version": "2.3.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/require-directory": { - "version": "2.1.1", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/require-main-filename": { - "version": "2.0.0", - "license": "ISC" - }, - "node_modules/resolve": { - "version": "1.22.10", - "dev": true, - "license": "MIT", - "dependencies": { - "is-core-module": "^2.16.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-from": { - "version": "5.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve-protobuf-schema": { - "version": "2.1.0", - "license": "MIT", - "dependencies": { - "protocol-buffers-schema": "^3.3.1" - } - }, - "node_modules/reusify": { - "version": "1.0.4", - "dev": true, - "license": "MIT", - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/rimraf": { - "version": "2.7.1", - "dev": true, - "license": "ISC", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - } - }, - "node_modules/rollup": { - "version": "4.31.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "1.0.6" - }, - "bin": { - "rollup": "dist/bin/rollup" - }, - "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" - }, - "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.31.0", - "@rollup/rollup-android-arm64": "4.31.0", - "@rollup/rollup-darwin-arm64": "4.31.0", - "@rollup/rollup-darwin-x64": "4.31.0", - "@rollup/rollup-freebsd-arm64": "4.31.0", - "@rollup/rollup-freebsd-x64": "4.31.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.31.0", - "@rollup/rollup-linux-arm-musleabihf": "4.31.0", - "@rollup/rollup-linux-arm64-gnu": "4.31.0", - "@rollup/rollup-linux-arm64-musl": "4.31.0", - "@rollup/rollup-linux-loongarch64-gnu": "4.31.0", - "@rollup/rollup-linux-powerpc64le-gnu": "4.31.0", - "@rollup/rollup-linux-riscv64-gnu": "4.31.0", - "@rollup/rollup-linux-s390x-gnu": "4.31.0", - "@rollup/rollup-linux-x64-gnu": "4.31.0", - "@rollup/rollup-linux-x64-musl": "4.31.0", - "@rollup/rollup-win32-arm64-msvc": "4.31.0", - "@rollup/rollup-win32-ia32-msvc": "4.31.0", - "@rollup/rollup-win32-x64-msvc": "4.31.0", - "fsevents": "~2.3.2" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/rw": { - "version": "1.3.3", - "license": "BSD-3-Clause" - }, - "node_modules/sade": { - "version": "1.8.1", - "license": "MIT", - "dependencies": { - "mri": "^1.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/sander": { - "version": "0.5.1", - "dev": true, - "license": "MIT", - "dependencies": { - "es6-promise": "^3.1.2", - "graceful-fs": "^4.1.3", - "mkdirp": "^0.5.1", - "rimraf": "^2.5.2" - } - }, - "node_modules/semver": { - "version": "7.6.3", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/set-blocking": { - "version": "2.0.0", - "license": "ISC" - }, - "node_modules/set-cookie-parser": { - "version": "2.7.1", - "dev": true, - "license": "MIT" - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/signal-exit": { - "version": "4.1.0", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/sirv": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@polka/url": "^1.0.0-next.24", - "mrmime": "^2.0.0", - "totalist": "^3.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/sorcery": { - "version": "0.11.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.4.14", - "buffer-crc32": "^1.0.0", - "minimist": "^1.2.0", - "sander": "^0.5.0" - }, - "bin": { - "sorcery": "bin/sorcery" - } - }, - "node_modules/source-map-js": { - "version": "1.2.1", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/string-width": { - "version": "4.2.3", - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs": { - "name": "string-width", - "version": "4.2.3", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi-cjs": { - "name": "strip-ansi", - "version": "6.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-indent": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "min-indent": "^1.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/sucrase": { - "version": "3.35.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.2", - "commander": "^4.0.0", - "glob": "^10.3.10", - "lines-and-columns": "^1.1.6", - "mz": "^2.7.0", - "pirates": "^4.0.1", - "ts-interface-checker": "^0.1.9" - }, - "bin": { - "sucrase": "bin/sucrase", - "sucrase-node": "bin/sucrase-node" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/sucrase/node_modules/brace-expansion": { - "version": "2.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/sucrase/node_modules/glob": { - "version": "10.4.5", - "dev": true, - "license": "ISC", - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/sucrase/node_modules/minimatch": { - "version": "9.0.5", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/supercluster": { - "version": "8.0.1", - "license": "ISC", - "dependencies": { - "kdbush": "^4.0.2" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/svelte": { - "version": "4.2.19", - "license": "MIT", - "dependencies": { - "@ampproject/remapping": "^2.2.1", - "@jridgewell/sourcemap-codec": "^1.4.15", - "@jridgewell/trace-mapping": "^0.3.18", - "@types/estree": "^1.0.1", - "acorn": "^8.9.0", - "aria-query": "^5.3.0", - "axobject-query": "^4.0.0", - "code-red": "^1.0.3", - "css-tree": "^2.3.1", - "estree-walker": "^3.0.3", - "is-reference": "^3.0.1", - "locate-character": "^3.0.0", - "magic-string": "^0.30.4", - "periscopic": "^3.1.0" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/svelte-check": { - "version": "3.8.6", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.17", - "chokidar": "^3.4.1", - "picocolors": "^1.0.0", - "sade": "^1.7.4", - "svelte-preprocess": "^5.1.3", - "typescript": "^5.0.3" - }, - "bin": { - "svelte-check": "bin/svelte-check" - }, - "peerDependencies": { - "svelte": "^3.55.0 || ^4.0.0-next.0 || ^4.0.0 || ^5.0.0-next.0" - } - }, - "node_modules/svelte-hmr": { - "version": "0.16.0", - "dev": true, - "license": "ISC", - "engines": { - "node": "^12.20 || ^14.13.1 || >= 16" - }, - "peerDependencies": { - "svelte": "^3.19.0 || ^4.0.0" - } - }, - "node_modules/svelte-i18n": { - "version": "4.0.1", - "license": "MIT", - "dependencies": { - "cli-color": "^2.0.3", - "deepmerge": "^4.2.2", - "esbuild": "^0.19.2", - "estree-walker": "^2", - "intl-messageformat": "^10.5.3", - "sade": "^1.8.1", - "tiny-glob": "^0.2.9" - }, - "bin": { - "svelte-i18n": "dist/cli.js" - }, - "engines": { - "node": ">= 16" - }, - "peerDependencies": { - "svelte": "^3 || ^4 || ^5" - } - }, - "node_modules/svelte-i18n/node_modules/@esbuild/linux-x64": { - "version": "0.19.12", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/svelte-i18n/node_modules/esbuild": { - "version": "0.19.12", - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=12" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.19.12", - "@esbuild/android-arm": "0.19.12", - "@esbuild/android-arm64": "0.19.12", - "@esbuild/android-x64": "0.19.12", - "@esbuild/darwin-arm64": "0.19.12", - "@esbuild/darwin-x64": "0.19.12", - "@esbuild/freebsd-arm64": "0.19.12", - "@esbuild/freebsd-x64": "0.19.12", - "@esbuild/linux-arm": "0.19.12", - "@esbuild/linux-arm64": "0.19.12", - "@esbuild/linux-ia32": "0.19.12", - "@esbuild/linux-loong64": "0.19.12", - "@esbuild/linux-mips64el": "0.19.12", - "@esbuild/linux-ppc64": "0.19.12", - "@esbuild/linux-riscv64": "0.19.12", - "@esbuild/linux-s390x": "0.19.12", - "@esbuild/linux-x64": "0.19.12", - "@esbuild/netbsd-x64": "0.19.12", - "@esbuild/openbsd-x64": "0.19.12", - "@esbuild/sunos-x64": "0.19.12", - "@esbuild/win32-arm64": "0.19.12", - "@esbuild/win32-ia32": "0.19.12", - "@esbuild/win32-x64": "0.19.12" - } - }, - "node_modules/svelte-maplibre": { - "version": "0.9.14", - "license": "MIT", - "dependencies": { - "d3-geo": "^3.1.0", - "dequal": "^2.0.3", - "just-compare": "^2.3.0", - "just-flush": "^2.3.0", - "maplibre-gl": "^4.0.0", - "pmtiles": "^3.0.3" - }, - "peerDependencies": { - "@deck.gl/core": "^8.8.0", - "@deck.gl/layers": "^8.8.0", - "@deck.gl/mapbox": "^8.8.0", - "svelte": "^3.54.0 || ^4.0.0 || ^5.0.0" - }, - "peerDependenciesMeta": { - "@deck.gl/core": { - "optional": true - }, - "@deck.gl/layers": { - "optional": true - }, - "@deck.gl/mapbox": { - "optional": true - } - } - }, - "node_modules/svelte-preprocess": { - "version": "5.1.4", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "@types/pug": "^2.0.6", - "detect-indent": "^6.1.0", - "magic-string": "^0.30.5", - "sorcery": "^0.11.0", - "strip-indent": "^3.0.0" - }, - "engines": { - "node": ">= 16.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.10.2", - "coffeescript": "^2.5.1", - "less": "^3.11.3 || ^4.0.0", - "postcss": "^7 || ^8", - "postcss-load-config": "^2.1.0 || ^3.0.0 || ^4.0.0 || ^5.0.0", - "pug": "^3.0.0", - "sass": "^1.26.8", - "stylus": "^0.55.0", - "sugarss": "^2.0.0 || ^3.0.0 || ^4.0.0", - "svelte": "^3.23.0 || ^4.0.0-next.0 || ^4.0.0 || ^5.0.0-next.0", - "typescript": ">=3.9.5 || ^4.0.0 || ^5.0.0" - }, - "peerDependenciesMeta": { - "@babel/core": { - "optional": true - }, - "coffeescript": { - "optional": true - }, - "less": { - "optional": true - }, - "postcss": { - "optional": true - }, - "postcss-load-config": { - "optional": true - }, - "pug": { - "optional": true - }, - "sass": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "typescript": { - "optional": true - } - } - }, - "node_modules/svelte/node_modules/estree-walker": { - "version": "3.0.3", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0" - } - }, - "node_modules/svelte/node_modules/is-reference": { - "version": "3.0.3", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.6" - } - }, - "node_modules/tailwindcss": { - "version": "3.4.17", - "dev": true, - "license": "MIT", - "dependencies": { - "@alloc/quick-lru": "^5.2.0", - "arg": "^5.0.2", - "chokidar": "^3.6.0", - "didyoumean": "^1.2.2", - "dlv": "^1.1.3", - "fast-glob": "^3.3.2", - "glob-parent": "^6.0.2", - "is-glob": "^4.0.3", - "jiti": "^1.21.6", - "lilconfig": "^3.1.3", - "micromatch": "^4.0.8", - "normalize-path": "^3.0.0", - "object-hash": "^3.0.0", - "picocolors": "^1.1.1", - "postcss": "^8.4.47", - "postcss-import": "^15.1.0", - "postcss-js": "^4.0.1", - "postcss-load-config": "^4.0.2", - "postcss-nested": "^6.2.0", - "postcss-selector-parser": "^6.1.2", - "resolve": "^1.22.8", - "sucrase": "^3.35.0" - }, - "bin": { - "tailwind": "lib/cli.js", - "tailwindcss": "lib/cli.js" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/tailwindcss/node_modules/glob-parent": { - "version": "6.0.2", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/tailwindcss/node_modules/postcss-selector-parser": { - "version": "6.1.2", - "dev": true, - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/tar": { - "version": "7.4.3", - "dev": true, - "license": "ISC", - "dependencies": { - "@isaacs/fs-minipass": "^4.0.0", - "chownr": "^3.0.0", - "minipass": "^7.1.2", - "minizlib": "^3.0.1", - "mkdirp": "^3.0.1", - "yallist": "^5.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/tar/node_modules/mkdirp": { - "version": "3.0.1", - "dev": true, - "license": "MIT", - "bin": { - "mkdirp": "dist/cjs/src/bin.js" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/thenify": { - "version": "3.3.1", - "dev": true, - "license": "MIT", - "dependencies": { - "any-promise": "^1.0.0" - } - }, - "node_modules/thenify-all": { - "version": "1.6.0", - "dev": true, - "license": "MIT", - "dependencies": { - "thenify": ">= 3.1.0 < 4" - }, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/timers-ext": { - "version": "0.1.8", - "license": "ISC", - "dependencies": { - "es5-ext": "^0.10.64", - "next-tick": "^1.1.0" - }, - "engines": { - "node": ">=0.12" - } - }, - "node_modules/tiny-glob": { - "version": "0.2.9", - "license": "MIT", - "dependencies": { - "globalyzer": "0.1.0", - "globrex": "^0.1.2" - } - }, - "node_modules/tinyexec": { - "version": "0.3.2", - "dev": true, - "license": "MIT" - }, - "node_modules/tinyqueue": { - "version": "3.0.0", - "license": "ISC" - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/totalist": { - "version": "3.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/tr46": { - "version": "0.0.3", - "dev": true, - "license": "MIT" - }, - "node_modules/ts-interface-checker": { - "version": "0.1.13", - "dev": true, - "license": "Apache-2.0" - }, - "node_modules/tslib": { - "version": "2.8.1", - "license": "0BSD" - }, - "node_modules/tsparticles": { - "version": "3.8.0", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/matteobruni" - }, - { - "type": "github", - "url": "https://github.com/sponsors/tsparticles" - }, - { - "type": "buymeacoffee", - "url": "https://www.buymeacoffee.com/matteobruni" - } - ], - "license": "MIT", - "dependencies": { - "@tsparticles/engine": "3.8.0", - "@tsparticles/interaction-external-trail": "3.8.0", - "@tsparticles/plugin-absorbers": "3.8.0", - "@tsparticles/plugin-emitters": "3.8.0", - "@tsparticles/plugin-emitters-shape-circle": "3.8.0", - "@tsparticles/plugin-emitters-shape-square": "3.8.0", - "@tsparticles/shape-text": "3.8.0", - "@tsparticles/slim": "3.8.0", - "@tsparticles/updater-destroy": "3.8.0", - "@tsparticles/updater-roll": "3.8.0", - "@tsparticles/updater-tilt": "3.8.0", - "@tsparticles/updater-twinkle": "3.8.0", - "@tsparticles/updater-wobble": "3.8.0" - } - }, - "node_modules/type": { - "version": "2.7.3", - "license": "ISC" - }, - "node_modules/typescript": { - "version": "5.7.3", - "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/ufo": { - "version": "1.5.4", - "dev": true, - "license": "MIT" - }, - "node_modules/undici-types": { - "version": "6.20.0", - "dev": true, - "license": "MIT" - }, - "node_modules/unplugin": { - "version": "1.16.1", - "dev": true, - "license": "MIT", - "dependencies": { - "acorn": "^8.14.0", - "webpack-virtual-modules": "^0.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/unplugin-icons": { - "version": "0.19.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@antfu/install-pkg": "^0.4.1", - "@antfu/utils": "^0.7.10", - "@iconify/utils": "^2.1.29", - "debug": "^4.3.6", - "kolorist": "^1.8.0", - "local-pkg": "^0.5.0", - "unplugin": "^1.12.0" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" - }, - "peerDependencies": { - "@svgr/core": ">=7.0.0", - "@svgx/core": "^1.0.1", - "@vue/compiler-sfc": "^3.0.2 || ^2.7.0", - "vue-template-compiler": "^2.6.12", - "vue-template-es2015-compiler": "^1.9.0" - }, - "peerDependenciesMeta": { - "@svgr/core": { - "optional": true - }, - "@svgx/core": { - "optional": true - }, - "@vue/compiler-sfc": { - "optional": true - }, - "vue-template-compiler": { - "optional": true - }, - "vue-template-es2015-compiler": { - "optional": true - } - } - }, - "node_modules/update-browserslist-db": { - "version": "1.1.2", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "escalade": "^3.2.0", - "picocolors": "^1.1.1" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "dev": true, - "license": "MIT" - }, - "node_modules/vite": { - "version": "5.4.14", - "dev": true, - "license": "MIT", - "dependencies": { - "esbuild": "^0.21.3", - "postcss": "^8.4.43", - "rollup": "^4.20.0" - }, - "bin": { - "vite": "bin/vite.js" - }, - "engines": { - "node": "^18.0.0 || >=20.0.0" - }, - "funding": { - "url": "https://github.com/vitejs/vite?sponsor=1" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - }, - "peerDependencies": { - "@types/node": "^18.0.0 || >=20.0.0", - "less": "*", - "lightningcss": "^1.21.0", - "sass": "*", - "sass-embedded": "*", - "stylus": "*", - "sugarss": "*", - "terser": "^5.4.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "less": { - "optional": true - }, - "lightningcss": { - "optional": true - }, - "sass": { - "optional": true - }, - "sass-embedded": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "terser": { - "optional": true - } - } - }, - "node_modules/vite/node_modules/@esbuild/linux-x64": { - "version": "0.21.5", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/esbuild": { - "version": "0.21.5", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=12" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.21.5", - "@esbuild/android-arm": "0.21.5", - "@esbuild/android-arm64": "0.21.5", - "@esbuild/android-x64": "0.21.5", - "@esbuild/darwin-arm64": "0.21.5", - "@esbuild/darwin-x64": "0.21.5", - "@esbuild/freebsd-arm64": "0.21.5", - "@esbuild/freebsd-x64": "0.21.5", - "@esbuild/linux-arm": "0.21.5", - "@esbuild/linux-arm64": "0.21.5", - "@esbuild/linux-ia32": "0.21.5", - "@esbuild/linux-loong64": "0.21.5", - "@esbuild/linux-mips64el": "0.21.5", - "@esbuild/linux-ppc64": "0.21.5", - "@esbuild/linux-riscv64": "0.21.5", - "@esbuild/linux-s390x": "0.21.5", - "@esbuild/linux-x64": "0.21.5", - "@esbuild/netbsd-x64": "0.21.5", - "@esbuild/openbsd-x64": "0.21.5", - "@esbuild/sunos-x64": "0.21.5", - "@esbuild/win32-arm64": "0.21.5", - "@esbuild/win32-ia32": "0.21.5", - "@esbuild/win32-x64": "0.21.5" - } - }, - "node_modules/vitefu": { - "version": "0.2.5", - "dev": true, - "license": "MIT", - "peerDependencies": { - "vite": "^3.0.0 || ^4.0.0 || ^5.0.0" - }, - "peerDependenciesMeta": { - "vite": { - "optional": true - } - } - }, - "node_modules/vt-pbf": { - "version": "3.1.3", - "license": "MIT", - "dependencies": { - "@mapbox/point-geometry": "0.1.0", - "@mapbox/vector-tile": "^1.3.1", - "pbf": "^3.2.1" - } - }, - "node_modules/webidl-conversions": { - "version": "3.0.1", - "dev": true, - "license": "BSD-2-Clause" - }, - "node_modules/webpack-virtual-modules": { - "version": "0.6.2", - "dev": true, - "license": "MIT" - }, - "node_modules/whatwg-url": { - "version": "5.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, - "node_modules/which": { - "version": "4.0.0", - "license": "ISC", - "dependencies": { - "isexe": "^3.1.1" - }, - "bin": { - "node-which": "bin/which.js" - }, - "engines": { - "node": "^16.13.0 || >=18.0.0" - } - }, - "node_modules/which-module": { - "version": "2.0.1", - "license": "ISC" - }, - "node_modules/wrap-ansi": { - "version": "6.2.0", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi-cjs": { - "name": "wrap-ansi", - "version": "7.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "dev": true, - "license": "ISC" - }, - "node_modules/y18n": { - "version": "4.0.3", - "license": "ISC" - }, - "node_modules/yallist": { - "version": "5.0.0", - "dev": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=18" - } - }, - "node_modules/yaml": { - "version": "2.7.0", - "dev": true, - "license": "ISC", - "bin": { - "yaml": "bin.mjs" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/yargs": { - "version": "15.4.1", - "license": "MIT", - "dependencies": { - "cliui": "^6.0.0", - "decamelize": "^1.2.0", - "find-up": "^4.1.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^4.2.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^18.1.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/yargs-parser": { - "version": "18.1.3", - "license": "ISC", - "dependencies": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - }, - "engines": { - "node": ">=6" - } - } - } -} From 69967b759fb37d641080f0e7d298a9d4808f8d20 Mon Sep 17 00:00:00 2001 From: Lars Lehmann Date: Thu, 23 Jan 2025 21:29:49 +0100 Subject: [PATCH 128/209] fix: Error loading calender when transport has no date --- frontend/src/routes/collections/[id]/+page.svelte | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/frontend/src/routes/collections/[id]/+page.svelte b/frontend/src/routes/collections/[id]/+page.svelte index 3a4c1f7e..dea338f9 100644 --- a/frontend/src/routes/collections/[id]/+page.svelte +++ b/frontend/src/routes/collections/[id]/+page.svelte @@ -90,12 +90,14 @@ if (transportations) { dates = dates.concat( - transportations.map((transportation) => ({ - id: transportation.id, - start: transportation.date || '', // Ensure it's a string - end: transportation.end_date || transportation.date || '', // Ensure it's a string - title: transportation.name + (transportation.type ? ` (${transportation.type})` : '') - })) + transportations + .filter((i) => i.date) + .map((transportation) => ({ + id: transportation.id, + start: transportation.date || '', // Ensure it's a string + end: transportation.end_date || transportation.date || '', // Ensure it's a string + title: transportation.name + (transportation.type ? ` (${transportation.type})` : '') + })) ); } From 8fee537d8b08f5f1a6e08e2f22f48ded1e7af44d Mon Sep 17 00:00:00 2001 From: Lars Lehmann Date: Thu, 23 Jan 2025 21:56:19 +0100 Subject: [PATCH 129/209] feat: Open collection calendar on start date --- frontend/src/routes/collections/[id]/+page.svelte | 3 +++ 1 file changed, 3 insertions(+) diff --git a/frontend/src/routes/collections/[id]/+page.svelte b/frontend/src/routes/collections/[id]/+page.svelte index dea338f9..c9b46606 100644 --- a/frontend/src/routes/collections/[id]/+page.svelte +++ b/frontend/src/routes/collections/[id]/+page.svelte @@ -164,6 +164,9 @@ (new Date(collection.end_date).getTime() - new Date(collection.start_date).getTime()) / (1000 * 60 * 60 * 24) ) + 1; + + // Update `options.evdateents` when `collection.start_date` changes + options = { ...options, date: collection.start_date }; } if (collection.transportations) { transportations = collection.transportations; From abaee8c9c4b78dc1abe3e4fcffbe0fefcb6ca98a Mon Sep 17 00:00:00 2001 From: Sean Morley Date: Thu, 23 Jan 2025 17:48:55 -0500 Subject: [PATCH 130/209] feat: Add attachments array to recommendation object and update options with collection start date --- frontend/src/routes/collections/[id]/+page.svelte | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/frontend/src/routes/collections/[id]/+page.svelte b/frontend/src/routes/collections/[id]/+page.svelte index c9b46606..5e3a9c8e 100644 --- a/frontend/src/routes/collections/[id]/+page.svelte +++ b/frontend/src/routes/collections/[id]/+page.svelte @@ -166,6 +166,7 @@ ) + 1; // Update `options.evdateents` when `collection.start_date` changes + // @ts-ignore options = { ...options, date: collection.start_date }; } if (collection.transportations) { @@ -232,7 +233,8 @@ id: '', name: recomendation.tag, user_id: '' - } + }, + attachments: [] }; isAdventureModalOpen = true; } From 6a5bfbda2d57709b403e21740cf3ff0745965263 Mon Sep 17 00:00:00 2001 From: Sean Morley Date: Thu, 23 Jan 2025 17:51:56 -0500 Subject: [PATCH 131/209] feat: Enhance adventure marker popup with image carousel and visit details --- .../src/routes/collections/[id]/+page.svelte | 55 ++++++++++++++++--- 1 file changed, 48 insertions(+), 7 deletions(-) diff --git a/frontend/src/routes/collections/[id]/+page.svelte b/frontend/src/routes/collections/[id]/+page.svelte index ba758e47..6f495252 100644 --- a/frontend/src/routes/collections/[id]/+page.svelte +++ b/frontend/src/routes/collections/[id]/+page.svelte @@ -33,6 +33,8 @@ import ChecklistModal from '$lib/components/ChecklistModal.svelte'; import AdventureModal from '$lib/components/AdventureModal.svelte'; import TransportationModal from '$lib/components/TransportationModal.svelte'; + import CardCarousel from '$lib/components/CardCarousel.svelte'; + import { goto } from '$app/navigation'; export let data: PageData; console.log(data); @@ -840,16 +842,55 @@ > {#each adventures as adventure} {#if adventure.longitude && adventure.latitude} - + {adventure.category?.icon} - -
    {adventure.name}
    -

    - {adventure.category?.display_name + ' ' + adventure.category?.icon} -

    -
    + {#if isPopupOpen} + (isPopupOpen = false)}> + {#if adventure.images && adventure.images.length > 0} + + {/if} +
    {adventure.name}
    +

    + {adventure.is_visited ? $t('adventures.visited') : $t('adventures.planned')} +

    +

    + {adventure.category?.display_name + ' ' + adventure.category?.icon} +

    + {#if adventure.visits && adventure.visits.length > 0} +

    + {#each adventure.visits as visit} + {visit.start_date + ? new Date(visit.start_date).toLocaleDateString(undefined, { + timeZone: 'UTC' + }) + : ''} + {visit.end_date && + visit.end_date !== '' && + visit.end_date !== visit.start_date + ? ' - ' + + new Date(visit.end_date).toLocaleDateString(undefined, { + timeZone: 'UTC' + }) + : ''} +
    + {/each} +

    + {/if} + +
    + {/if}
    {/if} {/each} From d326d3832949c968410a89f611d1319580a01692 Mon Sep 17 00:00:00 2001 From: Sean Morley Date: Sun, 26 Jan 2025 20:06:47 -0500 Subject: [PATCH 132/209] feat: Update session cookie domain handling using publicsuffix2 and psl libraries --- backend/server/main/settings.py | 17 ++++++++++++++--- backend/server/requirements.txt | 3 ++- frontend/package.json | 1 + frontend/pnpm-lock.yaml | 16 ++++++++++++++++ frontend/src/routes/login/+page.server.ts | 23 +++++++++++------------ 5 files changed, 44 insertions(+), 16 deletions(-) diff --git a/backend/server/main/settings.py b/backend/server/main/settings.py index d8aa026c..4471b0d8 100644 --- a/backend/server/main/settings.py +++ b/backend/server/main/settings.py @@ -14,6 +14,7 @@ from dotenv import load_dotenv from os import getenv from pathlib import Path from urllib.parse import urlparse +from publicsuffix2 import get_sld # Load environment variables from .env file load_dotenv() @@ -132,16 +133,26 @@ SESSION_COOKIE_SAMESITE = None SESSION_COOKIE_SECURE = FRONTEND_URL.startswith('https') +# Parse the FRONTEND_URL parsed_url = urlparse(FRONTEND_URL) hostname = parsed_url.hostname + +# Check if the hostname is an IP address is_ip_address = hostname.replace('.', '').isdigit() + if is_ip_address: # Do not set a domain for IP addresses SESSION_COOKIE_DOMAIN = None else: - # Calculate the cookie domain for valid domain names - domain_parts = hostname.split('.') - SESSION_COOKIE_DOMAIN = '.' + '.'.join(domain_parts[-2:]) if len(domain_parts) > 1 else hostname + # Use publicsuffix2 to calculate the correct cookie domain + cookie_domain = get_sld(hostname) + if cookie_domain: + SESSION_COOKIE_DOMAIN = f".{cookie_domain}" + else: + # Fallback to the hostname if parsing fails + SESSION_COOKIE_DOMAIN = hostname + +print("SESSION_COOKIE_DOMAIN:", SESSION_COOKIE_DOMAIN) # Static files (CSS, JavaScript, Images) diff --git a/backend/server/requirements.txt b/backend/server/requirements.txt index 80ba65b0..dcd01250 100644 --- a/backend/server/requirements.txt +++ b/backend/server/requirements.txt @@ -20,4 +20,5 @@ django-ical==1.9.2 icalendar==6.1.0 ijson==3.3.0 tqdm==4.67.1 -overpy==0.7 \ No newline at end of file +overpy==0.7 +publicsuffix2==2.20191221 \ No newline at end of file diff --git a/frontend/package.json b/frontend/package.json index fcf148cf..b80fe8da 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -44,6 +44,7 @@ "emoji-picker-element": "^1.26.0", "gsap": "^3.12.7", "marked": "^15.0.4", + "psl": "^1.15.0", "qrcode": "^1.5.4", "svelte-i18n": "^4.0.1", "svelte-maplibre": "^0.9.8", diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index 546738eb..ea075faa 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -23,6 +23,9 @@ importers: marked: specifier: ^15.0.4 version: 15.0.4 + psl: + specifier: ^1.15.0 + version: 1.15.0 qrcode: specifier: ^1.5.4 version: 1.5.4 @@ -1913,6 +1916,13 @@ packages: protocol-buffers-schema@3.6.0: resolution: {integrity: sha512-TdDRD+/QNdrCGCE7v8340QyuXd4kIWIgapsE2+n/SaGiSSbomYl4TjHlvIoCWRpE7wFt02EpB35VVA2ImcBVqw==} + psl@1.15.0: + resolution: {integrity: sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + qrcode@1.5.4: resolution: {integrity: sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg==} engines: {node: '>=10.13.0'} @@ -4154,6 +4164,12 @@ snapshots: protocol-buffers-schema@3.6.0: {} + psl@1.15.0: + dependencies: + punycode: 2.3.1 + + punycode@2.3.1: {} + qrcode@1.5.4: dependencies: dijkstrajs: 1.0.3 diff --git a/frontend/src/routes/login/+page.server.ts b/frontend/src/routes/login/+page.server.ts index b2571a18..f952225f 100644 --- a/frontend/src/routes/login/+page.server.ts +++ b/frontend/src/routes/login/+page.server.ts @@ -1,5 +1,6 @@ import { fail, redirect, type RequestEvent } from '@sveltejs/kit'; - +// @ts-ignore +import psl from 'psl'; import type { Actions, PageServerLoad, RouteParams } from './$types'; import { getRandomBackground, getRandomQuote } from '$lib'; import { fetchCSRFToken } from '$lib/index.server'; @@ -105,7 +106,7 @@ export const actions: Actions = { } }; -function handleSuccessfulLogin(event: RequestEvent, response: Response) { +function handleSuccessfulLogin(event: RequestEvent, response: Response) { const setCookieHeader = response.headers.get('Set-Cookie'); if (setCookieHeader) { const sessionIdRegex = /sessionid=([^;]+).*?expires=([^;]+)/; @@ -113,24 +114,22 @@ function handleSuccessfulLogin(event: RequestEvent, response: Response) { if (match) { const [, sessionId, expiryString] = match; - // Get the proper cookie domain + // Get the proper cookie domain using psl const hostname = event.url.hostname; - const domainParts = hostname.split('.'); - let cookieDomain: string | undefined = undefined; + let cookieDomain; // Check if hostname is an IP address const isIPAddress = /^\d{1,3}(\.\d{1,3}){3}$/.test(hostname); if (!isIPAddress) { - if (domainParts.length > 2) { - // For subdomains like app.mydomain.com -> .mydomain.com - cookieDomain = '.' + domainParts.slice(-2).join('.'); - } else if (domainParts.length === 2) { - // For root domains like mydomain.com -> .mydomain.com - cookieDomain = '.' + hostname; + const parsed = psl.parse(hostname); + + if (parsed && parsed.domain) { + // Use the parsed domain (e.g., mydomain.com) + cookieDomain = `.${parsed.domain}`; } } - // Do not set a domain for IP addresses or single-part hostnames + // Do not set a domain for IP addresses or invalid hostnames console.log('Setting sessionid cookie with domain:', cookieDomain); From f5dc0ceb0a203693d62ec35df503361f13ca89ec Mon Sep 17 00:00:00 2001 From: Sean Morley Date: Sun, 26 Jan 2025 20:18:50 -0500 Subject: [PATCH 133/209] feat: Refactor session cookie domain handling to use psl for improved domain parsing --- backend/server/main/settings.py | 3 --- frontend/src/routes/+page.server.ts | 22 +++++++++++----------- frontend/src/routes/login/+page.server.ts | 2 -- 3 files changed, 11 insertions(+), 16 deletions(-) diff --git a/backend/server/main/settings.py b/backend/server/main/settings.py index 4471b0d8..063bf35a 100644 --- a/backend/server/main/settings.py +++ b/backend/server/main/settings.py @@ -152,9 +152,6 @@ else: # Fallback to the hostname if parsing fails SESSION_COOKIE_DOMAIN = hostname -print("SESSION_COOKIE_DOMAIN:", SESSION_COOKIE_DOMAIN) - - # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/1.7/howto/static-files/ diff --git a/frontend/src/routes/+page.server.ts b/frontend/src/routes/+page.server.ts index 7855cd8c..b8c71b5d 100644 --- a/frontend/src/routes/+page.server.ts +++ b/frontend/src/routes/+page.server.ts @@ -1,5 +1,7 @@ const PUBLIC_SERVER_URL = process.env['PUBLIC_SERVER_URL']; import { redirect, type Actions } from '@sveltejs/kit'; +// @ts-ignore +import psl from 'psl'; import { themes } from '$lib'; import { fetchCSRFToken } from '$lib/index.server'; import type { PageServerLoad } from './$types'; @@ -43,23 +45,21 @@ export const actions: Actions = { credentials: 'include' }); - // Determine the proper cookie domain + // Get the proper cookie domain using psl const hostname = event.url.hostname; - const domainParts = hostname.split('.'); + let cookieDomain; + + // Check if hostname is an IP address const isIPAddress = /^\d{1,3}(\.\d{1,3}){3}$/.test(hostname); - let cookieDomain: string | undefined = undefined; if (!isIPAddress) { - // Handle domain names - if (domainParts.length > 2) { - // For subdomains like app.mydomain.com -> .mydomain.com - cookieDomain = '.' + domainParts.slice(-2).join('.'); - } else if (domainParts.length === 2) { - // For root domains like mydomain.com -> .mydomain.com - cookieDomain = '.' + hostname; + const parsed = psl.parse(hostname); + + if (parsed && parsed.domain) { + // Use the parsed domain (e.g., mydomain.com) + cookieDomain = `.${parsed.domain}`; } } - // No domain is set for IP addresses or single-part hostnames like "localhost" // Delete the session cookie event.cookies.delete('sessionid', { diff --git a/frontend/src/routes/login/+page.server.ts b/frontend/src/routes/login/+page.server.ts index f952225f..68dcd883 100644 --- a/frontend/src/routes/login/+page.server.ts +++ b/frontend/src/routes/login/+page.server.ts @@ -131,8 +131,6 @@ function handleSuccessfulLogin(event: RequestEvent, respo } // Do not set a domain for IP addresses or invalid hostnames - console.log('Setting sessionid cookie with domain:', cookieDomain); - event.cookies.set('sessionid', sessionId, { path: '/', httpOnly: true, From 3468e82b043c3da238cca48904295cb286f09568 Mon Sep 17 00:00:00 2001 From: Sean Morley Date: Mon, 27 Jan 2025 09:23:48 -0500 Subject: [PATCH 134/209] feat: Sanitize FRONTEND_URL by removing quotes for improved URL parsing --- backend/server/main/settings.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/backend/server/main/settings.py b/backend/server/main/settings.py index 063bf35a..c83c711d 100644 --- a/backend/server/main/settings.py +++ b/backend/server/main/settings.py @@ -127,13 +127,16 @@ USE_L10N = True USE_TZ = True -FRONTEND_URL = getenv('FRONTEND_URL', 'http://localhost:3000') +unParsedFrontenedUrl = getenv('FRONTEND_URL', 'http://localhost:3000') +FRONTEND_URL = unParsedFrontenedUrl.replace("'", "").replace('"', '') SESSION_COOKIE_SAMESITE = None SESSION_COOKIE_SECURE = FRONTEND_URL.startswith('https') # Parse the FRONTEND_URL +# Remove and ' from the URL + parsed_url = urlparse(FRONTEND_URL) hostname = parsed_url.hostname From 697851e21098ececa708e9e33e58ce32c8f566d2 Mon Sep 17 00:00:00 2001 From: Patrick McGuire <60325264+mcguirepr89@users.noreply.github.com> Date: Mon, 27 Jan 2025 09:38:59 -0500 Subject: [PATCH 135/209] Update email.md Removing the single quotes allow SMTP to work -- it did not work otherwise. Also, [Google recommends TLS over SSL](https://support.google.com/a/answer/2520500?sjid=7564827219172741277-NA) --- documentation/docs/configuration/email.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/documentation/docs/configuration/email.md b/documentation/docs/configuration/email.md index 53129105..e83ac6c9 100644 --- a/documentation/docs/configuration/email.md +++ b/documentation/docs/configuration/email.md @@ -13,14 +13,14 @@ environment: ```yaml environment: - - EMAIL_BACKEND='email' - - EMAIL_HOST='smtp.gmail.com' - - EMAIL_USE_TLS=False + - EMAIL_BACKEND=email + - EMAIL_HOST=smtp.gmail.com + - EMAIL_USE_TLS=True - EMAIL_PORT=587 - - EMAIL_USE_SSL=True - - EMAIL_HOST_USER='user' - - EMAIL_HOST_PASSWORD='password' - - DEFAULT_FROM_EMAIL='user@example.com' + - EMAIL_USE_SSL=False + - EMAIL_HOST_USER=user + - EMAIL_HOST_PASSWORD=password + - DEFAULT_FROM_EMAIL=user@example.com ``` ## Customizing Emails From b68e2dedaac6ab31051a6689bd24bb2098cb98dd Mon Sep 17 00:00:00 2001 From: Sean Morley Date: Mon, 27 Jan 2025 20:19:33 -0500 Subject: [PATCH 136/209] feat: Add new background image entry for adventurelog showcase --- frontend/src/lib/json/backgrounds.json | 5 +++++ .../backgrounds/adventurelog_showcase_6.webp | Bin 0 -> 778776 bytes 2 files changed, 5 insertions(+) create mode 100644 frontend/static/backgrounds/adventurelog_showcase_6.webp diff --git a/frontend/src/lib/json/backgrounds.json b/frontend/src/lib/json/backgrounds.json index be43594c..7b1b896a 100644 --- a/frontend/src/lib/json/backgrounds.json +++ b/frontend/src/lib/json/backgrounds.json @@ -24,6 +24,11 @@ "url": "backgrounds/adventurelog_showcase_5.webp", "author": "Sean Morley", "location": "Hoboken, New Jersey, USA" + }, + { + "url": "backgrounds/adventurelog_showcase_6.webp", + "author": "Sean Morley", + "location": "Smugglers' Notch Resort, Vermont, USA" } ] } diff --git a/frontend/static/backgrounds/adventurelog_showcase_6.webp b/frontend/static/backgrounds/adventurelog_showcase_6.webp new file mode 100644 index 0000000000000000000000000000000000000000..1cdf2c17f61eac6c5837dfe39e2616ca15edfb2c GIT binary patch literal 778776 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%0DgLNx!3OdgV6r{#>=~`9gX83_}z0exnyhN2_>zh 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 z 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

    -SE^OsXU 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

    m|u^9Vt_DdiTs<7dfDgZXu+dQ>{eBm5s2y|k&V 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 Date: Tue, 28 Jan 2025 14:04:41 -0600 Subject: [PATCH 137/209] Update unraid.md Update the unraid wiki page to be more precise and easier to understand --- documentation/docs/install/unraid.md | 49 +++++++++++++++++++++++----- 1 file changed, 41 insertions(+), 8 deletions(-) diff --git a/documentation/docs/install/unraid.md b/documentation/docs/install/unraid.md index cb844553..ed42b23c 100644 --- a/documentation/docs/install/unraid.md +++ b/documentation/docs/install/unraid.md @@ -1,29 +1,62 @@ # Installation with Unraid -AdventureLog is available in the Unraid Community Applications store. You can install it by searching for "AdventureLog" in the Community Applications store. +AdventureLog is available in the Unraid Community Applications store. You can install it by searching for `AdventureLog` in the Community Applications store, where you will find the frontend and the backend, the database can be found by searching `PostGIS`. -Community Applications Page: [AdventureLog on CA Store](https://unraid.net/community/apps?q=Adventurelog) +Community Applications Page for AdventureLog: [AdventureLog on CA Store](https://unraid.net/community/apps?q=AdventureLog) +Community Applications Page for PostGIS: [PostGIS on CA Store](https://unraid.net/community/apps?q=PostGIS) ## Installation Configuration -It is recommended to install applications in this order. +It is recommended to install the applications in the order of these instructions. +Also insure they are all on the same custom network so they can communicate to one another, you can create one by running the following command in your command line with example being set to your wanted name. This network will then showup for selection when making the apps/containers. +```bash +docker network create example +``` ## Database -- To find the Database Application, search for `PostGIS` on the Unraid App Store and fill out the fields as follows: -- Ensure that the POSTGRES_USER, POSTGRES_PASSWORD, POSTGRES_DB are set in the PostGIS container if not add them custom variables +- To find the Database Application, search for `PostGIS` on the Unraid App Store and fill out the fields as shown below +- Network type should be set to your custom network +- Change the repository version to `postgis/postgis:15-3.3` +- Ensure that the POSTGRES_DB, POSTGRES_USER, POSTGRES_PASSWORD are set in the PostGIS container, if not then add them as custom variables, the other variables are irrelevant for this setup and should be removed. +| Name | Required | Description | Default Value | +| ------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------- | +| `POSTGRES_DB` | Yes | What the name of the database in PostGIS will be. | N/A | +| `POSTGRES_USER` | Yes | Name of the user generated on first start that will have access to the database | N/A | +| `POSTGRES_PASSWORD` | Yes | Password of the user that will be generated on first start | N/A | ![/static/img/unraid-config-2.png](/unraid-config-2.png) ## Backend -- Cache Configuration: This option is useful only if your appdata share is stored on a cache drive, which is used to speed up read/write operations for your containerized applications. -- Note: if your running the server in a docker network that is other than "host" (for example "bridge") than you need to add the IP of the host machine in the CSRF Trusted Origins variable. +- Network type should be set to your custom network +- **Note:** If you're running the server in a docker network that is other than "host" (for example "bridge") than you need to add the IP of the host machine in the CSRF Trusted Origins variable instead of using localhost, this is only necessary when accessing locally, otherwise you will use the domain name. +| Name | Required | Description | Default Value | +| ----------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------- | +| `API Port` | Yes | This is the port of the backend. This is a port not a variable. | 8016 | +| `SECRET_KEY` | Yes | Secret Backend Key. Change to anything. | CHANGEME | +| `PGHOST` | Yes | This is how the backend will access the database, use the database containers name. | PostGIS | +| `PGDATABASE` | Yes | Name of the database in PostGIS to access. | database | +| `PGUSER` | Yes | Name of the User to access with. This is the same as the variable in the database. | adventure | +| `PGPASSWORD` | Yes | Password of the User it's accessing with. This is the same as the variable in the database. | changeme123 | +| `PGPORT` | No | Port to access the database at. | 5432 | +| `DJANGO_ADMIN_USERNAME` | Yes | Default username for admin access. | admin | +| `DJANGO_ADMIN_PASSWORD` | Yes | Default password for admin access, change after inital login. | admin | +| `DJANGO_ADMIN_EMAIL` | Yes | Default admin user's email. **Note:** You cannot make more than one user with each email. | admin@example.com | +| `PUBLIC_URL` | Yes | This needs to match how you will connect to the backend, so either localhost with matching port or domain. It is used for the creation of image urls. | http://localhost:8016 | +| `CSRF_TRUSTED_ORIGINS` | Yes | This needs to be changed to the urls of how you connect to your backend server and frontend. These values are comma seperated. | http://localhost:8016 | +| `FRONTEND_URL` | Yes | This needs to match how you will connect to the frontend, so either localhost with matching port or domain. This link should be available for all users. Used for email generation. | http://localhost:8015 | ![/static/img/unraid-config-1.png](/unraid-config-1.png) ## Frontend -- By default, the frontend connects to the backend using `http://server:8000`. This will work if both the frontend and backend are on the same network. Otherwise, you’ll need to configure it to use the exposed port (default: 8016). +- By default, the frontend connects to the backend using `http://server:8000`. This will work if both the frontend and backend are on the same network and the backend is named server. Otherwise, you’ll need to configure it to use the exposed port (default: 8016). +| Name | Required | Description | Default Value | +| ------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------- | +| `WEB UI Port` | Yes | The port of the frontend. This is not a variable. | 8015 | +| `PUBLIC_SERVER_URL` | Yes | What the frontend SSR server uses to connect to the backend. Change server to the name of the backend container. | http://server:8000 | +| `ORIGIN` | Sometimes | Set to the URL you will access the frontend from such as localhost with corret port or set it to the domain of what you will acess the app from. | http://localhost:8015 | +| `BODY_SIZE_LIMIT` | Yes | Used to set the maximum upload size to the server. Should be changed to prevent someone from uploading too much! Custom values must be set in **kiliobytes**. | Infinity | ![/static/img/unraid-config-3.png](/unraid-config-3.png) From 145627b1ebbf484c553e73bcf02226375850afca Mon Sep 17 00:00:00 2001 From: ThunderLord956 <97125592+ThunderLord956@users.noreply.github.com> Date: Tue, 28 Jan 2025 15:11:33 -0600 Subject: [PATCH 138/209] Update unraid.md Try to fix tables --- documentation/docs/install/unraid.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/documentation/docs/install/unraid.md b/documentation/docs/install/unraid.md index ed42b23c..5367e11d 100644 --- a/documentation/docs/install/unraid.md +++ b/documentation/docs/install/unraid.md @@ -19,6 +19,7 @@ docker network create example - Network type should be set to your custom network - Change the repository version to `postgis/postgis:15-3.3` - Ensure that the POSTGRES_DB, POSTGRES_USER, POSTGRES_PASSWORD are set in the PostGIS container, if not then add them as custom variables, the other variables are irrelevant for this setup and should be removed. + | Name | Required | Description | Default Value | | ------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------- | | `POSTGRES_DB` | Yes | What the name of the database in PostGIS will be. | N/A | @@ -31,6 +32,7 @@ docker network create example - Network type should be set to your custom network - **Note:** If you're running the server in a docker network that is other than "host" (for example "bridge") than you need to add the IP of the host machine in the CSRF Trusted Origins variable instead of using localhost, this is only necessary when accessing locally, otherwise you will use the domain name. + | Name | Required | Description | Default Value | | ----------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------- | | `API Port` | Yes | This is the port of the backend. This is a port not a variable. | 8016 | @@ -52,6 +54,7 @@ docker network create example ## Frontend - By default, the frontend connects to the backend using `http://server:8000`. This will work if both the frontend and backend are on the same network and the backend is named server. Otherwise, you’ll need to configure it to use the exposed port (default: 8016). + | Name | Required | Description | Default Value | | ------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------- | | `WEB UI Port` | Yes | The port of the frontend. This is not a variable. | 8015 | From 41cc47d6318f7d83ff9c67e608f185499cf84b95 Mon Sep 17 00:00:00 2001 From: ThunderLord956 <97125592+ThunderLord956@users.noreply.github.com> Date: Tue, 28 Jan 2025 15:13:10 -0600 Subject: [PATCH 139/209] Update unraid.md Separate 2 lines in Installation Configuration --- documentation/docs/install/unraid.md | 1 + 1 file changed, 1 insertion(+) diff --git a/documentation/docs/install/unraid.md b/documentation/docs/install/unraid.md index 5367e11d..5383b853 100644 --- a/documentation/docs/install/unraid.md +++ b/documentation/docs/install/unraid.md @@ -8,6 +8,7 @@ Community Applications Page for PostGIS: [PostGIS on CA Store](https://unraid.ne ## Installation Configuration It is recommended to install the applications in the order of these instructions. + Also insure they are all on the same custom network so they can communicate to one another, you can create one by running the following command in your command line with example being set to your wanted name. This network will then showup for selection when making the apps/containers. ```bash docker network create example From defd97fc02e87b0535a97819ca45d781b2a1a8db Mon Sep 17 00:00:00 2001 From: Thunder Date: Tue, 28 Jan 2025 23:23:47 -0600 Subject: [PATCH 140/209] Mass documentation spelling check, as well as improving the unraid installation guide, and some small changes to the docker installation guide. --- documentation/docs/changelogs/v0-8-0.md | 6 +- .../docs/configuration/social_auth.md | 2 +- .../configuration/social_auth/authentik.md | 4 +- .../docs/configuration/social_auth/github.md | 4 +- documentation/docs/configuration/updating.md | 2 +- documentation/docs/guides/v0-7-1_migration.md | 2 +- documentation/docs/install/docker.md | 38 ++++----- documentation/docs/install/kustomize.md | 2 +- .../docs/install/nginx_proxy_manager.md | 2 +- documentation/docs/install/unraid.md | 83 ++++++++++--------- 10 files changed, 75 insertions(+), 70 deletions(-) diff --git a/documentation/docs/changelogs/v0-8-0.md b/documentation/docs/changelogs/v0-8-0.md index 48d9c18e..90f6e433 100644 --- a/documentation/docs/changelogs/v0-8-0.md +++ b/documentation/docs/changelogs/v0-8-0.md @@ -19,7 +19,7 @@ I’m thrilled to announce the release of **AdventureLog v0.8.0**, a huge update ### 🚗 Transportation - **New Transportation Edit Modal**: Includes detailed origin and destination location information for better trip planning. -- **Autocomplete for Airport Codes**: Quickly find and add airport codes while planning transportations. +- **Autocomplete for Airport Codes**: Quickly find and add airport codes while planning transportation. - **New Transportation Card Design**: Redesigned for better clarity and aesthetics. --- @@ -27,7 +27,7 @@ I’m thrilled to announce the release of **AdventureLog v0.8.0**, a huge update ### 📝 Notes and Checklists - **New Modals for Notes and Checklists**: Simplified creation and editing of your notes and checklists. -- **Delete Confirmation**: Added a confirmation step when deleting notes, checklists, or transportations to prevent accidental deletions. +- **Delete Confirmation**: Added a confirmation step when deleting notes, checklists, or transportation to prevent accidental deletions. --- @@ -41,7 +41,7 @@ I’m thrilled to announce the release of **AdventureLog v0.8.0**, a huge update ### 🗓️ Calendar -- **Calendar View**: View your adventures and transportations in a calendar layout. +- **Calendar View**: View your adventures and transportation in a calendar layout. - **ICS File Export**: Export your calendar as an ICS file for use with external apps like Google Calendar or Outlook. --- diff --git a/documentation/docs/configuration/social_auth.md b/documentation/docs/configuration/social_auth.md index 9da70e16..68214b98 100644 --- a/documentation/docs/configuration/social_auth.md +++ b/documentation/docs/configuration/social_auth.md @@ -1,6 +1,6 @@ # Social Authentication -AdventureLog support autentication via 3rd party services and self-hosted identity providers. Once these services are enabled, users can log in to AdventureLog using their accounts from these services and link exising AdventureLog accounts to these services for easier access. +AdventureLog support authentication via 3rd party services and self-hosted identity providers. Once these services are enabled, users can log in to AdventureLog using their accounts from these services and link existing AdventureLog accounts to these services for easier access. The steps for each service varies so please refer to the specific service's documentation for more information. diff --git a/documentation/docs/configuration/social_auth/authentik.md b/documentation/docs/configuration/social_auth/authentik.md index 82a8d55f..1c13f50c 100644 --- a/documentation/docs/configuration/social_auth/authentik.md +++ b/documentation/docs/configuration/social_auth/authentik.md @@ -22,14 +22,14 @@ To enable Authentik as an identity provider, the administrator must first config ### AdventureLog Configuration -This configuration is done in the [Admin Panel](../../guides/admin_panel.md). You can either launch the pannel directly from the `Settings` page or navigate to `/admin` on your AdventureLog server. +This configuration is done in the [Admin Panel](../../guides/admin_panel.md). You can either launch the panel directly from the `Settings` page or navigate to `/admin` on your AdventureLog server. 1. Login to AdventureLog as an administrator and navigate to the `Settings` page. 2. Scroll down to the `Administration Settings` and launch the admin panel. 3. In the admin panel, navigate to the `Social Accounts` section and click the add button next to `Social applications`. Fill in the following fields: - Provider: `OpenID Connect` - - Provider ID: Autnentik Client ID + - Provider ID: Authentik Client ID - Name: `Authentik` - Client ID: Authentik Client ID - Secret Key: Authentik Client Secret diff --git a/documentation/docs/configuration/social_auth/github.md b/documentation/docs/configuration/social_auth/github.md index 2239dc7a..cbfe41b7 100644 --- a/documentation/docs/configuration/social_auth/github.md +++ b/documentation/docs/configuration/social_auth/github.md @@ -20,7 +20,7 @@ To enable GitHub as an identity provider, the administrator must first configure ### AdventureLog Configuration -This configuration is done in the [Admin Panel](../../guides/admin_panel.md). You can either launch the pannel directly from the `Settings` page or navigate to `/admin` on your AdventureLog server. +This configuration is done in the [Admin Panel](../../guides/admin_panel.md). You can either launch the panel directly from the `Settings` page or navigate to `/admin` on your AdventureLog server. 1. Login to AdventureLog as an administrator and navigate to the `Settings` page. 2. Scroll down to the `Administration Settings` and launch the admin panel. @@ -41,4 +41,4 @@ This configuration is done in the [Admin Panel](../../guides/admin_panel.md). Yo 4. Save the configuration. -Users should now be able to log in to AdventureLog using their GitHub account, and link it to exisiting accounts. +Users should now be able to log in to AdventureLog using their GitHub account, and link it to existing accounts. diff --git a/documentation/docs/configuration/updating.md b/documentation/docs/configuration/updating.md index aff9dd7d..85fec59b 100644 --- a/documentation/docs/configuration/updating.md +++ b/documentation/docs/configuration/updating.md @@ -1,6 +1,6 @@ # Updating -Updating AdventureLog when using docker can be quite easy. Run the folowing commands to pull the latest version and restart the containers. Make sure you backup your instance before updating just in case! +Updating AdventureLog when using docker can be quite easy. Run the following commands to pull the latest version and restart the containers. Make sure you backup your instance before updating just in case! Note: Make sure you are in the same directory as your `docker-compose.yml` file. diff --git a/documentation/docs/guides/v0-7-1_migration.md b/documentation/docs/guides/v0-7-1_migration.md index 548d9de5..77aaeaf2 100644 --- a/documentation/docs/guides/v0-7-1_migration.md +++ b/documentation/docs/guides/v0-7-1_migration.md @@ -1,6 +1,6 @@ # AdventureLog v0.7.1 Migration -In order to make installation easier, the AdventureLog v0.7.1 release has **removed the need for a seperate nginx container** and cofig to serve the media files. Instead, the media files are now served by an instance of nginx running in the same container as the Django application. +In order to make installation easier, the AdventureLog v0.7.1 release has **removed the need for a separate nginx container** and config to serve the media files. Instead, the media files are now served by an instance of nginx running in the same container as the Django application. ## Docker Compose Changes diff --git a/documentation/docs/install/docker.md b/documentation/docs/install/docker.md index c53d980e..0f987d24 100644 --- a/documentation/docs/install/docker.md +++ b/documentation/docs/install/docker.md @@ -25,31 +25,31 @@ wget https://raw.githubusercontent.com/seanmorley15/AdventureLog/main/docker-com Here is a summary of the configuration options available in the `docker-compose.yml` file: - + ### Frontend Container (web) -| Name | Required | Description | Default Value | -| ------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------- | -| `PUBLIC_SERVER_URL` | Yes | What the frontend SSR server uses to connect to the backend. | http://server:8000 | -| `ORIGIN` | Sometimes | Not needed if using HTTPS. If not, set it to the domain of what you will acess the app from. | http://localhost:8015 | -| `BODY_SIZE_LIMIT` | Yes | Used to set the maximum upload size to the server. Should be changed to prevent someone from uploading too much! Custom values must be set in **kiliobytes**. | Infinity | +| Name | Required | Description | Default Value | +| ------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------- | +| `PUBLIC_SERVER_URL` | Yes | What the frontend SSR server uses to connect to the backend. | ```http://server:8000``` | +| `ORIGIN` | Sometimes | Not needed if using HTTPS. If not, set it to the domain of what you will access the app from. | ```http://localhost:8015``` | +| `BODY_SIZE_LIMIT` | Yes | Used to set the maximum upload size to the server. Should be changed to prevent someone from uploading too much! Custom values must be set in **kilobytes**. | ```Infinity``` | ### 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 | -| `PGPORT` | No | Database port. | 5432 | -| `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. | admin@example.com | -| `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 publically accessible url to the **frontend** container. This link should be accessable for all users. Used for email generation. | http://localhost:8015 | +| Name | Required | Description | Default Value | +| ----------------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------- | +| `PGHOST` | Yes | Database host. | ```db``` | +| `PGDATABASE` | Yes | Database. | ```database``` | +| `PGUSER` | Yes | Database user. | ```adventure``` | +| `PGPASSWORD` | Yes | Database password. | ```changeme123``` | +| `PGPORT` | No | Database port. | ```5432``` | +| `DJANGO_ADMIN_USERNAME` | Yes | Default username. | ```admin``` | +| `DJANGO_ADMIN_PASSWORD` | Yes | Default password, change after initial login. | ```admin``` | +| `DJANGO_ADMIN_EMAIL` | Yes | Default user's email. | ```admin@example.com``` | +| `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 origins where you use your backend server and frontend. These values are comma separated. | ```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 diff --git a/documentation/docs/install/kustomize.md b/documentation/docs/install/kustomize.md index 5e9d0744..2c6e09bc 100644 --- a/documentation/docs/install/kustomize.md +++ b/documentation/docs/install/kustomize.md @@ -25,7 +25,7 @@ You must [expose tailnet IPs to your cluster](https://tailscale.com/kb/1438/kube ## Getting Started -Take a look at the [example config](https://github.com/seanmorley15/AdventureLog/blob/main/kustomization.yml) and modify it for your usecase. +Take a look at the [example config](https://github.com/seanmorley15/AdventureLog/blob/main/kustomization.yml) and modify it for your use case. ## Environment Variables diff --git a/documentation/docs/install/nginx_proxy_manager.md b/documentation/docs/install/nginx_proxy_manager.md index fc58b7a3..a6b661ab 100644 --- a/documentation/docs/install/nginx_proxy_manager.md +++ b/documentation/docs/install/nginx_proxy_manager.md @@ -21,7 +21,7 @@ Ensure that the Nginx Proxy Manager and AdventureLog containers are on the same docker network create nginx-proxy-manager ``` -Add the folowing to the bottom of the `docker-compose.yml` file for the Nginx Proxy Manager service and the AdventureLog service. +Add the following to the bottom of the `docker-compose.yml` file for the Nginx Proxy Manager service and the AdventureLog service. ```yaml networks: diff --git a/documentation/docs/install/unraid.md b/documentation/docs/install/unraid.md index 5383b853..b41cd0f5 100644 --- a/documentation/docs/install/unraid.md +++ b/documentation/docs/install/unraid.md @@ -1,66 +1,71 @@ # Installation with Unraid -AdventureLog is available in the Unraid Community Applications store. You can install it by searching for `AdventureLog` in the Community Applications store, where you will find the frontend and the backend, the database can be found by searching `PostGIS`. +AdventureLog is available in the Unraid Community Applications store. You can install it by searching for `AdventureLog` in the Community Applications store, where you will find the frontend and the backend. The database can be found by searching `PostGIS`. -Community Applications Page for AdventureLog: [AdventureLog on CA Store](https://unraid.net/community/apps?q=AdventureLog) +Community Applications Page for AdventureLog: [AdventureLog on CA Store](https://unraid.net/community/apps?q=AdventureLog)\ Community Applications Page for PostGIS: [PostGIS on CA Store](https://unraid.net/community/apps?q=PostGIS) ## Installation Configuration -It is recommended to install the applications in the order of these instructions. +- **Note:** It is recommended to install the applications in the order of these instructions, as failing to do so could cause issues.\ +- Container names can be set to whatever you desire. +- Also ensure they are all on the same custom network so they can communicate with one another. You can create one by running the following command in your command line, with `example` being set to your desired name. This network will then show up for selection when making the apps/containers. -Also insure they are all on the same custom network so they can communicate to one another, you can create one by running the following command in your command line with example being set to your wanted name. This network will then showup for selection when making the apps/containers. ```bash docker network create example ``` ## Database -- To find the Database Application, search for `PostGIS` on the Unraid App Store and fill out the fields as shown below -- Network type should be set to your custom network +- Network type should be set to your **custom network**. +- There is **no** AdventureLog---Database app, to find the database application search for `PostGIS` on the Unraid App Store then add and fill out the fields as shown below - Change the repository version to `postgis/postgis:15-3.3` -- Ensure that the POSTGRES_DB, POSTGRES_USER, POSTGRES_PASSWORD are set in the PostGIS container, if not then add them as custom variables, the other variables are irrelevant for this setup and should be removed. +- Ensure that the variables ```POSTGRES_DB```, ```POSTGRES_USER```, and ```POSTGRES_PASSWORD``` are set in the ```PostGIS``` container. If not, then add them as custom variables. The template should have ```POSTGRES_PASSWORD``` already and you will simply have to add ```POSTGRES_DB``` and ```POSTGRES_USER```. +- The forwarded port of ```5012``` is not needed unless you plan to access the database outside of the container's network. -| Name | Required | Description | Default Value | -| ------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------- | -| `POSTGRES_DB` | Yes | What the name of the database in PostGIS will be. | N/A | -| `POSTGRES_USER` | Yes | Name of the user generated on first start that will have access to the database | N/A | -| `POSTGRES_PASSWORD` | Yes | Password of the user that will be generated on first start | N/A | +| Name | Required | Description | Default Value | +| ------------------- | -------- | -------------------------------------------------------------------------------- | --------------- | +| `POSTGRES_DB` | Yes | The name of the database in PostGIS. | `N/A` | +| `POSTGRES_USER` | Yes | Name of the user generated on first start that will have access to the database. | `N/A` | +| `POSTGRES_PASSWORD` | Yes | Password of the user that will be generated on first start. | `N/A` | -![/static/img/unraid-config-2.png](/unraid-config-2.png) +- Here's some visual instructions of how to configure the database template, click the image to open larger version in new tab.\ +[![/static/img/unraid-config-2.png](/unraid-config-2.png)](/unraid-config-2.png) ## Backend -- Network type should be set to your custom network -- **Note:** If you're running the server in a docker network that is other than "host" (for example "bridge") than you need to add the IP of the host machine in the CSRF Trusted Origins variable instead of using localhost, this is only necessary when accessing locally, otherwise you will use the domain name. +- Network type should be set to your **custom network**. +- **Note:** If you're running the server in a docker network that is other than "host" (for example "bridge"), then you need to add the IP of the host machine in the CSRF Trusted Origins variable instead of using localhost. This is only necessary when accessing locally, otherwise you will use the domain name. -| Name | Required | Description | Default Value | -| ----------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------- | -| `API Port` | Yes | This is the port of the backend. This is a port not a variable. | 8016 | -| `SECRET_KEY` | Yes | Secret Backend Key. Change to anything. | CHANGEME | -| `PGHOST` | Yes | This is how the backend will access the database, use the database containers name. | PostGIS | -| `PGDATABASE` | Yes | Name of the database in PostGIS to access. | database | -| `PGUSER` | Yes | Name of the User to access with. This is the same as the variable in the database. | adventure | -| `PGPASSWORD` | Yes | Password of the User it's accessing with. This is the same as the variable in the database. | changeme123 | -| `PGPORT` | No | Port to access the database at. | 5432 | -| `DJANGO_ADMIN_USERNAME` | Yes | Default username for admin access. | admin | -| `DJANGO_ADMIN_PASSWORD` | Yes | Default password for admin access, change after inital login. | admin | -| `DJANGO_ADMIN_EMAIL` | Yes | Default admin user's email. **Note:** You cannot make more than one user with each email. | admin@example.com | -| `PUBLIC_URL` | Yes | This needs to match how you will connect to the backend, so either localhost with matching port or domain. It is used for the creation of image urls. | http://localhost:8016 | -| `CSRF_TRUSTED_ORIGINS` | Yes | This needs to be changed to the urls of how you connect to your backend server and frontend. These values are comma seperated. | http://localhost:8016 | -| `FRONTEND_URL` | Yes | This needs to match how you will connect to the frontend, so either localhost with matching port or domain. This link should be available for all users. Used for email generation. | http://localhost:8015 | +| Name | Required | Description | Default Value | +| ----------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------- | +| `API Port` | Yes | This is the port of the backend. This is a port, not a variable. | `8016` | +| `PGHOST` | Yes | This is how the backend will access the database. Use the database container's name. | `N/A` | +| `PGDATABASE` | Yes | Name of the database in PostGIS to access. | `N/A` | +| `PGUSER` | Yes | Name of the user to access with. This is the same as the variable in the database. | `N/A` | +| `PGPASSWORD` | Yes | Password of the user it's accessing with. This is the same as the variable in the database. | `N/A` | +| `SECRET_KEY` | Yes | Secret Backend Key. Change to anything. | `N/A` | +| `DJANGO_ADMIN_USERNAME` | Yes | Default username for admin access. | `admin` | +| `DJANGO_ADMIN_EMAIL` | Yes | Default admin user's email. **Note:** You cannot make more than one user with each email. | `N/A` | +| `DJANGO_ADMIN_PASSWORD` | Yes | Default password for admin access. Change after initial login. | `N/A` | +| `PUBLIC_URL` | Yes | This needs to match how you will connect to the backend, so either local ip with matching port or domain. It is used for the creation of image URLs. | `http://IP_ADDRESS:8016` | +| `FRONTEND_URL` | Yes | This needs to match how you will connect to the frontend, so either local ip with matching port or domain. This link should be available for all users. Used for email generation. | `http://IP_ADDRESS:8015` | +| `CSRF_TRUSTED_ORIGINS` | Yes | This needs to be changed to the URLs of how you connect to your backend server and frontend. These values are comma-separated and usually the same as the 2 above values. | `http://IP_ADDRESS:8016,http://IP_ADDRESS:8015` | -![/static/img/unraid-config-1.png](/unraid-config-1.png) +- Here's some visual instructions of how to configure the backend template, click the image to open larger version in new tab.\ +[![static/img/unraid-config-1.png](/unraid-config-1.png)](/unraid-config-1.png) ## Frontend -- By default, the frontend connects to the backend using `http://server:8000`. This will work if both the frontend and backend are on the same network and the backend is named server. Otherwise, you’ll need to configure it to use the exposed port (default: 8016). +- Network type should be set to your **custom network**. +- **Note:** The default value for ```PUBLIC_SERVER_URL``` is ```http://IP_ADDRESS:8000```, however ```IP_ADDRESS``` **should be changed** to the name of the backend container for simplicity. -| Name | Required | Description | Default Value | -| ------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------- | -| `WEB UI Port` | Yes | The port of the frontend. This is not a variable. | 8015 | -| `PUBLIC_SERVER_URL` | Yes | What the frontend SSR server uses to connect to the backend. Change server to the name of the backend container. | http://server:8000 | -| `ORIGIN` | Sometimes | Set to the URL you will access the frontend from such as localhost with corret port or set it to the domain of what you will acess the app from. | http://localhost:8015 | -| `BODY_SIZE_LIMIT` | Yes | Used to set the maximum upload size to the server. Should be changed to prevent someone from uploading too much! Custom values must be set in **kiliobytes**. | Infinity | +| Name | Required | Description | Default Value | +| ------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------ | +| `WEB UI Port` | Yes | The port of the frontend. This is not a variable. | `8015` | +| `PUBLIC_SERVER_URL` | Yes | What the frontend SSR server uses to connect to the backend. Change `IP_ADDRESS` to the name of the backend container. | `http://IP_ADDRESS:8000` | +| `ORIGIN` | Sometimes| Set to the URL you will access the frontend from, such as localhost with correct port, or set it to the domain of what you will access the app from. | `http://IP_ADDRESS:8015` | +| `BODY_SIZE_LIMIT` | Yes | Used to set the maximum upload size to the server. Should be changed to prevent someone from uploading too much! Custom values must be set in **kilobytes**. | `Infinity` | -![/static/img/unraid-config-3.png](/unraid-config-3.png) +- Here's some visual instructions of how to configure the frontend template, click the image to open larger version in new tab.\ +[![/static/img/unraid-config-3.png](/unraid-config-3.png)](/unraid-config-3.png) From 85b55660f9df6f511bfcc25d9a4781d8406d9b91 Mon Sep 17 00:00:00 2001 From: Sean Morley Date: Wed, 29 Jan 2025 22:50:53 -0500 Subject: [PATCH 141/209] feat: Update user profile handling and enhance public user details response --- CONTRIBUTING.md | 83 ++------ backend/server/.env.example | 4 +- backend/server/main/urls.py | 2 +- backend/server/users/views.py | 26 ++- frontend/src/lib/components/Avatar.svelte | 4 +- frontend/src/locales/en.json | 6 +- frontend/src/routes/profile/+page.server.ts | 29 --- frontend/src/routes/profile/+page.svelte | 112 ----------- .../src/routes/profile/[uuid]/+page.server.ts | 40 ++++ .../src/routes/profile/[uuid]/+page.svelte | 180 ++++++++++++++++++ 10 files changed, 274 insertions(+), 212 deletions(-) delete mode 100644 frontend/src/routes/profile/+page.server.ts delete mode 100644 frontend/src/routes/profile/+page.svelte create mode 100644 frontend/src/routes/profile/[uuid]/+page.server.ts create mode 100644 frontend/src/routes/profile/[uuid]/+page.svelte diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c980b888..7b49b3d2 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,91 +1,50 @@ # Contributing to AdventureLog -When contributing to this repository, please first discuss the change you wish to make via issue, -email, or any other method with the owners of this repository before making a change. - -Please note we have a code of conduct, please follow it in all your interactions with the project. +We’re excited to have you contribute to AdventureLog! To ensure that this community remains welcoming and productive for all users and developers, please follow this simple Code of Conduct. ## Pull Request Process -1. Please make sure you create an issue first for your change so you can link any pull requests to this issue. There should be a clear relationship between pull requests and issues. -2. Update the README.md with details of changes to the interface, this includes new environment - variables, exposed ports, useful file locations and container parameters. -3. Increase the version numbers in any examples files and the README.md to the new version that this - Pull Request would represent. The versioning scheme we use is [SemVer](http://semver.org/). -4. You may merge the Pull Request in once you have the sign-off of two other developers, or if you - do not have permission to do that, you may request the second reviewer to merge it for you. +1. **Open an Issue First**: Discuss any changes or features you plan to implement by opening an issue. This helps to clarify your idea and ensures there’s a shared understanding. +2. **Document Changes**: If your changes impact the user interface, add new environment variables, or introduce new container configurations, make sure to update the documentation accordingly. The documentation is located in the `documentation` folder. +3. **Pull Request**: Submit a pull request with your changes. Make sure to reference the issue you opened in the description. ## Code of Conduct ### Our Pledge -In the interest of fostering an open and welcoming environment, we as -contributors and maintainers pledge to making participation in our project and -our community a harassment-free experience for everyone, regardless of age, body -size, disability, ethnicity, gender identity and expression, level of experience, -nationality, personal appearance, race, religion, or sexual identity and -orientation. +At AdventureLog, we are committed to creating a community that fosters adventure, exploration, and innovation. We encourage diverse participation and strive to maintain a space where everyone feels welcome to contribute, regardless of their background or experience level. We ask that you contribute with respect and kindness, making sure to prioritize collaboration and mutual growth. ### Our Standards -Examples of behavior that contributes to creating a positive environment -include: +In order to maintain a positive environment, we encourage the following behaviors: -- Using welcoming and inclusive language -- Being respectful of differing viewpoints and experiences -- Gracefully accepting constructive criticism -- Focusing on what is best for the community -- Showing empathy towards other community members +- **Inclusivity**: Use welcoming and inclusive language that fosters collaboration across all perspectives and experiences. +- **Respect**: Respect differing opinions and engage with empathy, understanding that each person’s perspective is valuable. +- **Constructive Feedback**: Offer feedback that helps improve the project and allows contributors to grow from it. +- **Adventure Spirit**: Bring the same sense of curiosity, discovery, and positivity that drives AdventureLog into all interactions with the community. -Examples of unacceptable behavior by participants include: +Examples of unacceptable behavior include: -- The use of sexualized language or imagery and unwelcome sexual attention or - advances -- Trolling, insulting/derogatory comments, and personal or political attacks -- Public or private harassment -- Publishing others' private information, such as a physical or electronic - address, without explicit permission -- Other conduct which could reasonably be considered inappropriate in a - professional setting +- Personal attacks, trolling, or any form of harassment. +- Insensitive or discriminatory language, including sexualized comments or imagery. +- Spamming or misusing project spaces for personal gain. +- Publishing or using others’ private information without permission. +- Anything else that could be seen as disrespectful or unprofessional in a collaborative environment. ### Our Responsibilities -Project maintainers are responsible for clarifying the standards of acceptable -behavior and are expected to take appropriate and fair corrective action in -response to any instances of unacceptable behavior. +As maintainers of AdventureLog, we are committed to enforcing this Code of Conduct and taking corrective action when necessary. This may involve moderating comments, pulling code, or banning users who engage in harmful behaviors. -Project maintainers have the right and responsibility to remove, edit, or -reject comments, commits, code, wiki edits, issues, and other contributions -that are not aligned to this Code of Conduct, or to ban temporarily or -permanently any contributor for other behaviors that they deem inappropriate, -threatening, offensive, or harmful. +We strive to foster a community that balances open collaboration with respect for all contributors. ### Scope -This Code of Conduct applies both within project spaces and in public spaces -when an individual is representing the project or its community. Examples of -representing a project or community include using an official project e-mail -address, posting via an official social media account, or acting as an appointed -representative at an online or offline event. Representation of a project may be -further defined and clarified by project maintainers. +This Code of Conduct applies in all spaces related to AdventureLog. This includes our GitHub repository, discussions, documentation, social media accounts, and events—both online and in person. ### Enforcement -Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported by contacting the project team at [INSERT EMAIL ADDRESS]. All -complaints will be reviewed and investigated and will result in a response that -is deemed necessary and appropriate to the circumstances. The project team is -obligated to maintain confidentiality with regard to the reporter of an incident. -Further details of specific enforcement policies may be posted separately. - -Project maintainers who do not follow or enforce the Code of Conduct in good -faith may face temporary or permanent repercussions as determined by other -members of the project's leadership. +If you experience or witness unacceptable behavior, please report it to the project team at `contact@adventurelog.app`. All reports will be confidential and handled swiftly. The maintainers will investigate the issue and take appropriate action as needed. ### Attribution -This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, -available at [http://contributor-covenant.org/version/1/4][version] - -[homepage]: http://contributor-covenant.org -[version]: http://contributor-covenant.org/version/1/4/ +This Code of Conduct is inspired by the [Contributor Covenant](http://contributor-covenant.org), version 1.4, and adapted to fit the unique spirit of AdventureLog. diff --git a/backend/server/.env.example b/backend/server/.env.example index 4c1f9ad3..598aeb73 100644 --- a/backend/server/.env.example +++ b/backend/server/.env.example @@ -25,10 +25,10 @@ EMAIL_BACKEND='console' # ------------------- # # For Developers to start a Demo Database -# docker run --name postgres-admin -e POSTGRES_USER=admin -e POSTGRES_PASSWORD=admin -e POSTGRES_DB=admin -p 5432:5432 -d postgis/postgis:15-3.3 +# docker run --name adventurelog-development -e POSTGRES_USER=admin -e POSTGRES_PASSWORD=admin -e POSTGRES_DB=adventurelog -p 5432:5432 -d postgis/postgis:15-3.3 # PGHOST='localhost' -# PGDATABASE='admin' +# PGDATABASE='adventurelog' # PGUSER='admin' # PGPASSWORD='admin' # ------------------- # \ No newline at end of file diff --git a/backend/server/main/urls.py b/backend/server/main/urls.py index b7bb2a18..60ce08ea 100644 --- a/backend/server/main/urls.py +++ b/backend/server/main/urls.py @@ -22,7 +22,7 @@ urlpatterns = [ 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//', PublicUserDetailView.as_view(), name='public-user-detail'), + path('auth/user//', 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'), diff --git a/backend/server/users/views.py b/backend/server/users/views.py index b03760ec..e741a692 100644 --- a/backend/server/users/views.py +++ b/backend/server/users/views.py @@ -11,6 +11,8 @@ from django.shortcuts import get_object_or_404 from django.contrib.auth import get_user_model from .serializers import CustomUserDetailsSerializer as PublicUserSerializer from allauth.socialaccount.models import SocialApp +from adventures.serializers import AdventureSerializer, CollectionSerializer +from adventures.models import Adventure, Collection User = get_user_model() @@ -79,12 +81,28 @@ class PublicUserDetailView(APIView): }, operation_description="Get public user information." ) - def get(self, request, user_id): - user = get_object_or_404(User, uuid=user_id, public_profile=True) + def get(self, request, username): + print(request.user) + if request.user.username == username: + user = get_object_or_404(User, username=username) + else: + user = get_object_or_404(User, username=username, public_profile=True) + serializer = PublicUserSerializer(user) + # remove the email address from the response user.email = None - serializer = PublicUserSerializer(user) - return Response(serializer.data, status=status.HTTP_200_OK) + + # Get the users adventures and collections to include in the response + adventures = Adventure.objects.filter(user_id=user, is_public=True) + collections = Collection.objects.filter(user_id=user, is_public=True) + adventure_serializer = AdventureSerializer(adventures, many=True) + collection_serializer = CollectionSerializer(collections, many=True) + + return Response({ + 'user': serializer.data, + 'adventures': adventure_serializer.data, + 'collections': collection_serializer.data + }, status=status.HTTP_200_OK) class UserMetadataView(APIView): permission_classes = [IsAuthenticated] diff --git a/frontend/src/lib/components/Avatar.svelte b/frontend/src/lib/components/Avatar.svelte index 01eb0681..4bae0360 100644 --- a/frontend/src/lib/components/Avatar.svelte +++ b/frontend/src/lib/components/Avatar.svelte @@ -34,7 +34,9 @@ ? `${user.first_name} ${user.last_name}` : user.username}

    -
  • +
  • + +
  • diff --git a/frontend/src/locales/en.json b/frontend/src/locales/en.json index 2b989e27..0a9f9431 100644 --- a/frontend/src/locales/en.json +++ b/frontend/src/locales/en.json @@ -326,7 +326,11 @@ "new_password": "New Password (6+ characters)", "both_passwords_required": "Both passwords are required", "reset_failed": "Failed to reset password", - "or_3rd_party": "Or login with a third-party service" + "or_3rd_party": "Or login with a third-party service", + "no_public_adventures": "No public adventures found", + "no_public_collections": "No public collections found", + "user_adventures": "User Adventures", + "user_collections": "User Collections" }, "users": { "no_users_found": "No users found with public profiles." diff --git a/frontend/src/routes/profile/+page.server.ts b/frontend/src/routes/profile/+page.server.ts deleted file mode 100644 index 825a8679..00000000 --- a/frontend/src/routes/profile/+page.server.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { redirect } from '@sveltejs/kit'; -import type { PageServerLoad, RequestEvent } from '../$types'; -const PUBLIC_SERVER_URL = process.env['PUBLIC_SERVER_URL']; - -export const load: PageServerLoad = async (event: RequestEvent) => { - const endpoint = PUBLIC_SERVER_URL || 'http://localhost:8000'; - if (!event.locals.user || !event.cookies.get('sessionid')) { - return redirect(302, '/login'); - } - - let sessionId = event.cookies.get('sessionid'); - let stats = null; - - let res = await event.fetch(`${endpoint}/api/stats/counts/`, { - headers: { - Cookie: `sessionid=${sessionId}` - } - }); - if (!res.ok) { - console.error('Failed to fetch user stats'); - } else { - stats = await res.json(); - } - - return { - user: event.locals.user, - stats - }; -}; diff --git a/frontend/src/routes/profile/+page.svelte b/frontend/src/routes/profile/+page.svelte deleted file mode 100644 index 31fdfb6e..00000000 --- a/frontend/src/routes/profile/+page.svelte +++ /dev/null @@ -1,112 +0,0 @@ - - -
    -
    - - {#if data.user.profile_pic} -
    -
    - Profile -
    -
    - {/if} - - - {#if data.user && data.user.first_name && data.user.last_name} -

    - {data.user.first_name} - {data.user.last_name} -

    - {/if} -

    {data.user.username}

    - - - {#if data.user && data.user.date_joined} -
    -

    {$t('profile.member_since')}

    -
    - -

    - {new Date(data.user.date_joined).toLocaleDateString(undefined, { timeZone: 'UTC' })} -

    -
    -
    - {/if} -
    - - - {#if stats} -
    - -

    - {$t('profile.user_stats')} -

    - -
    -
    -
    -
    {$t('navbar.adventures')}
    -
    {stats.adventure_count}
    -
    - -
    -
    {$t('navbar.collections')}
    -
    {stats.trips_count}
    -
    - -
    -
    {$t('profile.visited_countries')}
    -
    - {Math.round((stats.visited_country_count / stats.total_countries) * 100)}% -
    -
    - {stats.visited_country_count}/{stats.total_countries} -
    -
    - -
    -
    {$t('profile.visited_regions')}
    -
    - {Math.round((stats.visited_region_count / stats.total_regions) * 100)}% -
    -
    - {stats.visited_region_count}/{stats.total_regions} -
    -
    - -
    -
    {$t('profile.visited_cities')}
    -
    - {Math.round((stats.visited_city_count / stats.total_cities) * 100)}% -
    -
    - {stats.visited_city_count}/{stats.total_cities} -
    -
    -
    -
    - {/if} -
    - - - Profile | AdventureLog - - diff --git a/frontend/src/routes/profile/[uuid]/+page.server.ts b/frontend/src/routes/profile/[uuid]/+page.server.ts new file mode 100644 index 00000000..c1abfce7 --- /dev/null +++ b/frontend/src/routes/profile/[uuid]/+page.server.ts @@ -0,0 +1,40 @@ +import { redirect, error } from '@sveltejs/kit'; +import type { PageServerLoad, RequestEvent } from '../../$types'; +const PUBLIC_SERVER_URL = process.env['PUBLIC_SERVER_URL']; + +export const load: PageServerLoad = async (event: RequestEvent) => { + const endpoint = PUBLIC_SERVER_URL || 'http://localhost:8000'; + + let uuid = event.params.uuid as string; + + if (!uuid) { + return error(404, 'Not found'); + } + + // let sessionId = event.cookies.get('sessionid'); + // let stats = null; + + // let res = await event.fetch(`${endpoint}/api/stats/counts/`, { + // headers: { + // Cookie: `sessionid=${sessionId}` + // } + // }); + // if (!res.ok) { + // console.error('Failed to fetch user stats'); + // } else { + // stats = await res.json(); + // } + + let userData = await event.fetch(`${endpoint}/auth/user/${uuid}/`); + if (!userData.ok) { + return error(404, 'Not found'); + } + + let data = await userData.json(); + + return { + user: data.user, + adventures: data.adventures, + collections: data.collections + }; +}; diff --git a/frontend/src/routes/profile/[uuid]/+page.svelte b/frontend/src/routes/profile/[uuid]/+page.svelte new file mode 100644 index 00000000..d2d784d2 --- /dev/null +++ b/frontend/src/routes/profile/[uuid]/+page.svelte @@ -0,0 +1,180 @@ + + +
    +
    + + {#if user.profile_pic} +
    +
    + Profile +
    +
    + {:else} + +
    +
    + {#if user.first_name && user.last_name} + Profile + {:else} + Profile + {/if} +
    +
    + {/if} + + + {#if user && user.first_name && user.last_name} +

    + {user.first_name} + {user.last_name} +

    + {/if} +

    {user.username}

    + + + {#if user && user.date_joined} +
    +

    {$t('profile.member_since')}

    +
    + +

    + {new Date(user.date_joined).toLocaleDateString(undefined, { timeZone: 'UTC' })} +

    +
    +
    + {/if} +
    + + + + + +
    + +

    + {$t('auth.user_adventures')} +

    + + {#if adventures && adventures.length === 0} +

    + {$t('auth.no_public_adventures')} +

    + {:else} +
    + {#each adventures as adventure} + + {/each} +
    + {/if} + + +
    + +

    + {$t('auth.user_collections')} +

    + + {#if collections && collections.length === 0} +

    + {$t('auth.no_public_collections')} +

    + {:else} +
    + {#each collections as collection} + + {/each} +
    + {/if} +
    + + + {user.first_name || user.username}'s Profile | AdventureLog + + From bc08362a4c0597d85fb951f9d1fb693394e4bffa Mon Sep 17 00:00:00 2001 From: Thies Date: Thu, 30 Jan 2025 12:12:05 +0100 Subject: [PATCH 142/209] navbar: Allow the typing of slash in input/textarea fields --- frontend/src/lib/components/Navbar.svelte | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/frontend/src/lib/components/Navbar.svelte b/frontend/src/lib/components/Navbar.svelte index 5e0d4755..49116901 100644 --- a/frontend/src/lib/components/Navbar.svelte +++ b/frontend/src/lib/components/Navbar.svelte @@ -17,9 +17,10 @@ // Event listener for focusing input function handleKeydown(event: KeyboardEvent) { - if (event.key === '/' && document.activeElement !== inputElement) { + // Ignore any keypresses in an input/textarea field, so we don't interfere with typing. + if (event.key === '/' && !["INPUT", "TEXTAREA"].includes((event.target as HTMLElement)?.tagName)) { event.preventDefault(); // Prevent browser's search shortcut - if (inputElement) { + if (inputElement) { inputElement.focus(); } } @@ -222,7 +223,7 @@ bind:value={query} class="grow" placeholder={$t('navbar.search')} - bind:this={inputElement} + bind:this={inputElement} />/ - {:else} + {:else if searchCategory === 'date'} + + {:else if searchCategory === 'album'} - {:else if searchCategory === 'date'} - + {:else if searchCategory === 'date'} + {:else if searchCategory === 'album'} +
    + {$t('adventures.basic_information')} +
    +
    + +
    + + +
    + +
    +
    + +
    + +
    +
    + +
    + + (hotel.rating = 1)} + checked={hotel.rating === 1} + /> + (hotel.rating = 2)} + checked={hotel.rating === 2} + /> + (hotel.rating = 3)} + checked={hotel.rating === 3} + /> + (hotel.rating = 4)} + checked={hotel.rating === 4} + /> + (hotel.rating = 5)} + checked={hotel.rating === 5} + /> + {#if hotel.rating} + + {/if} +
    +
    + +
    + + +
    +
    +

    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

    +
    + +
    + {$t('adventures.date_information')} +
    +
    + +
    + + + {#if collection && collection.start_date && collection.end_date} + {/if} +
    + +
    +
    + + {#if hotel.check_in} +
    + +
    + +
    +
    + {/if} +
    +
    + + + +
    + +
    + {$t('adventures.location_information')} +
    + +
    +
    + + +
    + + +
    + +
    +
    + diff --git a/frontend/src/lib/types.ts b/frontend/src/lib/types.ts index ab512a4f..e339ee04 100644 --- a/frontend/src/lib/types.ts +++ b/frontend/src/lib/types.ts @@ -262,3 +262,23 @@ export type Attachment = { user_id: string; name: string; }; + +export type Hotel = { + id: string; + user_id: string; + name: string; + description: string | null; + rating: number | null; + link: string | null; + check_in: string | null; // ISO 8601 date string + check_out: string | null; // ISO 8601 date string + reservation_number: string | null; + price: number | null; + latitude: number | null; + longitude: number | null; + location: string | null; + is_public: boolean; + collection: string | null; + created_at: string; // ISO 8601 date string + updated_at: string; // ISO 8601 date string +}; From ed1e24252cf9e5501f1155b814a0fb4318d024de Mon Sep 17 00:00:00 2001 From: Sean Morley Date: Mon, 3 Feb 2025 19:18:46 -0500 Subject: [PATCH 156/209] feat: Enhance HotelModal with geocoding, custom location handling, and improved hotel initialization --- frontend/src/lib/components/HotelModal.svelte | 255 +++++++++++++----- 1 file changed, 184 insertions(+), 71 deletions(-) diff --git a/frontend/src/lib/components/HotelModal.svelte b/frontend/src/lib/components/HotelModal.svelte index 128f0ad5..e44d1f8e 100644 --- a/frontend/src/lib/components/HotelModal.svelte +++ b/frontend/src/lib/components/HotelModal.svelte @@ -1,83 +1,119 @@ @@ -303,6 +325,97 @@
    +
    + +
    + {$t('adventures.location_information')} +
    +
    + +
    +
    +
    + + {#if is_custom_location} + + {/if} +
    +
    + +
    +
    + + + +
    +
    + {#if places.length > 0} +
    +

    {$t('adventures.search_results')}

    + +
    + {#each places as place} + + {/each} +
    +
    + {:else if noPlaces} +

    {$t('adventures.no_results')}

    + {/if} + +
    + + + + + {#each markers as marker} + + {/each} + +
    +
    +
    From ad5fb02ebb7940780adcae2f317688ad36e3fc4c Mon Sep 17 00:00:00 2001 From: Sean Morley Date: Mon, 3 Feb 2025 19:38:24 -0500 Subject: [PATCH 157/209] feat: Enhance AdventureViewSet to filter adventures based on visit status with improved boolean handling --- .../server/adventures/views/adventure_view.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/backend/server/adventures/views/adventure_view.py b/backend/server/adventures/views/adventure_view.py index 5249e008..71be405a 100644 --- a/backend/server/adventures/views/adventure_view.py +++ b/backend/server/adventures/views/adventure_view.py @@ -1,3 +1,4 @@ +from django.utils import timezone from django.db import transaction from django.core.exceptions import PermissionDenied from django.db.models import Q, Max @@ -5,7 +6,6 @@ from django.db.models.functions import Lower from rest_framework import viewsets from rest_framework.decorators import action from rest_framework.response import Response - from adventures.models import Adventure, Category from adventures.permissions import IsOwnerOrSharedWithFullAccess from adventures.serializers import AdventureSerializer @@ -98,9 +98,17 @@ class AdventureViewSet(viewsets.ModelViewSet): user_id=request.user.id ) - if is_visited.lower() in ['true', 'false']: - is_visited_bool = is_visited.lower() == 'true' - queryset = queryset.filter(is_visited=is_visited_bool) + is_visited_param = request.query_params.get('is_visited') + if is_visited_param is not None: + # Convert is_visited_param to a boolean + is_visited_bool = (is_visited_param.lower() == 'true') + + # Filter logic: "visited" means at least one visit with start_date <= today + now = timezone.now().date() + if is_visited_bool: + queryset = queryset.filter(visits__start_date__lte=now).distinct() + else: + queryset = queryset.exclude(visits__start_date__lte=now).distinct() queryset = self.apply_sorting(queryset) return self.paginate_and_respond(queryset, request) From 9c3a52ae855deaa230168728b04a9b0eea75b7fb Mon Sep 17 00:00:00 2001 From: Sean Morley Date: Mon, 3 Feb 2025 19:56:25 -0500 Subject: [PATCH 158/209] feat: Refactor user detail view and enhance localization strings for multiple languages --- backend/server/adventures/views/stats_view.py | 29 ++++++++--- backend/server/users/views.py | 1 - .../src/lib/components/AdventureModal.svelte | 3 +- frontend/src/locales/de.json | 9 +++- frontend/src/locales/en.json | 1 + frontend/src/locales/es.json | 9 +++- frontend/src/locales/fr.json | 9 +++- frontend/src/locales/it.json | 9 +++- frontend/src/locales/nl.json | 9 +++- frontend/src/locales/pl.json | 9 +++- frontend/src/locales/sv.json | 9 +++- frontend/src/locales/zh.json | 9 +++- .../src/routes/profile/[uuid]/+page.server.ts | 29 +++++------ .../src/routes/profile/[uuid]/+page.svelte | 52 ++++++++++--------- 14 files changed, 121 insertions(+), 66 deletions(-) diff --git a/backend/server/adventures/views/stats_view.py b/backend/server/adventures/views/stats_view.py index 23733a5f..37d9f07a 100644 --- a/backend/server/adventures/views/stats_view.py +++ b/backend/server/adventures/views/stats_view.py @@ -2,29 +2,42 @@ from rest_framework import viewsets from rest_framework.permissions import IsAuthenticated from rest_framework.response import Response from rest_framework.decorators import action +from django.shortcuts import get_object_or_404 from worldtravel.models import City, Region, Country, VisitedCity, VisitedRegion from adventures.models import Adventure, Collection +from users.serializers import CustomUserDetailsSerializer as PublicUserSerializer +from django.contrib.auth import get_user_model + +User = get_user_model() class StatsViewSet(viewsets.ViewSet): """ A simple ViewSet for listing the stats of a user. """ - permission_classes = [IsAuthenticated] + @action(detail=False, methods=['get'], url_path='counts/(?P[^/.]+)') + def counts(self, request, username): + if request.user.username == username: + user = get_object_or_404(User, username=username) + else: + user = get_object_or_404(User, username=username, public_profile=True) + serializer = PublicUserSerializer(user) + + # remove the email address from the response + user.email = None - @action(detail=False, methods=['get']) - def counts(self, request): + # get the counts for the user adventure_count = Adventure.objects.filter( - user_id=request.user.id).count() + user_id=user.id).count() trips_count = Collection.objects.filter( - user_id=request.user.id).count() + user_id=user.id).count() visited_city_count = VisitedCity.objects.filter( - user_id=request.user.id).count() + user_id=user.id).count() total_cities = City.objects.count() visited_region_count = VisitedRegion.objects.filter( - user_id=request.user.id).count() + user_id=user.id).count() total_regions = Region.objects.count() visited_country_count = VisitedRegion.objects.filter( - user_id=request.user.id).values('region__country').distinct().count() + user_id=user.id).values('region__country').distinct().count() total_countries = Country.objects.count() return Response({ 'adventure_count': adventure_count, diff --git a/backend/server/users/views.py b/backend/server/users/views.py index e741a692..0d0fca11 100644 --- a/backend/server/users/views.py +++ b/backend/server/users/views.py @@ -82,7 +82,6 @@ class PublicUserDetailView(APIView): operation_description="Get public user information." ) def get(self, request, username): - print(request.user) if request.user.username == username: user = get_object_or_404(User, username=username) else: diff --git a/frontend/src/lib/components/AdventureModal.svelte b/frontend/src/lib/components/AdventureModal.svelte index a0aa8f89..28cd752a 100644 --- a/frontend/src/lib/components/AdventureModal.svelte +++ b/frontend/src/lib/components/AdventureModal.svelte @@ -10,7 +10,6 @@ ReverseGeocode } from '$lib/types'; import { onMount } from 'svelte'; - import { enhance } from '$app/forms'; import { addToast } from '$lib/toasts'; import { deserialize } from '$app/forms'; import { t } from 'svelte-i18n'; @@ -1257,7 +1256,7 @@ it would also work to just use on:click on the MapLibre component itself. --> {#if immichIntegration} { url = e.detail; fetchImage(); diff --git a/frontend/src/locales/de.json b/frontend/src/locales/de.json index 6115fe02..c01123e5 100644 --- a/frontend/src/locales/de.json +++ b/frontend/src/locales/de.json @@ -234,7 +234,8 @@ "images": "Bilder", "primary": "Primär", "upload": "Hochladen", - "view_attachment": "Anhang anzeigen" + "view_attachment": "Anhang anzeigen", + "of": "von" }, "home": { "desc_1": "Entdecken, planen und erkunden Sie mit Leichtigkeit", @@ -302,7 +303,11 @@ "both_passwords_required": "Beide Passwörter sind erforderlich", "new_password": "Neues Passwort", "reset_failed": "Passwort konnte nicht zurückgesetzt werden", - "or_3rd_party": "Oder melden Sie sich bei einem Drittanbieter an" + "or_3rd_party": "Oder melden Sie sich bei einem Drittanbieter an", + "no_public_adventures": "Keine öffentlichen Abenteuer gefunden", + "no_public_collections": "Keine öffentlichen Sammlungen gefunden", + "user_adventures": "Benutzerabenteuer", + "user_collections": "Benutzersammlungen" }, "users": { "no_users_found": "Keine Benutzer mit öffentlichen Profilen gefunden." diff --git a/frontend/src/locales/en.json b/frontend/src/locales/en.json index 0a9f9431..4c4e645b 100644 --- a/frontend/src/locales/en.json +++ b/frontend/src/locales/en.json @@ -165,6 +165,7 @@ "delete_collection_success": "Collection deleted successfully!", "delete_collection_warning": "Are you sure you want to delete this collection? This will also delete all of the linked adventures. This action cannot be undone.", "cancel": "Cancel", + "of": "of", "delete_collection": "Delete Collection", "delete_adventure": "Delete Adventure", "adventure_delete_success": "Adventure deleted successfully!", diff --git a/frontend/src/locales/es.json b/frontend/src/locales/es.json index 0f00856d..6b98c340 100644 --- a/frontend/src/locales/es.json +++ b/frontend/src/locales/es.json @@ -281,7 +281,8 @@ "primary": "Primario", "upload": "Subir", "view_attachment": "Ver archivo adjunto", - "attachment_name": "Nombre del archivo adjunto" + "attachment_name": "Nombre del archivo adjunto", + "of": "de" }, "worldtravel": { "all": "Todo", @@ -326,7 +327,11 @@ "both_passwords_required": "Se requieren ambas contraseñas", "new_password": "Nueva contraseña", "reset_failed": "No se pudo restablecer la contraseña", - "or_3rd_party": "O inicie sesión con un servicio de terceros" + "or_3rd_party": "O inicie sesión con un servicio de terceros", + "no_public_adventures": "No se encontraron aventuras públicas", + "no_public_collections": "No se encontraron colecciones públicas", + "user_adventures": "Aventuras de usuario", + "user_collections": "Colecciones de usuarios" }, "users": { "no_users_found": "No se encontraron usuarios con perfiles públicos." diff --git a/frontend/src/locales/fr.json b/frontend/src/locales/fr.json index e99f79de..8d6df462 100644 --- a/frontend/src/locales/fr.json +++ b/frontend/src/locales/fr.json @@ -234,7 +234,8 @@ "images": "Images", "primary": "Primaire", "upload": "Télécharger", - "view_attachment": "Voir la pièce jointe" + "view_attachment": "Voir la pièce jointe", + "of": "de" }, "home": { "desc_1": "Découvrez, planifiez et explorez en toute simplicité", @@ -302,7 +303,11 @@ "both_passwords_required": "Les deux mots de passe sont requis", "new_password": "Nouveau mot de passe", "reset_failed": "Échec de la réinitialisation du mot de passe", - "or_3rd_party": "Ou connectez-vous avec un service tiers" + "or_3rd_party": "Ou connectez-vous avec un service tiers", + "no_public_adventures": "Aucune aventure publique trouvée", + "no_public_collections": "Aucune collection publique trouvée", + "user_adventures": "Aventures utilisateur", + "user_collections": "Collections d'utilisateurs" }, "users": { "no_users_found": "Aucun utilisateur trouvé avec des profils publics." diff --git a/frontend/src/locales/it.json b/frontend/src/locales/it.json index 12877dc4..ad0f0c18 100644 --- a/frontend/src/locales/it.json +++ b/frontend/src/locales/it.json @@ -234,7 +234,8 @@ "images": "Immagini", "primary": "Primario", "upload": "Caricamento", - "view_attachment": "Visualizza allegato" + "view_attachment": "Visualizza allegato", + "of": "Di" }, "home": { "desc_1": "Scopri, pianifica ed esplora con facilità", @@ -302,7 +303,11 @@ "both_passwords_required": "Sono necessarie entrambe le password", "new_password": "Nuova parola d'ordine", "reset_failed": "Impossibile reimpostare la password", - "or_3rd_party": "Oppure accedi con un servizio di terze parti" + "or_3rd_party": "Oppure accedi con un servizio di terze parti", + "no_public_adventures": "Nessuna avventura pubblica trovata", + "no_public_collections": "Nessuna collezione pubblica trovata", + "user_adventures": "Avventure utente", + "user_collections": "Collezioni utente" }, "users": { "no_users_found": "Nessun utente trovato con profili pubblici." diff --git a/frontend/src/locales/nl.json b/frontend/src/locales/nl.json index cdb1c5d6..9e6619f7 100644 --- a/frontend/src/locales/nl.json +++ b/frontend/src/locales/nl.json @@ -234,7 +234,8 @@ "images": "Afbeeldingen", "primary": "Primair", "upload": "Uploaden", - "view_attachment": "Bijlage bekijken" + "view_attachment": "Bijlage bekijken", + "of": "van" }, "home": { "desc_1": "Ontdek, plan en verken met gemak", @@ -302,7 +303,11 @@ "both_passwords_required": "Beide wachtwoorden zijn vereist", "new_password": "Nieuw wachtwoord", "reset_failed": "Kan het wachtwoord niet opnieuw instellen", - "or_3rd_party": "Of log in met een service van derden" + "or_3rd_party": "Of log in met een service van derden", + "no_public_adventures": "Geen openbare avonturen gevonden", + "no_public_collections": "Geen openbare collecties gevonden", + "user_adventures": "Gebruikersavonturen", + "user_collections": "Gebruikerscollecties" }, "users": { "no_users_found": "Er zijn geen gebruikers gevonden met openbare profielen." diff --git a/frontend/src/locales/pl.json b/frontend/src/locales/pl.json index 31134d52..982d8d80 100644 --- a/frontend/src/locales/pl.json +++ b/frontend/src/locales/pl.json @@ -281,7 +281,8 @@ "images": "Obrazy", "primary": "Podstawowy", "upload": "Wgrywać", - "view_attachment": "Zobacz załącznik" + "view_attachment": "Zobacz załącznik", + "of": "z" }, "worldtravel": { "country_list": "Lista krajów", @@ -326,7 +327,11 @@ "both_passwords_required": "Obydwa hasła są wymagane", "new_password": "Nowe hasło", "reset_failed": "Nie udało się zresetować hasła", - "or_3rd_party": "Lub zaloguj się za pomocą usługi strony trzeciej" + "or_3rd_party": "Lub zaloguj się za pomocą usługi strony trzeciej", + "no_public_adventures": "Nie znaleziono publicznych przygód", + "no_public_collections": "Nie znaleziono publicznych kolekcji", + "user_adventures": "Przygody użytkowników", + "user_collections": "Kolekcje użytkowników" }, "users": { "no_users_found": "Nie znaleziono użytkowników z publicznymi profilami." diff --git a/frontend/src/locales/sv.json b/frontend/src/locales/sv.json index 919c6d99..a2373ca4 100644 --- a/frontend/src/locales/sv.json +++ b/frontend/src/locales/sv.json @@ -234,7 +234,8 @@ "images": "Bilder", "primary": "Primär", "upload": "Ladda upp", - "view_attachment": "Visa bilaga" + "view_attachment": "Visa bilaga", + "of": "av" }, "home": { "desc_1": "Upptäck, planera och utforska med lätthet", @@ -326,7 +327,11 @@ "both_passwords_required": "Båda lösenorden krävs", "new_password": "Nytt lösenord", "reset_failed": "Det gick inte att återställa lösenordet", - "or_3rd_party": "Eller logga in med en tredjepartstjänst" + "or_3rd_party": "Eller logga in med en tredjepartstjänst", + "no_public_adventures": "Inga offentliga äventyr hittades", + "no_public_collections": "Inga offentliga samlingar hittades", + "user_adventures": "Användaräventyr", + "user_collections": "Användarsamlingar" }, "users": { "no_users_found": "Inga användare hittades med offentliga profiler." diff --git a/frontend/src/locales/zh.json b/frontend/src/locales/zh.json index 7270e3b6..1ecb2464 100644 --- a/frontend/src/locales/zh.json +++ b/frontend/src/locales/zh.json @@ -234,7 +234,8 @@ "images": "图片", "primary": "基本的", "upload": "上传", - "view_attachment": "查看附件" + "view_attachment": "查看附件", + "of": "的" }, "home": { "desc_1": "轻松发现、规划和探索", @@ -302,7 +303,11 @@ "both_passwords_required": "两个密码都需要", "new_password": "新密码", "reset_failed": "重置密码失败", - "or_3rd_party": "或者使用第三方服务登录" + "or_3rd_party": "或者使用第三方服务登录", + "no_public_adventures": "找不到公共冒险", + "no_public_collections": "找不到公共收藏", + "user_adventures": "用户冒险", + "user_collections": "用户收集" }, "worldtravel": { "all": "全部", diff --git a/frontend/src/routes/profile/[uuid]/+page.server.ts b/frontend/src/routes/profile/[uuid]/+page.server.ts index c1abfce7..f98f782e 100644 --- a/frontend/src/routes/profile/[uuid]/+page.server.ts +++ b/frontend/src/routes/profile/[uuid]/+page.server.ts @@ -1,31 +1,29 @@ import { redirect, error } from '@sveltejs/kit'; import type { PageServerLoad, RequestEvent } from '../../$types'; +import { t } from 'svelte-i18n'; const PUBLIC_SERVER_URL = process.env['PUBLIC_SERVER_URL']; export const load: PageServerLoad = async (event: RequestEvent) => { const endpoint = PUBLIC_SERVER_URL || 'http://localhost:8000'; - let uuid = event.params.uuid as string; + // @ts-ignore + let username = event.params.uuid as string; - if (!uuid) { + if (!username) { return error(404, 'Not found'); } // let sessionId = event.cookies.get('sessionid'); - // let stats = null; + let stats = null; - // let res = await event.fetch(`${endpoint}/api/stats/counts/`, { - // headers: { - // Cookie: `sessionid=${sessionId}` - // } - // }); - // if (!res.ok) { - // console.error('Failed to fetch user stats'); - // } else { - // stats = await res.json(); - // } + let res = await event.fetch(`${endpoint}/api/stats/counts/${username}`, {}); + if (!res.ok) { + console.error('Failed to fetch user stats'); + } else { + stats = await res.json(); + } - let userData = await event.fetch(`${endpoint}/auth/user/${uuid}/`); + let userData = await event.fetch(`${endpoint}/auth/user/${username}/`); if (!userData.ok) { return error(404, 'Not found'); } @@ -35,6 +33,7 @@ export const load: PageServerLoad = async (event: RequestEvent) => { return { user: data.user, adventures: data.adventures, - collections: data.collections + collections: data.collections, + stats: stats }; }; diff --git a/frontend/src/routes/profile/[uuid]/+page.svelte b/frontend/src/routes/profile/[uuid]/+page.svelte index d2d784d2..02d82c9a 100644 --- a/frontend/src/routes/profile/[uuid]/+page.svelte +++ b/frontend/src/routes/profile/[uuid]/+page.svelte @@ -5,26 +5,21 @@ import type { Adventure, Collection, User } from '$lib/types.js'; import { t } from 'svelte-i18n'; - // let stats: { - // visited_country_count: number; - // total_regions: number; - // trips_count: number; - // adventure_count: number; - // visited_region_count: number; - // total_countries: number; - // visited_city_count: number; - // total_cities: number; - // } | null; + let stats: { + visited_country_count: number; + total_regions: number; + trips_count: number; + adventure_count: number; + visited_region_count: number; + total_countries: number; + visited_city_count: number; + total_cities: number; + } | null; const user: User = data.user; const adventures: Adventure[] = data.adventures; const collections: Collection[] = data.collections; - - // console.log(user); - // console.log(adventures); - // console.log(collections); - - // stats = data.stats || null; + stats = data.stats || null;
    @@ -83,7 +78,7 @@
    - + {/if}
    From 8ea8043beb0bc0595a18a4bca2c517960c8e24c6 Mon Sep 17 00:00:00 2001 From: Sean Morley Date: Mon, 3 Feb 2025 19:57:22 -0500 Subject: [PATCH 159/209] feat: Update stats API endpoint to include username for personalized user statistics --- frontend/src/routes/dashboard/+page.server.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/frontend/src/routes/dashboard/+page.server.ts b/frontend/src/routes/dashboard/+page.server.ts index 5db5aef9..a87a45ad 100644 --- a/frontend/src/routes/dashboard/+page.server.ts +++ b/frontend/src/routes/dashboard/+page.server.ts @@ -20,11 +20,14 @@ export const load = (async (event) => { let stats = null; - let res = await event.fetch(`${serverEndpoint}/api/stats/counts/`, { - headers: { - Cookie: `sessionid=${event.cookies.get('sessionid')}` + let res = await event.fetch( + `${serverEndpoint}/api/stats/counts/${event.locals.user.username}`, + { + headers: { + Cookie: `sessionid=${event.cookies.get('sessionid')}` + } } - }); + ); if (!res.ok) { console.error('Failed to fetch user stats'); } else { From a00d2abe0d5a72c02053139e3f212c1e93f6238f Mon Sep 17 00:00:00 2001 From: Sean Morley Date: Tue, 4 Feb 2025 10:37:15 -0500 Subject: [PATCH 160/209] feat: Add achievements app with models, admin, and management command for seeding data --- backend/server/achievements/__init__.py | 0 backend/server/achievements/admin.py | 9 +++ backend/server/achievements/apps.py | 6 ++ .../achievements/management/__init__.py | 0 .../management/commands/__init__.py | 0 .../management/commands/achievement-seed.py | 66 +++++++++++++++++++ .../achievements/migrations/0001_initial.py | 39 +++++++++++ .../achievements/migrations/__init__.py | 0 backend/server/achievements/models.py | 33 ++++++++++ backend/server/achievements/tests.py | 3 + backend/server/achievements/views.py | 3 + backend/server/main/settings.py | 1 + 12 files changed, 160 insertions(+) create mode 100644 backend/server/achievements/__init__.py create mode 100644 backend/server/achievements/admin.py create mode 100644 backend/server/achievements/apps.py create mode 100644 backend/server/achievements/management/__init__.py create mode 100644 backend/server/achievements/management/commands/__init__.py create mode 100644 backend/server/achievements/management/commands/achievement-seed.py create mode 100644 backend/server/achievements/migrations/0001_initial.py create mode 100644 backend/server/achievements/migrations/__init__.py create mode 100644 backend/server/achievements/models.py create mode 100644 backend/server/achievements/tests.py create mode 100644 backend/server/achievements/views.py diff --git a/backend/server/achievements/__init__.py b/backend/server/achievements/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/backend/server/achievements/admin.py b/backend/server/achievements/admin.py new file mode 100644 index 00000000..af087ce9 --- /dev/null +++ b/backend/server/achievements/admin.py @@ -0,0 +1,9 @@ +from django.contrib import admin +from allauth.account.decorators import secure_admin_login +from achievements.models import Achievement, UserAchievement + +admin.autodiscover() +admin.site.login = secure_admin_login(admin.site.login) + +admin.site.register(Achievement) +admin.site.register(UserAchievement) \ No newline at end of file diff --git a/backend/server/achievements/apps.py b/backend/server/achievements/apps.py new file mode 100644 index 00000000..2a635e2d --- /dev/null +++ b/backend/server/achievements/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class AchievementsConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'achievements' diff --git a/backend/server/achievements/management/__init__.py b/backend/server/achievements/management/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/backend/server/achievements/management/commands/__init__.py b/backend/server/achievements/management/commands/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/backend/server/achievements/management/commands/achievement-seed.py b/backend/server/achievements/management/commands/achievement-seed.py new file mode 100644 index 00000000..7713e885 --- /dev/null +++ b/backend/server/achievements/management/commands/achievement-seed.py @@ -0,0 +1,66 @@ +import json +from django.core.management.base import BaseCommand +from achievements.models import Achievement + +US_STATE_CODES = [ + 'US-AL', 'US-AK', 'US-AZ', 'US-AR', 'US-CA', 'US-CO', 'US-CT', 'US-DE', + 'US-FL', 'US-GA', 'US-HI', 'US-ID', 'US-IL', 'US-IN', 'US-IA', 'US-KS', + 'US-KY', 'US-LA', 'US-ME', 'US-MD', 'US-MA', 'US-MI', 'US-MN', 'US-MS', + 'US-MO', 'US-MT', 'US-NE', 'US-NV', 'US-NH', 'US-NJ', 'US-NM', 'US-NY', + 'US-NC', 'US-ND', 'US-OH', 'US-OK', 'US-OR', 'US-PA', 'US-RI', 'US-SC', + 'US-SD', 'US-TN', 'US-TX', 'US-UT', 'US-VT', 'US-VA', 'US-WA', 'US-WV', + 'US-WI', 'US-WY' +] + +ACHIEVEMENTS = [ + { + "name": "First Adventure", + "key": "achievements.first_adventure", + "type": "adventure_count", + "description": "Log your first adventure!", + "condition": {"type": "adventure_count", "value": 1}, + }, + { + "name": "Explorer", + "key": "achievements.explorer", + "type": "adventure_count", + "description": "Log 10 adventures.", + "condition": {"type": "adventure_count", "value": 10}, + }, + { + "name": "Globetrotter", + "key": "achievements.globetrotter", + "type": "country_count", + "description": "Visit 5 different countries.", + "condition": {"type": "country_count", "value": 5}, + }, + { + "name": "American Dream", + "key": "achievements.american_dream", + "type": "country_count", + "description": "Visit all 50 states in the USA.", + "condition": {"type": "country_count", "items": US_STATE_CODES}, + } +] + + + + +class Command(BaseCommand): + help = "Seeds the database with predefined achievements" + + def handle(self, *args, **kwargs): + for achievement_data in ACHIEVEMENTS: + achievement, created = Achievement.objects.update_or_create( + name=achievement_data["name"], + defaults={ + "description": achievement_data["description"], + "condition": json.dumps(achievement_data["condition"]), + "type": achievement_data["type"], + "key": achievement_data["key"], + }, + ) + if created: + self.stdout.write(self.style.SUCCESS(f"✅ Created: {achievement.name}")) + else: + self.stdout.write(self.style.WARNING(f"🔄 Updated: {achievement.name}")) diff --git a/backend/server/achievements/migrations/0001_initial.py b/backend/server/achievements/migrations/0001_initial.py new file mode 100644 index 00000000..e38e509b --- /dev/null +++ b/backend/server/achievements/migrations/0001_initial.py @@ -0,0 +1,39 @@ +# Generated by Django 5.0.8 on 2025-02-04 04:34 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='Achievement', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=255, unique=True)), + ('description', models.TextField()), + ('icon', models.ImageField(blank=True, null=True, upload_to='achievements/')), + ('condition', models.JSONField()), + ], + ), + migrations.CreateModel( + name='UserAchievement', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('earned_at', models.DateTimeField(auto_now_add=True)), + ('achievement', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='achievements.achievement')), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + options={ + 'unique_together': {('user', 'achievement')}, + }, + ), + ] diff --git a/backend/server/achievements/migrations/__init__.py b/backend/server/achievements/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/backend/server/achievements/models.py b/backend/server/achievements/models.py new file mode 100644 index 00000000..8659bc21 --- /dev/null +++ b/backend/server/achievements/models.py @@ -0,0 +1,33 @@ +from django.db import models +from django.contrib.auth import get_user_model + +User = get_user_model() + +VALID_ACHIEVEMENT_TYPES = [ + "adventure_count", + "country_count", +] + +class Achievement(models.Model): + """Stores all possible achievements""" + name = models.CharField(max_length=255, unique=True) + key = models.CharField(max_length=255, unique=True) # Used for frontend lookups, e.g. "achievements.first_adventure" + type = models.CharField(max_length=255) # adventure_count, country_count, etc. + description = models.TextField() + icon = models.ImageField(upload_to="achievements/", null=True, blank=True) + condition = models.JSONField() # Stores rules like {"type": "adventure_count", "value": 10} + + def __str__(self): + return self.name + +class UserAchievement(models.Model): + """Tracks which achievements a user has earned""" + user = models.ForeignKey(User, on_delete=models.CASCADE) + achievement = models.ForeignKey(Achievement, on_delete=models.CASCADE) + earned_at = models.DateTimeField(auto_now_add=True) + + class Meta: + unique_together = ("user", "achievement") # Prevent duplicates + + def __str__(self): + return f"{self.user.username} - {self.achievement.name}" diff --git a/backend/server/achievements/tests.py b/backend/server/achievements/tests.py new file mode 100644 index 00000000..7ce503c2 --- /dev/null +++ b/backend/server/achievements/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/backend/server/achievements/views.py b/backend/server/achievements/views.py new file mode 100644 index 00000000..91ea44a2 --- /dev/null +++ b/backend/server/achievements/views.py @@ -0,0 +1,3 @@ +from django.shortcuts import render + +# Create your views here. diff --git a/backend/server/main/settings.py b/backend/server/main/settings.py index 25f29ba1..5549c0da 100644 --- a/backend/server/main/settings.py +++ b/backend/server/main/settings.py @@ -61,6 +61,7 @@ INSTALLED_APPS = ( 'users', 'integrations', 'django.contrib.gis', + 'achievements', # 'widget_tweaks', # 'slippers', From 5f344cad8309cd06f119703e7c649b83f0f9f40b Mon Sep 17 00:00:00 2001 From: Sean Morley Date: Tue, 4 Feb 2025 17:19:58 -0500 Subject: [PATCH 161/209] feat: Implement CDN update process with country data download and flag management --- cdn/Dockerfile | 3 ++ cdn/docker-compose.yml | 2 +- cdn/index.html | 89 +++++++++++++++++++++++++++++++++ cdn/main.py | 109 ++++++++++++++++++++++++++--------------- 4 files changed, 163 insertions(+), 40 deletions(-) create mode 100644 cdn/index.html diff --git a/cdn/Dockerfile b/cdn/Dockerfile index 5d23f509..544f8d46 100644 --- a/cdn/Dockerfile +++ b/cdn/Dockerfile @@ -26,6 +26,9 @@ RUN mkdir -p /var/www/html/data && cp -r /app/data/* /var/www/html/data/ # Copy Nginx configuration COPY nginx.conf /etc/nginx/nginx.conf +# Copy the index.html file to the Nginx serving directory +COPY index.html /usr/share/nginx/html/index.html + # Expose port 80 for Nginx EXPOSE 80 diff --git a/cdn/docker-compose.yml b/cdn/docker-compose.yml index 89e73935..5699dd81 100644 --- a/cdn/docker-compose.yml +++ b/cdn/docker-compose.yml @@ -1,5 +1,5 @@ services: - geojson: + cdn: build: . container_name: adventurelog-cdn ports: diff --git a/cdn/index.html b/cdn/index.html new file mode 100644 index 00000000..97157124 --- /dev/null +++ b/cdn/index.html @@ -0,0 +1,89 @@ + + + + + + AdventureLog CDN + + + + + + + diff --git a/cdn/main.py b/cdn/main.py index 36df16ca..06c0d333 100644 --- a/cdn/main.py +++ b/cdn/main.py @@ -2,55 +2,86 @@ import requests import json import os +# The version of the CDN, this should be updated when the CDN data is updated so the client can check if it has the latest version +ADVENTURELOG_CDN_VERSION = 'v0.0.1' + # https://github.com/dr5hn/countries-states-cities-database/tags -COUNTRY_REGION_JSON_VERSION = 'v2.5' # Should match the version stated in the settings.py file of AdventureLog +COUNTRY_REGION_JSON_VERSION = 'v2.5' # Test on past and latest versions to ensure that the data schema is consistent before updating + +def makeDataDir(): + """ + Creates the data directory if it doesn't exist + """ + path = os.path.join(os.path.dirname(__file__), 'data') + if not os.path.exists(path): + os.makedirs(path) + +def saveCdnVersion(): + """ + Saves the CDN version to a JSON file so the client can check if it has the latest version + """ + path = os.path.join(os.path.dirname(__file__), 'data', 'version.json') + with open(path, 'w') as f: + json.dump({'version': ADVENTURELOG_CDN_VERSION}, f) + print('CDN Version saved') def downloadCountriesStateCities(): + """ + Downloads the countries, states and cities data from the countries-states-cities-database repository + """ res = requests.get(f'https://raw.githubusercontent.com/dr5hn/countries-states-cities-database/{COUNTRY_REGION_JSON_VERSION}/json/countries%2Bstates%2Bcities.json') -def downloadGeojson(iso_code=None, region_name=None): + path = os.path.join(os.path.dirname(__file__), 'data', f'countries_states_cities_{COUNTRY_REGION_JSON_VERSION}.json') + + with open(path, 'w') as f: + f.write(res.text) + print('Countries, states and cities data downloaded successfully') + +def saveCountryFlag(country_code, name): """ - Download geojson data for a specific region using the Overpass API. - :param iso_code: ISO 3166-2 code for the region (e.g. "US-CT") - :param region_name: Name of the region (e.g. "Connecticut") - :return: Geojson data for the region + Downloads the flag of a country and saves it in the data/flags directory """ - base_url = "https://overpass-api.de/api/interpreter" - - # Get the directory where the script is located - script_dir = os.path.dirname(__file__) + # For standards, use the lowercase country_code + country_code = country_code.lower() + # Save the flag in the data/flags directory + flags_dir = os.path.join(os.path.dirname(__file__), 'data', 'flags') - # Ensure the ./data directory exists - data_dir = os.path.join(script_dir, "data") - os.makedirs(data_dir, exist_ok=True) + # Check if the flags directory exists, if not, create it + if not os.path.exists(flags_dir): + os.makedirs(flags_dir) - # Set the path for the geojson file - geojson_path = os.path.join(data_dir, "geojson.json") + # Check if the flag already exists in the media folder + flag_path = os.path.join(flags_dir, f'{country_code}.png') + if os.path.exists(flag_path): + # remove the flag if it already exists + os.remove(flag_path) + print(f'Flag for {country_code} ({name}) removed') - if iso_code: - query = f'[out:json];relation["boundary"="administrative"]["admin_level"="4"]["ISO3166-2"="{iso_code}"];out body;way(r);(._;>;);out geom;' - response = requests.post(base_url, data=query) - if response.ok and response.json().get("elements"): - data = response.json() - with open(geojson_path, "w", encoding="utf-8") as f: - json.dump(data, f, indent=4) - return data - - if region_name: - query = f'[out:json];relation["boundary"="administrative"]["admin_level"="4"]["name"="{region_name}"];out body;way(r);(._;>;);out geom;' - response = requests.post(base_url, data=query) - if response.ok and response.json().get("elements"): - data = response.json() - with open(geojson_path, "w", encoding="utf-8") as f: - json.dump(data, f, indent=4) - return data + res = requests.get(f'https://flagcdn.com/h240/{country_code}.png'.lower()) + if res.status_code == 200: + with open(flag_path, 'wb') as f: + f.write(res.content) + print(f'Flag for {country_code} downloaded') + else: + print(f'Error downloading flag for {country_code} ({name})') - return None # No results found +def saveCountryFlags(): + """ + Downloads the flags of all countries and saves them in the data/flags directory + """ + # Load the countries data + with open(os.path.join(os.path.dirname(__file__), 'data', f'countries_states_cities_{COUNTRY_REGION_JSON_VERSION}.json')) as f: + data = json.load(f) -# Example usage -data = downloadGeojson(iso_code="US-CT", region_name="Connecticut") + for country in data: + country_code = country['iso2'] + name = country['name'] + saveCountryFlag(country_code, name) -if data: - print(f"Region data found and saved to {os.path.join(os.path.dirname(__file__), 'data', 'geojson.json')}!") -else: - print("No region data found.") +# Run the functions +print('Starting CDN update') +makeDataDir() +saveCdnVersion() +downloadCountriesStateCities() +saveCountryFlags() +print('CDN update complete') \ No newline at end of file From 9b54af8d40240ea485179dd1291386c77b382503 Mon Sep 17 00:00:00 2001 From: Sean Morley Date: Tue, 4 Feb 2025 17:43:41 -0500 Subject: [PATCH 162/209] feat: Update country data management to use AdventureLog CDN for fetching flags and JSON files --- backend/server/main/settings.py | 4 +- .../management/commands/download-countries.py | 83 ++++++++++++++----- cdn/main.py | 2 +- 3 files changed, 67 insertions(+), 22 deletions(-) diff --git a/backend/server/main/settings.py b/backend/server/main/settings.py index 5549c0da..1c894c56 100644 --- a/backend/server/main/settings.py +++ b/backend/server/main/settings.py @@ -302,5 +302,5 @@ LOGGING = { }, }, } -# https://github.com/dr5hn/countries-states-cities-database/tags -COUNTRY_REGION_JSON_VERSION = 'v2.5' \ No newline at end of file + +ADVENTURELOG_CDN_URL = getenv('ADVENTURELOG_CDN_URL', 'https://cdn.adventurelog.app') \ No newline at end of file diff --git a/backend/server/worldtravel/management/commands/download-countries.py b/backend/server/worldtravel/management/commands/download-countries.py index f5c5702d..526a4f46 100644 --- a/backend/server/worldtravel/management/commands/download-countries.py +++ b/backend/server/worldtravel/management/commands/download-countries.py @@ -8,7 +8,7 @@ import ijson from django.conf import settings -COUNTRY_REGION_JSON_VERSION = settings.COUNTRY_REGION_JSON_VERSION +ADVENTURELOG_CDN_URL = settings.ADVENTURELOG_CDN_URL media_root = settings.MEDIA_ROOT @@ -27,7 +27,7 @@ def saveCountryFlag(country_code): print(f'Flag for {country_code} already exists') return - res = requests.get(f'https://flagcdn.com/h240/{country_code}.png'.lower()) + res = requests.get(f'{ADVENTURELOG_CDN_URL}/data/flags/{country_code}.png'.lower()) if res.status_code == 200: with open(flag_path, 'wb') as f: f.write(res.content) @@ -39,31 +39,76 @@ class Command(BaseCommand): help = 'Imports the world travel data' def add_arguments(self, parser): - parser.add_argument('--force', action='store_true', help='Force download the countries+regions+states.json file') + parser.add_argument('--force', action='store_true', help='Force re-download of AdventureLog setup content from the CDN') def handle(self, **options): force = options['force'] batch_size = 100 - countries_json_path = os.path.join(settings.MEDIA_ROOT, f'countries+regions+states-{COUNTRY_REGION_JSON_VERSION}.json') - if not os.path.exists(countries_json_path) or force: - res = requests.get(f'https://raw.githubusercontent.com/dr5hn/countries-states-cities-database/{COUNTRY_REGION_JSON_VERSION}/json/countries%2Bstates%2Bcities.json') - if res.status_code == 200: - with open(countries_json_path, 'w') as f: - f.write(res.text) - self.stdout.write(self.style.SUCCESS('countries+regions+states.json downloaded successfully')) + current_version_json = os.path.join(settings.MEDIA_ROOT, 'data_version.json') + cdn_version_json = requests.get(f'{ADVENTURELOG_CDN_URL}/data/version.json') + if cdn_version_json.status_code == 200: + cdn_version = cdn_version_json.json().get('version') + if os.path.exists(current_version_json): + with open(current_version_json, 'r') as f: + local_version = f.read().strip() + self.stdout.write(self.style.SUCCESS(f'Local version: {local_version}')) else: - self.stdout.write(self.style.ERROR('Error downloading countries+regions+states.json')) + local_version = None + + if force or local_version != cdn_version: + with open(current_version_json, 'w') as f: + f.write(cdn_version) + self.stdout.write(self.style.SUCCESS('Version updated successfully to ' + cdn_version)) + else: + self.stdout.write(self.style.SUCCESS('Data is already up-to-date.')) return - elif not os.path.isfile(countries_json_path): - self.stdout.write(self.style.ERROR('countries+regions+states.json is not a file')) - return - elif os.path.getsize(countries_json_path) == 0: - self.stdout.write(self.style.ERROR('countries+regions+states.json is empty')) - elif Country.objects.count() == 0 or Region.objects.count() == 0 or City.objects.count() == 0: - self.stdout.write(self.style.WARNING('Some region data is missing. Re-importing all data.')) else: - self.stdout.write(self.style.SUCCESS('Latest country, region, and state data already downloaded.')) + self.stdout.write(self.style.ERROR('Error downloading version.json')) return + + self.stdout.write(self.style.SUCCESS('Fetching latest data from the AdventureLog CDN located at: ' + ADVENTURELOG_CDN_URL)) + + # Delete the existing flags + flags_dir = os.path.join(media_root, 'flags') + if os.path.exists(flags_dir): + for file in os.listdir(flags_dir): + os.remove(os.path.join(flags_dir, file)) + + # Delete the existing countries, regions, and cities json files + countries_json_path = os.path.join(media_root, 'countries_states_cities.json') + if os.path.exists(countries_json_path): + os.remove(countries_json_path) + + # Download the latest countries, regions, and cities json file + res = requests.get(f'{ADVENTURELOG_CDN_URL}/data/countries_states_cities.json') + if res.status_code == 200: + with open(countries_json_path, 'w') as f: + f.write(res.text) + self.stdout.write(self.style.SUCCESS('countries_states_cities.json downloaded successfully')) + else: + self.stdout.write(self.style.ERROR('Error downloading countries_states_cities.json')) + return + + + # if not os.path.exists(version_json) or force: + # res = requests.get(f'https://raw.githubusercontent.com/dr5hn/countries-states-cities-database/{COUNTRY_REGION_JSON_VERSION}/json/countries%2Bstates%2Bcities.json') + # if res.status_code == 200: + # with open(countries_json_path, 'w') as f: + # f.write(res.text) + # self.stdout.write(self.style.SUCCESS('countries+regions+states.json downloaded successfully')) + # else: + # self.stdout.write(self.style.ERROR('Error downloading countries+regions+states.json')) + # return + # elif not os.path.isfile(countries_json_path): + # self.stdout.write(self.style.ERROR('countries+regions+states.json is not a file')) + # return + # elif os.path.getsize(countries_json_path) == 0: + # self.stdout.write(self.style.ERROR('countries+regions+states.json is empty')) + # elif Country.objects.count() == 0 or Region.objects.count() == 0 or City.objects.count() == 0: + # self.stdout.write(self.style.WARNING('Some region data is missing. Re-importing all data.')) + # else: + # self.stdout.write(self.style.SUCCESS('Latest country, region, and state data already downloaded.')) + # return with open(countries_json_path, 'r') as f: f = open(countries_json_path, 'rb') diff --git a/cdn/main.py b/cdn/main.py index 06c0d333..a33c2dca 100644 --- a/cdn/main.py +++ b/cdn/main.py @@ -31,7 +31,7 @@ def downloadCountriesStateCities(): """ res = requests.get(f'https://raw.githubusercontent.com/dr5hn/countries-states-cities-database/{COUNTRY_REGION_JSON_VERSION}/json/countries%2Bstates%2Bcities.json') - path = os.path.join(os.path.dirname(__file__), 'data', f'countries_states_cities_{COUNTRY_REGION_JSON_VERSION}.json') + path = os.path.join(os.path.dirname(__file__), 'data', f'countries_states_cities.json') with open(path, 'w') as f: f.write(res.text) From f7ff8f30b0b1e0b56e75402635bae91a9749112c Mon Sep 17 00:00:00 2001 From: Sean Morley Date: Tue, 4 Feb 2025 18:06:03 -0500 Subject: [PATCH 163/209] feat: Add CODEOWNERS file to define repository ownership --- .github/CODEOWNERS | 1 + 1 file changed, 1 insertion(+) create mode 100644 .github/CODEOWNERS diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 00000000..ca0f2f6c --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +* @seanmorley15 \ No newline at end of file From 921e756aefebfdea8b74b636fb5bea4dfc14f695 Mon Sep 17 00:00:00 2001 From: Sean Morley Date: Wed, 5 Feb 2025 16:50:05 -0500 Subject: [PATCH 164/209] feat: Enhance CDN management with entrypoint script and update country data handling --- .../management/commands/download-countries.py | 3 ++- cdn/Dockerfile | 20 +++++++++---------- cdn/entrypoint.sh | 9 +++++++++ cdn/main.py | 2 +- 4 files changed, 22 insertions(+), 12 deletions(-) create mode 100644 cdn/entrypoint.sh diff --git a/backend/server/worldtravel/management/commands/download-countries.py b/backend/server/worldtravel/management/commands/download-countries.py index 526a4f46..3a1cd11d 100644 --- a/backend/server/worldtravel/management/commands/download-countries.py +++ b/backend/server/worldtravel/management/commands/download-countries.py @@ -60,7 +60,7 @@ class Command(BaseCommand): f.write(cdn_version) self.stdout.write(self.style.SUCCESS('Version updated successfully to ' + cdn_version)) else: - self.stdout.write(self.style.SUCCESS('Data is already up-to-date.')) + self.stdout.write(self.style.SUCCESS('Data is already up-to-date. Run with --force to re-download')) return else: self.stdout.write(self.style.ERROR('Error downloading version.json')) @@ -78,6 +78,7 @@ class Command(BaseCommand): countries_json_path = os.path.join(media_root, 'countries_states_cities.json') if os.path.exists(countries_json_path): os.remove(countries_json_path) + self.stdout.write(self.style.SUCCESS('countries_states_cities.json deleted successfully')) # Download the latest countries, regions, and cities json file res = requests.get(f'{ADVENTURELOG_CDN_URL}/data/countries_states_cities.json') diff --git a/cdn/Dockerfile b/cdn/Dockerfile index 544f8d46..f47e79a0 100644 --- a/cdn/Dockerfile +++ b/cdn/Dockerfile @@ -7,20 +7,16 @@ WORKDIR /app # Install required Python packages RUN pip install --no-cache-dir requests osm2geojson -# Copy the script and data folder into the container +# Copy the script into the container COPY main.py /app/main.py -COPY data /app/data/ -# Ensure the data folder exists -RUN mkdir -p /app/data - -# Run the script to generate GeoJSON files -RUN python /app/main.py +# Run the script to generate the data folder and GeoJSON files (this runs inside the container) +RUN python -u /app/main.py # Install Nginx RUN apt update && apt install -y nginx && rm -rf /var/lib/apt/lists/* -# Copy the entire data folder to the Nginx serving directory +# Copy the entire generated data folder to the Nginx serving directory RUN mkdir -p /var/www/html/data && cp -r /app/data/* /var/www/html/data/ # Copy Nginx configuration @@ -32,5 +28,9 @@ COPY index.html /usr/share/nginx/html/index.html # Expose port 80 for Nginx EXPOSE 80 -# Start Nginx in the foreground -CMD ["nginx", "-g", "daemon off;"] +# Copy the entrypoint script into the container +COPY entrypoint.sh /app/entrypoint.sh +RUN chmod +x /app/entrypoint.sh + +# Set the entrypoint script as the default command +ENTRYPOINT ["/app/entrypoint.sh"] diff --git a/cdn/entrypoint.sh b/cdn/entrypoint.sh new file mode 100644 index 00000000..fffe2442 --- /dev/null +++ b/cdn/entrypoint.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +# Any setup tasks or checks can go here (if needed) +echo "AdventureLog CDN has started!" +echo "Refer to the documentation for information about connecting your AdventureLog instance to this CDN." +echo "Thanks to our data providers for making this possible! You can find them on the CDN site." + +# Start Nginx in the foreground (as the main process) +nginx -g 'daemon off;' diff --git a/cdn/main.py b/cdn/main.py index a33c2dca..2c052fad 100644 --- a/cdn/main.py +++ b/cdn/main.py @@ -70,7 +70,7 @@ def saveCountryFlags(): Downloads the flags of all countries and saves them in the data/flags directory """ # Load the countries data - with open(os.path.join(os.path.dirname(__file__), 'data', f'countries_states_cities_{COUNTRY_REGION_JSON_VERSION}.json')) as f: + with open(os.path.join(os.path.dirname(__file__), 'data', f'countries_states_cities.json')) as f: data = json.load(f) for country in data: From 271cba9abccd0fa7ed83f71df05869f62d3edf86 Mon Sep 17 00:00:00 2001 From: Sean Morley Date: Wed, 5 Feb 2025 16:55:07 -0500 Subject: [PATCH 165/209] feat: Add GitHub Actions workflows for uploading CDN images to GHCR and Docker Hub --- .github/workflows/cdn-beta.yml | 46 +++++++++++++++++++++++++++++++ .github/workflows/cdn-latest.yml | 46 +++++++++++++++++++++++++++++++ .github/workflows/cdn-release.yml | 43 +++++++++++++++++++++++++++++ 3 files changed, 135 insertions(+) create mode 100644 .github/workflows/cdn-beta.yml create mode 100644 .github/workflows/cdn-latest.yml create mode 100644 .github/workflows/cdn-release.yml diff --git a/.github/workflows/cdn-beta.yml b/.github/workflows/cdn-beta.yml new file mode 100644 index 00000000..d5c2c852 --- /dev/null +++ b/.github/workflows/cdn-beta.yml @@ -0,0 +1,46 @@ +name: Upload beta CDN image to GHCR and Docker Hub + +on: + push: + branches: + - development + paths: + - "cdn/**" + +env: + IMAGE_NAME: "adventurelog-cdn" + +jobs: + upload: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v2 + + - name: Login to GitHub Container Registry + uses: docker/login-action@v1 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.ACCESS_TOKEN }} + + - name: Login to Docker Hub + uses: docker/login-action@v1 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: set lower case owner name + run: | + echo "REPO_OWNER=${OWNER,,}" >>${GITHUB_ENV} + env: + OWNER: "${{ github.repository_owner }}" + + - name: Build Docker images + run: docker buildx build --platform linux/amd64,linux/arm64 --push -t ghcr.io/$REPO_OWNER/$IMAGE_NAME:beta -t ${{ secrets.DOCKERHUB_USERNAME }}/$IMAGE_NAME:beta ./cdn diff --git a/.github/workflows/cdn-latest.yml b/.github/workflows/cdn-latest.yml new file mode 100644 index 00000000..376ede98 --- /dev/null +++ b/.github/workflows/cdn-latest.yml @@ -0,0 +1,46 @@ +name: Upload latest CDN image to GHCR and Docker Hub + +on: + push: + branches: + - main + paths: + - "cdn/**" + +env: + IMAGE_NAME: "adventurelog-cdn" + +jobs: + upload: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v2 + + - name: Login to GitHub Container Registry + uses: docker/login-action@v1 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.ACCESS_TOKEN }} + + - name: Login to Docker Hub + uses: docker/login-action@v1 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: set lower case owner name + run: | + echo "REPO_OWNER=${OWNER,,}" >>${GITHUB_ENV} + env: + OWNER: "${{ github.repository_owner }}" + + - name: Build Docker images + run: docker buildx build --platform linux/amd64,linux/arm64 --push -t ghcr.io/$REPO_OWNER/$IMAGE_NAME:latest -t ${{ secrets.DOCKERHUB_USERNAME }}/$IMAGE_NAME:latest ./cdn diff --git a/.github/workflows/cdn-release.yml b/.github/workflows/cdn-release.yml new file mode 100644 index 00000000..2bba9afb --- /dev/null +++ b/.github/workflows/cdn-release.yml @@ -0,0 +1,43 @@ +name: Upload the tagged release CDN image to GHCR and Docker Hub + +on: + release: + types: [released] + +env: + IMAGE_NAME: "adventurelog-cdn" + +jobs: + upload: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v2 + + - name: Login to GitHub Container Registry + uses: docker/login-action@v1 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.ACCESS_TOKEN }} + + - name: Login to Docker Hub + uses: docker/login-action@v1 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: set lower case owner name + run: | + echo "REPO_OWNER=${OWNER,,}" >>${GITHUB_ENV} + env: + OWNER: "${{ github.repository_owner }}" + + - name: Build Docker images + run: docker buildx build --platform linux/amd64,linux/arm64 --push -t ghcr.io/$REPO_OWNER/$IMAGE_NAME:${{ github.event.release.tag_name }} -t ${{ secrets.DOCKERHUB_USERNAME }}/$IMAGE_NAME:${{ github.event.release.tag_name }} ./cdn From d1f50dfa17068a8ef359c975b42dc9589c9f36bd Mon Sep 17 00:00:00 2001 From: Sean Morley Date: Wed, 5 Feb 2025 19:38:04 -0500 Subject: [PATCH 166/209] feat: Add hotel management functionality with serializer and UI integration --- backend/server/adventures/serializers.py | 27 +- .../management/commands/download-countries.py | 30 +- .../src/lib/components/AdventureModal.svelte | 390 +----------------- frontend/src/lib/components/HotelModal.svelte | 132 +----- .../lib/components/LocationDropdown.svelte | 336 +++++++++++++++ frontend/src/lib/types.ts | 1 + frontend/src/locales/en.json | 1 + .../src/routes/collections/[id]/+page.svelte | 49 ++- 8 files changed, 425 insertions(+), 541 deletions(-) create mode 100644 frontend/src/lib/components/LocationDropdown.svelte diff --git a/backend/server/adventures/serializers.py b/backend/server/adventures/serializers.py index df0f9aca..5771c7c4 100644 --- a/backend/server/adventures/serializers.py +++ b/backend/server/adventures/serializers.py @@ -203,6 +203,17 @@ class TransportationSerializer(CustomModelSerializer): ] read_only_fields = ['id', 'created_at', 'updated_at', 'user_id'] +class HotelSerializer(CustomModelSerializer): + + class Meta: + model = Hotel + fields = [ + 'id', 'user_id', 'name', 'description', 'rating', 'link', 'check_in', 'check_out', + 'reservation_number', 'price', 'latitude', 'longitude', 'location', 'is_public', + 'collection', 'created_at', 'updated_at' + ] + read_only_fields = ['id', 'created_at', 'updated_at', 'user_id'] + class NoteSerializer(CustomModelSerializer): class Meta: @@ -289,10 +300,11 @@ class CollectionSerializer(CustomModelSerializer): transportations = TransportationSerializer(many=True, read_only=True, source='transportation_set') notes = NoteSerializer(many=True, read_only=True, source='note_set') checklists = ChecklistSerializer(many=True, read_only=True, source='checklist_set') + hotels = HotelSerializer(many=True, read_only=True, source='hotel_set') class Meta: model = Collection - fields = ['id', 'description', 'user_id', 'name', 'is_public', 'adventures', 'created_at', 'start_date', 'end_date', 'transportations', 'notes', 'updated_at', 'checklists', 'is_archived', 'shared_with', 'link'] + fields = ['id', 'description', 'user_id', 'name', 'is_public', 'adventures', 'created_at', 'start_date', 'end_date', 'transportations', 'notes', 'updated_at', 'checklists', 'is_archived', 'shared_with', 'link', 'hotels'] read_only_fields = ['id', 'created_at', 'updated_at', 'user_id'] def to_representation(self, instance): @@ -302,15 +314,4 @@ class CollectionSerializer(CustomModelSerializer): for user in instance.shared_with.all(): shared_uuids.append(str(user.uuid)) representation['shared_with'] = shared_uuids - return representation - -class HotelSerializer(CustomModelSerializer): - - class Meta: - model = Hotel - fields = [ - 'id', 'user_id', 'name', 'description', 'rating', 'link', 'check_in', 'check_out', - 'reservation_number', 'price', 'latitude', 'longitude', 'location', 'is_public', - 'collection', 'created_at', 'updated_at' - ] - read_only_fields = ['id', 'created_at', 'updated_at', 'user_id'] \ No newline at end of file + return representation \ No newline at end of file diff --git a/backend/server/worldtravel/management/commands/download-countries.py b/backend/server/worldtravel/management/commands/download-countries.py index 3a1cd11d..9930b8ee 100644 --- a/backend/server/worldtravel/management/commands/download-countries.py +++ b/backend/server/worldtravel/management/commands/download-countries.py @@ -45,8 +45,9 @@ class Command(BaseCommand): force = options['force'] batch_size = 100 current_version_json = os.path.join(settings.MEDIA_ROOT, 'data_version.json') - cdn_version_json = requests.get(f'{ADVENTURELOG_CDN_URL}/data/version.json') - if cdn_version_json.status_code == 200: + try: + cdn_version_json = requests.get(f'{ADVENTURELOG_CDN_URL}/data/version.json') + cdn_version_json.raise_for_status() cdn_version = cdn_version_json.json().get('version') if os.path.exists(current_version_json): with open(current_version_json, 'r') as f: @@ -62,8 +63,8 @@ class Command(BaseCommand): else: self.stdout.write(self.style.SUCCESS('Data is already up-to-date. Run with --force to re-download')) return - else: - self.stdout.write(self.style.ERROR('Error downloading version.json')) + except requests.RequestException as e: + self.stdout.write(self.style.ERROR(f'Error fetching version from the CDN: {e}, skipping data import. Try restarting the container once CDN connection has been restored.')) return self.stdout.write(self.style.SUCCESS('Fetching latest data from the AdventureLog CDN located at: ' + ADVENTURELOG_CDN_URL)) @@ -90,27 +91,6 @@ class Command(BaseCommand): self.stdout.write(self.style.ERROR('Error downloading countries_states_cities.json')) return - - # if not os.path.exists(version_json) or force: - # res = requests.get(f'https://raw.githubusercontent.com/dr5hn/countries-states-cities-database/{COUNTRY_REGION_JSON_VERSION}/json/countries%2Bstates%2Bcities.json') - # if res.status_code == 200: - # with open(countries_json_path, 'w') as f: - # f.write(res.text) - # self.stdout.write(self.style.SUCCESS('countries+regions+states.json downloaded successfully')) - # else: - # self.stdout.write(self.style.ERROR('Error downloading countries+regions+states.json')) - # return - # elif not os.path.isfile(countries_json_path): - # self.stdout.write(self.style.ERROR('countries+regions+states.json is not a file')) - # return - # elif os.path.getsize(countries_json_path) == 0: - # self.stdout.write(self.style.ERROR('countries+regions+states.json is empty')) - # elif Country.objects.count() == 0 or Region.objects.count() == 0 or City.objects.count() == 0: - # self.stdout.write(self.style.WARNING('Some region data is missing. Re-importing all data.')) - # else: - # self.stdout.write(self.style.SUCCESS('Latest country, region, and state data already downloaded.')) - # return - with open(countries_json_path, 'r') as f: f = open(countries_json_path, 'rb') parser = ijson.items(f, 'item') diff --git a/frontend/src/lib/components/AdventureModal.svelte b/frontend/src/lib/components/AdventureModal.svelte index 28cd752a..3a5afc97 100644 --- a/frontend/src/lib/components/AdventureModal.svelte +++ b/frontend/src/lib/components/AdventureModal.svelte @@ -1,51 +1,35 @@ @@ -810,150 +597,7 @@ -
    - -
    - {$t('adventures.location_information')} -
    -
    - -
    -
    -
    - - {#if is_custom_location} - - {/if} -
    -
    - -
    -
    - - - -
    -
    - {#if places.length > 0} -
    -

    {$t('adventures.search_results')}

    - -
    - {#each places as place} - - {/each} -
    -
    - {:else if noPlaces} -

    {$t('adventures.no_results')}

    - {/if} - -
    - - - - - {#each markers as marker} - - {/each} - - {#if reverseGeocodePlace} -
    -

    - {reverseGeocodePlace.city - ? reverseGeocodePlace.city + ', ' - : ''}{reverseGeocodePlace.region}, - {reverseGeocodePlace.country} -

    -

    - {reverseGeocodePlace.region}: - {reverseGeocodePlace.region_visited - ? $t('adventures.visited') - : $t('adventures.not_visited')} -

    - {#if reverseGeocodePlace.city} -

    - {reverseGeocodePlace.city}: - {reverseGeocodePlace.city_visited - ? $t('adventures.visited') - : $t('adventures.not_visited')} -

    - {/if} -
    - {#if !reverseGeocodePlace.region_visited || (!reverseGeocodePlace.city_visited && !willBeMarkedVisited)} - - {/if} - {#if (willBeMarkedVisited && !reverseGeocodePlace.region_visited && reverseGeocodePlace.region_id) || (!reverseGeocodePlace.city_visited && willBeMarkedVisited && reverseGeocodePlace.city_id)} - - {/if} - {/if} -
    -
    -
    +
    diff --git a/frontend/src/lib/components/HotelModal.svelte b/frontend/src/lib/components/HotelModal.svelte index e44d1f8e..241be481 100644 --- a/frontend/src/lib/components/HotelModal.svelte +++ b/frontend/src/lib/components/HotelModal.svelte @@ -6,6 +6,7 @@ import { appVersion } from '$lib/config'; import { DefaultMarker, MapEvents, MapLibre } from 'svelte-maplibre'; import type { Collection, Hotel, ReverseGeocode, OpenStreetMapPlace, Point } from '$lib/types'; + import LocationDropdown from './LocationDropdown.svelte'; const dispatch = createEventDispatcher(); @@ -17,7 +18,7 @@ let hotel: Hotel = { ...initializeHotel(hotelToEdit) }; let fullStartDate: string = ''; let fullEndDate: string = ''; - let reverseGeocodePlace: ReverseGeocode | null = null; + let reverseGeocodePlace: any | null = null; let query: string = ''; let places: OpenStreetMapPlace[] = []; let noPlaces: boolean = false; @@ -83,34 +84,6 @@ if (event.key === 'Escape') close(); } - // Geocode location search - async function geocode(e: Event | null) { - if (e) e.preventDefault(); - if (!query) { - alert($t('adventures.no_location')); - return; - } - const res = await fetch(`https://nominatim.openstreetmap.org/search?q=${query}&format=jsonv2`, { - headers: { 'User-Agent': `AdventureLog / ${appVersion}` } - }); - const data = (await res.json()) as OpenStreetMapPlace[]; - places = data; - noPlaces = data.length === 0; - } - - // Set custom location flag based on hotel location - $: is_custom_location = hotel.location !== (reverseGeocodePlace?.display_name || ''); - - // Add marker to map - async function addMarker(e: CustomEvent) { - markers = [{ lngLat: e.detail.lngLat, name: '', location: '', activity_type: '' }]; - } - - // Clear all markers from the map - function clearMap() { - markers = []; - } - // Handle form submission (save hotel) async function handleSubmit(event: Event) { event.preventDefault(); @@ -325,106 +298,7 @@
    -
    - -
    - {$t('adventures.location_information')} -
    -
    - -
    -
    -
    - - {#if is_custom_location} - - {/if} -
    -
    - -
    -
    - - - -
    -
    - {#if places.length > 0} -
    -

    {$t('adventures.search_results')}

    - -
    - {#each places as place} - - {/each} -
    -
    - {:else if noPlaces} -

    {$t('adventures.no_results')}

    - {/if} - -
    - - - - - {#each markers as marker} - - {/each} - -
    -
    -
    - -
    - -
    - {$t('adventures.location_information')} -
    - -
    -
    +
    diff --git a/frontend/src/lib/components/LocationDropdown.svelte b/frontend/src/lib/components/LocationDropdown.svelte new file mode 100644 index 00000000..42328f48 --- /dev/null +++ b/frontend/src/lib/components/LocationDropdown.svelte @@ -0,0 +1,336 @@ + + +
    + +
    + {$t('adventures.location_information')} +
    +
    + +
    +
    +
    + + {#if is_custom_location} + + {/if} +
    +
    + +
    +
    + + + +
    +
    + {#if places.length > 0} +
    +

    {$t('adventures.search_results')}

    + +
    + {#each places as place} + + {/each} +
    +
    + {:else if noPlaces} +

    {$t('adventures.no_results')}

    + {/if} + +
    + + + + + {#each markers as marker} + + {/each} + + {#if reverseGeocodePlace} +
    +

    + {reverseGeocodePlace.city + ? reverseGeocodePlace.city + ', ' + : ''}{reverseGeocodePlace.region}, + {reverseGeocodePlace.country} +

    +

    + {reverseGeocodePlace.region}: + {reverseGeocodePlace.region_visited + ? $t('adventures.visited') + : $t('adventures.not_visited')} +

    + {#if reverseGeocodePlace.city} +

    + {reverseGeocodePlace.city}: + {reverseGeocodePlace.city_visited + ? $t('adventures.visited') + : $t('adventures.not_visited')} +

    + {/if} +
    + {#if !reverseGeocodePlace.region_visited || (!reverseGeocodePlace.city_visited && !willBeMarkedVisited)} + + {/if} + {#if (willBeMarkedVisited && !reverseGeocodePlace.region_visited && reverseGeocodePlace.region_id) || (!reverseGeocodePlace.city_visited && willBeMarkedVisited && reverseGeocodePlace.city_id)} + + {/if} + {/if} +
    +
    +
    diff --git a/frontend/src/lib/types.ts b/frontend/src/lib/types.ts index e339ee04..30f5b27f 100644 --- a/frontend/src/lib/types.ts +++ b/frontend/src/lib/types.ts @@ -113,6 +113,7 @@ export type Collection = { end_date: string | null; transportations?: Transportation[]; notes?: Note[]; + hotels?: Hotel[]; checklists?: Checklist[]; is_archived?: boolean; shared_with: string[] | undefined; diff --git a/frontend/src/locales/en.json b/frontend/src/locales/en.json index 4c4e645b..011dfbf2 100644 --- a/frontend/src/locales/en.json +++ b/frontend/src/locales/en.json @@ -191,6 +191,7 @@ "no_description_found": "No description found", "adventure_created": "Adventure created", "adventure_create_error": "Failed to create adventure", + "hotel": "Hotel", "create_adventure": "Create Adventure", "adventure_updated": "Adventure updated", "adventure_update_error": "Failed to update adventure", diff --git a/frontend/src/routes/collections/[id]/+page.svelte b/frontend/src/routes/collections/[id]/+page.svelte index 6f495252..f19f7c73 100644 --- a/frontend/src/routes/collections/[id]/+page.svelte +++ b/frontend/src/routes/collections/[id]/+page.svelte @@ -1,5 +1,5 @@ {#if isShowingLinkModal} @@ -376,6 +404,15 @@ /> {/if} +{#if isShowingHotelModal} + (isShowingHotelModal = false)} + on:save={saveOrCreateHotel} + {collection} + /> +{/if} + {#if isAdventureModalOpen} {$t('adventures.checklist')} + {/each} {#if reverseGeocodePlace} -
    -

    +

    +

    {$t('adventures.location_details')}

    +

    + {$t('adventures.display_name')}: {reverseGeocodePlace.city - ? reverseGeocodePlace.city + ', ' - : ''}{reverseGeocodePlace.region}, - {reverseGeocodePlace.country} + ? reverseGeocodePlace.city + ', ' + : ''}{reverseGeocodePlace.region}, {reverseGeocodePlace.country}

    -

    - {reverseGeocodePlace.region}: - {reverseGeocodePlace.region_visited - ? $t('adventures.visited') - : $t('adventures.not_visited')} +

    + {$t('adventures.region')}: + {reverseGeocodePlace.region} + {reverseGeocodePlace.region_visited ? '✅' : '❌'}

    {#if reverseGeocodePlace.city} -

    - {reverseGeocodePlace.city}: - {reverseGeocodePlace.city_visited - ? $t('adventures.visited') - : $t('adventures.not_visited')} +

    + {$t('adventures.city')}: + {reverseGeocodePlace.city} + {reverseGeocodePlace.city_visited ? '✅' : '❌'}

    {/if}
    {#if !reverseGeocodePlace.region_visited || (!reverseGeocodePlace.city_visited && !willBeMarkedVisited)} - {/if} {#if (willBeMarkedVisited && !reverseGeocodePlace.region_visited && reverseGeocodePlace.region_id) || (!reverseGeocodePlace.city_visited && willBeMarkedVisited && reverseGeocodePlace.city_id)} -
    + +
    +

    About AdventureLog

    +

    + AdventureLog is a project that aims to provide a platform for users to + log their adventures and share them with the world. The project is + developed by Sean Morley and is + open source. View it on GitHub here: + AdventureLog. +

    +
    + +
    +
    +
    +

    Data Attributions

    +

    + The data provided in this CDN is sourced from the following + repositories: +

    + +
    +
    +
    +