From 486cded844639b409aa8a8f9ae8ab6fb4c98b325 Mon Sep 17 00:00:00 2001 From: gabdevele Date: Mon, 15 Jun 2026 14:43:00 +0200 Subject: [PATCH 1/3] feat(geocoding): implement address search and reverse geocoding APIs --- app/components/ReportForm.vue | 349 +++++++++++++++++++++++++--- server/api/geocoding/reverse.get.ts | 19 ++ server/api/geocoding/search.get.ts | 20 ++ server/utils/geocoding.ts | 123 ++++++++++ 4 files changed, 478 insertions(+), 33 deletions(-) create mode 100644 server/api/geocoding/reverse.get.ts create mode 100644 server/api/geocoding/search.get.ts create mode 100644 server/utils/geocoding.ts diff --git a/app/components/ReportForm.vue b/app/components/ReportForm.vue index ec546e8..c03254f 100644 --- a/app/components/ReportForm.vue +++ b/app/components/ReportForm.vue @@ -13,6 +13,20 @@ interface Emits { (e: 'submit', report: CreateReportRequest, media: File[]): void } +interface GeocodingSuggestion { + label: string + latitude: number + longitude: number +} + +interface SearchAddressesResponse { + suggestions: GeocodingSuggestion[] +} + +interface ReverseGeocodeResponse { + address: string +} + const props = withDefaults(defineProps(), { isLoading: false, categories: () => [] @@ -32,12 +46,23 @@ const media = ref([]) const errors = ref>({}) const successMessage = ref(false) const isGettingLocation = ref(false) +const addressSuggestions = ref([]) +const isSearchingAddress = ref(false) +const isResolvingAddress = ref(false) +const addressSearchError = ref('') +const showAddressSuggestions = ref(false) // Map variables const mapContainer = ref(null) let leaflet: typeof import('leaflet') | null = null let map: LeafletMap | null = null let marker: LeafletMarker | null = null +let addressSearchTimeout: ReturnType | null = null +let manualLocationSyncTimeout: ReturnType | null = null +let addressSearchRequestId = 0 +let reverseGeocodeRequestId = 0 +let lastSyncedLocationKey = '' +let skipNextAddressSearch = false const loadLeaflet = async () => { if (leaflet) return leaflet @@ -60,6 +85,175 @@ const loadLeaflet = async () => { return leaflet } +const toLocationKey = (lat: number, lng: number) => `${lat.toFixed(6)}:${lng.toFixed(6)}` + +const clearAddressSuggestions = () => { + addressSuggestions.value = [] + showAddressSuggestions.value = false +} + +const cancelPendingAddressSearch = () => { + addressSearchRequestId += 1 + isSearchingAddress.value = false + + if (addressSearchTimeout) { + clearTimeout(addressSearchTimeout) + addressSearchTimeout = null + } +} + +const cancelPendingReverseGeocode = () => { + reverseGeocodeRequestId += 1 + isResolvingAddress.value = false +} + +const setAddressValue = (address: string) => { + cancelPendingAddressSearch() + skipNextAddressSearch = true + form.value.address = address + + if (errors.value.address && address.trim()) { + errors.value.address = '' + } +} + +const fetchAddressSuggestions = async (query: string) => { + const requestId = ++addressSearchRequestId + isSearchingAddress.value = true + addressSearchError.value = '' + + try { + const response = await $fetch('/api/geocoding/search', { + query: { q: query } + }) + + if (requestId !== addressSearchRequestId) { + return + } + + addressSuggestions.value = response.suggestions + showAddressSuggestions.value = response.suggestions.length > 0 + + if (!response.suggestions.length) { + addressSearchError.value = 'Nessun indirizzo trovato nel Comune di Bari.' + } + } catch { + if (requestId !== addressSearchRequestId) { + return + } + + clearAddressSuggestions() + addressSearchError.value = 'Ricerca indirizzi non disponibile al momento.' + } finally { + if (requestId === addressSearchRequestId) { + isSearchingAddress.value = false + } + } +} + +const resolveAddressFromCoordinates = async (lat: number, lng: number, fallbackAddress = '') => { + const requestId = ++reverseGeocodeRequestId + isResolvingAddress.value = true + addressSearchError.value = '' + + try { + const response = await $fetch('/api/geocoding/reverse', { + query: { lat, lng } + }) + + if (requestId !== reverseGeocodeRequestId) { + return + } + + const resolvedAddress = response.address?.trim() || fallbackAddress.trim() + + if (resolvedAddress) { + setAddressValue(resolvedAddress) + clearAddressSuggestions() + return + } + + if (!form.value.address.trim()) { + addressSearchError.value = 'Indirizzo non trovato per coordinate selezionate.' + } + } catch { + if (requestId !== reverseGeocodeRequestId) { + return + } + + if (fallbackAddress.trim()) { + setAddressValue(fallbackAddress.trim()) + clearAddressSuggestions() + return + } + + if (!form.value.address.trim()) { + addressSearchError.value = 'Impossibile recuperare indirizzo da coordinate.' + } + } finally { + if (requestId === reverseGeocodeRequestId) { + isResolvingAddress.value = false + } + } +} + +const handleAddressFocus = () => { + if (addressSuggestions.value.length > 0) { + showAddressSuggestions.value = true + } +} + +const handleAddressBlur = () => { + setTimeout(() => { + showAddressSuggestions.value = false + }, 150) +} + +const updateLocation = async ( + lat: number, + lng: number, + options: { + zoomTo?: boolean + syncAddress?: boolean + fallbackAddress?: string + } = {} +) => { + const { zoomTo = false, syncAddress = true, fallbackAddress = '' } = options + + if (!isWithinBariBounds(lat, lng)) { + errors.value.location = 'Puoi inserire segnalazioni solo nel Comune di Bari.' + return + } + + const roundedLat = Number(lat.toFixed(6)) + const roundedLng = Number(lng.toFixed(6)) + + lastSyncedLocationKey = toLocationKey(roundedLat, roundedLng) + form.value.latitude = roundedLat + form.value.longitude = roundedLng + + if (map && leaflet) { + if (!marker) { + marker = leaflet.marker([roundedLat, roundedLng]).addTo(map) + } else { + marker.setLatLng([roundedLat, roundedLng]) + } + if (zoomTo) { + map.setView([roundedLat, roundedLng], 16) + } + } + + if (errors.value.location) { + errors.value.location = '' + } + + if (syncAddress) { + await resolveAddressFromCoordinates(roundedLat, roundedLng, fallbackAddress) + } else if (fallbackAddress.trim()) { + setAddressValue(fallbackAddress.trim()) + } +} + const initMap = async () => { if (!import.meta.client || !mapContainer.value || map) return @@ -88,38 +282,31 @@ const initMap = async () => { return } - updateLocation(e.latlng.lat, e.latlng.lng) + void updateLocation(e.latlng.lat, e.latlng.lng) }) // Se c'è già una posizione, imposta il marker if (form.value.latitude && form.value.longitude) { - updateLocation(form.value.latitude, form.value.longitude, true) + void updateLocation(form.value.latitude, form.value.longitude, { zoomTo: true }) } } -const updateLocation = (lat: number, lng: number, zoomTo = false) => { - if (!isWithinBariBounds(lat, lng)) { - errors.value.location = 'Puoi inserire segnalazioni solo nel Comune di Bari.' +const selectAddressSuggestion = async (suggestion: GeocodingSuggestion) => { + addressSearchError.value = '' + clearAddressSuggestions() + setAddressValue(suggestion.label) + await updateLocation(suggestion.latitude, suggestion.longitude, { + zoomTo: true, + syncAddress: false + }) +} + +const handleAddressEnter = async () => { + if (addressSuggestions.value.length === 0) { return } - form.value.latitude = Number(lat.toFixed(6)) - form.value.longitude = Number(lng.toFixed(6)) - - if (map && leaflet) { - if (!marker) { - marker = leaflet.marker([lat, lng]).addTo(map) - } else { - marker.setLatLng([lat, lng]) - } - if (zoomTo) { - map.setView([lat, lng], 16) - } - } - - if (errors.value.location) { - errors.value.location = '' - } + await selectAddressSuggestion(addressSuggestions.value[0]) } onMounted(() => { @@ -127,6 +314,14 @@ onMounted(() => { }) onUnmounted(() => { + cancelPendingAddressSearch() + cancelPendingReverseGeocode() + + if (manualLocationSyncTimeout) { + clearTimeout(manualLocationSyncTimeout) + manualLocationSyncTimeout = null + } + if (map) { map.remove() map = null @@ -168,7 +363,7 @@ const getGeolocation = async () => { navigator.geolocation.getCurrentPosition(resolve, reject) }) - updateLocation(position.coords.latitude, position.coords.longitude, true) + await updateLocation(position.coords.latitude, position.coords.longitude, { zoomTo: true }) } catch (error: any) { errors.value.location = 'Impossibile ottenere la posizione. Clicca sulla mappa per inserirla manualmente.' } finally { @@ -200,6 +395,12 @@ const resetForm = () => { } media.value = [] errors.value = {} + addressSearchError.value = '' + clearAddressSuggestions() + cancelPendingAddressSearch() + cancelPendingReverseGeocode() + lastSyncedLocationKey = '' + skipNextAddressSearch = false if (marker && map) { map.removeLayer(marker) @@ -214,6 +415,35 @@ watch(() => form.value.category_id, () => { } }) +watch(() => form.value.address, (newAddress) => { + if (skipNextAddressSearch) { + skipNextAddressSearch = false + return + } + + cancelPendingReverseGeocode() + + if (errors.value.address && newAddress.trim()) { + errors.value.address = '' + } + + if (addressSearchTimeout) { + clearTimeout(addressSearchTimeout) + addressSearchTimeout = null + } + + if (newAddress.trim().length < 3) { + isSearchingAddress.value = false + addressSearchError.value = '' + clearAddressSuggestions() + return + } + + addressSearchTimeout = setTimeout(() => { + void fetchAddressSuggestions(newAddress.trim()) + }, 350) +}) + // Sincronizza input manuali con la mappa watch([() => form.value.latitude, () => form.value.longitude], ([newLat, newLng]) => { if (newLat && newLng && map && leaflet) { @@ -226,6 +456,23 @@ watch([() => form.value.latitude, () => form.value.longitude], ([newLat, newLng] map.setView([newLat, newLng], 16) } } + + if (!newLat || !newLng) { + return + } + + const locationKey = toLocationKey(newLat, newLng) + if (locationKey === lastSyncedLocationKey) { + return + } + + if (manualLocationSyncTimeout) { + clearTimeout(manualLocationSyncTimeout) + } + + manualLocationSyncTimeout = setTimeout(() => { + void updateLocation(newLat, newLng, { zoomTo: true }) + }, 500) }) @@ -320,15 +567,44 @@ watch([() => form.value.latitude, () => form.value.longitude], ([newLat, newLng] Indirizzo * - +
+ +
+ +
+
+
+ Ricerca indirizzi in corso... +
+
+ Recupero indirizzo da coordinate... +
+
+ {{ addressSearchError }} +
{{ errors.address }}
@@ -399,6 +675,14 @@ watch([() => form.value.latitude, () => form.value.longitude], ([newLat, newLng] z-index: 1; /* Fix for Bootstrap elements overlapping map */ } +.address-suggestions { + top: calc(100% + 0.25rem); + left: 0; + z-index: 10; + max-height: 16rem; + overflow-y: auto; +} + textarea { font-family: inherit; resize: vertical; @@ -411,4 +695,3 @@ textarea { } - diff --git a/server/api/geocoding/reverse.get.ts b/server/api/geocoding/reverse.get.ts new file mode 100644 index 0000000..cdd1a19 --- /dev/null +++ b/server/api/geocoding/reverse.get.ts @@ -0,0 +1,19 @@ +import { createError, getQuery } from 'h3' +import { reverseGeocode } from '../../utils/geocoding' + +export default defineEventHandler(async (event) => { + const query = getQuery(event) + const latitude = typeof query.lat === 'string' ? Number.parseFloat(query.lat) : Number.NaN + const longitude = typeof query.lng === 'string' ? Number.parseFloat(query.lng) : Number.NaN + + if (!Number.isFinite(latitude) || !Number.isFinite(longitude)) { + throw createError({ + statusCode: 400, + statusMessage: 'Invalid coordinates' + }) + } + + const address = await reverseGeocode(latitude, longitude) + + return { address } +}) diff --git a/server/api/geocoding/search.get.ts b/server/api/geocoding/search.get.ts new file mode 100644 index 0000000..231d2b9 --- /dev/null +++ b/server/api/geocoding/search.get.ts @@ -0,0 +1,20 @@ +import { createError, getQuery } from 'h3' +import { searchAddresses } from '../../utils/geocoding' + +export default defineEventHandler(async (event) => { + const query = getQuery(event) + const rawText = typeof query.q === 'string' ? query.q.trim() : '' + const rawLimit = typeof query.limit === 'string' ? Number.parseInt(query.limit, 10) : 5 + const limit = Number.isFinite(rawLimit) ? Math.min(Math.max(rawLimit, 1), 8) : 5 + + if (rawText.length < 3) { + throw createError({ + statusCode: 400, + statusMessage: 'Query too short' + }) + } + + const suggestions = await searchAddresses(rawText, limit) + + return { suggestions } +}) diff --git a/server/utils/geocoding.ts b/server/utils/geocoding.ts new file mode 100644 index 0000000..29122d2 --- /dev/null +++ b/server/utils/geocoding.ts @@ -0,0 +1,123 @@ +interface NominatimAddress { + road?: string + house_number?: string + pedestrian?: string + footway?: string + path?: string + cycleway?: string + suburb?: string + neighbourhood?: string + city?: string + town?: string + village?: string + municipality?: string +} + +interface NominatimResult { + lat: string + lon: string + display_name: string + address?: NominatimAddress +} + +export interface GeocodingSuggestion { + label: string + latitude: number + longitude: number +} + +const NOMINATIM_BASE_URL = 'https://nominatim.openstreetmap.org' +const BARI_BOUNDS = { + north: 41.1696, + south: 41.0718, + east: 16.9521, + west: 16.7564 +} + +const NOMINATIM_HEADERS = { + 'User-Agent': 'Visione Frontend/1.0 (https://visione.studentindipendenti.it)', + 'Referer': 'https://visione.studentindipendenti.it/' +} + +const bariViewbox = `${BARI_BOUNDS.west},${BARI_BOUNDS.north},${BARI_BOUNDS.east},${BARI_BOUNDS.south}` + +const isWithinBariBounds = (latitude: number, longitude: number) => + latitude >= BARI_BOUNDS.south && + latitude <= BARI_BOUNDS.north && + longitude >= BARI_BOUNDS.west && + longitude <= BARI_BOUNDS.east + +const compactAddress = (displayName: string, address?: NominatimAddress) => { + if (!address) { + return displayName + } + + const streetName = address.road || address.pedestrian || address.footway || address.path || address.cycleway + const street = [streetName, address.house_number].filter(Boolean).join(' ') + const locality = address.city || address.town || address.village || address.municipality || 'Bari' + const area = address.suburb || address.neighbourhood + const formatted = [street, locality, area] + .filter(Boolean) + .filter((part, index, all) => all.indexOf(part) === index) + .join(', ') + + return formatted || displayName +} + +const toSuggestion = (result: NominatimResult): GeocodingSuggestion | null => { + const latitude = Number.parseFloat(result.lat) + const longitude = Number.parseFloat(result.lon) + + if (!Number.isFinite(latitude) || !Number.isFinite(longitude)) { + return null + } + + return { + label: compactAddress(result.display_name, result.address), + latitude, + longitude + } +} + +export const searchAddresses = async (query: string, limit = 5) => { + const params = new URLSearchParams({ + q: query, + format: 'jsonv2', + addressdetails: '1', + 'accept-language': 'it', + countrycodes: 'it', + bounded: '1', + viewbox: bariViewbox, + limit: String(limit) + }) + + const results = await $fetch(`${NOMINATIM_BASE_URL}/search?${params.toString()}`, { + headers: NOMINATIM_HEADERS + }) + + return results + .map(toSuggestion) + .filter((result): result is GeocodingSuggestion => Boolean(result)) + .filter((result) => isWithinBariBounds(result.latitude, result.longitude)) +} + +export const reverseGeocode = async (latitude: number, longitude: number) => { + const params = new URLSearchParams({ + lat: String(latitude), + lon: String(longitude), + zoom: '18', + format: 'jsonv2', + addressdetails: '1', + 'accept-language': 'it' + }) + + const result = await $fetch(`${NOMINATIM_BASE_URL}/reverse?${params.toString()}`, { + headers: NOMINATIM_HEADERS + }) + + if (!result) { + return '' + } + + return compactAddress(result.display_name, result.address) +} From e7219c84ab2c92d13066a25ac9c525c7e9906e61 Mon Sep 17 00:00:00 2001 From: gabdevele Date: Mon, 15 Jun 2026 15:08:33 +0200 Subject: [PATCH 2/3] feat(geocoding): remove server side fetch --- app/components/ReportForm.vue | 30 ++++++++++------------------- {server => app}/utils/geocoding.ts | 28 ++++----------------------- nuxt.config.ts | 24 +---------------------- server/api/geocoding/reverse.get.ts | 19 ------------------ server/api/geocoding/search.get.ts | 20 ------------------- 5 files changed, 15 insertions(+), 106 deletions(-) rename {server => app}/utils/geocoding.ts (81%) delete mode 100644 server/api/geocoding/reverse.get.ts delete mode 100644 server/api/geocoding/search.get.ts diff --git a/app/components/ReportForm.vue b/app/components/ReportForm.vue index c03254f..c701051 100644 --- a/app/components/ReportForm.vue +++ b/app/components/ReportForm.vue @@ -3,6 +3,7 @@ import type { Category, CreateReportRequest } from '~/types/api' import type { LeafletMouseEvent, Map as LeafletMap, Marker as LeafletMarker } from 'leaflet' import { onMounted, onUnmounted, ref, watch } from 'vue' import { BARI_BOUNDS, BARI_CENTER, isWithinBariBounds } from '~/utils/bari' +import { reverseGeocode, searchAddresses } from '~/utils/geocoding' interface Props { isLoading?: boolean @@ -19,14 +20,6 @@ interface GeocodingSuggestion { longitude: number } -interface SearchAddressesResponse { - suggestions: GeocodingSuggestion[] -} - -interface ReverseGeocodeResponse { - address: string -} - const props = withDefaults(defineProps(), { isLoading: false, categories: () => [] @@ -123,18 +116,20 @@ const fetchAddressSuggestions = async (query: string) => { addressSearchError.value = '' try { - const response = await $fetch('/api/geocoding/search', { - query: { q: query } - }) + if (requestId !== addressSearchRequestId) { + return + } + + const suggestions = await searchAddresses(query) if (requestId !== addressSearchRequestId) { return } - addressSuggestions.value = response.suggestions - showAddressSuggestions.value = response.suggestions.length > 0 + addressSuggestions.value = suggestions + showAddressSuggestions.value = suggestions.length > 0 - if (!response.suggestions.length) { + if (!suggestions.length) { addressSearchError.value = 'Nessun indirizzo trovato nel Comune di Bari.' } } catch { @@ -157,15 +152,11 @@ const resolveAddressFromCoordinates = async (lat: number, lng: number, fallbackA addressSearchError.value = '' try { - const response = await $fetch('/api/geocoding/reverse', { - query: { lat, lng } - }) - if (requestId !== reverseGeocodeRequestId) { return } - const resolvedAddress = response.address?.trim() || fallbackAddress.trim() + const resolvedAddress = (await reverseGeocode(lat, lng))?.trim() || fallbackAddress.trim() if (resolvedAddress) { setAddressValue(resolvedAddress) @@ -694,4 +685,3 @@ textarea { align-items: center; } - diff --git a/server/utils/geocoding.ts b/app/utils/geocoding.ts similarity index 81% rename from server/utils/geocoding.ts rename to app/utils/geocoding.ts index 29122d2..b177977 100644 --- a/server/utils/geocoding.ts +++ b/app/utils/geocoding.ts @@ -1,3 +1,5 @@ +import { BARI_BOUNDS, isWithinBariBounds } from '~/utils/bari' + interface NominatimAddress { road?: string house_number?: string @@ -27,26 +29,8 @@ export interface GeocodingSuggestion { } const NOMINATIM_BASE_URL = 'https://nominatim.openstreetmap.org' -const BARI_BOUNDS = { - north: 41.1696, - south: 41.0718, - east: 16.9521, - west: 16.7564 -} - -const NOMINATIM_HEADERS = { - 'User-Agent': 'Visione Frontend/1.0 (https://visione.studentindipendenti.it)', - 'Referer': 'https://visione.studentindipendenti.it/' -} - const bariViewbox = `${BARI_BOUNDS.west},${BARI_BOUNDS.north},${BARI_BOUNDS.east},${BARI_BOUNDS.south}` -const isWithinBariBounds = (latitude: number, longitude: number) => - latitude >= BARI_BOUNDS.south && - latitude <= BARI_BOUNDS.north && - longitude >= BARI_BOUNDS.west && - longitude <= BARI_BOUNDS.east - const compactAddress = (displayName: string, address?: NominatimAddress) => { if (!address) { return displayName @@ -91,9 +75,7 @@ export const searchAddresses = async (query: string, limit = 5) => { limit: String(limit) }) - const results = await $fetch(`${NOMINATIM_BASE_URL}/search?${params.toString()}`, { - headers: NOMINATIM_HEADERS - }) + const results = await $fetch(`${NOMINATIM_BASE_URL}/search?${params.toString()}`) return results .map(toSuggestion) @@ -111,9 +93,7 @@ export const reverseGeocode = async (latitude: number, longitude: number) => { 'accept-language': 'it' }) - const result = await $fetch(`${NOMINATIM_BASE_URL}/reverse?${params.toString()}`, { - headers: NOMINATIM_HEADERS - }) + const result = await $fetch(`${NOMINATIM_BASE_URL}/reverse?${params.toString()}`) if (!result) { return '' diff --git a/nuxt.config.ts b/nuxt.config.ts index 023216d..4eb3253 100644 --- a/nuxt.config.ts +++ b/nuxt.config.ts @@ -8,6 +8,7 @@ export default defineNuxtConfig({ '@nuxtjs/sitemap', '@nuxtjs/robots' ], + ssr: false, runtimeConfig: { public: { @@ -35,29 +36,6 @@ export default defineNuxtConfig({ ] }, - nitro: { - devProxy: { - '/api/v1': { - target: 'https://bk.visione.studentindipendenti.it/api/v1', - changeOrigin: true, - headers: { - host: 'bk.visione.studentindipendenti.it', - origin: 'https://visione.studentindipendenti.it', - referer: 'https://visione.studentindipendenti.it/' - } - }, - '/uploads': { - target: 'https://bk.visione.studentindipendenti.it/uploads', - changeOrigin: true, - headers: { - host: 'bk.visione.studentindipendenti.it', - origin: 'https://visione.studentindipendenti.it', - referer: 'https://visione.studentindipendenti.it/' - } - } - } - }, - app: { head: { link: [ diff --git a/server/api/geocoding/reverse.get.ts b/server/api/geocoding/reverse.get.ts deleted file mode 100644 index cdd1a19..0000000 --- a/server/api/geocoding/reverse.get.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { createError, getQuery } from 'h3' -import { reverseGeocode } from '../../utils/geocoding' - -export default defineEventHandler(async (event) => { - const query = getQuery(event) - const latitude = typeof query.lat === 'string' ? Number.parseFloat(query.lat) : Number.NaN - const longitude = typeof query.lng === 'string' ? Number.parseFloat(query.lng) : Number.NaN - - if (!Number.isFinite(latitude) || !Number.isFinite(longitude)) { - throw createError({ - statusCode: 400, - statusMessage: 'Invalid coordinates' - }) - } - - const address = await reverseGeocode(latitude, longitude) - - return { address } -}) diff --git a/server/api/geocoding/search.get.ts b/server/api/geocoding/search.get.ts deleted file mode 100644 index 231d2b9..0000000 --- a/server/api/geocoding/search.get.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { createError, getQuery } from 'h3' -import { searchAddresses } from '../../utils/geocoding' - -export default defineEventHandler(async (event) => { - const query = getQuery(event) - const rawText = typeof query.q === 'string' ? query.q.trim() : '' - const rawLimit = typeof query.limit === 'string' ? Number.parseInt(query.limit, 10) : 5 - const limit = Number.isFinite(rawLimit) ? Math.min(Math.max(rawLimit, 1), 8) : 5 - - if (rawText.length < 3) { - throw createError({ - statusCode: 400, - statusMessage: 'Query too short' - }) - } - - const suggestions = await searchAddresses(rawText, limit) - - return { suggestions } -}) From ed5836b4821653617d983d2116bf5f8831d64b9a Mon Sep 17 00:00:00 2001 From: ReLoia Date: Mon, 15 Jun 2026 15:26:03 +0200 Subject: [PATCH 3/3] docs: added TODO in nuxt config for SSR --- nuxt.config.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/nuxt.config.ts b/nuxt.config.ts index 4eb3253..6ede25a 100644 --- a/nuxt.config.ts +++ b/nuxt.config.ts @@ -8,6 +8,7 @@ export default defineNuxtConfig({ '@nuxtjs/sitemap', '@nuxtjs/robots' ], + // TODO: riattivare prima di pubblicare il sito ssr: false, runtimeConfig: {