diff --git a/backend/server/adventures/geocoding.py b/backend/server/adventures/geocoding.py index 31ba13f..5e5aafa 100644 --- a/backend/server/adventures/geocoding.py +++ b/backend/server/adventures/geocoding.py @@ -1,4 +1,6 @@ import requests +import time +import socket from worldtravel.models import Region, City, VisitedRegion, VisitedCity def extractIsoCode(user, data): @@ -56,16 +58,38 @@ def extractIsoCode(user, data): return {"region_id": iso_code, "region": region.name, "country": region.country.name, "country_id": region.country.country_code, "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, 'location_name': location_name} return {"error": "No region found"} +def is_host_resolvable(hostname: str) -> bool: + try: + socket.gethostbyname(hostname) + return True + except socket.error: + return False # silently fail + def reverse_geocode(lat, lon, user): - """ - Reverse geocode the given latitude and longitude using Nominatim API. - Returns a dictionary containing the region name, country name, and ISO code if found. - """ url = f"https://nominatim.openstreetmap.org/reverse?format=jsonv2&lat={lat}&lon={lon}" headers = {'User-Agent': 'AdventureLog Server'} - response = requests.get(url, headers=headers) + connect_timeout = 1 # instead of 0.3 + read_timeout = 5 # instead of 2 + + + if not is_host_resolvable("nominatim.openstreetmap.org"): + return {"error": "DNS resolution failed"} + + start = time.time() try: + response = requests.get(url, headers=headers, timeout=(connect_timeout, read_timeout)) + response.raise_for_status() data = response.json() - except requests.exceptions.JSONDecodeError: + elapsed = time.time() - start + return extractIsoCode(user, data) + + except requests.exceptions.ConnectionError as e: + return {"error": "Could not connect to geocoding service"} + except requests.exceptions.Timeout as e: + return {"error": "Geocoding service timed out"} + except requests.exceptions.HTTPError as e: + return {"error": f"HTTP error from geocoding service: {e}"} + except requests.exceptions.JSONDecodeError as e: return {"error": "Invalid response from geocoding service"} - return extractIsoCode(user, data) + except Exception as e: + return {"error": f"Unexpected geocoding error: {str(e)}"} \ No newline at end of file diff --git a/backend/server/adventures/serializers.py b/backend/server/adventures/serializers.py index 0fe059e..8ae8181 100644 --- a/backend/server/adventures/serializers.py +++ b/backend/server/adventures/serializers.py @@ -167,7 +167,6 @@ class AdventureSerializer(CustomModelSerializer): if category_data: category = self.get_or_create_category(category_data) instance.category = category - instance.save() if has_visits: current_visits = instance.visits.all() diff --git a/frontend/src/lib/components/AdventureModal.svelte b/frontend/src/lib/components/AdventureModal.svelte index 8a93284..e8feb0a 100644 --- a/frontend/src/lib/components/AdventureModal.svelte +++ b/frontend/src/lib/components/AdventureModal.svelte @@ -177,6 +177,8 @@ let wikiImageError: string = ''; let triggerMarkVisted: boolean = false; + let isLoading: boolean = false; + images = adventure.images || []; $: { if (!adventure.rating) { @@ -414,6 +416,7 @@ async function handleSubmit(event: Event) { event.preventDefault(); triggerMarkVisted = true; + isLoading = true; // if category icon is empty, set it to the default icon if (adventure.category?.icon == '' || adventure.category?.icon == null) { @@ -477,6 +480,7 @@ } } imageSearch = adventure.name; + isLoading = false; } @@ -669,7 +673,14 @@ {/if}
- + {#if !isLoading} + + {:else} + + {/if}
diff --git a/frontend/src/lib/components/LocationDropdown.svelte b/frontend/src/lib/components/LocationDropdown.svelte index b39628f..6420397 100644 --- a/frontend/src/lib/components/LocationDropdown.svelte +++ b/frontend/src/lib/components/LocationDropdown.svelte @@ -55,7 +55,7 @@ } $: if (triggerMarkVisted && willBeMarkedVisited) { - displaySuccessToast(); // since the server will trigger the geocode automatically, we just need to show the toast and let the server handle the rest. It's kinda a placebo effect... + displaySuccessToast(); // since the server will trigger the geocode automatically, we just need to show the toast and let the server handle the rest. It's kinda a placebo effect triggerMarkVisted = false; } diff --git a/frontend/src/routes/worldtravel/+page.svelte b/frontend/src/routes/worldtravel/+page.svelte index aeabc14..c0bc41c 100644 --- a/frontend/src/routes/worldtravel/+page.svelte +++ b/frontend/src/routes/worldtravel/+page.svelte @@ -165,13 +165,16 @@ {#if filteredCountries.length === 0}

{$t('worldtravel.no_countries_found')}

-
- {$t('settings.documentation_link')} -
+ + {#if allCountries.length === 0} +
+ {$t('settings.documentation_link')} +
+ {/if} {/if}