- Parte 3 : Viaggi
- Chat
This commit is contained in:
@@ -44,3 +44,4 @@ MIAB_ADMIN_PASSWORD=passpao1pabox@1A
|
|||||||
DS_API_KEY="sk-222e3addb3d8455d8b0516d93906eec7"
|
DS_API_KEY="sk-222e3addb3d8455d8b0516d93906eec7"
|
||||||
SERVER_A_URL="http://51.77.156.69:3000"
|
SERVER_A_URL="http://51.77.156.69:3000"
|
||||||
API_KEY_MSSQL="m68yADSr123MIVIDA@154$DSAGVOK"
|
API_KEY_MSSQL="m68yADSr123MIVIDA@154$DSAGVOK"
|
||||||
|
ORS_API_KEY="eyJvcmciOiI1YjNjZTM1OTc4NTExMTAwMDFjZjYyNDgiLCJpZCI6IjNjNTllZmY1ZTM1ZDQ5ODI5NThhOTIzYTQ5MDkxOWIwIiwiaCI6Im11cm11cjY0In0="
|
||||||
@@ -39,3 +39,4 @@ AUTH_NEW_SITES=123123123
|
|||||||
SCRIPTS_DIR=admin_scripts
|
SCRIPTS_DIR=admin_scripts
|
||||||
CLOUDFLARE_TOKENS=[{"label":"Paolo.arena77@gmail.com","value":"M9EM309v8WFquJKpYgZCw-TViM2wX6vB3wlK6GD0"},{"label":"gruppomacro.com","value":"bqmzGShoX7WqOBzkXocoECyBkPq3GfqcM5t6VFd8"}]
|
CLOUDFLARE_TOKENS=[{"label":"Paolo.arena77@gmail.com","value":"M9EM309v8WFquJKpYgZCw-TViM2wX6vB3wlK6GD0"},{"label":"gruppomacro.com","value":"bqmzGShoX7WqOBzkXocoECyBkPq3GfqcM5t6VFd8"}]
|
||||||
DS_API_KEY="sk-222e3addb3d8455d8b0516d93906eec7"
|
DS_API_KEY="sk-222e3addb3d8455d8b0516d93906eec7"
|
||||||
|
ORS_API_KEY="eyJvcmciOiI1YjNjZTM1OTc4NTExMTAwMDFjZjYyNDgiLCJpZCI6IjNjNTllZmY1ZTM1ZDQ5ODI5NThhOTIzYTQ5MDkxOWIwIiwiaCI6Im11cm11cjY0In0="
|
||||||
@@ -45,3 +45,4 @@ GROK_API="xai-PcNM5obgPaETtmnfDWPZk235D75ZgxENU2QmeqPfMQCHh9dwCDVeRRe0oVVA2YOpiU
|
|||||||
REPLICATE_API_TOKEN="r8_AVhM6igwvoOnUA65cHVZdhEDfTqBVk94WTB0u"
|
REPLICATE_API_TOKEN="r8_AVhM6igwvoOnUA65cHVZdhEDfTqBVk94WTB0u"
|
||||||
FAL_KEY="7d251c88-21b5-4b55-8b3e-4bafd910f99f:b81c0a36a25b052f26eb8ac226c7efff"
|
FAL_KEY="7d251c88-21b5-4b55-8b3e-4bafd910f99f:b81c0a36a25b052f26eb8ac226c7efff"
|
||||||
HF_TOKEN="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"
|
HF_TOKEN="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"
|
||||||
|
ORS_API_KEY="eyJvcmciOiI1YjNjZTM1OTc4NTExMTAwMDFjZjYyNDgiLCJpZCI6IjNjNTllZmY1ZTM1ZDQ5ODI5NThhOTIzYTQ5MDkxOWIwIiwiaCI6Im11cm11cjY0In0="
|
||||||
@@ -42,3 +42,4 @@ MIAB_ADMIN_EMAIL=admin@lamiaposta.org
|
|||||||
MIAB_ADMIN_PASSWORD=passpao1pabox@1A
|
MIAB_ADMIN_PASSWORD=passpao1pabox@1A
|
||||||
SERVER_A_URL="http://51.77.156.69:3000"
|
SERVER_A_URL="http://51.77.156.69:3000"
|
||||||
API_KEY_MSSQL="m68yADSr123MIVIDA@154$DSAGVOK"
|
API_KEY_MSSQL="m68yADSr123MIVIDA@154$DSAGVOK"
|
||||||
|
ORS_API_KEY="eyJvcmciOiI1YjNjZTM1OTc4NTExMTAwMDFjZjYyNDgiLCJpZCI6IjNjNTllZmY1ZTM1ZDQ5ODI5NThhOTIzYTQ5MDkxOWIwIiwiaCI6Im11cm11cjY0In0="
|
||||||
@@ -39,3 +39,4 @@ CLOUDFLARE_TOKENS=[{"label":"Paolo.arena77@gmail.com","value":"M9EM309v8WFquJKpY
|
|||||||
MIAB_HOST=box.lamiaposta.org
|
MIAB_HOST=box.lamiaposta.org
|
||||||
MIAB_ADMIN_EMAIL=admin@lamiaposta.org
|
MIAB_ADMIN_EMAIL=admin@lamiaposta.org
|
||||||
MIAB_ADMIN_PASSWORD=passpao1pabox@1A
|
MIAB_ADMIN_PASSWORD=passpao1pabox@1A
|
||||||
|
ORS_API_KEY="eyJvcmciOiI1YjNjZTM1OTc4NTExMTAwMDFjZjYyNDgiLCJpZCI6IjNjNTllZmY1ZTM1ZDQ5ODI5NThhOTIzYTQ5MDkxOWIwIiwiaCI6Im11cm11cjY0In0="
|
||||||
@@ -55,7 +55,11 @@ class UserController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Send response with tokens
|
// Send response with tokens
|
||||||
res.header('x-auth', result.token).header('x-refrtok', result.refreshToken).header('x-browser-random', result.browser_random).send(result.user);
|
res
|
||||||
|
.header('x-auth', result.token)
|
||||||
|
.header('x-refrtok', result.refreshToken)
|
||||||
|
.header('x-browser-random', result.browser_random)
|
||||||
|
.send(result.user);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error in registration:', error.message);
|
console.error('Error in registration:', error.message);
|
||||||
res.status(400).send({
|
res.status(400).send({
|
||||||
@@ -103,11 +107,15 @@ class UserController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Send response with tokens
|
// Send response with tokens
|
||||||
res.header('x-auth', result.token).header('x-refrtok', result.refreshToken).header('x-browser-random', result.browser_random).send({
|
res
|
||||||
usertosend: result.user,
|
.header('x-auth', result.token)
|
||||||
code: server_constants.RIS_CODE_OK,
|
.header('x-refrtok', result.refreshToken)
|
||||||
subsExistonDb: result.subsExistonDb,
|
.header('x-browser-random', result.browser_random)
|
||||||
});
|
.send({
|
||||||
|
usertosend: result.user,
|
||||||
|
code: server_constants.RIS_CODE_OK,
|
||||||
|
subsExistonDb: result.subsExistonDb,
|
||||||
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error in login:', error.message);
|
console.error('Error in login:', error.message);
|
||||||
res.status(400).send({
|
res.status(400).send({
|
||||||
@@ -487,6 +495,7 @@ class UserController {
|
|||||||
const { User } = require('../models/user');
|
const { User } = require('../models/user');
|
||||||
return User.isCollaboratore(user.perm);
|
return User.isCollaboratore(user.perm);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = UserController;
|
module.exports = UserController;
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -5,7 +5,7 @@ const { User } = require('../models/user');
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @desc Crea un feedback per un viaggio
|
* @desc Crea un feedback per un viaggio
|
||||||
* @route POST /api/trasporti/feedback
|
* @route POST /api/viaggi/feedback
|
||||||
* @access Private
|
* @access Private
|
||||||
*/
|
*/
|
||||||
const createFeedback = async (req, res) => {
|
const createFeedback = async (req, res) => {
|
||||||
@@ -144,7 +144,7 @@ const createFeedback = async (req, res) => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @desc Ottieni i feedback ricevuti da un utente
|
* @desc Ottieni i feedback ricevuti da un utente
|
||||||
* @route GET /api/trasporti/feedback/user/:userId
|
* @route GET /api/viaggi/feedback/user/:userId
|
||||||
* @access Public
|
* @access Public
|
||||||
*/
|
*/
|
||||||
const getUserFeedback = async (req, res) => {
|
const getUserFeedback = async (req, res) => {
|
||||||
@@ -206,13 +206,13 @@ const getUserFeedback = async (req, res) => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @desc Ottieni statistiche feedback per un utente
|
* @desc Ottieni statistiche feedback per un utente
|
||||||
* @route GET /api/trasporti/feedback/user/:userId/stats
|
* @route GET /api/viaggi/feedback/user/:userId/stats
|
||||||
* @access Public
|
* @access Public
|
||||||
*/
|
*/
|
||||||
const getUserFeedbackStats = async (req, res) => {
|
const getUserFeedbackStats = async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const { userId } = req.params;
|
const { userId } = req.params;
|
||||||
const { idapp } = req.query;
|
const idapp = req.user.idapp;
|
||||||
|
|
||||||
if (!idapp) {
|
if (!idapp) {
|
||||||
return res.status(400).json({
|
return res.status(400).json({
|
||||||
@@ -246,13 +246,13 @@ const getUserFeedbackStats = async (req, res) => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @desc Ottieni i feedback per un viaggio
|
* @desc Ottieni i feedback per un viaggio
|
||||||
* @route GET /api/trasporti/feedback/ride/:rideId
|
* @route GET /api/viaggi/feedback/ride/:rideId
|
||||||
* @access Public (con info limitate) / Private (info complete)
|
* @access Public (con info limitate) / Private (info complete)
|
||||||
*/
|
*/
|
||||||
const getRideFeedback = async (req, res) => {
|
const getRideFeedback = async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const { rideId } = req.params;
|
const { rideId } = req.params;
|
||||||
const { idapp } = req.query;
|
const idapp = req.user.idapp;
|
||||||
const userId = req.user?._id;
|
const userId = req.user?._id;
|
||||||
|
|
||||||
if (!idapp) {
|
if (!idapp) {
|
||||||
@@ -337,14 +337,14 @@ const getRideFeedback = async (req, res) => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @desc Verifica se l'utente può lasciare un feedback
|
* @desc Verifica se l'utente può lasciare un feedback
|
||||||
* @route GET /api/trasporti/feedback/can-leave/:rideId/:toUserId
|
* @route GET /api/viaggi/feedback/can-leave/:rideId/:toUserId
|
||||||
* @access Private
|
* @access Private
|
||||||
* @note NUOVA FUNZIONE - Era mancante!
|
* @note NUOVA FUNZIONE - Era mancante!
|
||||||
*/
|
*/
|
||||||
const canLeaveFeedback = async (req, res) => {
|
const canLeaveFeedback = async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const { rideId, toUserId } = req.params;
|
const { rideId, toUserId } = req.params;
|
||||||
const { idapp } = req.query;
|
const idapp = req.user.idapp;
|
||||||
const fromUserId = req.user._id;
|
const fromUserId = req.user._id;
|
||||||
|
|
||||||
if (!idapp) {
|
if (!idapp) {
|
||||||
@@ -476,7 +476,7 @@ const canLeaveFeedback = async (req, res) => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @desc Rispondi a un feedback ricevuto
|
* @desc Rispondi a un feedback ricevuto
|
||||||
* @route POST /api/trasporti/feedback/:id/response
|
* @route POST /api/viaggi/feedback/:id/response
|
||||||
* @access Private
|
* @access Private
|
||||||
*/
|
*/
|
||||||
const respondToFeedback = async (req, res) => {
|
const respondToFeedback = async (req, res) => {
|
||||||
@@ -542,7 +542,7 @@ const respondToFeedback = async (req, res) => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @desc Segna un feedback come utile
|
* @desc Segna un feedback come utile
|
||||||
* @route POST /api/trasporti/feedback/:id/helpful
|
* @route POST /api/viaggi/feedback/:id/helpful
|
||||||
* @access Private
|
* @access Private
|
||||||
*/
|
*/
|
||||||
const markAsHelpful = async (req, res) => {
|
const markAsHelpful = async (req, res) => {
|
||||||
@@ -605,7 +605,7 @@ const markAsHelpful = async (req, res) => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @desc Segnala un feedback inappropriato
|
* @desc Segnala un feedback inappropriato
|
||||||
* @route POST /api/trasporti/feedback/:id/report
|
* @route POST /api/viaggi/feedback/:id/report
|
||||||
* @access Private
|
* @access Private
|
||||||
*/
|
*/
|
||||||
const reportFeedback = async (req, res) => {
|
const reportFeedback = async (req, res) => {
|
||||||
@@ -679,7 +679,7 @@ const reportFeedback = async (req, res) => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @desc Ottieni i miei feedback dati
|
* @desc Ottieni i miei feedback dati
|
||||||
* @route GET /api/trasporti/feedback/my/given
|
* @route GET /api/viaggi/feedback/my/given
|
||||||
* @access Private
|
* @access Private
|
||||||
*/
|
*/
|
||||||
const getMyGivenFeedback = async (req, res) => {
|
const getMyGivenFeedback = async (req, res) => {
|
||||||
@@ -729,7 +729,7 @@ const getMyGivenFeedback = async (req, res) => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @desc Ottieni i miei feedback ricevuti
|
* @desc Ottieni i miei feedback ricevuti
|
||||||
* @route GET /api/trasporti/feedback/my/received
|
* @route GET /api/viaggi/feedback/my/received
|
||||||
* @access Private
|
* @access Private
|
||||||
*/
|
*/
|
||||||
const getMyReceivedFeedback = async (req, res) => {
|
const getMyReceivedFeedback = async (req, res) => {
|
||||||
|
|||||||
@@ -1,39 +1,43 @@
|
|||||||
/**
|
/**
|
||||||
* Controller per Geocoding usando servizi Open Source
|
* Controller per Geocoding usando OpenRouteService
|
||||||
* - Nominatim (OpenStreetMap) per geocoding/reverse
|
* Documentazione: https://openrouteservice.org/dev/#/api-docs
|
||||||
* - OSRM per routing
|
|
||||||
* - Photon per autocomplete
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const https = require('https');
|
const https = require('https');
|
||||||
const http = require('http');
|
|
||||||
|
|
||||||
// Configurazione servizi
|
// Configurazione OpenRouteService
|
||||||
const NOMINATIM_BASE = 'https://nominatim.openstreetmap.org';
|
const ORS_BASE = 'https://api.openrouteservice.org';
|
||||||
const PHOTON_BASE = 'https://photon.komoot.io';
|
const ORS_API_KEY = process.env.ORS_API_KEY || 'YOUR_API_KEY_HERE';
|
||||||
const OSRM_BASE = 'https://router.project-osrm.org';
|
|
||||||
|
|
||||||
// User-Agent richiesto da Nominatim
|
|
||||||
const USER_AGENT = 'FreePlanetApp/1.0';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper per fare richieste HTTP/HTTPS
|
* Helper per fare richieste HTTPS a OpenRouteService
|
||||||
*/
|
*/
|
||||||
const makeRequest = (url) => {
|
const makeRequest = (url, method = 'GET', body = null) => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const client = url.startsWith('https') ? https : http;
|
const urlObj = new URL(url);
|
||||||
|
|
||||||
const req = client.get(url, {
|
const options = {
|
||||||
|
hostname: urlObj.hostname,
|
||||||
|
path: urlObj.pathname + urlObj.search,
|
||||||
|
method,
|
||||||
headers: {
|
headers: {
|
||||||
'User-Agent': USER_AGENT,
|
Authorization: ORS_API_KEY,
|
||||||
'Accept': 'application/json'
|
Accept: 'application/json',
|
||||||
}
|
'Content-Type': 'application/json',
|
||||||
}, (res) => {
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const req = https.request(options, (res) => {
|
||||||
let data = '';
|
let data = '';
|
||||||
res.on('data', chunk => data += chunk);
|
res.on('data', (chunk) => (data += chunk));
|
||||||
res.on('end', () => {
|
res.on('end', () => {
|
||||||
try {
|
try {
|
||||||
resolve(JSON.parse(data));
|
const parsed = JSON.parse(data);
|
||||||
|
if (res.statusCode >= 400) {
|
||||||
|
reject(new Error(parsed.error?.message || `HTTP ${res.statusCode}`));
|
||||||
|
} else {
|
||||||
|
resolve(parsed);
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
reject(new Error('Errore parsing risposta'));
|
reject(new Error('Errore parsing risposta'));
|
||||||
}
|
}
|
||||||
@@ -41,338 +45,436 @@ const makeRequest = (url) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
req.on('error', reject);
|
req.on('error', reject);
|
||||||
req.setTimeout(10000, () => {
|
req.setTimeout(15000, () => {
|
||||||
req.destroy();
|
req.destroy();
|
||||||
reject(new Error('Timeout richiesta'));
|
reject(new Error('Timeout richiesta'));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (body) {
|
||||||
|
req.write(JSON.stringify(body));
|
||||||
|
}
|
||||||
|
req.end();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @desc Autocomplete città (Photon API)
|
* @desc Autocomplete città (ORS Geocode Autocomplete)
|
||||||
* @route GET /api/trasporti/geo/autocomplete
|
* @route GET /api/geo/autocomplete
|
||||||
*/
|
*/
|
||||||
const autocomplete = async (req, res) => {
|
const autocomplete = async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const { q, limit = 5, lang = 'it' } = req.query;
|
const { q, limit = 5, lang = 'it', country = 'IT' } = req.query;
|
||||||
|
|
||||||
if (!q || q.length < 2) {
|
if (!q || q.length < 2) {
|
||||||
return res.status(400).json({
|
return res.status(400).json({
|
||||||
success: false,
|
success: false,
|
||||||
message: 'Query deve essere almeno 2 caratteri'
|
message: 'Query deve essere almeno 2 caratteri',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Photon API - gratuito e veloce
|
const params = new URLSearchParams({
|
||||||
const url = `${PHOTON_BASE}/api/?q=${encodeURIComponent(q)}&limit=${limit}&lang=${lang}&osm_tag=place:city&osm_tag=place:town&osm_tag=place:village`;
|
text: q,
|
||||||
|
size: limit,
|
||||||
|
lang,
|
||||||
|
'boundary.country': country,
|
||||||
|
layers: 'locality,county,region', // Solo città/comuni
|
||||||
|
});
|
||||||
|
|
||||||
|
const url = `${ORS_BASE}/geocode/autocomplete?${params}`;
|
||||||
const data = await makeRequest(url);
|
const data = await makeRequest(url);
|
||||||
|
|
||||||
// Formatta risultati
|
const results = data.features.map((feature) => ({
|
||||||
const results = data.features.map(feature => ({
|
id: feature.properties.id,
|
||||||
city: feature.properties.name,
|
city: feature.properties.name,
|
||||||
province: feature.properties.county || feature.properties.state,
|
locality: feature.properties.locality,
|
||||||
region: feature.properties.state,
|
county: feature.properties.county,
|
||||||
|
region: feature.properties.region,
|
||||||
country: feature.properties.country,
|
country: feature.properties.country,
|
||||||
postalCode: feature.properties.postcode,
|
postalCode: feature.properties.postalcode,
|
||||||
coordinates: {
|
coordinates: {
|
||||||
lat: feature.geometry.coordinates[1],
|
lat: feature.geometry.coordinates[1],
|
||||||
lng: feature.geometry.coordinates[0]
|
lng: feature.geometry.coordinates[0],
|
||||||
},
|
},
|
||||||
displayName: [
|
displayName: feature.properties.label,
|
||||||
feature.properties.name,
|
type: feature.properties.layer,
|
||||||
feature.properties.county,
|
confidence: feature.properties.confidence,
|
||||||
feature.properties.state,
|
|
||||||
feature.properties.country
|
|
||||||
].filter(Boolean).join(', '),
|
|
||||||
type: feature.properties.osm_value || 'place'
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
res.status(200).json({
|
res.status(200).json({
|
||||||
success: true,
|
success: true,
|
||||||
data: results
|
count: results.length,
|
||||||
|
data: results,
|
||||||
});
|
});
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Errore autocomplete:', error);
|
console.error('Errore autocomplete:', error);
|
||||||
res.status(500).json({
|
res.status(500).json({
|
||||||
success: false,
|
success: false,
|
||||||
message: 'Errore durante la ricerca',
|
message: 'Errore durante la ricerca',
|
||||||
error: error.message
|
error: error.message,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @desc Geocoding - indirizzo a coordinate (Nominatim)
|
* @desc Geocoding - indirizzo a coordinate (ORS Geocode Search)
|
||||||
* @route GET /api/trasporti/geo/geocode
|
* @route GET /api/geo/geocode
|
||||||
*/
|
*/
|
||||||
const geocode = async (req, res) => {
|
const geocode = async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const { address, city, country = 'Italy' } = req.query;
|
const { address, city, country = 'IT', limit = 5, lang = 'it' } = req.query;
|
||||||
|
|
||||||
const searchQuery = [address, city, country].filter(Boolean).join(', ');
|
const searchQuery = [address, city].filter(Boolean).join(', ');
|
||||||
|
|
||||||
if (!searchQuery) {
|
if (!searchQuery) {
|
||||||
return res.status(400).json({
|
return res.status(400).json({
|
||||||
success: false,
|
success: false,
|
||||||
message: 'Fornisci un indirizzo o città da cercare'
|
message: 'Fornisci un indirizzo o città da cercare',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const url = `${NOMINATIM_BASE}/search?format=json&q=${encodeURIComponent(searchQuery)}&limit=5&addressdetails=1`;
|
const params = new URLSearchParams({
|
||||||
|
text: searchQuery,
|
||||||
|
size: limit,
|
||||||
|
lang,
|
||||||
|
'boundary.country': country,
|
||||||
|
});
|
||||||
|
|
||||||
|
const url = `${ORS_BASE}/geocode/search?${params}`;
|
||||||
const data = await makeRequest(url);
|
const data = await makeRequest(url);
|
||||||
|
|
||||||
if (!data || data.length === 0) {
|
if (!data.features || data.features.length === 0) {
|
||||||
return res.status(404).json({
|
return res.status(404).json({
|
||||||
success: false,
|
success: false,
|
||||||
message: 'Nessun risultato trovato'
|
message: 'Nessun risultato trovato',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const results = data.map(item => ({
|
const results = data.features.map((feature) => ({
|
||||||
displayName: item.display_name,
|
id: feature.properties.id,
|
||||||
city: item.address.city || item.address.town || item.address.village || item.address.municipality,
|
displayName: feature.properties.label,
|
||||||
address: item.address.road ? `${item.address.road}${item.address.house_number ? ' ' + item.address.house_number : ''}` : null,
|
name: feature.properties.name,
|
||||||
province: item.address.county || item.address.province,
|
street: feature.properties.street,
|
||||||
region: item.address.state,
|
houseNumber: feature.properties.housenumber,
|
||||||
country: item.address.country,
|
city: feature.properties.locality || feature.properties.county,
|
||||||
postalCode: item.address.postcode,
|
county: feature.properties.county,
|
||||||
|
region: feature.properties.region,
|
||||||
|
country: feature.properties.country,
|
||||||
|
postalCode: feature.properties.postalcode,
|
||||||
coordinates: {
|
coordinates: {
|
||||||
lat: parseFloat(item.lat),
|
lat: feature.geometry.coordinates[1],
|
||||||
lng: parseFloat(item.lon)
|
lng: feature.geometry.coordinates[0],
|
||||||
},
|
},
|
||||||
type: item.type,
|
type: feature.properties.layer,
|
||||||
importance: item.importance
|
confidence: feature.properties.confidence,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
res.status(200).json({
|
res.status(200).json({
|
||||||
success: true,
|
success: true,
|
||||||
data: results
|
count: results.length,
|
||||||
|
data: results,
|
||||||
});
|
});
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Errore geocoding:', error);
|
console.error('Errore geocoding:', error);
|
||||||
res.status(500).json({
|
res.status(500).json({
|
||||||
success: false,
|
success: false,
|
||||||
message: 'Errore durante il geocoding',
|
message: 'Errore durante il geocoding',
|
||||||
error: error.message
|
error: error.message,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @desc Reverse geocoding - coordinate a indirizzo (Nominatim)
|
* @desc Reverse geocoding - coordinate a indirizzo (ORS Reverse)
|
||||||
* @route GET /api/trasporti/geo/reverse
|
* @route GET /api/geo/reverse
|
||||||
*/
|
*/
|
||||||
const reverseGeocode = async (req, res) => {
|
const reverseGeocode = async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const { lat, lng } = req.query;
|
const { lat, lng, lang = 'it' } = req.query;
|
||||||
|
|
||||||
if (!lat || !lng) {
|
if (!lat || !lng) {
|
||||||
return res.status(400).json({
|
return res.status(400).json({
|
||||||
success: false,
|
success: false,
|
||||||
message: 'Coordinate lat e lng richieste'
|
message: 'Coordinate lat e lng richieste',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const url = `${NOMINATIM_BASE}/reverse?format=json&lat=${lat}&lon=${lng}&addressdetails=1`;
|
const params = new URLSearchParams({
|
||||||
|
'point.lat': lat,
|
||||||
|
'point.lon': lng,
|
||||||
|
lang,
|
||||||
|
size: '1',
|
||||||
|
layers: 'address,street,locality',
|
||||||
|
});
|
||||||
|
|
||||||
|
const url = `${ORS_BASE}/geocode/reverse?${params}`;
|
||||||
const data = await makeRequest(url);
|
const data = await makeRequest(url);
|
||||||
|
|
||||||
if (!data || data.error) {
|
if (!data.features || data.features.length === 0) {
|
||||||
return res.status(404).json({
|
return res.status(404).json({
|
||||||
success: false,
|
success: false,
|
||||||
message: 'Nessun risultato trovato'
|
message: 'Nessun risultato trovato',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const feature = data.features[0];
|
||||||
|
|
||||||
const result = {
|
const result = {
|
||||||
displayName: data.display_name,
|
displayName: feature.properties.label,
|
||||||
city: data.address.city || data.address.town || data.address.village || data.address.municipality,
|
name: feature.properties.name,
|
||||||
address: data.address.road ? `${data.address.road}${data.address.house_number ? ' ' + data.address.house_number : ''}` : null,
|
street: feature.properties.street,
|
||||||
province: data.address.county || data.address.province,
|
houseNumber: feature.properties.housenumber,
|
||||||
region: data.address.state,
|
city: feature.properties.locality || feature.properties.county,
|
||||||
country: data.address.country,
|
county: feature.properties.county,
|
||||||
postalCode: data.address.postcode,
|
region: feature.properties.region,
|
||||||
|
country: feature.properties.country,
|
||||||
|
postalCode: feature.properties.postalcode,
|
||||||
coordinates: {
|
coordinates: {
|
||||||
lat: parseFloat(lat),
|
lat: parseFloat(lat),
|
||||||
lng: parseFloat(lng)
|
lng: parseFloat(lng),
|
||||||
}
|
},
|
||||||
|
distance: feature.properties.distance, // distanza dal punto esatto
|
||||||
};
|
};
|
||||||
|
|
||||||
res.status(200).json({
|
res.status(200).json({
|
||||||
success: true,
|
success: true,
|
||||||
data: result
|
data: result,
|
||||||
});
|
});
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Errore reverse geocoding:', error);
|
console.error('Errore reverse geocoding:', error);
|
||||||
res.status(500).json({
|
res.status(500).json({
|
||||||
success: false,
|
success: false,
|
||||||
message: 'Errore durante il reverse geocoding',
|
message: 'Errore durante il reverse geocoding',
|
||||||
error: error.message
|
error: error.message,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @desc Calcola percorso tra due punti (OSRM)
|
* @desc Calcola percorso tra due o più punti (ORS Directions)
|
||||||
* @route GET /api/trasporti/geo/route
|
* @route POST /api/geo/route
|
||||||
|
* @body { coordinates: [[lng,lat], [lng,lat], ...], profile: 'driving-car' }
|
||||||
*/
|
*/
|
||||||
const getRoute = async (req, res) => {
|
const getRoute = async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const {
|
const {
|
||||||
startLat, startLng,
|
startLat,
|
||||||
endLat, endLng,
|
startLng,
|
||||||
waypoints // formato: "lat1,lng1;lat2,lng2;..."
|
endLat,
|
||||||
|
endLng,
|
||||||
|
waypoints, // formato: "lat1,lng1;lat2,lng2;..."
|
||||||
|
profile = 'driving-car', // driving-car, driving-hgv, cycling-regular, foot-walking
|
||||||
|
language = 'it',
|
||||||
|
units = 'km',
|
||||||
} = req.query;
|
} = req.query;
|
||||||
|
|
||||||
if (!startLat || !startLng || !endLat || !endLng) {
|
if (!startLat || !startLng || !endLat || !endLng) {
|
||||||
return res.status(400).json({
|
return res.status(400).json({
|
||||||
success: false,
|
success: false,
|
||||||
message: 'Coordinate di partenza e arrivo richieste'
|
message: 'Coordinate di partenza e arrivo richieste',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Costruisci stringa coordinate
|
// Costruisci array coordinate [lng, lat] (formato GeoJSON)
|
||||||
let coordinates = `${startLng},${startLat}`;
|
const coordinates = [[parseFloat(startLng), parseFloat(startLat)]];
|
||||||
|
|
||||||
if (waypoints) {
|
if (waypoints) {
|
||||||
const waypointsList = waypoints.split(';');
|
const waypointsList = waypoints.split(';');
|
||||||
waypointsList.forEach(wp => {
|
waypointsList.forEach((wp) => {
|
||||||
const [lat, lng] = wp.split(',');
|
const [lat, lng] = wp.split(',').map(parseFloat);
|
||||||
coordinates += `;${lng},${lat}`;
|
coordinates.push([lng, lat]);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
coordinates += `;${endLng},${endLat}`;
|
coordinates.push([parseFloat(endLng), parseFloat(endLat)]);
|
||||||
|
|
||||||
const url = `${OSRM_BASE}/route/v1/driving/${coordinates}?overview=full&geometries=polyline&steps=true`;
|
// Richiesta POST a ORS Directions
|
||||||
|
const url = `${ORS_BASE}/v2/directions/${profile}`;
|
||||||
|
|
||||||
const data = await makeRequest(url);
|
const body = {
|
||||||
|
coordinates,
|
||||||
|
language,
|
||||||
|
units,
|
||||||
|
geometry: true,
|
||||||
|
instructions: true,
|
||||||
|
maneuvers: true,
|
||||||
|
};
|
||||||
|
|
||||||
if (!data || data.code !== 'Ok' || !data.routes || data.routes.length === 0) {
|
const data = await makeRequest(url, 'POST', body);
|
||||||
|
|
||||||
|
if (!data.routes || data.routes.length === 0) {
|
||||||
return res.status(404).json({
|
return res.status(404).json({
|
||||||
success: false,
|
success: false,
|
||||||
message: 'Impossibile calcolare il percorso'
|
message: 'Impossibile calcolare il percorso',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const route = data.routes[0];
|
const route = data.routes[0];
|
||||||
|
const summary = route.summary;
|
||||||
// Estrai città attraversate (dalle istruzioni)
|
|
||||||
const citiesAlongRoute = [];
|
|
||||||
if (route.legs) {
|
|
||||||
route.legs.forEach(leg => {
|
|
||||||
if (leg.steps) {
|
|
||||||
leg.steps.forEach(step => {
|
|
||||||
if (step.name && step.name.length > 0) {
|
|
||||||
// Qui potresti fare reverse geocoding per ottenere città
|
|
||||||
// Per ora usiamo i nomi delle strade principali
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const result = {
|
const result = {
|
||||||
distance: Math.round(route.distance / 1000 * 10) / 10, // km
|
distance: Math.round(summary.distance * 10) / 10, // km
|
||||||
duration: Math.round(route.duration / 60), // minuti
|
duration: Math.round(summary.duration / 60), // minuti
|
||||||
polyline: route.geometry, // Polyline encoded
|
durationFormatted: formatDuration(summary.duration),
|
||||||
legs: route.legs.map(leg => ({
|
bbox: data.bbox, // Bounding box
|
||||||
distance: Math.round(leg.distance / 1000 * 10) / 10,
|
geometry: route.geometry, // Polyline encoded
|
||||||
duration: Math.round(leg.duration / 60),
|
segments: route.segments.map((segment) => ({
|
||||||
summary: leg.summary,
|
distance: Math.round(segment.distance * 10) / 10,
|
||||||
steps: leg.steps ? leg.steps.slice(0, 10).map(s => ({ // Limita step
|
duration: Math.round(segment.duration / 60),
|
||||||
instruction: s.maneuver ? s.maneuver.instruction : '',
|
steps: segment.steps.map((step) => ({
|
||||||
name: s.name,
|
instruction: step.instruction,
|
||||||
distance: Math.round(s.distance),
|
name: step.name,
|
||||||
duration: Math.round(s.duration / 60)
|
distance: Math.round(step.distance * 100) / 100,
|
||||||
})) : []
|
duration: Math.round(step.duration / 60),
|
||||||
}))
|
type: step.type,
|
||||||
|
maneuver: step.maneuver,
|
||||||
|
})),
|
||||||
|
})),
|
||||||
};
|
};
|
||||||
|
|
||||||
res.status(200).json({
|
res.status(200).json({
|
||||||
success: true,
|
success: true,
|
||||||
data: result
|
data: result,
|
||||||
});
|
});
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Errore calcolo percorso:', error);
|
console.error('Errore calcolo percorso:', error);
|
||||||
res.status(500).json({
|
res.status(500).json({
|
||||||
success: false,
|
success: false,
|
||||||
message: 'Errore durante il calcolo del percorso',
|
message: 'Errore durante il calcolo del percorso',
|
||||||
error: error.message
|
error: error.message,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @desc Calcola matrice distanze tra più punti (ORS Matrix)
|
||||||
|
* @route POST /api/geo/matrix
|
||||||
|
*/
|
||||||
|
const getMatrix = async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { locations, profile = 'driving-car' } = req.body;
|
||||||
|
|
||||||
|
if (!locations || locations.length < 2) {
|
||||||
|
return res.status(400).json({
|
||||||
|
success: false,
|
||||||
|
message: 'Almeno 2 location richieste',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Formato locations: [[lng, lat], [lng, lat], ...]
|
||||||
|
const url = `${ORS_BASE}/v2/matrix/${profile}`;
|
||||||
|
|
||||||
|
const body = {
|
||||||
|
locations,
|
||||||
|
metrics: ['distance', 'duration'],
|
||||||
|
units: 'km',
|
||||||
|
};
|
||||||
|
|
||||||
|
const data = await makeRequest(url, 'POST', body);
|
||||||
|
|
||||||
|
const result = {
|
||||||
|
distances: data.distances, // Matrice distanze in km
|
||||||
|
durations: data.durations, // Matrice durate in secondi
|
||||||
|
sources: data.sources,
|
||||||
|
destinations: data.destinations,
|
||||||
|
};
|
||||||
|
|
||||||
|
res.status(200).json({
|
||||||
|
success: true,
|
||||||
|
data: result,
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Errore calcolo matrice:', error);
|
||||||
|
res.status(500).json({
|
||||||
|
success: false,
|
||||||
|
message: 'Errore durante il calcolo della matrice',
|
||||||
|
error: error.message,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @desc Suggerisci città intermedie su un percorso
|
* @desc Suggerisci città intermedie su un percorso
|
||||||
* @route GET /api/trasporti/geo/suggest-waypoints
|
* @route GET /api/geo/suggest-waypoints
|
||||||
*/
|
*/
|
||||||
const suggestWaypoints = async (req, res) => {
|
const suggestWaypoints = async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const { startLat, startLng, endLat, endLng } = req.query;
|
const { startLat, startLng, endLat, endLng, count = 3 } = req.query;
|
||||||
|
|
||||||
if (!startLat || !startLng || !endLat || !endLng) {
|
if (!startLat || !startLng || !endLat || !endLng) {
|
||||||
return res.status(400).json({
|
return res.status(400).json({
|
||||||
success: false,
|
success: false,
|
||||||
message: 'Coordinate di partenza e arrivo richieste'
|
message: 'Coordinate di partenza e arrivo richieste',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prima ottieni il percorso
|
// Prima ottieni il percorso
|
||||||
const routeUrl = `${OSRM_BASE}/route/v1/driving/${startLng},${startLat};${endLng},${endLat}?overview=full&geometries=geojson`;
|
const routeUrl = `${ORS_BASE}/v2/directions/driving-car`;
|
||||||
|
const routeBody = {
|
||||||
|
coordinates: [
|
||||||
|
[parseFloat(startLng), parseFloat(startLat)],
|
||||||
|
[parseFloat(endLng), parseFloat(endLat)],
|
||||||
|
],
|
||||||
|
geometry: true,
|
||||||
|
};
|
||||||
|
|
||||||
const routeData = await makeRequest(routeUrl);
|
const routeData = await makeRequest(routeUrl, 'POST', routeBody);
|
||||||
|
|
||||||
if (!routeData || routeData.code !== 'Ok') {
|
if (!routeData.routes || routeData.routes.length === 0) {
|
||||||
return res.status(404).json({
|
return res.status(404).json({
|
||||||
success: false,
|
success: false,
|
||||||
message: 'Impossibile calcolare il percorso'
|
message: 'Impossibile calcolare il percorso',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prendi punti lungo il percorso (ogni ~50km circa)
|
// Decodifica polyline per ottenere punti
|
||||||
const coordinates = routeData.routes[0].geometry.coordinates;
|
const geometry = routeData.routes[0].geometry;
|
||||||
const totalPoints = coordinates.length;
|
const decodedPoints = decodePolyline(geometry);
|
||||||
const step = Math.max(1, Math.floor(totalPoints / 6)); // ~5 punti intermedi
|
|
||||||
|
// Seleziona punti equidistanti lungo il percorso
|
||||||
|
const totalPoints = decodedPoints.length;
|
||||||
|
const step = Math.floor(totalPoints / (parseInt(count) + 1));
|
||||||
|
|
||||||
const sampledPoints = [];
|
const sampledPoints = [];
|
||||||
for (let i = step; i < totalPoints - step; i += step) {
|
for (let i = 1; i <= count; i++) {
|
||||||
sampledPoints.push(coordinates[i]);
|
const index = Math.min(step * i, totalPoints - 1);
|
||||||
|
sampledPoints.push(decodedPoints[index]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fai reverse geocoding per ogni punto
|
// Fai reverse geocoding per ogni punto
|
||||||
const cities = [];
|
const cities = [];
|
||||||
const seenCities = new Set();
|
const seenCities = new Set();
|
||||||
|
|
||||||
for (const point of sampledPoints.slice(0, 5)) { // Limita a 5 richieste
|
for (const point of sampledPoints) {
|
||||||
try {
|
try {
|
||||||
const reverseUrl = `${NOMINATIM_BASE}/reverse?format=json&lat=${point[1]}&lon=${point[0]}&addressdetails=1&zoom=10`;
|
const params = new URLSearchParams({
|
||||||
|
'point.lat': point[1],
|
||||||
|
'point.lon': point[0],
|
||||||
|
lang: 'it',
|
||||||
|
size: '1',
|
||||||
|
layers: 'locality,county',
|
||||||
|
});
|
||||||
|
|
||||||
|
const reverseUrl = `${ORS_BASE}/geocode/reverse?${params}`;
|
||||||
const data = await makeRequest(reverseUrl);
|
const data = await makeRequest(reverseUrl);
|
||||||
|
|
||||||
if (data && data.address) {
|
if (data.features && data.features.length > 0) {
|
||||||
const cityName = data.address.city || data.address.town || data.address.village;
|
const feature = data.features[0];
|
||||||
|
const cityName = feature.properties.locality || feature.properties.county;
|
||||||
|
|
||||||
if (cityName && !seenCities.has(cityName.toLowerCase())) {
|
if (cityName && !seenCities.has(cityName.toLowerCase())) {
|
||||||
seenCities.add(cityName.toLowerCase());
|
seenCities.add(cityName.toLowerCase());
|
||||||
cities.push({
|
cities.push({
|
||||||
city: cityName,
|
city: cityName,
|
||||||
province: data.address.county || data.address.province,
|
county: feature.properties.county,
|
||||||
region: data.address.state,
|
region: feature.properties.region,
|
||||||
coordinates: {
|
coordinates: {
|
||||||
lat: point[1],
|
lat: point[1],
|
||||||
lng: point[0]
|
lng: point[0],
|
||||||
}
|
},
|
||||||
|
displayName: feature.properties.label,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rate limiting - aspetta 1 secondo tra le richieste (requisito Nominatim)
|
|
||||||
await new Promise(resolve => setTimeout(resolve, 1100));
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log('Errore reverse per punto:', e.message);
|
console.log('Errore reverse per punto:', e.message);
|
||||||
}
|
}
|
||||||
@@ -380,124 +482,192 @@ const suggestWaypoints = async (req, res) => {
|
|||||||
|
|
||||||
res.status(200).json({
|
res.status(200).json({
|
||||||
success: true,
|
success: true,
|
||||||
data: cities
|
count: cities.length,
|
||||||
|
data: cities,
|
||||||
});
|
});
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Errore suggerimento waypoints:', error);
|
console.error('Errore suggerimento waypoints:', error);
|
||||||
res.status(500).json({
|
res.status(500).json({
|
||||||
success: false,
|
success: false,
|
||||||
message: 'Errore durante il suggerimento delle tappe',
|
message: 'Errore durante il suggerimento delle tappe',
|
||||||
error: error.message
|
error: error.message,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @desc Cerca città italiane (ottimizzato per Italia)
|
* @desc Cerca città italiane (ottimizzato)
|
||||||
* @route GET /api/trasporti/geo/cities/it
|
* @route GET /api/geo/cities/it
|
||||||
*/
|
*/
|
||||||
const searchItalianCities = async (req, res) => {
|
const searchItalianCities = async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const { q, limit = 10 } = req.query;
|
const { q, limit = 10, region } = req.query;
|
||||||
|
|
||||||
if (!q || q.length < 2) {
|
if (!q || q.length < 2) {
|
||||||
return res.status(400).json({
|
return res.status(400).json({
|
||||||
success: false,
|
success: false,
|
||||||
message: 'Query deve essere almeno 2 caratteri'
|
message: 'Query deve essere almeno 2 caratteri',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Usa Nominatim con filtro Italia
|
const params = new URLSearchParams({
|
||||||
const url = `${NOMINATIM_BASE}/search?format=json&q=${encodeURIComponent(q)}&countrycodes=it&limit=${limit}&addressdetails=1&featuretype=city`;
|
text: q,
|
||||||
|
size: limit,
|
||||||
|
lang: 'it',
|
||||||
|
'boundary.country': 'IT',
|
||||||
|
layers: 'locality,county',
|
||||||
|
});
|
||||||
|
|
||||||
|
// Filtro opzionale per regione
|
||||||
|
if (region) {
|
||||||
|
params.append('region', region);
|
||||||
|
}
|
||||||
|
|
||||||
|
const url = `${ORS_BASE}/geocode/search?${params}`;
|
||||||
const data = await makeRequest(url);
|
const data = await makeRequest(url);
|
||||||
|
|
||||||
const results = data
|
const results = data.features
|
||||||
.filter(item =>
|
.filter((f) => f.properties.locality || f.properties.county)
|
||||||
item.address &&
|
.map((feature) => ({
|
||||||
(item.address.city || item.address.town || item.address.village)
|
city: feature.properties.locality || feature.properties.name,
|
||||||
)
|
county: feature.properties.county,
|
||||||
.map(item => ({
|
region: feature.properties.region,
|
||||||
city: item.address.city || item.address.town || item.address.village,
|
postalCode: feature.properties.postalcode,
|
||||||
province: item.address.county || item.address.province,
|
|
||||||
region: item.address.state,
|
|
||||||
postalCode: item.address.postcode,
|
|
||||||
coordinates: {
|
coordinates: {
|
||||||
lat: parseFloat(item.lat),
|
lat: feature.geometry.coordinates[1],
|
||||||
lng: parseFloat(item.lon)
|
lng: feature.geometry.coordinates[0],
|
||||||
},
|
},
|
||||||
displayName: `${item.address.city || item.address.town || item.address.village}, ${item.address.county || item.address.state}`
|
displayName: `${feature.properties.locality || feature.properties.name}, ${feature.properties.region}`,
|
||||||
|
confidence: feature.properties.confidence,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Rimuovi duplicati
|
// Rimuovi duplicati
|
||||||
const unique = results.filter((v, i, a) =>
|
const unique = results.filter(
|
||||||
a.findIndex(t => t.city.toLowerCase() === v.city.toLowerCase()) === i
|
(v, i, a) => a.findIndex((t) => t.city?.toLowerCase() === v.city?.toLowerCase()) === i
|
||||||
);
|
);
|
||||||
|
|
||||||
res.status(200).json({
|
res.status(200).json({
|
||||||
success: true,
|
success: true,
|
||||||
data: unique
|
count: unique.length,
|
||||||
|
data: unique,
|
||||||
});
|
});
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Errore ricerca città italiane:', error);
|
console.error('Errore ricerca città italiane:', error);
|
||||||
res.status(500).json({
|
res.status(500).json({
|
||||||
success: false,
|
success: false,
|
||||||
message: 'Errore durante la ricerca',
|
message: 'Errore durante la ricerca',
|
||||||
error: error.message
|
error: error.message,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @desc Calcola distanza e durata tra due punti
|
* @desc Calcola distanza e durata tra due punti (semplificato)
|
||||||
* @route GET /api/trasporti/geo/distance
|
* @route GET /api/geo/distance
|
||||||
*/
|
*/
|
||||||
const getDistance = async (req, res) => {
|
const getDistance = async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const { startLat, startLng, endLat, endLng } = req.query;
|
const { startLat, startLng, endLat, endLng, profile = 'driving-car' } = req.query;
|
||||||
|
|
||||||
if (!startLat || !startLng || !endLat || !endLng) {
|
if (!startLat || !startLng || !endLat || !endLng) {
|
||||||
return res.status(400).json({
|
return res.status(400).json({
|
||||||
success: false,
|
success: false,
|
||||||
message: 'Tutte le coordinate sono richieste'
|
message: 'Tutte le coordinate sono richieste',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const url = `${OSRM_BASE}/route/v1/driving/${startLng},${startLat};${endLng},${endLat}?overview=false`;
|
const url = `${ORS_BASE}/v2/directions/${profile}`;
|
||||||
|
|
||||||
const data = await makeRequest(url);
|
const body = {
|
||||||
|
coordinates: [
|
||||||
|
[parseFloat(startLng), parseFloat(startLat)],
|
||||||
|
[parseFloat(endLng), parseFloat(endLat)],
|
||||||
|
],
|
||||||
|
geometry: false,
|
||||||
|
instructions: false,
|
||||||
|
};
|
||||||
|
|
||||||
if (!data || data.code !== 'Ok' || !data.routes || data.routes.length === 0) {
|
const data = await makeRequest(url, 'POST', body);
|
||||||
|
|
||||||
|
if (!data.routes || data.routes.length === 0) {
|
||||||
return res.status(404).json({
|
return res.status(404).json({
|
||||||
success: false,
|
success: false,
|
||||||
message: 'Impossibile calcolare la distanza'
|
message: 'Impossibile calcolare la distanza',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const route = data.routes[0];
|
const summary = data.routes[0].summary;
|
||||||
|
|
||||||
res.status(200).json({
|
res.status(200).json({
|
||||||
success: true,
|
success: true,
|
||||||
data: {
|
data: {
|
||||||
distance: Math.round(route.distance / 1000 * 10) / 10, // km
|
distance: Math.round(summary.distance * 10) / 10, // km
|
||||||
duration: Math.round(route.duration / 60), // minuti
|
duration: Math.round(summary.duration / 60), // minuti
|
||||||
durationFormatted: formatDuration(route.duration)
|
durationFormatted: formatDuration(summary.duration),
|
||||||
}
|
profile,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Errore calcolo distanza:', error);
|
console.error('Errore calcolo distanza:', error);
|
||||||
res.status(500).json({
|
res.status(500).json({
|
||||||
success: false,
|
success: false,
|
||||||
message: 'Errore durante il calcolo della distanza',
|
message: 'Errore durante il calcolo della distanza',
|
||||||
error: error.message
|
error: error.message,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Helper per formattare durata
|
/**
|
||||||
|
* @desc Ottieni isocrone (aree raggiungibili in X minuti)
|
||||||
|
* @route GET /api/geo/isochrone
|
||||||
|
*/
|
||||||
|
const getIsochrone = async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { lat, lng, minutes = 30, profile = 'driving-car' } = req.query;
|
||||||
|
|
||||||
|
if (!lat || !lng) {
|
||||||
|
return res.status(400).json({
|
||||||
|
success: false,
|
||||||
|
message: 'Coordinate richieste',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const url = `${ORS_BASE}/v2/isochrones/${profile}`;
|
||||||
|
|
||||||
|
const body = {
|
||||||
|
locations: [[parseFloat(lng), parseFloat(lat)]],
|
||||||
|
range: [parseInt(minutes) * 60], // secondi
|
||||||
|
range_type: 'time',
|
||||||
|
};
|
||||||
|
|
||||||
|
const data = await makeRequest(url, 'POST', body);
|
||||||
|
|
||||||
|
res.status(200).json({
|
||||||
|
success: true,
|
||||||
|
data: {
|
||||||
|
type: 'FeatureCollection',
|
||||||
|
features: data.features,
|
||||||
|
center: { lat: parseFloat(lat), lng: parseFloat(lng) },
|
||||||
|
minutes: parseInt(minutes),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Errore calcolo isocrone:', error);
|
||||||
|
res.status(500).json({
|
||||||
|
success: false,
|
||||||
|
message: 'Errore durante il calcolo isocrone',
|
||||||
|
error: error.message,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// ============================================
|
||||||
|
// HELPER FUNCTIONS
|
||||||
|
// ============================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formatta durata in formato leggibile
|
||||||
|
*/
|
||||||
const formatDuration = (seconds) => {
|
const formatDuration = (seconds) => {
|
||||||
const hours = Math.floor(seconds / 3600);
|
const hours = Math.floor(seconds / 3600);
|
||||||
const minutes = Math.round((seconds % 3600) / 60);
|
const minutes = Math.round((seconds % 3600) / 60);
|
||||||
@@ -511,12 +681,55 @@ const formatDuration = (seconds) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decodifica polyline encoded (formato Google/ORS)
|
||||||
|
*/
|
||||||
|
const decodePolyline = (encoded) => {
|
||||||
|
const points = [];
|
||||||
|
let index = 0;
|
||||||
|
let lat = 0;
|
||||||
|
let lng = 0;
|
||||||
|
|
||||||
|
while (index < encoded.length) {
|
||||||
|
let b;
|
||||||
|
let shift = 0;
|
||||||
|
let result = 0;
|
||||||
|
|
||||||
|
do {
|
||||||
|
b = encoded.charCodeAt(index++) - 63;
|
||||||
|
result |= (b & 0x1f) << shift;
|
||||||
|
shift += 5;
|
||||||
|
} while (b >= 0x20);
|
||||||
|
|
||||||
|
const dlat = result & 1 ? ~(result >> 1) : result >> 1;
|
||||||
|
lat += dlat;
|
||||||
|
|
||||||
|
shift = 0;
|
||||||
|
result = 0;
|
||||||
|
|
||||||
|
do {
|
||||||
|
b = encoded.charCodeAt(index++) - 63;
|
||||||
|
result |= (b & 0x1f) << shift;
|
||||||
|
shift += 5;
|
||||||
|
} while (b >= 0x20);
|
||||||
|
|
||||||
|
const dlng = result & 1 ? ~(result >> 1) : result >> 1;
|
||||||
|
lng += dlng;
|
||||||
|
|
||||||
|
points.push([lng / 1e5, lat / 1e5]); // [lng, lat] formato GeoJSON
|
||||||
|
}
|
||||||
|
|
||||||
|
return points;
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
autocomplete,
|
autocomplete,
|
||||||
geocode,
|
geocode,
|
||||||
reverseGeocode,
|
reverseGeocode,
|
||||||
getRoute,
|
getRoute,
|
||||||
|
getMatrix,
|
||||||
suggestWaypoints,
|
suggestWaypoints,
|
||||||
searchItalianCities,
|
searchItalianCities,
|
||||||
getDistance
|
getDistance,
|
||||||
|
getIsochrone,
|
||||||
};
|
};
|
||||||
522
src/controllers/geocodingController_OLD.js
Normal file
522
src/controllers/geocodingController_OLD.js
Normal file
@@ -0,0 +1,522 @@
|
|||||||
|
/**
|
||||||
|
* Controller per Geocoding usando servizi Open Source
|
||||||
|
* - Nominatim (OpenStreetMap) per geocoding/reverse
|
||||||
|
* - OSRM per routing
|
||||||
|
* - Photon per autocomplete
|
||||||
|
*/
|
||||||
|
|
||||||
|
const https = require('https');
|
||||||
|
const http = require('http');
|
||||||
|
|
||||||
|
// Configurazione servizi
|
||||||
|
const NOMINATIM_BASE = 'https://nominatim.openstreetmap.org';
|
||||||
|
const PHOTON_BASE = 'https://photon.komoot.io';
|
||||||
|
const OSRM_BASE = 'https://router.project-osrm.org';
|
||||||
|
|
||||||
|
// User-Agent richiesto da Nominatim
|
||||||
|
const USER_AGENT = 'FreePlanetApp/1.0';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper per fare richieste HTTP/HTTPS
|
||||||
|
*/
|
||||||
|
const makeRequest = (url) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const client = url.startsWith('https') ? https : http;
|
||||||
|
|
||||||
|
const req = client.get(url, {
|
||||||
|
headers: {
|
||||||
|
'User-Agent': USER_AGENT,
|
||||||
|
'Accept': 'application/json'
|
||||||
|
}
|
||||||
|
}, (res) => {
|
||||||
|
let data = '';
|
||||||
|
res.on('data', chunk => data += chunk);
|
||||||
|
res.on('end', () => {
|
||||||
|
try {
|
||||||
|
resolve(JSON.parse(data));
|
||||||
|
} catch (e) {
|
||||||
|
reject(new Error('Errore parsing risposta'));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
req.on('error', reject);
|
||||||
|
req.setTimeout(10000, () => {
|
||||||
|
req.destroy();
|
||||||
|
reject(new Error('Timeout richiesta'));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @desc Autocomplete città (Photon API)
|
||||||
|
* @route GET /api/viaggi/geo/autocomplete
|
||||||
|
*/
|
||||||
|
const autocomplete = async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { q, limit = 5, lang = 'it' } = req.query;
|
||||||
|
|
||||||
|
if (!q || q.length < 2) {
|
||||||
|
return res.status(400).json({
|
||||||
|
success: false,
|
||||||
|
message: 'Query deve essere almeno 2 caratteri'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Photon API - gratuito e veloce
|
||||||
|
const url = `${PHOTON_BASE}/api/?q=${encodeURIComponent(q)}&limit=${limit}&lang=${lang}&osm_tag=place:city&osm_tag=place:town&osm_tag=place:village`;
|
||||||
|
|
||||||
|
const data = await makeRequest(url);
|
||||||
|
|
||||||
|
// Formatta risultati
|
||||||
|
const results = data.features.map(feature => ({
|
||||||
|
city: feature.properties.name,
|
||||||
|
province: feature.properties.county || feature.properties.state,
|
||||||
|
region: feature.properties.state,
|
||||||
|
country: feature.properties.country,
|
||||||
|
postalCode: feature.properties.postcode,
|
||||||
|
coordinates: {
|
||||||
|
lat: feature.geometry.coordinates[1],
|
||||||
|
lng: feature.geometry.coordinates[0]
|
||||||
|
},
|
||||||
|
displayName: [
|
||||||
|
feature.properties.name,
|
||||||
|
feature.properties.county,
|
||||||
|
feature.properties.state,
|
||||||
|
feature.properties.country
|
||||||
|
].filter(Boolean).join(', '),
|
||||||
|
type: feature.properties.osm_value || 'place'
|
||||||
|
}));
|
||||||
|
|
||||||
|
res.status(200).json({
|
||||||
|
success: true,
|
||||||
|
data: results
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Errore autocomplete:', error);
|
||||||
|
res.status(500).json({
|
||||||
|
success: false,
|
||||||
|
message: 'Errore durante la ricerca',
|
||||||
|
error: error.message
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @desc Geocoding - indirizzo a coordinate (Nominatim)
|
||||||
|
* @route GET /api/viaggi/geo/geocode
|
||||||
|
*/
|
||||||
|
const geocode = async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { address, city, country = 'Italy' } = req.query;
|
||||||
|
|
||||||
|
const searchQuery = [address, city, country].filter(Boolean).join(', ');
|
||||||
|
|
||||||
|
if (!searchQuery) {
|
||||||
|
return res.status(400).json({
|
||||||
|
success: false,
|
||||||
|
message: 'Fornisci un indirizzo o città da cercare'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const url = `${NOMINATIM_BASE}/search?format=json&q=${encodeURIComponent(searchQuery)}&limit=5&addressdetails=1`;
|
||||||
|
|
||||||
|
const data = await makeRequest(url);
|
||||||
|
|
||||||
|
if (!data || data.length === 0) {
|
||||||
|
return res.status(404).json({
|
||||||
|
success: false,
|
||||||
|
message: 'Nessun risultato trovato'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const results = data.map(item => ({
|
||||||
|
displayName: item.display_name,
|
||||||
|
city: item.address.city || item.address.town || item.address.village || item.address.municipality,
|
||||||
|
address: item.address.road ? `${item.address.road}${item.address.house_number ? ' ' + item.address.house_number : ''}` : null,
|
||||||
|
province: item.address.county || item.address.province,
|
||||||
|
region: item.address.state,
|
||||||
|
country: item.address.country,
|
||||||
|
postalCode: item.address.postcode,
|
||||||
|
coordinates: {
|
||||||
|
lat: parseFloat(item.lat),
|
||||||
|
lng: parseFloat(item.lon)
|
||||||
|
},
|
||||||
|
type: item.type,
|
||||||
|
importance: item.importance
|
||||||
|
}));
|
||||||
|
|
||||||
|
res.status(200).json({
|
||||||
|
success: true,
|
||||||
|
data: results
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Errore geocoding:', error);
|
||||||
|
res.status(500).json({
|
||||||
|
success: false,
|
||||||
|
message: 'Errore durante il geocoding',
|
||||||
|
error: error.message
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @desc Reverse geocoding - coordinate a indirizzo (Nominatim)
|
||||||
|
* @route GET /api/viaggi/geo/reverse
|
||||||
|
*/
|
||||||
|
const reverseGeocode = async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { lat, lng } = req.query;
|
||||||
|
|
||||||
|
if (!lat || !lng) {
|
||||||
|
return res.status(400).json({
|
||||||
|
success: false,
|
||||||
|
message: 'Coordinate lat e lng richieste'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const url = `${NOMINATIM_BASE}/reverse?format=json&lat=${lat}&lon=${lng}&addressdetails=1`;
|
||||||
|
|
||||||
|
const data = await makeRequest(url);
|
||||||
|
|
||||||
|
if (!data || data.error) {
|
||||||
|
return res.status(404).json({
|
||||||
|
success: false,
|
||||||
|
message: 'Nessun risultato trovato'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = {
|
||||||
|
displayName: data.display_name,
|
||||||
|
city: data.address.city || data.address.town || data.address.village || data.address.municipality,
|
||||||
|
address: data.address.road ? `${data.address.road}${data.address.house_number ? ' ' + data.address.house_number : ''}` : null,
|
||||||
|
province: data.address.county || data.address.province,
|
||||||
|
region: data.address.state,
|
||||||
|
country: data.address.country,
|
||||||
|
postalCode: data.address.postcode,
|
||||||
|
coordinates: {
|
||||||
|
lat: parseFloat(lat),
|
||||||
|
lng: parseFloat(lng)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
res.status(200).json({
|
||||||
|
success: true,
|
||||||
|
data: result
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Errore reverse geocoding:', error);
|
||||||
|
res.status(500).json({
|
||||||
|
success: false,
|
||||||
|
message: 'Errore durante il reverse geocoding',
|
||||||
|
error: error.message
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @desc Calcola percorso tra due punti (OSRM)
|
||||||
|
* @route GET /api/viaggi/geo/route
|
||||||
|
*/
|
||||||
|
const getRoute = async (req, res) => {
|
||||||
|
try {
|
||||||
|
const {
|
||||||
|
startLat, startLng,
|
||||||
|
endLat, endLng,
|
||||||
|
waypoints // formato: "lat1,lng1;lat2,lng2;..."
|
||||||
|
} = req.query;
|
||||||
|
|
||||||
|
if (!startLat || !startLng || !endLat || !endLng) {
|
||||||
|
return res.status(400).json({
|
||||||
|
success: false,
|
||||||
|
message: 'Coordinate di partenza e arrivo richieste'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Costruisci stringa coordinate
|
||||||
|
let coordinates = `${startLng},${startLat}`;
|
||||||
|
|
||||||
|
if (waypoints) {
|
||||||
|
const waypointsList = waypoints.split(';');
|
||||||
|
waypointsList.forEach(wp => {
|
||||||
|
const [lat, lng] = wp.split(',');
|
||||||
|
coordinates += `;${lng},${lat}`;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
coordinates += `;${endLng},${endLat}`;
|
||||||
|
|
||||||
|
const url = `${OSRM_BASE}/route/v1/driving/${coordinates}?overview=full&geometries=polyline&steps=true`;
|
||||||
|
|
||||||
|
const data = await makeRequest(url);
|
||||||
|
|
||||||
|
if (!data || data.code !== 'Ok' || !data.routes || data.routes.length === 0) {
|
||||||
|
return res.status(404).json({
|
||||||
|
success: false,
|
||||||
|
message: 'Impossibile calcolare il percorso'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const route = data.routes[0];
|
||||||
|
|
||||||
|
// Estrai città attraversate (dalle istruzioni)
|
||||||
|
const citiesAlongRoute = [];
|
||||||
|
if (route.legs) {
|
||||||
|
route.legs.forEach(leg => {
|
||||||
|
if (leg.steps) {
|
||||||
|
leg.steps.forEach(step => {
|
||||||
|
if (step.name && step.name.length > 0) {
|
||||||
|
// Qui potresti fare reverse geocoding per ottenere città
|
||||||
|
// Per ora usiamo i nomi delle strade principali
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = {
|
||||||
|
distance: Math.round(route.distance / 1000 * 10) / 10, // km
|
||||||
|
duration: Math.round(route.duration / 60), // minuti
|
||||||
|
polyline: route.geometry, // Polyline encoded
|
||||||
|
legs: route.legs.map(leg => ({
|
||||||
|
distance: Math.round(leg.distance / 1000 * 10) / 10,
|
||||||
|
duration: Math.round(leg.duration / 60),
|
||||||
|
summary: leg.summary,
|
||||||
|
steps: leg.steps ? leg.steps.slice(0, 10).map(s => ({ // Limita step
|
||||||
|
instruction: s.maneuver ? s.maneuver.instruction : '',
|
||||||
|
name: s.name,
|
||||||
|
distance: Math.round(s.distance),
|
||||||
|
duration: Math.round(s.duration / 60)
|
||||||
|
})) : []
|
||||||
|
}))
|
||||||
|
};
|
||||||
|
|
||||||
|
res.status(200).json({
|
||||||
|
success: true,
|
||||||
|
data: result
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Errore calcolo percorso:', error);
|
||||||
|
res.status(500).json({
|
||||||
|
success: false,
|
||||||
|
message: 'Errore durante il calcolo del percorso',
|
||||||
|
error: error.message
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @desc Suggerisci città intermedie su un percorso
|
||||||
|
* @route GET /api/viaggi/geo/suggest-waypoints
|
||||||
|
*/
|
||||||
|
const suggestWaypoints = async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { startLat, startLng, endLat, endLng } = req.query;
|
||||||
|
|
||||||
|
if (!startLat || !startLng || !endLat || !endLng) {
|
||||||
|
return res.status(400).json({
|
||||||
|
success: false,
|
||||||
|
message: 'Coordinate di partenza e arrivo richieste'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prima ottieni il percorso
|
||||||
|
const routeUrl = `${OSRM_BASE}/route/v1/driving/${startLng},${startLat};${endLng},${endLat}?overview=full&geometries=geojson`;
|
||||||
|
|
||||||
|
const routeData = await makeRequest(routeUrl);
|
||||||
|
|
||||||
|
if (!routeData || routeData.code !== 'Ok') {
|
||||||
|
return res.status(404).json({
|
||||||
|
success: false,
|
||||||
|
message: 'Impossibile calcolare il percorso'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prendi punti lungo il percorso (ogni ~50km circa)
|
||||||
|
const coordinates = routeData.routes[0].geometry.coordinates;
|
||||||
|
const totalPoints = coordinates.length;
|
||||||
|
const step = Math.max(1, Math.floor(totalPoints / 6)); // ~5 punti intermedi
|
||||||
|
|
||||||
|
const sampledPoints = [];
|
||||||
|
for (let i = step; i < totalPoints - step; i += step) {
|
||||||
|
sampledPoints.push(coordinates[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fai reverse geocoding per ogni punto
|
||||||
|
const cities = [];
|
||||||
|
const seenCities = new Set();
|
||||||
|
|
||||||
|
for (const point of sampledPoints.slice(0, 5)) { // Limita a 5 richieste
|
||||||
|
try {
|
||||||
|
const reverseUrl = `${NOMINATIM_BASE}/reverse?format=json&lat=${point[1]}&lon=${point[0]}&addressdetails=1&zoom=10`;
|
||||||
|
const data = await makeRequest(reverseUrl);
|
||||||
|
|
||||||
|
if (data && data.address) {
|
||||||
|
const cityName = data.address.city || data.address.town || data.address.village;
|
||||||
|
if (cityName && !seenCities.has(cityName.toLowerCase())) {
|
||||||
|
seenCities.add(cityName.toLowerCase());
|
||||||
|
cities.push({
|
||||||
|
city: cityName,
|
||||||
|
province: data.address.county || data.address.province,
|
||||||
|
region: data.address.state,
|
||||||
|
coordinates: {
|
||||||
|
lat: point[1],
|
||||||
|
lng: point[0]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rate limiting - aspetta 1 secondo tra le richieste (requisito Nominatim)
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 1100));
|
||||||
|
} catch (e) {
|
||||||
|
console.log('Errore reverse per punto:', e.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
res.status(200).json({
|
||||||
|
success: true,
|
||||||
|
data: cities
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Errore suggerimento waypoints:', error);
|
||||||
|
res.status(500).json({
|
||||||
|
success: false,
|
||||||
|
message: 'Errore durante il suggerimento delle tappe',
|
||||||
|
error: error.message
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @desc Cerca città italiane (ottimizzato per Italia)
|
||||||
|
* @route GET /api/viaggi/geo/cities/it
|
||||||
|
*/
|
||||||
|
const searchItalianCities = async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { q, limit = 10 } = req.query;
|
||||||
|
|
||||||
|
if (!q || q.length < 2) {
|
||||||
|
return res.status(400).json({
|
||||||
|
success: false,
|
||||||
|
message: 'Query deve essere almeno 2 caratteri'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Usa Nominatim con filtro Italia
|
||||||
|
const url = `${NOMINATIM_BASE}/search?format=json&q=${encodeURIComponent(q)}&countrycodes=it&limit=${limit}&addressdetails=1&featuretype=city`;
|
||||||
|
|
||||||
|
const data = await makeRequest(url);
|
||||||
|
|
||||||
|
const results = data
|
||||||
|
.filter(item =>
|
||||||
|
item.address &&
|
||||||
|
(item.address.city || item.address.town || item.address.village)
|
||||||
|
)
|
||||||
|
.map(item => ({
|
||||||
|
city: item.address.city || item.address.town || item.address.village,
|
||||||
|
province: item.address.county || item.address.province,
|
||||||
|
region: item.address.state,
|
||||||
|
postalCode: item.address.postcode,
|
||||||
|
coordinates: {
|
||||||
|
lat: parseFloat(item.lat),
|
||||||
|
lng: parseFloat(item.lon)
|
||||||
|
},
|
||||||
|
displayName: `${item.address.city || item.address.town || item.address.village}, ${item.address.county || item.address.state}`
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Rimuovi duplicati
|
||||||
|
const unique = results.filter((v, i, a) =>
|
||||||
|
a.findIndex(t => t.city.toLowerCase() === v.city.toLowerCase()) === i
|
||||||
|
);
|
||||||
|
|
||||||
|
res.status(200).json({
|
||||||
|
success: true,
|
||||||
|
data: unique
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Errore ricerca città italiane:', error);
|
||||||
|
res.status(500).json({
|
||||||
|
success: false,
|
||||||
|
message: 'Errore durante la ricerca',
|
||||||
|
error: error.message
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @desc Calcola distanza e durata tra due punti
|
||||||
|
* @route GET /api/viaggi/geo/distance
|
||||||
|
*/
|
||||||
|
const getDistance = async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { startLat, startLng, endLat, endLng } = req.query;
|
||||||
|
|
||||||
|
if (!startLat || !startLng || !endLat || !endLng) {
|
||||||
|
return res.status(400).json({
|
||||||
|
success: false,
|
||||||
|
message: 'Tutte le coordinate sono richieste'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const url = `${OSRM_BASE}/route/v1/driving/${startLng},${startLat};${endLng},${endLat}?overview=false`;
|
||||||
|
|
||||||
|
const data = await makeRequest(url);
|
||||||
|
|
||||||
|
if (!data || data.code !== 'Ok' || !data.routes || data.routes.length === 0) {
|
||||||
|
return res.status(404).json({
|
||||||
|
success: false,
|
||||||
|
message: 'Impossibile calcolare la distanza'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const route = data.routes[0];
|
||||||
|
|
||||||
|
res.status(200).json({
|
||||||
|
success: true,
|
||||||
|
data: {
|
||||||
|
distance: Math.round(route.distance / 1000 * 10) / 10, // km
|
||||||
|
duration: Math.round(route.duration / 60), // minuti
|
||||||
|
durationFormatted: formatDuration(route.duration)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Errore calcolo distanza:', error);
|
||||||
|
res.status(500).json({
|
||||||
|
success: false,
|
||||||
|
message: 'Errore durante il calcolo della distanza',
|
||||||
|
error: error.message
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Helper per formattare durata
|
||||||
|
const formatDuration = (seconds) => {
|
||||||
|
const hours = Math.floor(seconds / 3600);
|
||||||
|
const minutes = Math.round((seconds % 3600) / 60);
|
||||||
|
|
||||||
|
if (hours === 0) {
|
||||||
|
return `${minutes} min`;
|
||||||
|
} else if (minutes === 0) {
|
||||||
|
return `${hours} h`;
|
||||||
|
} else {
|
||||||
|
return `${hours} h ${minutes} min`;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
autocomplete,
|
||||||
|
geocode,
|
||||||
|
reverseGeocode,
|
||||||
|
getRoute,
|
||||||
|
suggestWaypoints,
|
||||||
|
searchItalianCities,
|
||||||
|
getDistance
|
||||||
|
};
|
||||||
@@ -1,15 +1,15 @@
|
|||||||
const Ride = require('../models/Ride');
|
const Ride = require('../models/Ride');
|
||||||
const User = require('../models/user');
|
const { User } = require('../models/user');
|
||||||
const RideRequest = require('../models/RideRequest');
|
const RideRequest = require('../models/RideRequest');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @desc Crea un nuovo viaggio (offerta o richiesta)
|
* @desc Crea un nuovo viaggio (offerta o richiesta)
|
||||||
* @route POST /api/trasporti/rides
|
* @route POST /api/viaggi/rides
|
||||||
* @access Private
|
* @access Private
|
||||||
*/
|
*/
|
||||||
const createRide = async (req, res) => {
|
const createRide = async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const { idapp } = req.body;
|
const idapp = req.user.idapp;
|
||||||
const userId = req.user._id;
|
const userId = req.user._id;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@@ -111,9 +111,7 @@ const createRide = async (req, res) => {
|
|||||||
|
|
||||||
// Aggiorna profilo utente come driver se è un'offerta
|
// Aggiorna profilo utente come driver se è un'offerta
|
||||||
if (type === 'offer') {
|
if (type === 'offer') {
|
||||||
await User.findByIdAndUpdate(userId, {
|
await User.findByIdAndUpdate(userId, { $set: { 'profile.driverProfile.isDriver': true } }, { new: true });
|
||||||
'profile.driverProfile.isDriver': true
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Popola i dati per la risposta
|
// Popola i dati per la risposta
|
||||||
@@ -137,12 +135,12 @@ const createRide = async (req, res) => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @desc Ottieni lista viaggi con filtri
|
* @desc Ottieni lista viaggi con filtri
|
||||||
* @route GET /api/trasporti/rides
|
* @route GET /api/viaggi/rides
|
||||||
* @access Public
|
* @access Public
|
||||||
*/
|
*/
|
||||||
const getRides = async (req, res) => {
|
const getRides = async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const { idapp } = req.query;
|
const idapp = req.query.idapp;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
type,
|
type,
|
||||||
@@ -272,15 +270,14 @@ const getRides = async (req, res) => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @desc Ottieni singolo viaggio per ID
|
* @desc Ottieni singolo viaggio per ID
|
||||||
* @route GET /api/trasporti/rides/:id
|
* @route GET /api/viaggi/rides/:id
|
||||||
* @access Public
|
* @access Public
|
||||||
*/
|
*/
|
||||||
const getRideById = async (req, res) => {
|
const getRideById = async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const { id } = req.params;
|
const { id } = req.params;
|
||||||
const { idapp } = req.query;
|
|
||||||
|
|
||||||
const ride = await Ride.findOne({ _id: id, idapp })
|
const ride = await Ride.findOne({ _id: id })
|
||||||
.populate('userId', 'username name surname profile.img profile.Biografia profile.driverProfile')
|
.populate('userId', 'username name surname profile.img profile.Biografia profile.driverProfile')
|
||||||
.populate('confirmedPassengers.userId', 'username name surname profile.img')
|
.populate('confirmedPassengers.userId', 'username name surname profile.img')
|
||||||
.populate('contribution.contribTypes.contribTypeId');
|
.populate('contribution.contribTypes.contribTypeId');
|
||||||
@@ -313,14 +310,15 @@ const getRideById = async (req, res) => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @desc Aggiorna un viaggio
|
* @desc Aggiorna un viaggio
|
||||||
* @route PUT /api/trasporti/rides/:id
|
* @route PUT /api/viaggi/rides/:id
|
||||||
* @access Private
|
* @access Private
|
||||||
*/
|
*/
|
||||||
const updateRide = async (req, res) => {
|
const updateRide = async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const { id } = req.params;
|
const { id } = req.params;
|
||||||
const userId = req.user._id;
|
const userId = req.user._id;
|
||||||
const { idapp, ...updateData } = req.body;
|
const idapp = req.user.idapp;
|
||||||
|
const { ...updateData } = req.body;
|
||||||
|
|
||||||
// Trova il viaggio
|
// Trova il viaggio
|
||||||
const ride = await Ride.findOne({ _id: id, idapp });
|
const ride = await Ride.findOne({ _id: id, idapp });
|
||||||
@@ -333,7 +331,7 @@ const updateRide = async (req, res) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Verifica proprietario
|
// Verifica proprietario
|
||||||
if (ride.userId.toString() !== userId) {
|
if (!ride.userId.equals(userId)) {
|
||||||
return res.status(403).json({
|
return res.status(403).json({
|
||||||
success: false,
|
success: false,
|
||||||
message: 'Non sei autorizzato a modificare questo viaggio'
|
message: 'Non sei autorizzato a modificare questo viaggio'
|
||||||
@@ -400,14 +398,15 @@ const updateRide = async (req, res) => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @desc Cancella un viaggio
|
* @desc Cancella un viaggio
|
||||||
* @route DELETE /api/trasporti/rides/:id
|
* @route DELETE /api/viaggi/rides/:id
|
||||||
* @access Private
|
* @access Private
|
||||||
*/
|
*/
|
||||||
const deleteRide = async (req, res) => {
|
const deleteRide = async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const { id } = req.params;
|
const { id } = req.params;
|
||||||
const userId = req.user._id;
|
const userId = req.user._id;
|
||||||
const { idapp, reason } = req.body;
|
const idapp = req.user.idapp;
|
||||||
|
const { reason } = req.body;
|
||||||
|
|
||||||
const ride = await Ride.findOne({ _id: id, idapp });
|
const ride = await Ride.findOne({ _id: id, idapp });
|
||||||
|
|
||||||
@@ -419,7 +418,7 @@ const deleteRide = async (req, res) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Verifica proprietario
|
// Verifica proprietario
|
||||||
if (ride.userId.toString() !== userId) {
|
if (!ride.userId.equals(userId)) {
|
||||||
return res.status(403).json({
|
return res.status(403).json({
|
||||||
success: false,
|
success: false,
|
||||||
message: 'Non sei autorizzato a cancellare questo viaggio'
|
message: 'Non sei autorizzato a cancellare questo viaggio'
|
||||||
@@ -463,13 +462,14 @@ const deleteRide = async (req, res) => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @desc Ottieni viaggi dell'utente corrente
|
* @desc Ottieni viaggi dell'utente corrente
|
||||||
* @route GET /api/trasporti/rides/my
|
* @route GET /api/viaggi/rides/my
|
||||||
* @access Private
|
* @access Private
|
||||||
*/
|
*/
|
||||||
const getMyRides = async (req, res) => {
|
const getMyRides = async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const userId = req.user._id;
|
const userId = req.user._id;
|
||||||
const { idapp, type, role, status, page = 1, limit = 20 } = req.query;
|
const idapp = req.query.idapp;
|
||||||
|
const { type, role, status, page = 1, limit = 20 } = req.query;
|
||||||
|
|
||||||
let query = { idapp };
|
let query = { idapp };
|
||||||
|
|
||||||
@@ -539,12 +539,12 @@ const getMyRides = async (req, res) => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @desc Cerca viaggi con match intelligente
|
* @desc Cerca viaggi con match intelligente
|
||||||
* @route GET /api/trasporti/rides/search
|
* @route GET /api/viaggi/rides/search
|
||||||
* @access Public
|
* @access Public
|
||||||
*/
|
*/
|
||||||
const searchRides = async (req, res) => {
|
const searchRides = async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const { idapp } = req.query;
|
const idapp = req.query.idapp;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
from,
|
from,
|
||||||
@@ -659,14 +659,14 @@ const searchRides = async (req, res) => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @desc Completa un viaggio
|
* @desc Completa un viaggio
|
||||||
* @route POST /api/trasporti/rides/:id/complete
|
* @route POST /api/viaggi/rides/:id/complete
|
||||||
* @access Private
|
* @access Private
|
||||||
*/
|
*/
|
||||||
const completeRide = async (req, res) => {
|
const completeRide = async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const { id } = req.params;
|
const { id } = req.params;
|
||||||
const userId = req.user._id;
|
const userId = req.user._id;
|
||||||
const { idapp } = req.body;
|
const idapp = req.user.idapp;
|
||||||
|
|
||||||
const ride = await Ride.findOne({ _id: id, idapp });
|
const ride = await Ride.findOne({ _id: id, idapp });
|
||||||
|
|
||||||
@@ -677,7 +677,7 @@ const completeRide = async (req, res) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ride.userId.toString() !== userId) {
|
if (!ride.userId.equals(userId)) {
|
||||||
return res.status(403).json({
|
return res.status(403).json({
|
||||||
success: false,
|
success: false,
|
||||||
message: 'Solo il conducente può completare il viaggio'
|
message: 'Solo il conducente può completare il viaggio'
|
||||||
@@ -723,12 +723,12 @@ const completeRide = async (req, res) => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @desc Ottieni statistiche viaggi per homepage widget
|
* @desc Ottieni statistiche viaggi per homepage widget
|
||||||
* @route GET /api/trasporti/rides/stats
|
* @route GET /api/viaggi/rides/stats
|
||||||
* @access Private
|
* @access Private
|
||||||
*/
|
*/
|
||||||
const getRidesStats = async (req, res) => {
|
const getRidesStats = async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const { idapp } = req.query;
|
const idapp = req.query.idapp;
|
||||||
const userId = req.user._id;
|
const userId = req.user._id;
|
||||||
|
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
@@ -800,7 +800,7 @@ const getRidesStats = async (req, res) => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get aggregated data for dashboard widgets
|
* Get aggregated data for dashboard widgets
|
||||||
* GET /api/trasporti/widget/data
|
* GET /api/viaggi/widget/data
|
||||||
*/
|
*/
|
||||||
const getWidgetData = async (req, res) => {
|
const getWidgetData = async (req, res) => {
|
||||||
try {
|
try {
|
||||||
@@ -981,7 +981,7 @@ const getWidgetData = async (req, res) => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get comprehensive statistics summary for user
|
* Get comprehensive statistics summary for user
|
||||||
* GET /api/trasporti/stats/summary
|
* GET /api/viaggi/stats/summary
|
||||||
*/
|
*/
|
||||||
const getStatsSummary = async (req, res) => {
|
const getStatsSummary = async (req, res) => {
|
||||||
try {
|
try {
|
||||||
@@ -1420,7 +1420,7 @@ const getStatsSummary = async (req, res) => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get city suggestions for autocomplete
|
* Get city suggestions for autocomplete
|
||||||
* GET /api/trasporti/cities/suggestions?q=query
|
* GET /api/viaggi/cities/suggestions?q=query
|
||||||
*/
|
*/
|
||||||
const getCitySuggestions = async (req, res) => {
|
const getCitySuggestions = async (req, res) => {
|
||||||
try {
|
try {
|
||||||
@@ -1679,7 +1679,7 @@ const getCitySuggestions = async (req, res) => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get recent cities from user's trip history
|
* Get recent cities from user's trip history
|
||||||
* GET /api/trasporti/cities/recent
|
* GET /api/viaggi/cities/recent
|
||||||
*/
|
*/
|
||||||
const getRecentCities = async (req, res) => {
|
const getRecentCities = async (req, res) => {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ const Message = require('../models/Message');
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @desc Crea una richiesta di passaggio
|
* @desc Crea una richiesta di passaggio
|
||||||
* @route POST /api/trasporti/requests
|
* @route POST /api/viaggi/requests
|
||||||
* @access Private
|
* @access Private
|
||||||
*/
|
*/
|
||||||
const createRequest = async (req, res) => {
|
const createRequest = async (req, res) => {
|
||||||
@@ -154,7 +154,7 @@ const createRequest = async (req, res) => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @desc Ottieni le richieste per un viaggio (per il conducente)
|
* @desc Ottieni le richieste per un viaggio (per il conducente)
|
||||||
* @route GET /api/trasporti/requests/ride/:rideId
|
* @route GET /api/viaggi/requests/ride/:rideId
|
||||||
* @access Private
|
* @access Private
|
||||||
*/
|
*/
|
||||||
const getRequestsForRide = async (req, res) => {
|
const getRequestsForRide = async (req, res) => {
|
||||||
@@ -207,7 +207,7 @@ const getRequestsForRide = async (req, res) => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @desc Ottieni le mie richieste (come passeggero)
|
* @desc Ottieni le mie richieste (come passeggero)
|
||||||
* @route GET /api/trasporti/requests/my
|
* @route GET /api/viaggi/requests/my
|
||||||
* @access Private
|
* @access Private
|
||||||
*/
|
*/
|
||||||
const getMyRequests = async (req, res) => {
|
const getMyRequests = async (req, res) => {
|
||||||
@@ -266,13 +266,13 @@ const getMyRequests = async (req, res) => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @desc Ottieni richieste pendenti (per il conducente)
|
* @desc Ottieni richieste pendenti (per il conducente)
|
||||||
* @route GET /api/trasporti/requests/pending
|
* @route GET /api/viaggi/requests/pending
|
||||||
* @access Private
|
* @access Private
|
||||||
*/
|
*/
|
||||||
const getPendingRequests = async (req, res) => {
|
const getPendingRequests = async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const userId = req.user._id;
|
const userId = req.user._id;
|
||||||
const { idapp } = req.query;
|
const idapp = req.user.idapp;
|
||||||
|
|
||||||
if (!idapp) {
|
if (!idapp) {
|
||||||
return res.status(400).json({
|
return res.status(400).json({
|
||||||
@@ -307,7 +307,7 @@ const getPendingRequests = async (req, res) => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @desc Accetta una richiesta di passaggio
|
* @desc Accetta una richiesta di passaggio
|
||||||
* @route PUT /api/trasporti/requests/:id/accept
|
* @route PUT /api/viaggi/requests/:id/accept
|
||||||
* @access Private (solo conducente)
|
* @access Private (solo conducente)
|
||||||
*/
|
*/
|
||||||
const acceptRequest = async (req, res) => {
|
const acceptRequest = async (req, res) => {
|
||||||
@@ -403,7 +403,7 @@ const acceptRequest = async (req, res) => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @desc Rifiuta una richiesta di passaggio
|
* @desc Rifiuta una richiesta di passaggio
|
||||||
* @route PUT /api/trasporti/requests/:id/reject
|
* @route PUT /api/viaggi/requests/:id/reject
|
||||||
* @access Private (solo conducente)
|
* @access Private (solo conducente)
|
||||||
*/
|
*/
|
||||||
const rejectRequest = async (req, res) => {
|
const rejectRequest = async (req, res) => {
|
||||||
@@ -477,7 +477,7 @@ const rejectRequest = async (req, res) => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @desc Cancella una richiesta (dal passeggero)
|
* @desc Cancella una richiesta (dal passeggero)
|
||||||
* @route PUT /api/trasporti/requests/:id/cancel
|
* @route PUT /api/viaggi/requests/:id/cancel
|
||||||
* @access Private
|
* @access Private
|
||||||
*/
|
*/
|
||||||
const cancelRequest = async (req, res) => {
|
const cancelRequest = async (req, res) => {
|
||||||
@@ -550,7 +550,7 @@ const cancelRequest = async (req, res) => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @desc Ottieni una singola richiesta
|
* @desc Ottieni una singola richiesta
|
||||||
* @route GET /api/trasporti/requests/:id
|
* @route GET /api/viaggi/requests/:id
|
||||||
* @access Private
|
* @access Private
|
||||||
*/
|
*/
|
||||||
const getRequestById = async (req, res) => {
|
const getRequestById = async (req, res) => {
|
||||||
@@ -604,7 +604,7 @@ const getRequestById = async (req, res) => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @desc Ottieni richieste ricevute (io come conducente)
|
* @desc Ottieni richieste ricevute (io come conducente)
|
||||||
* @route GET /api/trasporti/requests/received
|
* @route GET /api/viaggi/requests/received
|
||||||
* @access Private
|
* @access Private
|
||||||
*/
|
*/
|
||||||
const getReceivedRequests = async (req, res) => {
|
const getReceivedRequests = async (req, res) => {
|
||||||
@@ -684,7 +684,7 @@ const getReceivedRequests = async (req, res) => {
|
|||||||
};
|
};
|
||||||
/**
|
/**
|
||||||
* @desc Ottieni richieste inviate (io come passeggero)
|
* @desc Ottieni richieste inviate (io come passeggero)
|
||||||
* @route GET /api/trasporti/requests/sent
|
* @route GET /api/viaggi/requests/sent
|
||||||
* @access Private
|
* @access Private
|
||||||
*/
|
*/
|
||||||
const getSentRequests = async (req, res) => {
|
const getSentRequests = async (req, res) => {
|
||||||
|
|||||||
@@ -79,6 +79,16 @@ const ChatSchema = new Schema(
|
|||||||
ref: 'User',
|
ref: 'User',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
deletedBy: [
|
||||||
|
{
|
||||||
|
type: Schema.Types.ObjectId,
|
||||||
|
ref: 'User',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
clearedBefore: {
|
||||||
|
type: Map,
|
||||||
|
of: Date,
|
||||||
|
},
|
||||||
metadata: {
|
metadata: {
|
||||||
type: Schema.Types.Mixed,
|
type: Schema.Types.Mixed,
|
||||||
},
|
},
|
||||||
@@ -117,9 +127,7 @@ ChatSchema.methods.incrementUnread = function (excludeUserId) {
|
|||||||
|
|
||||||
this.participants.forEach((participantId) => {
|
this.participants.forEach((participantId) => {
|
||||||
// Gestisci sia ObjectId che oggetti popolati
|
// Gestisci sia ObjectId che oggetti popolati
|
||||||
const id = participantId._id
|
const id = participantId._id ? participantId._id.toString() : participantId.toString();
|
||||||
? participantId._id.toString()
|
|
||||||
: participantId.toString();
|
|
||||||
|
|
||||||
if (id !== excludeIdStr) {
|
if (id !== excludeIdStr) {
|
||||||
const current = this.unreadCount.get(id) || 0;
|
const current = this.unreadCount.get(id) || 0;
|
||||||
@@ -171,7 +179,6 @@ ChatSchema.methods.isBlockedFor = function (userId) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// Metodo statico per trovare o creare una chat diretta
|
// Metodo statico per trovare o creare una chat diretta
|
||||||
ChatSchema.statics.findOrCreateDirect = async function (idapp, userId1, userId2, rideId = null) {
|
ChatSchema.statics.findOrCreateDirect = async function (idapp, userId1, userId2, rideId = null) {
|
||||||
// Cerca chat esistente tra i due utenti
|
// Cerca chat esistente tra i due utenti
|
||||||
|
|||||||
@@ -55,6 +55,10 @@ const LocationSchema = new Schema(
|
|||||||
|
|
||||||
// Schema per i waypoint (tappe intermedie)
|
// Schema per i waypoint (tappe intermedie)
|
||||||
const WaypointSchema = new Schema({
|
const WaypointSchema = new Schema({
|
||||||
|
/*_id: {
|
||||||
|
type: String,
|
||||||
|
required: false
|
||||||
|
},*/
|
||||||
location: {
|
location: {
|
||||||
type: LocationSchema,
|
type: LocationSchema,
|
||||||
required: true,
|
required: true,
|
||||||
@@ -70,7 +74,7 @@ const WaypointSchema = new Schema({
|
|||||||
type: Number, // minuti di sosta
|
type: Number, // minuti di sosta
|
||||||
default: 0,
|
default: 0,
|
||||||
},
|
},
|
||||||
});
|
}, { _id: false }); // 👈 AGGIUNGI QUESTO
|
||||||
|
|
||||||
// Schema per la ricorrenza del viaggio
|
// Schema per la ricorrenza del viaggio
|
||||||
const RecurrenceSchema = new Schema(
|
const RecurrenceSchema = new Schema(
|
||||||
|
|||||||
@@ -606,7 +606,6 @@ const UserSchema = new mongoose.Schema(
|
|||||||
{
|
{
|
||||||
type: {
|
type: {
|
||||||
type: String,
|
type: String,
|
||||||
enum: ['auto', 'moto', 'furgone', 'minibus', 'altro'],
|
|
||||||
default: 'auto',
|
default: 'auto',
|
||||||
},
|
},
|
||||||
brand: {
|
brand: {
|
||||||
@@ -640,7 +639,6 @@ const UserSchema = new mongoose.Schema(
|
|||||||
features: [
|
features: [
|
||||||
{
|
{
|
||||||
type: String,
|
type: String,
|
||||||
enum: ['aria_condizionata', 'wifi', 'presa_usb', 'bluetooth', 'bagagliaio_grande', 'seggiolino_bimbi'],
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
photos: [
|
photos: [
|
||||||
@@ -704,7 +702,6 @@ const UserSchema = new mongoose.Schema(
|
|||||||
},
|
},
|
||||||
responseTime: {
|
responseTime: {
|
||||||
type: String,
|
type: String,
|
||||||
enum: ['within_hour', 'within_day', 'within_days'],
|
|
||||||
default: 'within_day',
|
default: 'within_day',
|
||||||
},
|
},
|
||||||
totalKmShared: {
|
totalKmShared: {
|
||||||
@@ -743,22 +740,18 @@ const UserSchema = new mongoose.Schema(
|
|||||||
// Preferenze di viaggio
|
// Preferenze di viaggio
|
||||||
smoking: {
|
smoking: {
|
||||||
type: String,
|
type: String,
|
||||||
enum: ['yes', 'no', 'outside_only'],
|
|
||||||
default: 'no',
|
default: 'no',
|
||||||
},
|
},
|
||||||
pets: {
|
pets: {
|
||||||
type: String,
|
type: String,
|
||||||
enum: ['no', 'small', 'medium', 'large', 'all'],
|
|
||||||
default: 'small',
|
default: 'small',
|
||||||
},
|
},
|
||||||
music: {
|
music: {
|
||||||
type: String,
|
type: String,
|
||||||
enum: ['no_music', 'quiet', 'moderate', 'loud', 'passenger_choice'],
|
|
||||||
default: 'moderate',
|
default: 'moderate',
|
||||||
},
|
},
|
||||||
conversation: {
|
conversation: {
|
||||||
type: String,
|
type: String,
|
||||||
enum: ['quiet', 'moderate', 'chatty'],
|
|
||||||
default: 'moderate',
|
default: 'moderate',
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -835,7 +828,6 @@ const UserSchema = new mongoose.Schema(
|
|||||||
},
|
},
|
||||||
type: {
|
type: {
|
||||||
type: String,
|
type: String,
|
||||||
enum: ['home', 'work', 'other'],
|
|
||||||
default: 'other',
|
default: 'other',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ const PageView = require('../models/PageView');
|
|||||||
|
|
||||||
const fal = require('@fal-ai/client');
|
const fal = require('@fal-ai/client');
|
||||||
|
|
||||||
|
|
||||||
const imageGenerator = require('../services/imageGenerator'); // Assicurati che il percorso sia corretto
|
const imageGenerator = require('../services/imageGenerator'); // Assicurati che il percorso sia corretto
|
||||||
|
|
||||||
const posterEditor = require('../services/PosterEditor'); // <--- Importa la nuova classe
|
const posterEditor = require('../services/PosterEditor'); // <--- Importa la nuova classe
|
||||||
@@ -33,9 +32,8 @@ const { MyElem } = require('../models/myelem');
|
|||||||
|
|
||||||
const axios = require('axios');
|
const axios = require('axios');
|
||||||
|
|
||||||
const trasportiRoutes = require('../routes/trasportiRoutes');
|
const viaggiRoutes = require('../routes/viaggiRoutes');
|
||||||
router.use('/trasporti', trasportiRoutes);
|
router.use('/viaggi', viaggiRoutes);
|
||||||
|
|
||||||
|
|
||||||
// Importa le routes video
|
// Importa le routes video
|
||||||
const videoRoutes = require('../routes/videoRoutes');
|
const videoRoutes = require('../routes/videoRoutes');
|
||||||
@@ -43,7 +41,6 @@ const videoRoutes = require('../routes/videoRoutes');
|
|||||||
// Monta le routes video
|
// Monta le routes video
|
||||||
router.use('/video', videoRoutes);
|
router.use('/video', videoRoutes);
|
||||||
|
|
||||||
|
|
||||||
router.use('/templates', authenticate, templatesRouter);
|
router.use('/templates', authenticate, templatesRouter);
|
||||||
router.use('/posters', authenticate, postersRouter);
|
router.use('/posters', authenticate, postersRouter);
|
||||||
router.use('/assets', authenticate, assetsRouter);
|
router.use('/assets', authenticate, assetsRouter);
|
||||||
@@ -524,8 +521,15 @@ router.post('/chatbot', authenticate, async (req, res) => {
|
|||||||
|
|
||||||
router.post('/generateposter', async (req, res) => {
|
router.post('/generateposter', async (req, res) => {
|
||||||
const {
|
const {
|
||||||
titolo, data, ora, luogo, descrizione, contatti, fotoDescrizione, stile,
|
titolo,
|
||||||
provider = 'hf' // Default a HF (Gratis)
|
data,
|
||||||
|
ora,
|
||||||
|
luogo,
|
||||||
|
descrizione,
|
||||||
|
contatti,
|
||||||
|
fotoDescrizione,
|
||||||
|
stile,
|
||||||
|
provider = 'hf', // Default a HF (Gratis)
|
||||||
} = req.body;
|
} = req.body;
|
||||||
|
|
||||||
// 1. Prompt per l'AI: Chiediamo SOLO lo sfondo, VIETIAMO il testo.
|
// 1. Prompt per l'AI: Chiediamo SOLO lo sfondo, VIETIAMO il testo.
|
||||||
@@ -547,21 +551,50 @@ router.post('/generateposter', async (req, res) => {
|
|||||||
data,
|
data,
|
||||||
ora,
|
ora,
|
||||||
luogo,
|
luogo,
|
||||||
contatti
|
contatti,
|
||||||
});
|
});
|
||||||
|
|
||||||
res.json({
|
res.json({
|
||||||
success: true,
|
success: true,
|
||||||
imageUrl: finalPosterBase64, // Restituisce l'immagine completa in base64
|
imageUrl: finalPosterBase64, // Restituisce l'immagine completa in base64
|
||||||
step: 'AI + Canvas Composition'
|
step: 'AI + Canvas Composition',
|
||||||
});
|
});
|
||||||
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Errore:', err.message);
|
console.error('Errore:', err.message);
|
||||||
res.status(500).json({ error: err.message });
|
res.status(500).json({ error: err.message });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
router.get('/users/search', authenticate, async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { User } = require('../models/user');
|
||||||
|
|
||||||
|
const { q, idapp } = req.query;
|
||||||
|
|
||||||
|
if (!q || q.length < 2) {
|
||||||
|
return res.status(400).json({ success: false, message: 'Query too short' });
|
||||||
|
}
|
||||||
|
|
||||||
|
const query = q.trim();
|
||||||
|
|
||||||
|
const users = await User.find({
|
||||||
|
idapp,
|
||||||
|
$or: [
|
||||||
|
{ name: { $regex: query, $options: 'i' } },
|
||||||
|
{ surname: { $regex: query, $options: 'i' } },
|
||||||
|
{ username: { $regex: query, $options: 'i' } },
|
||||||
|
],
|
||||||
|
_id: { $ne: req.user?._id }, // escludi l'utente corrente se autenticato
|
||||||
|
})
|
||||||
|
.select('_id name surname username profile') // solo campi necessari
|
||||||
|
.limit(10); // evita overload
|
||||||
|
|
||||||
|
res.json({ success: true, data: users });
|
||||||
|
} catch (error) {
|
||||||
|
console.error('User search error:', error);
|
||||||
|
res.status(500).json({ success: false, message: 'Server error' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = router;
|
||||||
|
|||||||
@@ -317,7 +317,11 @@ router.post('/', async (req, res) => {
|
|||||||
await telegrambot.askConfirmationUser(myuser.idapp, shared_consts.CallFunz.REGISTRATION, myuser);
|
await telegrambot.askConfirmationUser(myuser.idapp, shared_consts.CallFunz.REGISTRATION, myuser);
|
||||||
|
|
||||||
const { token, refreshToken, browser_random } = await myuser.generateAuthToken(req, browser_random);
|
const { token, refreshToken, browser_random } = await myuser.generateAuthToken(req, browser_random);
|
||||||
res.header('x-auth', token).header('x-refrtok', refreshToken).header('x-browser-random', browser_random).send(myuser);
|
res
|
||||||
|
.header('x-auth', token)
|
||||||
|
.header('x-refrtok', refreshToken)
|
||||||
|
.header('x-browser-random', browser_random)
|
||||||
|
.send(myuser);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -368,7 +372,11 @@ router.post('/', async (req, res) => {
|
|||||||
// if (!tools.testing()) {
|
// if (!tools.testing()) {
|
||||||
await sendemail.sendEmail_Registration(user.lang, user.email, user, user.idapp, user.linkreg);
|
await sendemail.sendEmail_Registration(user.lang, user.email, user, user.idapp, user.linkreg);
|
||||||
// }
|
// }
|
||||||
res.header('x-auth', ris.token).header('x-refrtok', ris.refreshToken).header('x-browser-random', ris.browser_random).send(user);
|
res
|
||||||
|
.header('x-auth', ris.token)
|
||||||
|
.header('x-refrtok', ris.refreshToken)
|
||||||
|
.header('x-browser-random', ris.browser_random)
|
||||||
|
.send(user);
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
@@ -411,7 +419,9 @@ router.patch('/:id', authenticate, (req, res) => {
|
|||||||
|
|
||||||
if (!User.isAdmin(req.user.perm)) {
|
if (!User.isAdmin(req.user.perm)) {
|
||||||
// If without permissions, exit
|
// If without permissions, exit
|
||||||
return res.status(server_constants.RIS_CODE_ERR_UNAUTHORIZED).send({ code: server_constants.RIS_CODE_ERR_UNAUTHORIZED, msg: '' });
|
return res
|
||||||
|
.status(server_constants.RIS_CODE_ERR_UNAUTHORIZED)
|
||||||
|
.send({ code: server_constants.RIS_CODE_ERR_UNAUTHORIZED, msg: '' });
|
||||||
}
|
}
|
||||||
|
|
||||||
User.findByIdAndUpdate(id, { $set: body })
|
User.findByIdAndUpdate(id, { $set: body })
|
||||||
@@ -591,9 +601,14 @@ router.post('/panel', authenticate, async (req, res) => {
|
|||||||
idapp = req.body.idapp;
|
idapp = req.body.idapp;
|
||||||
locale = req.body.locale;
|
locale = req.body.locale;
|
||||||
|
|
||||||
if (!req.user || !User.isAdmin(req.user.perm) && !User.isManager(req.user.perm) && !User.isFacilitatore(req.user.perm)) {
|
if (
|
||||||
|
!req.user ||
|
||||||
|
(!User.isAdmin(req.user.perm) && !User.isManager(req.user.perm) && !User.isFacilitatore(req.user.perm))
|
||||||
|
) {
|
||||||
// If without permissions, exit
|
// If without permissions, exit
|
||||||
return res.status(server_constants.RIS_CODE_ERR_UNAUTHORIZED).send({ code: server_constants.RIS_CODE_ERR_UNAUTHORIZED, msg: '' });
|
return res
|
||||||
|
.status(server_constants.RIS_CODE_ERR_UNAUTHORIZED)
|
||||||
|
.send({ code: server_constants.RIS_CODE_ERR_UNAUTHORIZED, msg: '' });
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -953,7 +968,9 @@ router.post('/friends/cmd', authenticate, async (req, res) => {
|
|||||||
usernameDest !== usernameLogged &&
|
usernameDest !== usernameLogged &&
|
||||||
(cmd === shared_consts.FRIENDSCMD.SETFRIEND || cmd === shared_consts.FRIENDSCMD.SETHANDSHAKE)
|
(cmd === shared_consts.FRIENDSCMD.SETFRIEND || cmd === shared_consts.FRIENDSCMD.SETHANDSHAKE)
|
||||||
) {
|
) {
|
||||||
return res.status(server_constants.RIS_CODE_ERR_UNAUTHORIZED).send({ code: server_constants.RIS_CODE_ERR_UNAUTHORIZED, msg: '' });
|
return res
|
||||||
|
.status(server_constants.RIS_CODE_ERR_UNAUTHORIZED)
|
||||||
|
.send({ code: server_constants.RIS_CODE_ERR_UNAUTHORIZED, msg: '' });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1119,7 +1136,10 @@ async function eseguiDbOpUser(idapp, mydata, locale, req, res) {
|
|||||||
} else if (mydata.dbop === 'noNameSurname') {
|
} else if (mydata.dbop === 'noNameSurname') {
|
||||||
await User.findOneAndUpdate({ _id: mydata._id }, { $set: { 'profile.noNameSurname': mydata.value } });
|
await User.findOneAndUpdate({ _id: mydata._id }, { $set: { 'profile.noNameSurname': mydata.value } });
|
||||||
} else if (mydata.dbop === 'telegram_verification_skipped') {
|
} else if (mydata.dbop === 'telegram_verification_skipped') {
|
||||||
await User.findOneAndUpdate({ _id: mydata._id }, { $set: { 'profile.telegram_verification_skipped': mydata.value } });
|
await User.findOneAndUpdate(
|
||||||
|
{ _id: mydata._id },
|
||||||
|
{ $set: { 'profile.telegram_verification_skipped': mydata.value } }
|
||||||
|
);
|
||||||
} else if (mydata.dbop === 'pwdLikeAdmin') {
|
} else if (mydata.dbop === 'pwdLikeAdmin') {
|
||||||
await User.setPwdComeQuellaDellAdmin(mydata);
|
await User.setPwdComeQuellaDellAdmin(mydata);
|
||||||
} else if (mydata.dbop === 'ripristinaPwdPrec') {
|
} else if (mydata.dbop === 'ripristinaPwdPrec') {
|
||||||
@@ -1129,7 +1149,7 @@ async function eseguiDbOpUser(idapp, mydata, locale, req, res) {
|
|||||||
} else if (mydata.dbop === 'noComune') {
|
} else if (mydata.dbop === 'noComune') {
|
||||||
await User.findOneAndUpdate({ _id: mydata._id }, { $set: { 'profile.noComune': mydata.value } });
|
await User.findOneAndUpdate({ _id: mydata._id }, { $set: { 'profile.noComune': mydata.value } });
|
||||||
} else if (mydata.dbop === 'verifiedemail') {
|
} else if (mydata.dbop === 'verifiedemail') {
|
||||||
await User.findOneAndUpdate({ _id: mydata._id }, { $set: { 'verified_email': mydata.value } });
|
await User.findOneAndUpdate({ _id: mydata._id }, { $set: { verified_email: mydata.value } });
|
||||||
} else if (mydata.dbop === 'resendVerificationEmail') {
|
} else if (mydata.dbop === 'resendVerificationEmail') {
|
||||||
// Invia la email di Verifica email
|
// Invia la email di Verifica email
|
||||||
const ris = await sendemail.sendEmail_ReVerifyingEmail(mydata, idapp);
|
const ris = await sendemail.sendEmail_ReVerifyingEmail(mydata, idapp);
|
||||||
|
|||||||
37
src/routes/geoRoutes.js
Normal file
37
src/routes/geoRoutes.js
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
const express = require('express');
|
||||||
|
const router = express.Router();
|
||||||
|
const {
|
||||||
|
autocomplete,
|
||||||
|
geocode,
|
||||||
|
reverseGeocode,
|
||||||
|
getRoute,
|
||||||
|
getMatrix,
|
||||||
|
suggestWaypoints,
|
||||||
|
searchItalianCities,
|
||||||
|
getDistance,
|
||||||
|
getIsochrone
|
||||||
|
} = require('../controllers/geocodingController');
|
||||||
|
|
||||||
|
// Rate limiting opzionale
|
||||||
|
const rateLimit = require('express-rate-limit');
|
||||||
|
|
||||||
|
const geoLimiter = rateLimit({
|
||||||
|
windowMs: 60 * 1000, // 1 minuto
|
||||||
|
max: 60, // 60 richieste per minuto
|
||||||
|
message: { success: false, message: 'Troppe richieste, riprova tra poco' }
|
||||||
|
});
|
||||||
|
|
||||||
|
router.use(geoLimiter);
|
||||||
|
|
||||||
|
// Routes
|
||||||
|
router.get('/autocomplete', autocomplete);
|
||||||
|
router.get('/geocode', geocode);
|
||||||
|
router.get('/reverse', reverseGeocode);
|
||||||
|
router.get('/route', getRoute);
|
||||||
|
router.post('/matrix', getMatrix);
|
||||||
|
router.get('/suggest-waypoints', suggestWaypoints);
|
||||||
|
router.get('/cities/it', searchItalianCities);
|
||||||
|
router.get('/distance', getDistance);
|
||||||
|
router.get('/isochrone', getIsochrone);
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
@@ -14,7 +14,12 @@ const rideController = require('../controllers/rideController');
|
|||||||
const rideRequestController = require('../controllers/rideRequestController');
|
const rideRequestController = require('../controllers/rideRequestController');
|
||||||
const chatController = require('../controllers/chatController');
|
const chatController = require('../controllers/chatController');
|
||||||
const feedbackController = require('../controllers/feedbackController');
|
const feedbackController = require('../controllers/feedbackController');
|
||||||
const geocodingController = require('../controllers/geocodingController');
|
// const geocodingController = require('../controllers/geocodingController');
|
||||||
|
|
||||||
|
const geoRoutes = require('./geoRoutes'); // 👈 Importa geoRoutes
|
||||||
|
|
||||||
|
router.use('/geo', geoRoutes); // 👈 Monta come sub-router
|
||||||
|
|
||||||
|
|
||||||
// Middleware di autenticazione (usa il tuo esistente)
|
// Middleware di autenticazione (usa il tuo esistente)
|
||||||
const { authenticate } = require('../middleware/authenticate');
|
const { authenticate } = require('../middleware/authenticate');
|
||||||
@@ -24,42 +29,42 @@ const { authenticate } = require('../middleware/authenticate');
|
|||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @route POST /api/trasporti/rides
|
* @route POST /api/viaggi/rides
|
||||||
* @desc Crea nuovo viaggio (offerta o richiesta)
|
* @desc Crea nuovo viaggio (offerta o richiesta)
|
||||||
* @access Private
|
* @access Private
|
||||||
*/
|
*/
|
||||||
router.post('/rides', authenticate, rideController.createRide);
|
router.post('/rides', authenticate, rideController.createRide);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @route GET /api/trasporti/rides
|
* @route GET /api/viaggi/rides
|
||||||
* @desc Ottieni lista viaggi con filtri
|
* @desc Ottieni lista viaggi con filtri
|
||||||
* @access Public
|
* @access Public
|
||||||
*/
|
*/
|
||||||
router.get('/rides', rideController.getRides);
|
router.get('/rides', rideController.getRides);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @route GET /api/trasporti/rides/search
|
* @route GET /api/viaggi/rides/search
|
||||||
* @desc Ricerca viaggi avanzata
|
* @desc Ricerca viaggi avanzata
|
||||||
* @access Public
|
* @access Public
|
||||||
*/
|
*/
|
||||||
router.get('/rides/search', rideController.searchRides);
|
router.get('/rides/search', rideController.searchRides);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @route GET /api/trasporti/rides/stats
|
* @route GET /api/viaggi/rides/stats
|
||||||
* @desc Statistiche per widget homepage
|
* @desc Statistiche per widget homepage
|
||||||
* @access Private
|
* @access Private
|
||||||
*/
|
*/
|
||||||
router.get('/rides/stats', authenticate, rideController.getRidesStats);
|
router.get('/rides/stats', authenticate, rideController.getRidesStats);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @route GET /api/trasporti/rides/my
|
* @route GET /api/viaggi/rides/my
|
||||||
* @desc I miei viaggi (come driver e passenger)
|
* @desc I miei viaggi (come driver e passenger)
|
||||||
* @access Private
|
* @access Private
|
||||||
*/
|
*/
|
||||||
router.get('/rides/my', authenticate, rideController.getMyRides);
|
router.get('/rides/my', authenticate, rideController.getMyRides);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @route GET /api/trasporti/rides/match
|
* @route GET /api/viaggi/rides/match
|
||||||
* @desc Match automatico offerta/richiesta
|
* @desc Match automatico offerta/richiesta
|
||||||
* @access Private
|
* @access Private
|
||||||
* @note ⚠️ IMPORTANTE: Questa route DEVE stare PRIMA di /rides/:id
|
* @note ⚠️ IMPORTANTE: Questa route DEVE stare PRIMA di /rides/:id
|
||||||
@@ -67,28 +72,28 @@ router.get('/rides/my', authenticate, rideController.getMyRides);
|
|||||||
//router.get('/rides/match', authenticate, rideController.findMatches);
|
//router.get('/rides/match', authenticate, rideController.findMatches);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @route GET /api/trasporti/rides/:id
|
* @route GET /api/viaggi/rides/:id
|
||||||
* @desc Dettaglio singolo viaggio
|
* @desc Dettaglio singolo viaggio
|
||||||
* @access Public
|
* @access Public
|
||||||
*/
|
*/
|
||||||
router.get('/rides/:id', rideController.getRideById);
|
router.get('/rides/:id', rideController.getRideById);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @route PUT /api/trasporti/rides/:id
|
* @route PUT /api/viaggi/rides/:id
|
||||||
* @desc Aggiorna viaggio
|
* @desc Aggiorna viaggio
|
||||||
* @access Private (solo owner)
|
* @access Private (solo owner)
|
||||||
*/
|
*/
|
||||||
router.put('/rides/:id', authenticate, rideController.updateRide);
|
router.put('/rides/:id', authenticate, rideController.updateRide);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @route DELETE /api/trasporti/rides/:id
|
* @route DELETE /api/viaggi/rides/:id
|
||||||
* @desc Cancella viaggio
|
* @desc Cancella viaggio
|
||||||
* @access Private (solo owner)
|
* @access Private (solo owner)
|
||||||
*/
|
*/
|
||||||
router.delete('/rides/:id', authenticate, rideController.deleteRide);
|
router.delete('/rides/:id', authenticate, rideController.deleteRide);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @route POST /api/trasporti/rides/:id/complete
|
* @route POST /api/viaggi/rides/:id/complete
|
||||||
* @desc Completa un viaggio
|
* @desc Completa un viaggio
|
||||||
* @access Private (solo driver)
|
* @access Private (solo driver)
|
||||||
*/
|
*/
|
||||||
@@ -99,28 +104,28 @@ router.post('/rides/:id/complete', authenticate, rideController.completeRide);
|
|||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @route GET /api/trasporti/widget/data
|
* @route GET /api/viaggi/widget/data
|
||||||
* @desc Dati completi per widget homepage
|
* @desc Dati completi per widget homepage
|
||||||
* @access Private
|
* @access Private
|
||||||
*/
|
*/
|
||||||
router.get('/widget/data', authenticate, rideController.getWidgetData);
|
router.get('/widget/data', authenticate, rideController.getWidgetData);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @route GET /api/trasporti/stats/summary
|
* @route GET /api/viaggi/stats/summary
|
||||||
* @desc Stats rapide per header widget (offerte, richieste, match)
|
* @desc Stats rapide per header widget (offerte, richieste, match)
|
||||||
* @access Public
|
* @access Public
|
||||||
*/
|
*/
|
||||||
router.get('/stats/summary', authenticate, rideController.getStatsSummary);
|
router.get('/stats/summary', authenticate, rideController.getStatsSummary);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @route GET /api/trasporti/cities/suggestions
|
* @route GET /api/viaggi/cities/suggestions
|
||||||
* @desc Suggerimenti città per autocomplete (basato su viaggi esistenti)
|
* @desc Suggerimenti città per autocomplete (basato su viaggi esistenti)
|
||||||
* @access Public
|
* @access Public
|
||||||
*/
|
*/
|
||||||
router.get('/cities/suggestions', rideController.getCitySuggestions);
|
router.get('/cities/suggestions', rideController.getCitySuggestions);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @route GET /api/trasporti/cities/recents
|
* @route GET /api/viaggi/cities/recents
|
||||||
* @desc città recenti per autocomplete
|
* @desc città recenti per autocomplete
|
||||||
* @access Public
|
* @access Public
|
||||||
*/
|
*/
|
||||||
@@ -131,56 +136,56 @@ router.get('/cities/recent', authenticate, rideController.getRecentCities);
|
|||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @route POST /api/trasporti/requests
|
* @route POST /api/viaggi/requests
|
||||||
* @desc Crea richiesta passaggio per un viaggio
|
* @desc Crea richiesta passaggio per un viaggio
|
||||||
* @access Private
|
* @access Private
|
||||||
*/
|
*/
|
||||||
router.post('/requests', authenticate, rideRequestController.createRequest);
|
router.post('/requests', authenticate, rideRequestController.createRequest);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @route GET /api/trasporti/requests/received
|
* @route GET /api/viaggi/requests/received
|
||||||
* @desc Richieste ricevute (sono conducente)
|
* @desc Richieste ricevute (sono conducente)
|
||||||
* @access Private
|
* @access Private
|
||||||
*/
|
*/
|
||||||
router.get('/requests/received', authenticate, rideRequestController.getReceivedRequests);
|
router.get('/requests/received', authenticate, rideRequestController.getReceivedRequests);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @route GET /api/trasporti/requests/sent
|
* @route GET /api/viaggi/requests/sent
|
||||||
* @desc Richieste inviate (sono passeggero)
|
* @desc Richieste inviate (sono passeggero)
|
||||||
* @access Private
|
* @access Private
|
||||||
*/
|
*/
|
||||||
router.get('/requests/sent', authenticate, rideRequestController.getSentRequests);
|
router.get('/requests/sent', authenticate, rideRequestController.getSentRequests);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @route GET /api/trasporti/requests/ride/:rideId
|
* @route GET /api/viaggi/requests/ride/:rideId
|
||||||
* @desc Richieste per un viaggio specifico
|
* @desc Richieste per un viaggio specifico
|
||||||
* @access Private (solo owner del viaggio)
|
* @access Private (solo owner del viaggio)
|
||||||
*/
|
*/
|
||||||
router.get('/requests/ride/:rideId', authenticate, rideRequestController.getRequestsForRide);
|
router.get('/requests/ride/:rideId', authenticate, rideRequestController.getRequestsForRide);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @route GET /api/trasporti/requests/:id
|
* @route GET /api/viaggi/requests/:id
|
||||||
* @desc Dettaglio singola richiesta
|
* @desc Dettaglio singola richiesta
|
||||||
* @access Private (driver o passenger)
|
* @access Private (driver o passenger)
|
||||||
*/
|
*/
|
||||||
router.get('/requests/:id', authenticate, rideRequestController.getRequestById);
|
router.get('/requests/:id', authenticate, rideRequestController.getRequestById);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @route POST /api/trasporti/requests/:id/accept
|
* @route POST /api/viaggi/requests/:id/accept
|
||||||
* @desc Accetta richiesta passaggio
|
* @desc Accetta richiesta passaggio
|
||||||
* @access Private (solo driver)
|
* @access Private (solo driver)
|
||||||
*/
|
*/
|
||||||
router.post('/requests/:id/accept', authenticate, rideRequestController.acceptRequest);
|
router.post('/requests/:id/accept', authenticate, rideRequestController.acceptRequest);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @route POST /api/trasporti/requests/:id/reject
|
* @route POST /api/viaggi/requests/:id/reject
|
||||||
* @desc Rifiuta richiesta passaggio
|
* @desc Rifiuta richiesta passaggio
|
||||||
* @access Private (solo driver)
|
* @access Private (solo driver)
|
||||||
*/
|
*/
|
||||||
router.post('/requests/:id/reject', authenticate, rideRequestController.rejectRequest);
|
router.post('/requests/:id/reject', authenticate, rideRequestController.rejectRequest);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @route POST /api/trasporti/requests/:id/cancel
|
* @route POST /api/viaggi/requests/:id/cancel
|
||||||
* @desc Cancella richiesta/prenotazione
|
* @desc Cancella richiesta/prenotazione
|
||||||
* @access Private (driver o passenger)
|
* @access Private (driver o passenger)
|
||||||
*/
|
*/
|
||||||
@@ -191,72 +196,72 @@ router.post('/requests/:id/cancel', authenticate, rideRequestController.cancelRe
|
|||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @route GET /api/trasporti/chats
|
* @route GET /api/viaggi/chats
|
||||||
* @desc Lista tutte le mie chat
|
* @desc Lista tutte le mie chat
|
||||||
* @access Private
|
* @access Private
|
||||||
*/
|
*/
|
||||||
router.get('/chats', authenticate, chatController.getMyChats);
|
router.get('/chats', authenticate, chatController.getUserChats);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @route GET /api/trasporti/chats/unread/count
|
* @route GET /api/viaggi/chats/unread/count
|
||||||
* @desc Conta messaggi non letti totali
|
* @desc Conta messaggi non letti totali
|
||||||
* @access Private
|
* @access Private
|
||||||
*/
|
*/
|
||||||
router.get('/chats/unread/count', authenticate, chatController.getUnreadCount);
|
router.get('/chats/unread/count', authenticate, chatController.getUnreadCount);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @route POST /api/trasporti/chats/direct
|
* @route POST /api/viaggi/chats/direct
|
||||||
* @desc Ottieni o crea chat diretta con utente
|
* @desc Ottieni o crea chat diretta con utente
|
||||||
* @access Private
|
* @access Private
|
||||||
*/
|
*/
|
||||||
router.post('/chats/direct', authenticate, chatController.getOrCreateDirectChat);
|
router.post('/chats/direct', authenticate, chatController.getOrCreateDirectChat);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @route GET /api/trasporti/chats/:id
|
* @route GET /api/viaggi/chats/:id
|
||||||
* @desc Dettaglio chat
|
* @desc Dettaglio chat
|
||||||
* @access Private (solo partecipanti)
|
* @access Private (solo partecipanti)
|
||||||
*/
|
*/
|
||||||
router.get('/chats/:id', authenticate, chatController.getChatById);
|
router.get('/chats/:chatId', authenticate, chatController.getChatById);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @route GET /api/trasporti/chats/:id/messages
|
* @route GET /api/viaggi/chats/:id/messages
|
||||||
* @desc Messaggi di una chat
|
* @desc Messaggi di una chat
|
||||||
* @access Private (solo partecipanti)
|
* @access Private (solo partecipanti)
|
||||||
*/
|
*/
|
||||||
router.get('/chats/:id/messages', authenticate, chatController.getChatMessages);
|
router.get('/chats/:chatId/messages', authenticate, chatController.getChatMessages);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @route POST /api/trasporti/chats/:id/messages
|
* @route POST /api/viaggi/chats/:id/messages
|
||||||
* @desc Invia messaggio
|
* @desc Invia messaggio
|
||||||
* @access Private (solo partecipanti)
|
* @access Private (solo partecipanti)
|
||||||
*/
|
*/
|
||||||
router.post('/chats/:id/messages', authenticate, chatController.sendMessage);
|
router.post('/chats/:chatId/messages', authenticate, chatController.sendMessage);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @route PUT /api/trasporti/chats/:id/read
|
* @route PUT /api/viaggi/chats/:id/read
|
||||||
* @desc Segna chat come letta
|
* @desc Segna chat come letta
|
||||||
* @access Private (solo partecipanti)
|
* @access Private (solo partecipanti)
|
||||||
* @fix Corretto: markAsRead → markChatAsRead
|
* @fix Corretto: markAsRead → markChatAsRead
|
||||||
*/
|
*/
|
||||||
router.put('/chats/:id/read', authenticate, chatController.markChatAsRead);
|
router.put('/chats/:chatId/read', authenticate, chatController.markChatAsRead);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @route PUT /api/trasporti/chats/:id/block
|
* @route PUT /api/viaggi/chats/:id/block
|
||||||
* @desc Blocca/sblocca chat
|
* @desc Blocca/sblocca chat
|
||||||
* @access Private (solo partecipanti)
|
* @access Private (solo partecipanti)
|
||||||
*/
|
*/
|
||||||
router.put('/chats/:id/block', authenticate, chatController.toggleBlockChat);
|
router.put('/chats/:chatId/block', authenticate, chatController.toggleBlockChat);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @route PUT /api/trasporti/chats/:id/mute
|
* @route PUT /api/viaggi/chats/:id/mute
|
||||||
* @desc Muta/smuta notifiche di una chat
|
* @desc Muta/smuta notifiche di una chat
|
||||||
* @access Private (solo partecipanti)
|
* @access Private (solo partecipanti)
|
||||||
* @fix Aggiunta route mancante
|
* @fix Aggiunta route mancante
|
||||||
*/
|
*/
|
||||||
router.put('/chats/:id/mute', authenticate, chatController.toggleMuteChat);
|
router.put('/chats/:chatId/mute', authenticate, chatController.toggleMuteChat);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @route DELETE /api/trasporti/chats/:chatId/messages/:messageId
|
* @route DELETE /api/viaggi/chats/:chatId/messages/:messageId
|
||||||
* @desc Elimina messaggio
|
* @desc Elimina messaggio
|
||||||
* @access Private (solo mittente)
|
* @access Private (solo mittente)
|
||||||
* @fix Corretto: /messages/:id → /chats/:chatId/messages/:messageId
|
* @fix Corretto: /messages/:id → /chats/:chatId/messages/:messageId
|
||||||
@@ -264,81 +269,81 @@ router.put('/chats/:id/mute', authenticate, chatController.toggleMuteChat);
|
|||||||
router.delete('/chats/:chatId/messages/:messageId', authenticate, chatController.deleteMessage);
|
router.delete('/chats/:chatId/messages/:messageId', authenticate, chatController.deleteMessage);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @route DELETE /api/trasporti/chats/:id
|
* @route DELETE /api/viaggi/chats/:id
|
||||||
* @desc Elimina chat (soft delete)
|
* @desc Elimina chat (soft delete)
|
||||||
* @access Private (solo partecipanti)
|
* @access Private (solo partecipanti)
|
||||||
*/
|
*/
|
||||||
router.delete('/chats/:id', authenticate, chatController.deleteChat);
|
router.delete('/chats/:chatId', authenticate, chatController.deleteChat);
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// ⭐ FEEDBACK - Recensioni
|
// ⭐ FEEDBACK - Recensioni
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @route POST /api/trasporti/feedback
|
* @route POST /api/viaggi/feedback
|
||||||
* @desc Crea feedback per un viaggio
|
* @desc Crea feedback per un viaggio
|
||||||
* @access Private
|
* @access Private
|
||||||
*/
|
*/
|
||||||
router.post('/feedback', authenticate, feedbackController.createFeedback);
|
router.post('/feedback', authenticate, feedbackController.createFeedback);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @route GET /api/trasporti/feedback/my/received
|
* @route GET /api/viaggi/feedback/my/received
|
||||||
* @desc I miei feedback ricevuti
|
* @desc I miei feedback ricevuti
|
||||||
* @access Private
|
* @access Private
|
||||||
*/
|
*/
|
||||||
router.get('/feedback/my/received', authenticate, feedbackController.getMyReceivedFeedbacks);
|
router.get('/feedback/my/received', authenticate, feedbackController.getMyReceivedFeedbacks);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @route GET /api/trasporti/feedback/my/given
|
* @route GET /api/viaggi/feedback/my/given
|
||||||
* @desc I miei feedback lasciati
|
* @desc I miei feedback lasciati
|
||||||
* @access Private
|
* @access Private
|
||||||
*/
|
*/
|
||||||
router.get('/feedback/my/given', authenticate, feedbackController.getMyGivenFeedbacks);
|
router.get('/feedback/my/given', authenticate, feedbackController.getMyGivenFeedbacks);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @route GET /api/trasporti/feedback/user/:userId
|
* @route GET /api/viaggi/feedback/user/:userId
|
||||||
* @desc Feedback di un utente
|
* @desc Feedback di un utente
|
||||||
* @access Public
|
* @access Public
|
||||||
*/
|
*/
|
||||||
router.get('/feedback/user/:userId', feedbackController.getFeedbacksForUser);
|
router.get('/feedback/user/:userId', feedbackController.getFeedbacksForUser);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @route GET /api/trasporti/feedback/user/:userId/stats
|
* @route GET /api/viaggi/feedback/user/:userId/stats
|
||||||
* @desc Statistiche feedback utente
|
* @desc Statistiche feedback utente
|
||||||
* @access Public
|
* @access Public
|
||||||
*/
|
*/
|
||||||
router.get('/feedback/user/:userId/stats', feedbackController.getUserFeedbackStats);
|
router.get('/feedback/user/:userId/stats', feedbackController.getUserFeedbackStats);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @route GET /api/trasporti/feedback/ride/:rideId
|
* @route GET /api/viaggi/feedback/ride/:rideId
|
||||||
* @desc Feedback per un viaggio
|
* @desc Feedback per un viaggio
|
||||||
* @access Public
|
* @access Public
|
||||||
*/
|
*/
|
||||||
router.get('/feedback/ride/:rideId', feedbackController.getRideFeedback);
|
router.get('/feedback/ride/:rideId', feedbackController.getRideFeedback);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @route GET /api/trasporti/feedback/can-leave/:rideId/:toUserId
|
* @route GET /api/viaggi/feedback/can-leave/:rideId/:toUserId
|
||||||
* @desc Verifica se posso lasciare feedback
|
* @desc Verifica se posso lasciare feedback
|
||||||
* @access Private
|
* @access Private
|
||||||
*/
|
*/
|
||||||
router.get('/feedback/can-leave/:rideId/:toUserId', authenticate, feedbackController.canLeaveFeedback);
|
router.get('/feedback/can-leave/:rideId/:toUserId', authenticate, feedbackController.canLeaveFeedback);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @route POST /api/trasporti/feedback/:id/response
|
* @route POST /api/viaggi/feedback/:id/response
|
||||||
* @desc Rispondi a un feedback
|
* @desc Rispondi a un feedback
|
||||||
* @access Private (solo destinatario)
|
* @access Private (solo destinatario)
|
||||||
*/
|
*/
|
||||||
router.post('/feedback/:id/response', authenticate, feedbackController.respondToFeedback);
|
router.post('/feedback/:id/response', authenticate, feedbackController.respondToFeedback);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @route POST /api/trasporti/feedback/:id/report
|
* @route POST /api/viaggi/feedback/:id/report
|
||||||
* @desc Segnala feedback
|
* @desc Segnala feedback
|
||||||
* @access Private
|
* @access Private
|
||||||
*/
|
*/
|
||||||
router.post('/feedback/:id/report', authenticate, feedbackController.reportFeedback);
|
router.post('/feedback/:id/report', authenticate, feedbackController.reportFeedback);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @route POST /api/trasporti/feedback/:id/helpful
|
* @route POST /api/viaggi/feedback/:id/helpful
|
||||||
* @desc Segna feedback come utile
|
* @desc Segna feedback come utile
|
||||||
* @access Private
|
* @access Private
|
||||||
*/
|
*/
|
||||||
@@ -348,68 +353,68 @@ router.post('/feedback/:id/helpful', authenticate, feedbackController.markAsHelp
|
|||||||
// 🗺️ GEO - Geocoding & Mappe (Open Source)
|
// 🗺️ GEO - Geocoding & Mappe (Open Source)
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
||||||
/**
|
// /* /**
|
||||||
* @route GET /api/trasporti/geo/autocomplete
|
// * @route GET /api/viaggi/geo/autocomplete
|
||||||
* @desc Autocomplete città (Photon)
|
// * @desc Autocomplete città (Photon)
|
||||||
* @access Public
|
// * @access Public
|
||||||
*/
|
// */
|
||||||
router.get('/geo/autocomplete', geocodingController.autocomplete);
|
// router.get('/geo/autocomplete', geocodingController.autocomplete);
|
||||||
|
|
||||||
/**
|
// /**
|
||||||
* @route GET /api/trasporti/geo/cities/it
|
// * @route GET /api/viaggi/geo/cities/it
|
||||||
* @desc Cerca città italiane
|
// * @desc Cerca città italiane
|
||||||
* @access Public
|
// * @access Public
|
||||||
*/
|
// */
|
||||||
router.get('/geo/cities/it', geocodingController.searchItalianCities);
|
// router.get('/geo/cities/it', geocodingController.searchItalianCities);
|
||||||
|
|
||||||
/**
|
// /**
|
||||||
* @route GET /api/trasporti/geo/geocode
|
// * @route GET /api/viaggi/geo/geocode
|
||||||
* @desc Indirizzo → Coordinate
|
// * @desc Indirizzo → Coordinate
|
||||||
* @access Public
|
// * @access Public
|
||||||
*/
|
// */
|
||||||
router.get('/geo/geocode', geocodingController.geocode);
|
// router.get('/geo/geocode', geocodingController.geocode);
|
||||||
|
|
||||||
/**
|
// /**
|
||||||
* @route GET /api/trasporti/geo/reverse
|
// * @route GET /api/viaggi/geo/reverse
|
||||||
* @desc Coordinate → Indirizzo
|
// * @desc Coordinate → Indirizzo
|
||||||
* @access Public
|
// * @access Public
|
||||||
*/
|
// */
|
||||||
router.get('/geo/reverse', geocodingController.reverseGeocode);
|
// router.get('/geo/reverse', geocodingController.reverseGeocode);
|
||||||
|
|
||||||
/**
|
// /**
|
||||||
* @route GET /api/trasporti/geo/route
|
// * @route GET /api/viaggi/geo/route
|
||||||
* @desc Calcola percorso tra punti
|
// * @desc Calcola percorso tra punti
|
||||||
* @access Public
|
// * @access Public
|
||||||
*/
|
// */
|
||||||
router.get('/geo/route', geocodingController.getRoute);
|
// router.get('/geo/route', geocodingController.getRoute);
|
||||||
|
|
||||||
/**
|
// /**
|
||||||
* @route GET /api/trasporti/geo/distance
|
// * @route GET /api/viaggi/geo/distance
|
||||||
* @desc Calcola distanza e durata
|
// * @desc Calcola distanza e durata
|
||||||
* @access Public
|
// * @access Public
|
||||||
*/
|
// */
|
||||||
router.get('/geo/distance', geocodingController.getDistance);
|
// router.get('/geo/distance', geocodingController.getDistance);
|
||||||
|
|
||||||
/**
|
// /**
|
||||||
* @route GET /api/trasporti/geo/suggest-waypoints
|
// * @route GET /api/viaggi/geo/suggest-waypoints
|
||||||
* @desc Suggerisci città intermedie sul percorso
|
// * @desc Suggerisci città intermedie sul percorso
|
||||||
* @access Public
|
// * @access Public
|
||||||
*/
|
// */
|
||||||
router.get('/geo/suggest-waypoints', geocodingController.suggestWaypoints);
|
// router.get('/geo/suggest-waypoints', geocodingController.suggestWaypoints);
|
||||||
|
|
||||||
// ============================================================
|
/// ============================================================
|
||||||
// 🔧 UTILITY & DRIVER PROFILE
|
// 🔧 UTILITY & DRIVER PROFILE
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @route GET /api/trasporti/driver/user/:userId
|
* @route GET /api/viaggi/driver/user/:userId
|
||||||
* @desc Profilo pubblico del conducente
|
* @desc Profilo pubblico del conducente
|
||||||
* @access Public
|
* @access Public
|
||||||
*/
|
*/
|
||||||
router.get('/driver/user/:userId', async (req, res) => {
|
router.get('/driver/user/:userId', async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const { userId } = req.params;
|
const { userId } = req.params;
|
||||||
const { idapp } = req.query;
|
const idapp = req.user.idapp;
|
||||||
|
|
||||||
const { User } = require('../models/user');
|
const { User } = require('../models/user');
|
||||||
const Ride = require('../models/Ride');
|
const Ride = require('../models/Ride');
|
||||||
@@ -502,13 +507,13 @@ router.get('/driver/user/:userId', async (req, res) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @route GET /api/trasporti/driver/vehicles
|
* @route GET /api/viaggi/driver/vehicles
|
||||||
* @desc Ottieni veicoli dell'utente corrente
|
* @desc Ottieni veicoli dell'utente corrente
|
||||||
* @access Private
|
* @access Private
|
||||||
*/
|
*/
|
||||||
router.get('/driver/vehicles', authenticate, async (req, res) => {
|
router.get('/driver/vehicles', authenticate, async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const { idapp } = req.query;
|
const idapp = req.user.idapp;
|
||||||
const userId = req.user._id; // Assumo che ci sia un middleware di autenticazione
|
const userId = req.user._id; // Assumo che ci sia un middleware di autenticazione
|
||||||
|
|
||||||
if (!userId) {
|
if (!userId) {
|
||||||
@@ -539,7 +544,7 @@ router.get('/driver/vehicles', authenticate, async (req, res) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @route PUT /api/trasporti/driver/profile
|
* @route PUT /api/viaggi/driver/profile
|
||||||
* @desc Aggiorna profilo conducente
|
* @desc Aggiorna profilo conducente
|
||||||
* @access Private
|
* @access Private
|
||||||
*/
|
*/
|
||||||
@@ -585,14 +590,14 @@ router.put('/driver/profile', authenticate, async (req, res) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @route POST /api/trasporti/driver/vehicles
|
* @route POST /api/viaggi/driver/vehicles
|
||||||
* @desc Aggiungi veicolo
|
* @desc Aggiungi veicolo
|
||||||
* @access Private
|
* @access Private
|
||||||
*/
|
*/
|
||||||
router.post('/driver/vehicles', authenticate, async (req, res) => {
|
router.post('/driver/vehicles', authenticate, async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const userId = req.user._id;
|
const userId = req.user._id;
|
||||||
const vehicle = req.body;
|
const vehicle = req.body.vehicle ? req.body.vehicle : req.body;
|
||||||
|
|
||||||
const { User } = require('../models/user');
|
const { User } = require('../models/user');
|
||||||
|
|
||||||
@@ -621,7 +626,7 @@ router.post('/driver/vehicles', authenticate, async (req, res) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @route PUT /api/trasporti/driver/vehicles/:vehicleId
|
* @route PUT /api/viaggi/driver/vehicles/:vehicleId
|
||||||
* @desc Aggiorna veicolo
|
* @desc Aggiorna veicolo
|
||||||
* @access Private
|
* @access Private
|
||||||
*/
|
*/
|
||||||
@@ -667,7 +672,7 @@ router.put('/driver/vehicles/:vehicleId', authenticate, async (req, res) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @route GET /api/trasporti/driver/vehicles/:vehicleId
|
* @route GET /api/viaggi/driver/vehicles/:vehicleId
|
||||||
* @desc Ottieni dettagli di un veicolo specifico
|
* @desc Ottieni dettagli di un veicolo specifico
|
||||||
* @access Private
|
* @access Private
|
||||||
*/
|
*/
|
||||||
@@ -715,7 +720,7 @@ router.get('/driver/vehicles/:vehicleId', authenticate, async (req, res) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @route DELETE /api/trasporti/driver/vehicles/:vehicleId
|
* @route DELETE /api/viaggi/driver/vehicles/:vehicleId
|
||||||
* @desc Rimuovi veicolo
|
* @desc Rimuovi veicolo
|
||||||
* @access Private
|
* @access Private
|
||||||
*/
|
*/
|
||||||
@@ -745,7 +750,7 @@ router.delete('/driver/vehicles/:vehicleId', authenticate, async (req, res) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @route POST /api/trasporti/driver/vehicles/:vehicleId/default
|
* @route POST /api/viaggi/driver/vehicles/:vehicleId/default
|
||||||
* @desc Imposta veicolo come predefinito
|
* @desc Imposta veicolo come predefinito
|
||||||
* @access Private
|
* @access Private
|
||||||
*/
|
*/
|
||||||
@@ -784,13 +789,13 @@ router.post('/driver/vehicles/:vehicleId/default', authenticate, async (req, res
|
|||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @route GET /api/trasporti/contrib-types
|
* @route GET /api/viaggi/contrib-types
|
||||||
* @desc Lista tipi di contributo disponibili
|
* @desc Lista tipi di contributo disponibili
|
||||||
* @access Public
|
* @access Public
|
||||||
*/
|
*/
|
||||||
router.get('/contrib-types', async (req, res) => {
|
router.get('/contrib-types', async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const { idapp } = req.query;
|
const idapp = req.query.idapp;
|
||||||
|
|
||||||
const contribTypes = await Contribtype.find({ idapp });
|
const contribTypes = await Contribtype.find({ idapp });
|
||||||
|
|
||||||
@@ -848,7 +853,7 @@ const uploadVehiclePhoto = multer({
|
|||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @route POST /api/trasporti/upload/vehicle-photos
|
* @route POST /api/viaggi/upload/vehicle-photos
|
||||||
* @desc Upload multiple foto veicolo (max 5)
|
* @desc Upload multiple foto veicolo (max 5)
|
||||||
* @access Private
|
* @access Private
|
||||||
*/
|
*/
|
||||||
@@ -913,7 +918,7 @@ router.post(
|
|||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @route POST /api/trasporti/upload/vehicle-photo
|
* @route POST /api/viaggi/upload/vehicle-photo
|
||||||
* @desc Upload foto veicolo
|
* @desc Upload foto veicolo
|
||||||
* @access Private
|
* @access Private
|
||||||
*/
|
*/
|
||||||
@@ -977,7 +982,7 @@ router.post(
|
|||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @route DELETE /api/trasporti/upload/vehicle-photo
|
* @route DELETE /api/viaggi/upload/vehicle-photo
|
||||||
* @desc Elimina foto veicolo
|
* @desc Elimina foto veicolo
|
||||||
* @access Private
|
* @access Private
|
||||||
*/
|
*/
|
||||||
@@ -13,7 +13,7 @@ function setupExpress(app, corsOptions) {
|
|||||||
app.use(helmet());
|
app.use(helmet());
|
||||||
app.use(morgan('dev'));
|
app.use(morgan('dev'));
|
||||||
app.use(cors(corsOptions));
|
app.use(cors(corsOptions));
|
||||||
app.set('trust proxy', true);
|
app.set('trust proxy', (process.env.NODE_ENV === 'development') ? false : true);
|
||||||
|
|
||||||
// parser
|
// parser
|
||||||
app.use(express.json({ limit: '100mb' }));
|
app.use(express.json({ limit: '100mb' }));
|
||||||
|
|||||||
Reference in New Issue
Block a user