- Parte 3 : Viaggi
- Chat
This commit is contained in:
@@ -43,4 +43,5 @@ MIAB_ADMIN_EMAIL=admin@lamiaposta.org
|
||||
MIAB_ADMIN_PASSWORD=passpao1pabox@1A
|
||||
DS_API_KEY="sk-222e3addb3d8455d8b0516d93906eec7"
|
||||
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
|
||||
CLOUDFLARE_TOKENS=[{"label":"Paolo.arena77@gmail.com","value":"M9EM309v8WFquJKpYgZCw-TViM2wX6vB3wlK6GD0"},{"label":"gruppomacro.com","value":"bqmzGShoX7WqOBzkXocoECyBkPq3GfqcM5t6VFd8"}]
|
||||
DS_API_KEY="sk-222e3addb3d8455d8b0516d93906eec7"
|
||||
ORS_API_KEY="eyJvcmciOiI1YjNjZTM1OTc4NTExMTAwMDFjZjYyNDgiLCJpZCI6IjNjNTllZmY1ZTM1ZDQ5ODI5NThhOTIzYTQ5MDkxOWIwIiwiaCI6Im11cm11cjY0In0="
|
||||
@@ -44,4 +44,5 @@ OLLAMA_DEFAULT_MODEL=llama3.2:3b
|
||||
GROK_API="xai-PcNM5obgPaETtmnfDWPZk235D75ZgxENU2QmeqPfMQCHh9dwCDVeRRe0oVVA2YOpiUDh1uJieZsMasja"
|
||||
REPLICATE_API_TOKEN="r8_AVhM6igwvoOnUA65cHVZdhEDfTqBVk94WTB0u"
|
||||
FAL_KEY="7d251c88-21b5-4b55-8b3e-4bafd910f99f:b81c0a36a25b052f26eb8ac226c7efff"
|
||||
HF_TOKEN="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"
|
||||
HF_TOKEN="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"
|
||||
ORS_API_KEY="eyJvcmciOiI1YjNjZTM1OTc4NTExMTAwMDFjZjYyNDgiLCJpZCI6IjNjNTllZmY1ZTM1ZDQ5ODI5NThhOTIzYTQ5MDkxOWIwIiwiaCI6Im11cm11cjY0In0="
|
||||
@@ -41,4 +41,5 @@ MIAB_HOST=box.lamiaposta.org
|
||||
MIAB_ADMIN_EMAIL=admin@lamiaposta.org
|
||||
MIAB_ADMIN_PASSWORD=passpao1pabox@1A
|
||||
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="
|
||||
@@ -38,4 +38,5 @@ SCRIPTS_DIR=admin_scripts
|
||||
CLOUDFLARE_TOKENS=[{"label":"Paolo.arena77@gmail.com","value":"M9EM309v8WFquJKpYgZCw-TViM2wX6vB3wlK6GD0"},{"label":"gruppomacro.com","value":"bqmzGShoX7WqOBzkXocoECyBkPq3GfqcM5t6VFd8"}]
|
||||
MIAB_HOST=box.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
|
||||
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) {
|
||||
console.error('Error in registration:', error.message);
|
||||
res.status(400).send({
|
||||
@@ -103,11 +107,15 @@ class UserController {
|
||||
}
|
||||
|
||||
// Send response with tokens
|
||||
res.header('x-auth', result.token).header('x-refrtok', result.refreshToken).header('x-browser-random', result.browser_random).send({
|
||||
usertosend: result.user,
|
||||
code: server_constants.RIS_CODE_OK,
|
||||
subsExistonDb: result.subsExistonDb,
|
||||
});
|
||||
res
|
||||
.header('x-auth', result.token)
|
||||
.header('x-refrtok', result.refreshToken)
|
||||
.header('x-browser-random', result.browser_random)
|
||||
.send({
|
||||
usertosend: result.user,
|
||||
code: server_constants.RIS_CODE_OK,
|
||||
subsExistonDb: result.subsExistonDb,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error in login:', error.message);
|
||||
res.status(400).send({
|
||||
@@ -487,6 +495,7 @@ class UserController {
|
||||
const { User } = require('../models/user');
|
||||
return User.isCollaboratore(user.perm);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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
|
||||
* @route POST /api/trasporti/feedback
|
||||
* @route POST /api/viaggi/feedback
|
||||
* @access Private
|
||||
*/
|
||||
const createFeedback = async (req, res) => {
|
||||
@@ -144,7 +144,7 @@ const createFeedback = async (req, res) => {
|
||||
|
||||
/**
|
||||
* @desc Ottieni i feedback ricevuti da un utente
|
||||
* @route GET /api/trasporti/feedback/user/:userId
|
||||
* @route GET /api/viaggi/feedback/user/:userId
|
||||
* @access Public
|
||||
*/
|
||||
const getUserFeedback = async (req, res) => {
|
||||
@@ -206,13 +206,13 @@ const getUserFeedback = async (req, res) => {
|
||||
|
||||
/**
|
||||
* @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
|
||||
*/
|
||||
const getUserFeedbackStats = async (req, res) => {
|
||||
try {
|
||||
const { userId } = req.params;
|
||||
const { idapp } = req.query;
|
||||
const idapp = req.user.idapp;
|
||||
|
||||
if (!idapp) {
|
||||
return res.status(400).json({
|
||||
@@ -246,13 +246,13 @@ const getUserFeedbackStats = async (req, res) => {
|
||||
|
||||
/**
|
||||
* @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)
|
||||
*/
|
||||
const getRideFeedback = async (req, res) => {
|
||||
try {
|
||||
const { rideId } = req.params;
|
||||
const { idapp } = req.query;
|
||||
const idapp = req.user.idapp;
|
||||
const userId = req.user?._id;
|
||||
|
||||
if (!idapp) {
|
||||
@@ -337,14 +337,14 @@ const getRideFeedback = async (req, res) => {
|
||||
|
||||
/**
|
||||
* @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
|
||||
* @note NUOVA FUNZIONE - Era mancante!
|
||||
*/
|
||||
const canLeaveFeedback = async (req, res) => {
|
||||
try {
|
||||
const { rideId, toUserId } = req.params;
|
||||
const { idapp } = req.query;
|
||||
const idapp = req.user.idapp;
|
||||
const fromUserId = req.user._id;
|
||||
|
||||
if (!idapp) {
|
||||
@@ -476,7 +476,7 @@ const canLeaveFeedback = async (req, res) => {
|
||||
|
||||
/**
|
||||
* @desc Rispondi a un feedback ricevuto
|
||||
* @route POST /api/trasporti/feedback/:id/response
|
||||
* @route POST /api/viaggi/feedback/:id/response
|
||||
* @access Private
|
||||
*/
|
||||
const respondToFeedback = async (req, res) => {
|
||||
@@ -542,7 +542,7 @@ const respondToFeedback = async (req, res) => {
|
||||
|
||||
/**
|
||||
* @desc Segna un feedback come utile
|
||||
* @route POST /api/trasporti/feedback/:id/helpful
|
||||
* @route POST /api/viaggi/feedback/:id/helpful
|
||||
* @access Private
|
||||
*/
|
||||
const markAsHelpful = async (req, res) => {
|
||||
@@ -605,7 +605,7 @@ const markAsHelpful = async (req, res) => {
|
||||
|
||||
/**
|
||||
* @desc Segnala un feedback inappropriato
|
||||
* @route POST /api/trasporti/feedback/:id/report
|
||||
* @route POST /api/viaggi/feedback/:id/report
|
||||
* @access Private
|
||||
*/
|
||||
const reportFeedback = async (req, res) => {
|
||||
@@ -679,7 +679,7 @@ const reportFeedback = async (req, res) => {
|
||||
|
||||
/**
|
||||
* @desc Ottieni i miei feedback dati
|
||||
* @route GET /api/trasporti/feedback/my/given
|
||||
* @route GET /api/viaggi/feedback/my/given
|
||||
* @access Private
|
||||
*/
|
||||
const getMyGivenFeedback = async (req, res) => {
|
||||
@@ -729,7 +729,7 @@ const getMyGivenFeedback = async (req, res) => {
|
||||
|
||||
/**
|
||||
* @desc Ottieni i miei feedback ricevuti
|
||||
* @route GET /api/trasporti/feedback/my/received
|
||||
* @route GET /api/viaggi/feedback/my/received
|
||||
* @access Private
|
||||
*/
|
||||
const getMyReceivedFeedback = async (req, res) => {
|
||||
|
||||
@@ -1,39 +1,43 @@
|
||||
/**
|
||||
* Controller per Geocoding usando servizi Open Source
|
||||
* - Nominatim (OpenStreetMap) per geocoding/reverse
|
||||
* - OSRM per routing
|
||||
* - Photon per autocomplete
|
||||
* Controller per Geocoding usando OpenRouteService
|
||||
* Documentazione: https://openrouteservice.org/dev/#/api-docs
|
||||
*/
|
||||
|
||||
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';
|
||||
// Configurazione OpenRouteService
|
||||
const ORS_BASE = 'https://api.openrouteservice.org';
|
||||
const ORS_API_KEY = process.env.ORS_API_KEY || 'YOUR_API_KEY_HERE';
|
||||
|
||||
/**
|
||||
* 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) => {
|
||||
const client = url.startsWith('https') ? https : http;
|
||||
|
||||
const req = client.get(url, {
|
||||
const urlObj = new URL(url);
|
||||
|
||||
const options = {
|
||||
hostname: urlObj.hostname,
|
||||
path: urlObj.pathname + urlObj.search,
|
||||
method,
|
||||
headers: {
|
||||
'User-Agent': USER_AGENT,
|
||||
'Accept': 'application/json'
|
||||
}
|
||||
}, (res) => {
|
||||
Authorization: ORS_API_KEY,
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
};
|
||||
|
||||
const req = https.request(options, (res) => {
|
||||
let data = '';
|
||||
res.on('data', chunk => data += chunk);
|
||||
res.on('data', (chunk) => (data += chunk));
|
||||
res.on('end', () => {
|
||||
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) {
|
||||
reject(new Error('Errore parsing risposta'));
|
||||
}
|
||||
@@ -41,338 +45,436 @@ const makeRequest = (url) => {
|
||||
});
|
||||
|
||||
req.on('error', reject);
|
||||
req.setTimeout(10000, () => {
|
||||
req.setTimeout(15000, () => {
|
||||
req.destroy();
|
||||
reject(new Error('Timeout richiesta'));
|
||||
});
|
||||
|
||||
if (body) {
|
||||
req.write(JSON.stringify(body));
|
||||
}
|
||||
req.end();
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @desc Autocomplete città (Photon API)
|
||||
* @route GET /api/trasporti/geo/autocomplete
|
||||
* @desc Autocomplete città (ORS Geocode Autocomplete)
|
||||
* @route GET /api/geo/autocomplete
|
||||
*/
|
||||
const autocomplete = async (req, res) => {
|
||||
try {
|
||||
const { q, limit = 5, lang = 'it' } = req.query;
|
||||
const { q, limit = 5, lang = 'it', country = 'IT' } = req.query;
|
||||
|
||||
if (!q || q.length < 2) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: 'Query deve essere almeno 2 caratteri'
|
||||
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 params = new URLSearchParams({
|
||||
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);
|
||||
|
||||
// Formatta risultati
|
||||
const results = data.features.map(feature => ({
|
||||
const results = data.features.map((feature) => ({
|
||||
id: feature.properties.id,
|
||||
city: feature.properties.name,
|
||||
province: feature.properties.county || feature.properties.state,
|
||||
region: feature.properties.state,
|
||||
locality: feature.properties.locality,
|
||||
county: feature.properties.county,
|
||||
region: feature.properties.region,
|
||||
country: feature.properties.country,
|
||||
postalCode: feature.properties.postcode,
|
||||
postalCode: feature.properties.postalcode,
|
||||
coordinates: {
|
||||
lat: feature.geometry.coordinates[1],
|
||||
lng: feature.geometry.coordinates[0]
|
||||
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'
|
||||
displayName: feature.properties.label,
|
||||
type: feature.properties.layer,
|
||||
confidence: feature.properties.confidence,
|
||||
}));
|
||||
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
data: results
|
||||
count: results.length,
|
||||
data: results,
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('Errore autocomplete:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: 'Errore durante la ricerca',
|
||||
error: error.message
|
||||
error: error.message,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @desc Geocoding - indirizzo a coordinate (Nominatim)
|
||||
* @route GET /api/trasporti/geo/geocode
|
||||
* @desc Geocoding - indirizzo a coordinate (ORS Geocode Search)
|
||||
* @route GET /api/geo/geocode
|
||||
*/
|
||||
const geocode = async (req, res) => {
|
||||
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) {
|
||||
return res.status(400).json({
|
||||
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);
|
||||
|
||||
if (!data || data.length === 0) {
|
||||
if (!data.features || data.features.length === 0) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: 'Nessun risultato trovato'
|
||||
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,
|
||||
const results = data.features.map((feature) => ({
|
||||
id: feature.properties.id,
|
||||
displayName: feature.properties.label,
|
||||
name: feature.properties.name,
|
||||
street: feature.properties.street,
|
||||
houseNumber: feature.properties.housenumber,
|
||||
city: feature.properties.locality || feature.properties.county,
|
||||
county: feature.properties.county,
|
||||
region: feature.properties.region,
|
||||
country: feature.properties.country,
|
||||
postalCode: feature.properties.postalcode,
|
||||
coordinates: {
|
||||
lat: parseFloat(item.lat),
|
||||
lng: parseFloat(item.lon)
|
||||
lat: feature.geometry.coordinates[1],
|
||||
lng: feature.geometry.coordinates[0],
|
||||
},
|
||||
type: item.type,
|
||||
importance: item.importance
|
||||
type: feature.properties.layer,
|
||||
confidence: feature.properties.confidence,
|
||||
}));
|
||||
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
data: results
|
||||
count: results.length,
|
||||
data: results,
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('Errore geocoding:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: 'Errore durante il geocoding',
|
||||
error: error.message
|
||||
error: error.message,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @desc Reverse geocoding - coordinate a indirizzo (Nominatim)
|
||||
* @route GET /api/trasporti/geo/reverse
|
||||
* @desc Reverse geocoding - coordinate a indirizzo (ORS Reverse)
|
||||
* @route GET /api/geo/reverse
|
||||
*/
|
||||
const reverseGeocode = async (req, res) => {
|
||||
try {
|
||||
const { lat, lng } = req.query;
|
||||
const { lat, lng, lang = 'it' } = req.query;
|
||||
|
||||
if (!lat || !lng) {
|
||||
return res.status(400).json({
|
||||
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);
|
||||
|
||||
if (!data || data.error) {
|
||||
if (!data.features || data.features.length === 0) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: 'Nessun risultato trovato'
|
||||
message: 'Nessun risultato trovato',
|
||||
});
|
||||
}
|
||||
|
||||
const feature = data.features[0];
|
||||
|
||||
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,
|
||||
displayName: feature.properties.label,
|
||||
name: feature.properties.name,
|
||||
street: feature.properties.street,
|
||||
houseNumber: feature.properties.housenumber,
|
||||
city: feature.properties.locality || feature.properties.county,
|
||||
county: feature.properties.county,
|
||||
region: feature.properties.region,
|
||||
country: feature.properties.country,
|
||||
postalCode: feature.properties.postalcode,
|
||||
coordinates: {
|
||||
lat: parseFloat(lat),
|
||||
lng: parseFloat(lng)
|
||||
}
|
||||
lng: parseFloat(lng),
|
||||
},
|
||||
distance: feature.properties.distance, // distanza dal punto esatto
|
||||
};
|
||||
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
data: result
|
||||
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
|
||||
error: error.message,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @desc Calcola percorso tra due punti (OSRM)
|
||||
* @route GET /api/trasporti/geo/route
|
||||
* @desc Calcola percorso tra due o più punti (ORS Directions)
|
||||
* @route POST /api/geo/route
|
||||
* @body { coordinates: [[lng,lat], [lng,lat], ...], profile: 'driving-car' }
|
||||
*/
|
||||
const getRoute = async (req, res) => {
|
||||
try {
|
||||
const {
|
||||
startLat, startLng,
|
||||
endLat, endLng,
|
||||
waypoints // formato: "lat1,lng1;lat2,lng2;..."
|
||||
const {
|
||||
startLat,
|
||||
startLng,
|
||||
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;
|
||||
|
||||
if (!startLat || !startLng || !endLat || !endLng) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: 'Coordinate di partenza e arrivo richieste'
|
||||
message: 'Coordinate di partenza e arrivo richieste',
|
||||
});
|
||||
}
|
||||
|
||||
// Costruisci stringa coordinate
|
||||
let coordinates = `${startLng},${startLat}`;
|
||||
|
||||
// Costruisci array coordinate [lng, lat] (formato GeoJSON)
|
||||
const coordinates = [[parseFloat(startLng), parseFloat(startLat)]];
|
||||
|
||||
if (waypoints) {
|
||||
const waypointsList = waypoints.split(';');
|
||||
waypointsList.forEach(wp => {
|
||||
const [lat, lng] = wp.split(',');
|
||||
coordinates += `;${lng},${lat}`;
|
||||
waypointsList.forEach((wp) => {
|
||||
const [lat, lng] = wp.split(',').map(parseFloat);
|
||||
coordinates.push([lng, lat]);
|
||||
});
|
||||
}
|
||||
|
||||
coordinates += `;${endLng},${endLat}`;
|
||||
|
||||
const url = `${OSRM_BASE}/route/v1/driving/${coordinates}?overview=full&geometries=polyline&steps=true`;
|
||||
coordinates.push([parseFloat(endLng), parseFloat(endLat)]);
|
||||
|
||||
const data = await makeRequest(url);
|
||||
// Richiesta POST a ORS Directions
|
||||
const url = `${ORS_BASE}/v2/directions/${profile}`;
|
||||
|
||||
if (!data || data.code !== 'Ok' || !data.routes || data.routes.length === 0) {
|
||||
const body = {
|
||||
coordinates,
|
||||
language,
|
||||
units,
|
||||
geometry: true,
|
||||
instructions: true,
|
||||
maneuvers: true,
|
||||
};
|
||||
|
||||
const data = await makeRequest(url, 'POST', body);
|
||||
|
||||
if (!data.routes || data.routes.length === 0) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: 'Impossibile calcolare il percorso'
|
||||
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 summary = route.summary;
|
||||
|
||||
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)
|
||||
})) : []
|
||||
}))
|
||||
distance: Math.round(summary.distance * 10) / 10, // km
|
||||
duration: Math.round(summary.duration / 60), // minuti
|
||||
durationFormatted: formatDuration(summary.duration),
|
||||
bbox: data.bbox, // Bounding box
|
||||
geometry: route.geometry, // Polyline encoded
|
||||
segments: route.segments.map((segment) => ({
|
||||
distance: Math.round(segment.distance * 10) / 10,
|
||||
duration: Math.round(segment.duration / 60),
|
||||
steps: segment.steps.map((step) => ({
|
||||
instruction: step.instruction,
|
||||
name: step.name,
|
||||
distance: Math.round(step.distance * 100) / 100,
|
||||
duration: Math.round(step.duration / 60),
|
||||
type: step.type,
|
||||
maneuver: step.maneuver,
|
||||
})),
|
||||
})),
|
||||
};
|
||||
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
data: result
|
||||
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
|
||||
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
|
||||
* @route GET /api/trasporti/geo/suggest-waypoints
|
||||
* @route GET /api/geo/suggest-waypoints
|
||||
*/
|
||||
const suggestWaypoints = async (req, res) => {
|
||||
try {
|
||||
const { startLat, startLng, endLat, endLng } = req.query;
|
||||
const { startLat, startLng, endLat, endLng, count = 3 } = req.query;
|
||||
|
||||
if (!startLat || !startLng || !endLat || !endLng) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: 'Coordinate di partenza e arrivo richieste'
|
||||
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 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({
|
||||
success: false,
|
||||
message: 'Impossibile calcolare il percorso'
|
||||
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
|
||||
// Decodifica polyline per ottenere punti
|
||||
const geometry = routeData.routes[0].geometry;
|
||||
const decodedPoints = decodePolyline(geometry);
|
||||
|
||||
// Seleziona punti equidistanti lungo il percorso
|
||||
const totalPoints = decodedPoints.length;
|
||||
const step = Math.floor(totalPoints / (parseInt(count) + 1));
|
||||
|
||||
const sampledPoints = [];
|
||||
for (let i = step; i < totalPoints - step; i += step) {
|
||||
sampledPoints.push(coordinates[i]);
|
||||
for (let i = 1; i <= count; i++) {
|
||||
const index = Math.min(step * i, totalPoints - 1);
|
||||
sampledPoints.push(decodedPoints[index]);
|
||||
}
|
||||
|
||||
// Fai reverse geocoding per ogni punto
|
||||
const cities = [];
|
||||
const seenCities = new Set();
|
||||
|
||||
for (const point of sampledPoints.slice(0, 5)) { // Limita a 5 richieste
|
||||
for (const point of sampledPoints) {
|
||||
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);
|
||||
|
||||
if (data && data.address) {
|
||||
const cityName = data.address.city || data.address.town || data.address.village;
|
||||
|
||||
if (data.features && data.features.length > 0) {
|
||||
const feature = data.features[0];
|
||||
const cityName = feature.properties.locality || feature.properties.county;
|
||||
|
||||
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,
|
||||
county: feature.properties.county,
|
||||
region: feature.properties.region,
|
||||
coordinates: {
|
||||
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) {
|
||||
console.log('Errore reverse per punto:', e.message);
|
||||
}
|
||||
@@ -380,128 +482,196 @@ const suggestWaypoints = async (req, res) => {
|
||||
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
data: cities
|
||||
count: cities.length,
|
||||
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
|
||||
error: error.message,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @desc Cerca città italiane (ottimizzato per Italia)
|
||||
* @route GET /api/trasporti/geo/cities/it
|
||||
* @desc Cerca città italiane (ottimizzato)
|
||||
* @route GET /api/geo/cities/it
|
||||
*/
|
||||
const searchItalianCities = async (req, res) => {
|
||||
try {
|
||||
const { q, limit = 10 } = req.query;
|
||||
const { q, limit = 10, region } = req.query;
|
||||
|
||||
if (!q || q.length < 2) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: 'Query deve essere almeno 2 caratteri'
|
||||
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 params = new URLSearchParams({
|
||||
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 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,
|
||||
const results = data.features
|
||||
.filter((f) => f.properties.locality || f.properties.county)
|
||||
.map((feature) => ({
|
||||
city: feature.properties.locality || feature.properties.name,
|
||||
county: feature.properties.county,
|
||||
region: feature.properties.region,
|
||||
postalCode: feature.properties.postalcode,
|
||||
coordinates: {
|
||||
lat: parseFloat(item.lat),
|
||||
lng: parseFloat(item.lon)
|
||||
lat: feature.geometry.coordinates[1],
|
||||
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
|
||||
const unique = results.filter((v, i, a) =>
|
||||
a.findIndex(t => t.city.toLowerCase() === v.city.toLowerCase()) === i
|
||||
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
|
||||
count: unique.length,
|
||||
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
|
||||
error: error.message,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @desc Calcola distanza e durata tra due punti
|
||||
* @route GET /api/trasporti/geo/distance
|
||||
* @desc Calcola distanza e durata tra due punti (semplificato)
|
||||
* @route GET /api/geo/distance
|
||||
*/
|
||||
const getDistance = async (req, res) => {
|
||||
try {
|
||||
const { startLat, startLng, endLat, endLng } = req.query;
|
||||
const { startLat, startLng, endLat, endLng, profile = 'driving-car' } = req.query;
|
||||
|
||||
if (!startLat || !startLng || !endLat || !endLng) {
|
||||
return res.status(400).json({
|
||||
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({
|
||||
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({
|
||||
success: true,
|
||||
data: {
|
||||
distance: Math.round(route.distance / 1000 * 10) / 10, // km
|
||||
duration: Math.round(route.duration / 60), // minuti
|
||||
durationFormatted: formatDuration(route.duration)
|
||||
}
|
||||
distance: Math.round(summary.distance * 10) / 10, // km
|
||||
duration: Math.round(summary.duration / 60), // minuti
|
||||
durationFormatted: formatDuration(summary.duration),
|
||||
profile,
|
||||
},
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('Errore calcolo distanza:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
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 hours = Math.floor(seconds / 3600);
|
||||
const minutes = Math.round((seconds % 3600) / 60);
|
||||
|
||||
|
||||
if (hours === 0) {
|
||||
return `${minutes} min`;
|
||||
} else if (minutes === 0) {
|
||||
@@ -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 = {
|
||||
autocomplete,
|
||||
geocode,
|
||||
reverseGeocode,
|
||||
getRoute,
|
||||
getMatrix,
|
||||
suggestWaypoints,
|
||||
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 User = require('../models/user');
|
||||
const { User } = require('../models/user');
|
||||
const RideRequest = require('../models/RideRequest');
|
||||
|
||||
/**
|
||||
* @desc Crea un nuovo viaggio (offerta o richiesta)
|
||||
* @route POST /api/trasporti/rides
|
||||
* @route POST /api/viaggi/rides
|
||||
* @access Private
|
||||
*/
|
||||
const createRide = async (req, res) => {
|
||||
try {
|
||||
const { idapp } = req.body;
|
||||
const idapp = req.user.idapp;
|
||||
const userId = req.user._id;
|
||||
|
||||
const {
|
||||
@@ -111,9 +111,7 @@ const createRide = async (req, res) => {
|
||||
|
||||
// Aggiorna profilo utente come driver se è un'offerta
|
||||
if (type === 'offer') {
|
||||
await User.findByIdAndUpdate(userId, {
|
||||
'profile.driverProfile.isDriver': true
|
||||
});
|
||||
await User.findByIdAndUpdate(userId, { $set: { 'profile.driverProfile.isDriver': true } }, { new: true });
|
||||
}
|
||||
|
||||
// Popola i dati per la risposta
|
||||
@@ -137,12 +135,12 @@ const createRide = async (req, res) => {
|
||||
|
||||
/**
|
||||
* @desc Ottieni lista viaggi con filtri
|
||||
* @route GET /api/trasporti/rides
|
||||
* @route GET /api/viaggi/rides
|
||||
* @access Public
|
||||
*/
|
||||
const getRides = async (req, res) => {
|
||||
try {
|
||||
const { idapp } = req.query;
|
||||
const idapp = req.query.idapp;
|
||||
|
||||
const {
|
||||
type,
|
||||
@@ -272,15 +270,14 @@ const getRides = async (req, res) => {
|
||||
|
||||
/**
|
||||
* @desc Ottieni singolo viaggio per ID
|
||||
* @route GET /api/trasporti/rides/:id
|
||||
* @route GET /api/viaggi/rides/:id
|
||||
* @access Public
|
||||
*/
|
||||
const getRideById = async (req, res) => {
|
||||
try {
|
||||
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('confirmedPassengers.userId', 'username name surname profile.img')
|
||||
.populate('contribution.contribTypes.contribTypeId');
|
||||
@@ -313,14 +310,15 @@ const getRideById = async (req, res) => {
|
||||
|
||||
/**
|
||||
* @desc Aggiorna un viaggio
|
||||
* @route PUT /api/trasporti/rides/:id
|
||||
* @route PUT /api/viaggi/rides/:id
|
||||
* @access Private
|
||||
*/
|
||||
const updateRide = async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const userId = req.user._id;
|
||||
const { idapp, ...updateData } = req.body;
|
||||
const idapp = req.user.idapp;
|
||||
const { ...updateData } = req.body;
|
||||
|
||||
// Trova il viaggio
|
||||
const ride = await Ride.findOne({ _id: id, idapp });
|
||||
@@ -333,7 +331,7 @@ const updateRide = async (req, res) => {
|
||||
}
|
||||
|
||||
// Verifica proprietario
|
||||
if (ride.userId.toString() !== userId) {
|
||||
if (!ride.userId.equals(userId)) {
|
||||
return res.status(403).json({
|
||||
success: false,
|
||||
message: 'Non sei autorizzato a modificare questo viaggio'
|
||||
@@ -400,14 +398,15 @@ const updateRide = async (req, res) => {
|
||||
|
||||
/**
|
||||
* @desc Cancella un viaggio
|
||||
* @route DELETE /api/trasporti/rides/:id
|
||||
* @route DELETE /api/viaggi/rides/:id
|
||||
* @access Private
|
||||
*/
|
||||
const deleteRide = async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
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 });
|
||||
|
||||
@@ -419,7 +418,7 @@ const deleteRide = async (req, res) => {
|
||||
}
|
||||
|
||||
// Verifica proprietario
|
||||
if (ride.userId.toString() !== userId) {
|
||||
if (!ride.userId.equals(userId)) {
|
||||
return res.status(403).json({
|
||||
success: false,
|
||||
message: 'Non sei autorizzato a cancellare questo viaggio'
|
||||
@@ -463,13 +462,14 @@ const deleteRide = async (req, res) => {
|
||||
|
||||
/**
|
||||
* @desc Ottieni viaggi dell'utente corrente
|
||||
* @route GET /api/trasporti/rides/my
|
||||
* @route GET /api/viaggi/rides/my
|
||||
* @access Private
|
||||
*/
|
||||
const getMyRides = async (req, res) => {
|
||||
try {
|
||||
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 };
|
||||
|
||||
@@ -539,12 +539,12 @@ const getMyRides = async (req, res) => {
|
||||
|
||||
/**
|
||||
* @desc Cerca viaggi con match intelligente
|
||||
* @route GET /api/trasporti/rides/search
|
||||
* @route GET /api/viaggi/rides/search
|
||||
* @access Public
|
||||
*/
|
||||
const searchRides = async (req, res) => {
|
||||
try {
|
||||
const { idapp } = req.query;
|
||||
const idapp = req.query.idapp;
|
||||
|
||||
const {
|
||||
from,
|
||||
@@ -659,14 +659,14 @@ const searchRides = async (req, res) => {
|
||||
|
||||
/**
|
||||
* @desc Completa un viaggio
|
||||
* @route POST /api/trasporti/rides/:id/complete
|
||||
* @route POST /api/viaggi/rides/:id/complete
|
||||
* @access Private
|
||||
*/
|
||||
const completeRide = async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const userId = req.user._id;
|
||||
const { idapp } = req.body;
|
||||
const idapp = req.user.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({
|
||||
success: false,
|
||||
message: 'Solo il conducente può completare il viaggio'
|
||||
@@ -723,12 +723,12 @@ const completeRide = async (req, res) => {
|
||||
|
||||
/**
|
||||
* @desc Ottieni statistiche viaggi per homepage widget
|
||||
* @route GET /api/trasporti/rides/stats
|
||||
* @route GET /api/viaggi/rides/stats
|
||||
* @access Private
|
||||
*/
|
||||
const getRidesStats = async (req, res) => {
|
||||
try {
|
||||
const { idapp } = req.query;
|
||||
const idapp = req.query.idapp;
|
||||
const userId = req.user._id;
|
||||
|
||||
const now = new Date();
|
||||
@@ -800,7 +800,7 @@ const getRidesStats = async (req, res) => {
|
||||
|
||||
/**
|
||||
* Get aggregated data for dashboard widgets
|
||||
* GET /api/trasporti/widget/data
|
||||
* GET /api/viaggi/widget/data
|
||||
*/
|
||||
const getWidgetData = async (req, res) => {
|
||||
try {
|
||||
@@ -981,7 +981,7 @@ const getWidgetData = async (req, res) => {
|
||||
|
||||
/**
|
||||
* Get comprehensive statistics summary for user
|
||||
* GET /api/trasporti/stats/summary
|
||||
* GET /api/viaggi/stats/summary
|
||||
*/
|
||||
const getStatsSummary = async (req, res) => {
|
||||
try {
|
||||
@@ -1420,7 +1420,7 @@ const getStatsSummary = async (req, res) => {
|
||||
|
||||
/**
|
||||
* Get city suggestions for autocomplete
|
||||
* GET /api/trasporti/cities/suggestions?q=query
|
||||
* GET /api/viaggi/cities/suggestions?q=query
|
||||
*/
|
||||
const getCitySuggestions = async (req, res) => {
|
||||
try {
|
||||
@@ -1679,7 +1679,7 @@ const getCitySuggestions = async (req, res) => {
|
||||
|
||||
/**
|
||||
* Get recent cities from user's trip history
|
||||
* GET /api/trasporti/cities/recent
|
||||
* GET /api/viaggi/cities/recent
|
||||
*/
|
||||
const getRecentCities = async (req, res) => {
|
||||
try {
|
||||
|
||||
@@ -5,7 +5,7 @@ const Message = require('../models/Message');
|
||||
|
||||
/**
|
||||
* @desc Crea una richiesta di passaggio
|
||||
* @route POST /api/trasporti/requests
|
||||
* @route POST /api/viaggi/requests
|
||||
* @access Private
|
||||
*/
|
||||
const createRequest = async (req, res) => {
|
||||
@@ -154,7 +154,7 @@ const createRequest = async (req, res) => {
|
||||
|
||||
/**
|
||||
* @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
|
||||
*/
|
||||
const getRequestsForRide = async (req, res) => {
|
||||
@@ -207,7 +207,7 @@ const getRequestsForRide = async (req, res) => {
|
||||
|
||||
/**
|
||||
* @desc Ottieni le mie richieste (come passeggero)
|
||||
* @route GET /api/trasporti/requests/my
|
||||
* @route GET /api/viaggi/requests/my
|
||||
* @access Private
|
||||
*/
|
||||
const getMyRequests = async (req, res) => {
|
||||
@@ -266,13 +266,13 @@ const getMyRequests = async (req, res) => {
|
||||
|
||||
/**
|
||||
* @desc Ottieni richieste pendenti (per il conducente)
|
||||
* @route GET /api/trasporti/requests/pending
|
||||
* @route GET /api/viaggi/requests/pending
|
||||
* @access Private
|
||||
*/
|
||||
const getPendingRequests = async (req, res) => {
|
||||
try {
|
||||
const userId = req.user._id;
|
||||
const { idapp } = req.query;
|
||||
const idapp = req.user.idapp;
|
||||
|
||||
if (!idapp) {
|
||||
return res.status(400).json({
|
||||
@@ -307,7 +307,7 @@ const getPendingRequests = async (req, res) => {
|
||||
|
||||
/**
|
||||
* @desc Accetta una richiesta di passaggio
|
||||
* @route PUT /api/trasporti/requests/:id/accept
|
||||
* @route PUT /api/viaggi/requests/:id/accept
|
||||
* @access Private (solo conducente)
|
||||
*/
|
||||
const acceptRequest = async (req, res) => {
|
||||
@@ -403,7 +403,7 @@ const acceptRequest = async (req, res) => {
|
||||
|
||||
/**
|
||||
* @desc Rifiuta una richiesta di passaggio
|
||||
* @route PUT /api/trasporti/requests/:id/reject
|
||||
* @route PUT /api/viaggi/requests/:id/reject
|
||||
* @access Private (solo conducente)
|
||||
*/
|
||||
const rejectRequest = async (req, res) => {
|
||||
@@ -477,7 +477,7 @@ const rejectRequest = async (req, res) => {
|
||||
|
||||
/**
|
||||
* @desc Cancella una richiesta (dal passeggero)
|
||||
* @route PUT /api/trasporti/requests/:id/cancel
|
||||
* @route PUT /api/viaggi/requests/:id/cancel
|
||||
* @access Private
|
||||
*/
|
||||
const cancelRequest = async (req, res) => {
|
||||
@@ -550,7 +550,7 @@ const cancelRequest = async (req, res) => {
|
||||
|
||||
/**
|
||||
* @desc Ottieni una singola richiesta
|
||||
* @route GET /api/trasporti/requests/:id
|
||||
* @route GET /api/viaggi/requests/:id
|
||||
* @access Private
|
||||
*/
|
||||
const getRequestById = async (req, res) => {
|
||||
@@ -604,7 +604,7 @@ const getRequestById = async (req, res) => {
|
||||
|
||||
/**
|
||||
* @desc Ottieni richieste ricevute (io come conducente)
|
||||
* @route GET /api/trasporti/requests/received
|
||||
* @route GET /api/viaggi/requests/received
|
||||
* @access Private
|
||||
*/
|
||||
const getReceivedRequests = async (req, res) => {
|
||||
@@ -684,7 +684,7 @@ const getReceivedRequests = async (req, res) => {
|
||||
};
|
||||
/**
|
||||
* @desc Ottieni richieste inviate (io come passeggero)
|
||||
* @route GET /api/trasporti/requests/sent
|
||||
* @route GET /api/viaggi/requests/sent
|
||||
* @access Private
|
||||
*/
|
||||
const getSentRequests = async (req, res) => {
|
||||
|
||||
@@ -79,6 +79,16 @@ const ChatSchema = new Schema(
|
||||
ref: 'User',
|
||||
},
|
||||
],
|
||||
deletedBy: [
|
||||
{
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: 'User',
|
||||
},
|
||||
],
|
||||
clearedBefore: {
|
||||
type: Map,
|
||||
of: Date,
|
||||
},
|
||||
metadata: {
|
||||
type: Schema.Types.Mixed,
|
||||
},
|
||||
@@ -114,19 +124,17 @@ ChatSchema.methods.getUnreadForUser = function (userId) {
|
||||
// ✅ FIX: incrementUnread (assicura conversione corretta)
|
||||
ChatSchema.methods.incrementUnread = function (excludeUserId) {
|
||||
const excludeIdStr = excludeUserId.toString();
|
||||
|
||||
|
||||
this.participants.forEach((participantId) => {
|
||||
// Gestisci sia ObjectId che oggetti popolati
|
||||
const id = participantId._id
|
||||
? participantId._id.toString()
|
||||
: participantId.toString();
|
||||
|
||||
const id = participantId._id ? participantId._id.toString() : participantId.toString();
|
||||
|
||||
if (id !== excludeIdStr) {
|
||||
const current = this.unreadCount.get(id) || 0;
|
||||
this.unreadCount.set(id, current + 1);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
return this.save();
|
||||
};
|
||||
|
||||
@@ -151,7 +159,7 @@ ChatSchema.methods.updateLastMessage = function (message) {
|
||||
// ✅ FIX: Gestisce sia ObjectId che oggetti User popolati
|
||||
ChatSchema.methods.hasParticipant = function (userId) {
|
||||
const userIdStr = userId.toString();
|
||||
|
||||
|
||||
return this.participants.some((p) => {
|
||||
// Se p è un oggetto popolato (ha _id), usa p._id
|
||||
// Altrimenti p è già un ObjectId
|
||||
@@ -164,14 +172,13 @@ ChatSchema.methods.hasParticipant = function (userId) {
|
||||
// ✅ FIX: Metodo isBlockedFor (stesso problema)
|
||||
ChatSchema.methods.isBlockedFor = function (userId) {
|
||||
const userIdStr = userId.toString();
|
||||
|
||||
|
||||
return this.blockedBy.some((id) => {
|
||||
const blockedId = id._id ? id._id.toString() : id.toString();
|
||||
return blockedId === userIdStr;
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
// Metodo statico per trovare o creare una chat diretta
|
||||
ChatSchema.statics.findOrCreateDirect = async function (idapp, userId1, userId2, rideId = null) {
|
||||
// Cerca chat esistente tra i due utenti
|
||||
|
||||
@@ -55,6 +55,10 @@ const LocationSchema = new Schema(
|
||||
|
||||
// Schema per i waypoint (tappe intermedie)
|
||||
const WaypointSchema = new Schema({
|
||||
/*_id: {
|
||||
type: String,
|
||||
required: false
|
||||
},*/
|
||||
location: {
|
||||
type: LocationSchema,
|
||||
required: true,
|
||||
@@ -70,7 +74,7 @@ const WaypointSchema = new Schema({
|
||||
type: Number, // minuti di sosta
|
||||
default: 0,
|
||||
},
|
||||
});
|
||||
}, { _id: false }); // 👈 AGGIUNGI QUESTO
|
||||
|
||||
// Schema per la ricorrenza del viaggio
|
||||
const RecurrenceSchema = new Schema(
|
||||
|
||||
@@ -606,7 +606,6 @@ const UserSchema = new mongoose.Schema(
|
||||
{
|
||||
type: {
|
||||
type: String,
|
||||
enum: ['auto', 'moto', 'furgone', 'minibus', 'altro'],
|
||||
default: 'auto',
|
||||
},
|
||||
brand: {
|
||||
@@ -640,7 +639,6 @@ const UserSchema = new mongoose.Schema(
|
||||
features: [
|
||||
{
|
||||
type: String,
|
||||
enum: ['aria_condizionata', 'wifi', 'presa_usb', 'bluetooth', 'bagagliaio_grande', 'seggiolino_bimbi'],
|
||||
},
|
||||
],
|
||||
photos: [
|
||||
@@ -704,7 +702,6 @@ const UserSchema = new mongoose.Schema(
|
||||
},
|
||||
responseTime: {
|
||||
type: String,
|
||||
enum: ['within_hour', 'within_day', 'within_days'],
|
||||
default: 'within_day',
|
||||
},
|
||||
totalKmShared: {
|
||||
@@ -743,22 +740,18 @@ const UserSchema = new mongoose.Schema(
|
||||
// Preferenze di viaggio
|
||||
smoking: {
|
||||
type: String,
|
||||
enum: ['yes', 'no', 'outside_only'],
|
||||
default: 'no',
|
||||
},
|
||||
pets: {
|
||||
type: String,
|
||||
enum: ['no', 'small', 'medium', 'large', 'all'],
|
||||
default: 'small',
|
||||
},
|
||||
music: {
|
||||
type: String,
|
||||
enum: ['no_music', 'quiet', 'moderate', 'loud', 'passenger_choice'],
|
||||
default: 'moderate',
|
||||
},
|
||||
conversation: {
|
||||
type: String,
|
||||
enum: ['quiet', 'moderate', 'chatty'],
|
||||
default: 'moderate',
|
||||
},
|
||||
|
||||
@@ -835,7 +828,6 @@ const UserSchema = new mongoose.Schema(
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
enum: ['home', 'work', 'other'],
|
||||
default: 'other',
|
||||
},
|
||||
},
|
||||
|
||||
@@ -13,7 +13,6 @@ const PageView = require('../models/PageView');
|
||||
|
||||
const fal = require('@fal-ai/client');
|
||||
|
||||
|
||||
const imageGenerator = require('../services/imageGenerator'); // Assicurati che il percorso sia corretto
|
||||
|
||||
const posterEditor = require('../services/PosterEditor'); // <--- Importa la nuova classe
|
||||
@@ -33,9 +32,8 @@ const { MyElem } = require('../models/myelem');
|
||||
|
||||
const axios = require('axios');
|
||||
|
||||
const trasportiRoutes = require('../routes/trasportiRoutes');
|
||||
router.use('/trasporti', trasportiRoutes);
|
||||
|
||||
const viaggiRoutes = require('../routes/viaggiRoutes');
|
||||
router.use('/viaggi', viaggiRoutes);
|
||||
|
||||
// Importa le routes video
|
||||
const videoRoutes = require('../routes/videoRoutes');
|
||||
@@ -43,7 +41,6 @@ const videoRoutes = require('../routes/videoRoutes');
|
||||
// Monta le routes video
|
||||
router.use('/video', videoRoutes);
|
||||
|
||||
|
||||
router.use('/templates', authenticate, templatesRouter);
|
||||
router.use('/posters', authenticate, postersRouter);
|
||||
router.use('/assets', authenticate, assetsRouter);
|
||||
@@ -523,9 +520,16 @@ router.post('/chatbot', authenticate, async (req, res) => {
|
||||
});
|
||||
|
||||
router.post('/generateposter', async (req, res) => {
|
||||
const {
|
||||
titolo, data, ora, luogo, descrizione, contatti, fotoDescrizione, stile,
|
||||
provider = 'hf' // Default a HF (Gratis)
|
||||
const {
|
||||
titolo,
|
||||
data,
|
||||
ora,
|
||||
luogo,
|
||||
descrizione,
|
||||
contatti,
|
||||
fotoDescrizione,
|
||||
stile,
|
||||
provider = 'hf', // Default a HF (Gratis)
|
||||
} = req.body;
|
||||
|
||||
// 1. Prompt per l'AI: Chiediamo SOLO lo sfondo, VIETIAMO il testo.
|
||||
@@ -547,21 +551,50 @@ router.post('/generateposter', async (req, res) => {
|
||||
data,
|
||||
ora,
|
||||
luogo,
|
||||
contatti
|
||||
contatti,
|
||||
});
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
res.json({
|
||||
success: true,
|
||||
imageUrl: finalPosterBase64, // Restituisce l'immagine completa in base64
|
||||
step: 'AI + Canvas Composition'
|
||||
step: 'AI + Canvas Composition',
|
||||
});
|
||||
|
||||
} catch (err) {
|
||||
console.error('Errore:', 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;
|
||||
|
||||
@@ -317,7 +317,11 @@ router.post('/', async (req, res) => {
|
||||
await telegrambot.askConfirmationUser(myuser.idapp, shared_consts.CallFunz.REGISTRATION, myuser);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -368,7 +372,11 @@ router.post('/', async (req, res) => {
|
||||
// if (!tools.testing()) {
|
||||
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;
|
||||
});
|
||||
})
|
||||
@@ -411,7 +419,9 @@ router.patch('/:id', authenticate, (req, res) => {
|
||||
|
||||
if (!User.isAdmin(req.user.perm)) {
|
||||
// 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 })
|
||||
@@ -512,7 +522,7 @@ router.post('/profile', authenticate, (req, res) => {
|
||||
|
||||
try {
|
||||
// Check if ìs a Notif to read
|
||||
if (idnotif) {
|
||||
if (idnotif) {
|
||||
SendNotif.setNotifAsRead(idapp, usernameOrig, idnotif);
|
||||
}
|
||||
|
||||
@@ -591,9 +601,14 @@ router.post('/panel', authenticate, async (req, res) => {
|
||||
idapp = req.body.idapp;
|
||||
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
|
||||
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 {
|
||||
@@ -669,7 +684,7 @@ router.post('/newtok', async (req, res) => {
|
||||
}
|
||||
|
||||
const recFound = await User.findByRefreshTokenAnyAccess(refreshToken);
|
||||
|
||||
|
||||
if (!recFound) {
|
||||
return res.status(403).send({ error: 'Refresh token non valido' });
|
||||
}
|
||||
@@ -953,7 +968,9 @@ router.post('/friends/cmd', authenticate, async (req, res) => {
|
||||
usernameDest !== usernameLogged &&
|
||||
(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') {
|
||||
await User.findOneAndUpdate({ _id: mydata._id }, { $set: { 'profile.noNameSurname': mydata.value } });
|
||||
} 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') {
|
||||
await User.setPwdComeQuellaDellAdmin(mydata);
|
||||
} else if (mydata.dbop === 'ripristinaPwdPrec') {
|
||||
@@ -1129,10 +1149,10 @@ async function eseguiDbOpUser(idapp, mydata, locale, req, res) {
|
||||
} else if (mydata.dbop === 'noComune') {
|
||||
await User.findOneAndUpdate({ _id: mydata._id }, { $set: { 'profile.noComune': mydata.value } });
|
||||
} 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') {
|
||||
// Invia la email di Verifica email
|
||||
const ris = await sendemail.sendEmail_ReVerifyingEmail(mydata, idapp);
|
||||
const ris = await sendemail.sendEmail_ReVerifyingEmail(mydata, idapp);
|
||||
} else if (mydata.dbop === 'noCircIta') {
|
||||
await User.findOneAndUpdate({ _id: mydata._id }, { $set: { 'profile.noCircIta': mydata.value } });
|
||||
} else if (mydata.dbop === 'insert_circuito_ita') {
|
||||
|
||||
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 chatController = require('../controllers/chatController');
|
||||
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)
|
||||
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)
|
||||
* @access Private
|
||||
*/
|
||||
router.post('/rides', authenticate, rideController.createRide);
|
||||
|
||||
/**
|
||||
* @route GET /api/trasporti/rides
|
||||
* @route GET /api/viaggi/rides
|
||||
* @desc Ottieni lista viaggi con filtri
|
||||
* @access Public
|
||||
*/
|
||||
router.get('/rides', rideController.getRides);
|
||||
|
||||
/**
|
||||
* @route GET /api/trasporti/rides/search
|
||||
* @route GET /api/viaggi/rides/search
|
||||
* @desc Ricerca viaggi avanzata
|
||||
* @access Public
|
||||
*/
|
||||
router.get('/rides/search', rideController.searchRides);
|
||||
|
||||
/**
|
||||
* @route GET /api/trasporti/rides/stats
|
||||
* @route GET /api/viaggi/rides/stats
|
||||
* @desc Statistiche per widget homepage
|
||||
* @access Private
|
||||
*/
|
||||
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)
|
||||
* @access Private
|
||||
*/
|
||||
router.get('/rides/my', authenticate, rideController.getMyRides);
|
||||
|
||||
/**
|
||||
* @route GET /api/trasporti/rides/match
|
||||
* @route GET /api/viaggi/rides/match
|
||||
* @desc Match automatico offerta/richiesta
|
||||
* @access Private
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* @route GET /api/trasporti/rides/:id
|
||||
* @route GET /api/viaggi/rides/:id
|
||||
* @desc Dettaglio singolo viaggio
|
||||
* @access Public
|
||||
*/
|
||||
router.get('/rides/:id', rideController.getRideById);
|
||||
|
||||
/**
|
||||
* @route PUT /api/trasporti/rides/:id
|
||||
* @route PUT /api/viaggi/rides/:id
|
||||
* @desc Aggiorna viaggio
|
||||
* @access Private (solo owner)
|
||||
*/
|
||||
router.put('/rides/:id', authenticate, rideController.updateRide);
|
||||
|
||||
/**
|
||||
* @route DELETE /api/trasporti/rides/:id
|
||||
* @route DELETE /api/viaggi/rides/:id
|
||||
* @desc Cancella viaggio
|
||||
* @access Private (solo owner)
|
||||
*/
|
||||
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
|
||||
* @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
|
||||
* @access Private
|
||||
*/
|
||||
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)
|
||||
* @access Public
|
||||
*/
|
||||
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)
|
||||
* @access Public
|
||||
*/
|
||||
router.get('/cities/suggestions', rideController.getCitySuggestions);
|
||||
|
||||
/**
|
||||
* @route GET /api/trasporti/cities/recents
|
||||
* @route GET /api/viaggi/cities/recents
|
||||
* @desc città recenti per autocomplete
|
||||
* @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
|
||||
* @access Private
|
||||
*/
|
||||
router.post('/requests', authenticate, rideRequestController.createRequest);
|
||||
|
||||
/**
|
||||
* @route GET /api/trasporti/requests/received
|
||||
* @route GET /api/viaggi/requests/received
|
||||
* @desc Richieste ricevute (sono conducente)
|
||||
* @access Private
|
||||
*/
|
||||
router.get('/requests/received', authenticate, rideRequestController.getReceivedRequests);
|
||||
|
||||
/**
|
||||
* @route GET /api/trasporti/requests/sent
|
||||
* @route GET /api/viaggi/requests/sent
|
||||
* @desc Richieste inviate (sono passeggero)
|
||||
* @access Private
|
||||
*/
|
||||
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
|
||||
* @access Private (solo owner del viaggio)
|
||||
*/
|
||||
router.get('/requests/ride/:rideId', authenticate, rideRequestController.getRequestsForRide);
|
||||
|
||||
/**
|
||||
* @route GET /api/trasporti/requests/:id
|
||||
* @route GET /api/viaggi/requests/:id
|
||||
* @desc Dettaglio singola richiesta
|
||||
* @access Private (driver o passenger)
|
||||
*/
|
||||
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
|
||||
* @access Private (solo driver)
|
||||
*/
|
||||
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
|
||||
* @access Private (solo driver)
|
||||
*/
|
||||
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
|
||||
* @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
|
||||
* @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
|
||||
* @access Private
|
||||
*/
|
||||
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
|
||||
* @access Private
|
||||
*/
|
||||
router.post('/chats/direct', authenticate, chatController.getOrCreateDirectChat);
|
||||
|
||||
/**
|
||||
* @route GET /api/trasporti/chats/:id
|
||||
* @route GET /api/viaggi/chats/:id
|
||||
* @desc Dettaglio chat
|
||||
* @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
|
||||
* @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
|
||||
* @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
|
||||
* @access Private (solo partecipanti)
|
||||
* @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
|
||||
* @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
|
||||
* @access Private (solo partecipanti)
|
||||
* @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
|
||||
* @access Private (solo mittente)
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* @route DELETE /api/trasporti/chats/:id
|
||||
* @route DELETE /api/viaggi/chats/:id
|
||||
* @desc Elimina chat (soft delete)
|
||||
* @access Private (solo partecipanti)
|
||||
*/
|
||||
router.delete('/chats/:id', authenticate, chatController.deleteChat);
|
||||
router.delete('/chats/:chatId', authenticate, chatController.deleteChat);
|
||||
|
||||
// ============================================================
|
||||
// ⭐ FEEDBACK - Recensioni
|
||||
// ============================================================
|
||||
|
||||
/**
|
||||
* @route POST /api/trasporti/feedback
|
||||
* @route POST /api/viaggi/feedback
|
||||
* @desc Crea feedback per un viaggio
|
||||
* @access Private
|
||||
*/
|
||||
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
|
||||
* @access Private
|
||||
*/
|
||||
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
|
||||
* @access Private
|
||||
*/
|
||||
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
|
||||
* @access Public
|
||||
*/
|
||||
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
|
||||
* @access Public
|
||||
*/
|
||||
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
|
||||
* @access Public
|
||||
*/
|
||||
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
|
||||
* @access Private
|
||||
*/
|
||||
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
|
||||
* @access Private (solo destinatario)
|
||||
*/
|
||||
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
|
||||
* @access Private
|
||||
*/
|
||||
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
|
||||
* @access Private
|
||||
*/
|
||||
@@ -348,68 +353,68 @@ router.post('/feedback/:id/helpful', authenticate, feedbackController.markAsHelp
|
||||
// 🗺️ GEO - Geocoding & Mappe (Open Source)
|
||||
// ============================================================
|
||||
|
||||
/**
|
||||
* @route GET /api/trasporti/geo/autocomplete
|
||||
* @desc Autocomplete città (Photon)
|
||||
* @access Public
|
||||
*/
|
||||
router.get('/geo/autocomplete', geocodingController.autocomplete);
|
||||
// /* /**
|
||||
// * @route GET /api/viaggi/geo/autocomplete
|
||||
// * @desc Autocomplete città (Photon)
|
||||
// * @access Public
|
||||
// */
|
||||
// router.get('/geo/autocomplete', geocodingController.autocomplete);
|
||||
|
||||
/**
|
||||
* @route GET /api/trasporti/geo/cities/it
|
||||
* @desc Cerca città italiane
|
||||
* @access Public
|
||||
*/
|
||||
router.get('/geo/cities/it', geocodingController.searchItalianCities);
|
||||
// /**
|
||||
// * @route GET /api/viaggi/geo/cities/it
|
||||
// * @desc Cerca città italiane
|
||||
// * @access Public
|
||||
// */
|
||||
// router.get('/geo/cities/it', geocodingController.searchItalianCities);
|
||||
|
||||
/**
|
||||
* @route GET /api/trasporti/geo/geocode
|
||||
* @desc Indirizzo → Coordinate
|
||||
* @access Public
|
||||
*/
|
||||
router.get('/geo/geocode', geocodingController.geocode);
|
||||
// /**
|
||||
// * @route GET /api/viaggi/geo/geocode
|
||||
// * @desc Indirizzo → Coordinate
|
||||
// * @access Public
|
||||
// */
|
||||
// router.get('/geo/geocode', geocodingController.geocode);
|
||||
|
||||
/**
|
||||
* @route GET /api/trasporti/geo/reverse
|
||||
* @desc Coordinate → Indirizzo
|
||||
* @access Public
|
||||
*/
|
||||
router.get('/geo/reverse', geocodingController.reverseGeocode);
|
||||
// /**
|
||||
// * @route GET /api/viaggi/geo/reverse
|
||||
// * @desc Coordinate → Indirizzo
|
||||
// * @access Public
|
||||
// */
|
||||
// router.get('/geo/reverse', geocodingController.reverseGeocode);
|
||||
|
||||
/**
|
||||
* @route GET /api/trasporti/geo/route
|
||||
* @desc Calcola percorso tra punti
|
||||
* @access Public
|
||||
*/
|
||||
router.get('/geo/route', geocodingController.getRoute);
|
||||
// /**
|
||||
// * @route GET /api/viaggi/geo/route
|
||||
// * @desc Calcola percorso tra punti
|
||||
// * @access Public
|
||||
// */
|
||||
// router.get('/geo/route', geocodingController.getRoute);
|
||||
|
||||
/**
|
||||
* @route GET /api/trasporti/geo/distance
|
||||
* @desc Calcola distanza e durata
|
||||
* @access Public
|
||||
*/
|
||||
router.get('/geo/distance', geocodingController.getDistance);
|
||||
// /**
|
||||
// * @route GET /api/viaggi/geo/distance
|
||||
// * @desc Calcola distanza e durata
|
||||
// * @access Public
|
||||
// */
|
||||
// router.get('/geo/distance', geocodingController.getDistance);
|
||||
|
||||
/**
|
||||
* @route GET /api/trasporti/geo/suggest-waypoints
|
||||
* @desc Suggerisci città intermedie sul percorso
|
||||
* @access Public
|
||||
*/
|
||||
router.get('/geo/suggest-waypoints', geocodingController.suggestWaypoints);
|
||||
// /**
|
||||
// * @route GET /api/viaggi/geo/suggest-waypoints
|
||||
// * @desc Suggerisci città intermedie sul percorso
|
||||
// * @access Public
|
||||
// */
|
||||
// router.get('/geo/suggest-waypoints', geocodingController.suggestWaypoints);
|
||||
|
||||
// ============================================================
|
||||
/// ============================================================
|
||||
// 🔧 UTILITY & DRIVER PROFILE
|
||||
// ============================================================
|
||||
|
||||
/**
|
||||
* @route GET /api/trasporti/driver/user/:userId
|
||||
* @route GET /api/viaggi/driver/user/:userId
|
||||
* @desc Profilo pubblico del conducente
|
||||
* @access Public
|
||||
*/
|
||||
router.get('/driver/user/:userId', async (req, res) => {
|
||||
try {
|
||||
const { userId } = req.params;
|
||||
const { idapp } = req.query;
|
||||
const idapp = req.user.idapp;
|
||||
|
||||
const { User } = require('../models/user');
|
||||
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
|
||||
* @access Private
|
||||
*/
|
||||
router.get('/driver/vehicles', authenticate, async (req, res) => {
|
||||
try {
|
||||
const { idapp } = req.query;
|
||||
const idapp = req.user.idapp;
|
||||
const userId = req.user._id; // Assumo che ci sia un middleware di autenticazione
|
||||
|
||||
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
|
||||
* @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
|
||||
* @access Private
|
||||
*/
|
||||
router.post('/driver/vehicles', authenticate, async (req, res) => {
|
||||
try {
|
||||
const userId = req.user._id;
|
||||
const vehicle = req.body;
|
||||
const vehicle = req.body.vehicle ? req.body.vehicle : req.body;
|
||||
|
||||
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
|
||||
* @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
|
||||
* @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
|
||||
* @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
|
||||
* @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
|
||||
* @access Public
|
||||
*/
|
||||
router.get('/contrib-types', async (req, res) => {
|
||||
try {
|
||||
const { idapp } = req.query;
|
||||
const idapp = req.query.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)
|
||||
* @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
|
||||
* @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
|
||||
* @access Private
|
||||
*/
|
||||
@@ -13,7 +13,7 @@ function setupExpress(app, corsOptions) {
|
||||
app.use(helmet());
|
||||
app.use(morgan('dev'));
|
||||
app.use(cors(corsOptions));
|
||||
app.set('trust proxy', true);
|
||||
app.set('trust proxy', (process.env.NODE_ENV === 'development') ? false : true);
|
||||
|
||||
// parser
|
||||
app.use(express.json({ limit: '100mb' }));
|
||||
|
||||
Reference in New Issue
Block a user