1053 lines
29 KiB
JavaScript
1053 lines
29 KiB
JavaScript
const express = require('express');
|
|
const router = express.Router();
|
|
const path = require('path');
|
|
|
|
const multer = require('multer');
|
|
const fs = require('fs');
|
|
|
|
const tools = require('../tools/general');
|
|
|
|
const { Contribtype } = require('../models/contribtype'); // Adatta al tuo path
|
|
|
|
// Import Controllers
|
|
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 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');
|
|
|
|
// ============================================================
|
|
// 🚗 RIDES - Gestione Viaggi
|
|
// ============================================================
|
|
|
|
/**
|
|
* @route POST /api/viaggi/rides
|
|
* @desc Crea nuovo viaggio (offerta o richiesta)
|
|
* @access Private
|
|
*/
|
|
router.post('/rides', authenticate, rideController.createRide);
|
|
|
|
/**
|
|
* @route GET /api/viaggi/rides
|
|
* @desc Ottieni lista viaggi con filtri
|
|
* @access Public
|
|
*/
|
|
router.get('/rides', rideController.getRides);
|
|
|
|
|
|
/**
|
|
* @route GET /api/viaggi/rides/search
|
|
* @desc Ricerca viaggi avanzata
|
|
* @access Public
|
|
*/
|
|
router.get('/rides/search', rideController.searchRides);
|
|
|
|
/**
|
|
* @route GET /api/viaggi/rides/stats
|
|
* @desc Statistiche per widget homepage
|
|
* @access Private
|
|
*/
|
|
router.get('/rides/stats', authenticate, rideController.getRidesStats);
|
|
|
|
/**
|
|
* @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/viaggi/rides/match
|
|
* @desc Match automatico offerta/richiesta
|
|
* @access Private
|
|
* @note ⚠️ IMPORTANTE: Questa route DEVE stare PRIMA di /rides/:id
|
|
*/
|
|
//router.get('/rides/match', authenticate, rideController.findMatches);
|
|
|
|
router.get('/rides/cancelled', authenticate, rideController.getCancelledRides);
|
|
router.get('/rides/statscomm', authenticate, rideController.getCommunityStatsComm);
|
|
router.get('/rides/community', authenticate, rideController.getCommunityRides);
|
|
router.get('/rides/calendar', authenticate, rideController.getCalendarRides);
|
|
|
|
/**
|
|
* @route POST /api/viaggi/rides/:rideId/request
|
|
* @desc Richiedi posto su un viaggio
|
|
* @access Private
|
|
*/
|
|
router.post('/rides/:rideId/request', authenticate, rideRequestController.createRequestFromRide);
|
|
|
|
/**
|
|
* @route GET /api/viaggi/rides/:id
|
|
* @desc Dettaglio singolo viaggio
|
|
* @access Public
|
|
*/
|
|
router.get('/rides/:id', rideController.getRideById);
|
|
|
|
/**
|
|
* @route PUT /api/viaggi/rides/:id
|
|
* @desc Aggiorna viaggio
|
|
* @access Private (solo owner)
|
|
*/
|
|
router.put('/rides/:id', authenticate, rideController.updateRide);
|
|
|
|
/**
|
|
* @route DELETE /api/viaggi/rides/:id
|
|
* @desc Cancella viaggio
|
|
* @access Private (solo owner)
|
|
*/
|
|
router.delete('/rides/:id', authenticate, rideController.deleteRide);
|
|
|
|
/**
|
|
* @route POST /api/viaggi/rides/:id/complete
|
|
* @desc Completa un viaggio
|
|
* @access Private (solo driver)
|
|
*/
|
|
router.post('/rides/:id/complete', authenticate, rideController.completeRide);
|
|
|
|
router.post('/rides/:rideId/favorite', authenticate, rideController.toggleFavoriteRide);
|
|
|
|
// ============================================================
|
|
// 📊 WIDGET & STATS
|
|
// ============================================================
|
|
|
|
/**
|
|
* @route GET /api/viaggi/widget/data
|
|
* @desc Dati completi per widget homepage
|
|
* @access Private
|
|
*/
|
|
router.get('/widget/data', authenticate, rideController.getWidgetData);
|
|
|
|
/**
|
|
* @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/viaggi/cities/recents
|
|
* @desc città recenti per autocomplete
|
|
* @access Public
|
|
*/
|
|
router.get('/cities/recent', authenticate, rideController.getRecentCities);
|
|
|
|
// ============================================================
|
|
// 📩 REQUESTS - Richieste Passaggio
|
|
// ============================================================
|
|
|
|
/**
|
|
* @route POST /api/viaggi/requests
|
|
* @desc Crea richiesta passaggio per un viaggio
|
|
* @access Private
|
|
*/
|
|
router.post('/requests', authenticate, rideRequestController.createRequest);
|
|
|
|
// ⚠️ IMPORTANTE: Route specifiche PRIMA di quelle con :id
|
|
|
|
/**
|
|
* @route GET /api/viaggi/requests/received
|
|
* @desc Richieste ricevute (sono conducente)
|
|
* @access Private
|
|
*/
|
|
router.get('/requests/received', authenticate, rideRequestController.getReceivedRequests);
|
|
|
|
/**
|
|
* @route GET /api/viaggi/requests/sent
|
|
* @desc Richieste inviate (sono passeggero)
|
|
* @access Private
|
|
*/
|
|
router.get('/requests/sent', authenticate, rideRequestController.getSentRequests);
|
|
|
|
/**
|
|
* @route GET /api/viaggi/requests/pending
|
|
* @desc Richieste pendenti (per il conducente)
|
|
* @access Private
|
|
*/
|
|
router.get('/requests/pending', authenticate, rideRequestController.getPendingRequests);
|
|
|
|
/**
|
|
* @route GET /api/viaggi/requests/my
|
|
* @desc Le mie richieste (come passeggero)
|
|
* @access Private
|
|
*/
|
|
router.get('/requests/my', authenticate, rideRequestController.getMyRequests);
|
|
|
|
/**
|
|
* @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 con :id DOPO tutte le route specifiche
|
|
|
|
/**
|
|
* @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/viaggi/requests/:id/accept
|
|
* @desc Accetta richiesta passaggio
|
|
* @access Private (solo driver)
|
|
*/
|
|
router.post('/requests/:id/accept', authenticate, rideRequestController.acceptRequest);
|
|
|
|
/**
|
|
* @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/viaggi/requests/:id/cancel
|
|
* @desc Cancella richiesta/prenotazione
|
|
* @access Private (driver o passenger)
|
|
*/
|
|
router.post('/requests/:id/cancel', authenticate, rideRequestController.cancelRequest);
|
|
// ============================================================
|
|
// 💬 CHAT - Messaggistica
|
|
// ============================================================
|
|
|
|
/**
|
|
* @route GET /api/viaggi/chats
|
|
* @desc Lista tutte le mie chat
|
|
* @access Private
|
|
*/
|
|
router.get('/chats', authenticate, chatController.getUserChats);
|
|
|
|
/**
|
|
* @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/viaggi/chats/direct
|
|
* @desc Ottieni o crea chat diretta con utente
|
|
* @access Private
|
|
*/
|
|
router.post('/chats/direct', authenticate, chatController.getOrCreateDirectChat);
|
|
|
|
/**
|
|
* @route GET /api/viaggi/chats/:id
|
|
* @desc Dettaglio chat
|
|
* @access Private (solo partecipanti)
|
|
*/
|
|
router.get('/chats/:chatId', authenticate, chatController.getChatById);
|
|
|
|
/**
|
|
* @route GET /api/viaggi/chats/:id/messages
|
|
* @desc Messaggi di una chat
|
|
* @access Private (solo partecipanti)
|
|
*/
|
|
router.get('/chats/:chatId/messages', authenticate, chatController.getChatMessages);
|
|
|
|
/**
|
|
* @route POST /api/viaggi/chats/:id/messages
|
|
* @desc Invia messaggio
|
|
* @access Private (solo partecipanti)
|
|
*/
|
|
router.post('/chats/:chatId/messages', authenticate, chatController.sendMessage);
|
|
|
|
/**
|
|
* @route PUT /api/viaggi/chats/:id/read
|
|
* @desc Segna chat come letta
|
|
* @access Private (solo partecipanti)
|
|
* @fix Corretto: markAsRead → markChatAsRead
|
|
*/
|
|
router.put('/chats/:chatId/read', authenticate, chatController.markChatAsRead);
|
|
|
|
/**
|
|
* @route PUT /api/viaggi/chats/:id/block
|
|
* @desc Blocca/sblocca chat
|
|
* @access Private (solo partecipanti)
|
|
*/
|
|
router.put('/chats/:chatId/block', authenticate, chatController.toggleBlockChat);
|
|
|
|
/**
|
|
* @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/:chatId/mute', authenticate, chatController.toggleMuteChat);
|
|
|
|
/**
|
|
* @route DELETE /api/viaggi/chats/:chatId/messages/:messageId
|
|
* @desc Elimina messaggio
|
|
* @access Private (solo mittente)
|
|
* @fix Corretto: /messages/:id → /chats/:chatId/messages/:messageId
|
|
*/
|
|
router.delete('/chats/:chatId/messages/:messageId', authenticate, chatController.deleteMessage);
|
|
|
|
/**
|
|
* @route DELETE /api/viaggi/chats/:id
|
|
* @desc Elimina chat (soft delete)
|
|
* @access Private (solo partecipanti)
|
|
*/
|
|
router.delete('/chats/:chatId', authenticate, chatController.deleteChat);
|
|
|
|
// ============================================================
|
|
// ⭐ FEEDBACK - Recensioni
|
|
// ============================================================
|
|
|
|
/**
|
|
* @route POST /api/viaggi/feedback
|
|
* @desc Crea feedback per un viaggio
|
|
* @access Private
|
|
*/
|
|
router.post('/feedback', authenticate, feedbackController.createFeedback);
|
|
|
|
/**
|
|
* @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/viaggi/feedback/my/given
|
|
* @desc I miei feedback lasciati
|
|
* @access Private
|
|
*/
|
|
router.get('/feedback/my/given', authenticate, feedbackController.getMyGivenFeedbacks);
|
|
|
|
/**
|
|
* @route GET /api/viaggi/feedback/user/:userId
|
|
* @desc Feedback di un utente
|
|
* @access Public
|
|
*/
|
|
router.get('/feedback/user/:userId', feedbackController.getFeedbacksForUser);
|
|
|
|
/**
|
|
* @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/viaggi/feedback/ride/:rideId
|
|
* @desc Feedback per un viaggio
|
|
* @access Public
|
|
*/
|
|
router.get('/feedback/ride/:rideId', feedbackController.getRideFeedback);
|
|
|
|
/**
|
|
* @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/viaggi/feedback/:id/response
|
|
* @desc Rispondi a un feedback
|
|
* @access Private (solo destinatario)
|
|
*/
|
|
router.post('/feedback/:id/response', authenticate, feedbackController.respondToFeedback);
|
|
|
|
/**
|
|
* @route POST /api/viaggi/feedback/:id/report
|
|
* @desc Segnala feedback
|
|
* @access Private
|
|
*/
|
|
router.post('/feedback/:id/report', authenticate, feedbackController.reportFeedback);
|
|
|
|
/**
|
|
* @route POST /api/viaggi/feedback/:id/helpful
|
|
* @desc Segna feedback come utile
|
|
* @access Private
|
|
*/
|
|
router.post('/feedback/:id/helpful', authenticate, feedbackController.markAsHelpful);
|
|
|
|
// ============================================================
|
|
// 🗺️ GEO - Geocoding & Mappe (Open Source)
|
|
// ============================================================
|
|
|
|
// /* /**
|
|
// * @route GET /api/viaggi/geo/autocomplete
|
|
// * @desc Autocomplete città (Photon)
|
|
// * @access Public
|
|
// */
|
|
// router.get('/geo/autocomplete', geocodingController.autocomplete);
|
|
|
|
// /**
|
|
// * @route GET /api/viaggi/geo/cities/it
|
|
// * @desc Cerca città italiane
|
|
// * @access Public
|
|
// */
|
|
// router.get('/geo/cities/it', geocodingController.searchItalianCities);
|
|
|
|
// /**
|
|
// * @route GET /api/viaggi/geo/geocode
|
|
// * @desc Indirizzo → Coordinate
|
|
// * @access Public
|
|
// */
|
|
// router.get('/geo/geocode', geocodingController.geocode);
|
|
|
|
// /**
|
|
// * @route GET /api/viaggi/geo/reverse
|
|
// * @desc Coordinate → Indirizzo
|
|
// * @access Public
|
|
// */
|
|
// router.get('/geo/reverse', geocodingController.reverseGeocode);
|
|
|
|
// /**
|
|
// * @route GET /api/viaggi/geo/route
|
|
// * @desc Calcola percorso tra punti
|
|
// * @access Public
|
|
// */
|
|
// router.get('/geo/route', geocodingController.getRoute);
|
|
|
|
// /**
|
|
// * @route GET /api/viaggi/geo/distance
|
|
// * @desc Calcola distanza e durata
|
|
// * @access Public
|
|
// */
|
|
// router.get('/geo/distance', geocodingController.getDistance);
|
|
|
|
// /**
|
|
// * @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/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.idapp;
|
|
|
|
const { User } = require('../models/user');
|
|
const Ride = require('../models/viaggi/Ride');
|
|
const Feedback = require('../models/viaggi/Feedback');
|
|
|
|
// Dati utente
|
|
const user = await User.findById(userId).select(
|
|
'username name surname profile.img profile.Biografia profile.driverProfile profile.preferences.languages'
|
|
);
|
|
|
|
if (!user) {
|
|
return res.status(404).json({
|
|
success: false,
|
|
message: 'Utente non trovato',
|
|
});
|
|
}
|
|
|
|
// Statistiche viaggi
|
|
const [ridesAsDriver, ridesAsPassenger, completedRides] = await Promise.all([
|
|
Ride.countDocuments({ idapp, userId, type: 'offer' }),
|
|
Ride.countDocuments({ idapp, 'confirmedPassengers.userId': userId }),
|
|
Ride.countDocuments({ idapp, userId, status: 'completed' }),
|
|
]);
|
|
|
|
// Ultimi viaggi come driver
|
|
const recentRides = await Ride.find({
|
|
idapp,
|
|
userId,
|
|
type: 'offer',
|
|
status: { $in: ['active', 'completed'] },
|
|
})
|
|
.select('departure destination departureDate status')
|
|
.sort({ departureDate: -1 })
|
|
.limit(5);
|
|
|
|
// Statistiche feedback
|
|
let feedbackStats = { averageRating: 0, totalFeedback: 0 };
|
|
try {
|
|
feedbackStats = await Feedback.getStatsForUser(idapp, userId);
|
|
} catch (e) {
|
|
console.log('Feedback stats non disponibili');
|
|
}
|
|
|
|
// Ultimi feedback ricevuti
|
|
let recentFeedback = [];
|
|
try {
|
|
recentFeedback = await Feedback.find({
|
|
idapp,
|
|
toUserId: userId,
|
|
isPublic: true,
|
|
})
|
|
.populate('fromUserId', 'username name profile.img')
|
|
.sort({ createdAt: -1 })
|
|
.limit(3);
|
|
} catch (e) {
|
|
console.log('Recent feedback non disponibili');
|
|
}
|
|
|
|
res.status(200).json({
|
|
success: true,
|
|
data: {
|
|
user: {
|
|
_id: user._id,
|
|
username: user.username,
|
|
name: user.name,
|
|
surname: user.surname,
|
|
img: user.profile?.img,
|
|
bio: user.profile?.Biografia,
|
|
driverProfile: user.profile?.driverProfile,
|
|
languages: user.profile?.preferences?.languages,
|
|
},
|
|
stats: {
|
|
ridesAsDriver,
|
|
ridesAsPassenger,
|
|
completedRides,
|
|
...feedbackStats,
|
|
},
|
|
recentRides,
|
|
recentFeedback,
|
|
},
|
|
});
|
|
} catch (error) {
|
|
console.error('Errore recupero profilo driver:', error);
|
|
res.status(500).json({
|
|
success: false,
|
|
message: 'Errore durante il recupero del profilo',
|
|
error: error.message,
|
|
});
|
|
}
|
|
});
|
|
|
|
/**
|
|
* @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.user.idapp;
|
|
const userId = req.user._id; // Assumo che ci sia un middleware di autenticazione
|
|
|
|
if (!userId) {
|
|
return res.status(401).json({
|
|
success: false,
|
|
message: 'Autenticazione richiesta',
|
|
});
|
|
}
|
|
|
|
const { User } = require('../models/user');
|
|
|
|
const user = await User.findById(userId).select('profile.driverProfile.vehicles');
|
|
|
|
const vehicles = user?.profile?.driverProfile?.vehicles || [];
|
|
|
|
res.status(200).json({
|
|
success: true,
|
|
data: vehicles,
|
|
});
|
|
} catch (error) {
|
|
console.error('Errore recupero veicoli:', error);
|
|
res.status(500).json({
|
|
success: false,
|
|
message: 'Errore durante il recupero dei veicoli',
|
|
error: error.message,
|
|
});
|
|
}
|
|
});
|
|
|
|
/**
|
|
* @route PUT /api/viaggi/driver/profile
|
|
* @desc Aggiorna profilo conducente
|
|
* @access Private
|
|
*/
|
|
router.put('/driver/profile', authenticate, async (req, res) => {
|
|
try {
|
|
const userId = req.user._id;
|
|
const { idapp, driverProfile, preferences } = req.body;
|
|
|
|
const { User } = require('../models/user');
|
|
|
|
const updateData = {};
|
|
|
|
if (driverProfile) {
|
|
// Merge con dati esistenti
|
|
Object.keys(driverProfile).forEach((key) => {
|
|
updateData[`profile.driverProfile.${key}`] = driverProfile[key];
|
|
});
|
|
}
|
|
|
|
if (preferences) {
|
|
Object.keys(preferences).forEach((key) => {
|
|
updateData[`profile.preferences.${key}`] = preferences[key];
|
|
});
|
|
}
|
|
|
|
const user = await User.findByIdAndUpdate(userId, { $set: updateData }, { new: true }).select(
|
|
'profile.driverProfile profile.preferences'
|
|
);
|
|
|
|
res.status(200).json({
|
|
success: true,
|
|
message: 'Profilo aggiornato',
|
|
data: user.profile,
|
|
});
|
|
} catch (error) {
|
|
console.error('Errore aggiornamento profilo:', error);
|
|
res.status(500).json({
|
|
success: false,
|
|
message: "Errore durante l'aggiornamento",
|
|
error: error.message,
|
|
});
|
|
}
|
|
});
|
|
|
|
/**
|
|
* @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.vehicle ? req.body.vehicle : req.body;
|
|
|
|
const { User } = require('../models/user');
|
|
|
|
const user = await User.findByIdAndUpdate(
|
|
userId,
|
|
{
|
|
$push: { 'profile.driverProfile.vehicles': vehicle },
|
|
$set: { 'profile.driverProfile.isDriver': true },
|
|
},
|
|
{ new: true }
|
|
).select('profile.driverProfile.vehicles');
|
|
|
|
res.status(201).json({
|
|
success: true,
|
|
message: 'Veicolo aggiunto',
|
|
data: user.profile.driverProfile.vehicles,
|
|
});
|
|
} catch (error) {
|
|
console.error('Errore aggiunta veicolo:', error);
|
|
res.status(500).json({
|
|
success: false,
|
|
message: "Errore durante l'aggiunta del veicolo",
|
|
error: error.message,
|
|
});
|
|
}
|
|
});
|
|
|
|
/**
|
|
* @route PUT /api/viaggi/driver/vehicles/:vehicleId
|
|
* @desc Aggiorna veicolo
|
|
* @access Private
|
|
*/
|
|
router.put('/driver/vehicles/:vehicleId', authenticate, async (req, res) => {
|
|
try {
|
|
const userId = req.user._id;
|
|
const { vehicleId } = req.params;
|
|
const vehicle = req.body;
|
|
|
|
const { User } = require('../models/user');
|
|
|
|
const user = await User.findOneAndUpdate(
|
|
{
|
|
_id: userId,
|
|
'profile.driverProfile.vehicles._id': vehicleId,
|
|
},
|
|
{
|
|
$set: { 'profile.driverProfile.vehicles.$': { ...vehicle, _id: vehicleId } },
|
|
},
|
|
{ new: true }
|
|
).select('profile.driverProfile.vehicles');
|
|
|
|
if (!user) {
|
|
return res.status(404).json({
|
|
success: false,
|
|
message: 'Veicolo non trovato',
|
|
});
|
|
}
|
|
|
|
res.status(200).json({
|
|
success: true,
|
|
message: 'Veicolo aggiornato',
|
|
data: user.profile.driverProfile.vehicles,
|
|
});
|
|
} catch (error) {
|
|
console.error('Errore aggiornamento veicolo:', error);
|
|
res.status(500).json({
|
|
success: false,
|
|
message: "Errore durante l'aggiornamento",
|
|
error: error.message,
|
|
});
|
|
}
|
|
});
|
|
|
|
/**
|
|
* @route GET /api/viaggi/driver/vehicles/:vehicleId
|
|
* @desc Ottieni dettagli di un veicolo specifico
|
|
* @access Private
|
|
*/
|
|
router.get('/driver/vehicles/:vehicleId', authenticate, async (req, res) => {
|
|
try {
|
|
const userId = req.user._id;
|
|
const { vehicleId } = req.params;
|
|
|
|
const { User } = require('../models/user');
|
|
|
|
const user = await User.findOne({
|
|
_id: userId,
|
|
'profile.driverProfile.vehicles._id': vehicleId,
|
|
}).select('profile.driverProfile.vehicles');
|
|
|
|
if (!user) {
|
|
return res.status(404).json({
|
|
success: false,
|
|
message: 'Veicolo non trovato',
|
|
});
|
|
}
|
|
|
|
// Trova il veicolo specifico nell'array
|
|
const vehicle = user.profile.driverProfile.vehicles.find((v) => v._id.toString() === vehicleId);
|
|
|
|
if (!vehicle) {
|
|
return res.status(404).json({
|
|
success: false,
|
|
message: 'Veicolo non trovato',
|
|
});
|
|
}
|
|
|
|
res.status(200).json({
|
|
success: true,
|
|
data: vehicle,
|
|
});
|
|
} catch (error) {
|
|
console.error('Errore recupero veicolo:', error);
|
|
res.status(500).json({
|
|
success: false,
|
|
message: 'Errore durante il recupero del veicolo',
|
|
error: error.message,
|
|
});
|
|
}
|
|
});
|
|
|
|
/**
|
|
* @route DELETE /api/viaggi/driver/vehicles/:vehicleId
|
|
* @desc Rimuovi veicolo
|
|
* @access Private
|
|
*/
|
|
router.delete('/driver/vehicles/:vehicleId', authenticate, async (req, res) => {
|
|
try {
|
|
const userId = req.user._id;
|
|
const { vehicleId } = req.params;
|
|
|
|
const { User } = require('../models/user');
|
|
|
|
await User.findByIdAndUpdate(userId, {
|
|
$pull: { 'profile.driverProfile.vehicles': { _id: vehicleId } },
|
|
});
|
|
|
|
res.status(200).json({
|
|
success: true,
|
|
message: 'Veicolo rimosso',
|
|
});
|
|
} catch (error) {
|
|
console.error('Errore rimozione veicolo:', error);
|
|
res.status(500).json({
|
|
success: false,
|
|
message: 'Errore durante la rimozione',
|
|
error: error.message,
|
|
});
|
|
}
|
|
});
|
|
|
|
/**
|
|
* @route POST /api/viaggi/driver/vehicles/:vehicleId/default
|
|
* @desc Imposta veicolo come predefinito
|
|
* @access Private
|
|
*/
|
|
router.post('/driver/vehicles/:vehicleId/default', authenticate, async (req, res) => {
|
|
try {
|
|
const userId = req.user._id;
|
|
const { vehicleId } = req.params;
|
|
|
|
const { User } = require('../models/user');
|
|
|
|
// Prima rimuovi isDefault da tutti
|
|
await User.updateOne({ _id: userId }, { $set: { 'profile.driverProfile.vehicles.$[].isDefault': false } });
|
|
|
|
// Poi imposta quello selezionato
|
|
await User.updateOne(
|
|
{ _id: userId, 'profile.driverProfile.vehicles._id': vehicleId },
|
|
{ $set: { 'profile.driverProfile.vehicles.$.isDefault': true } }
|
|
);
|
|
|
|
res.status(200).json({
|
|
success: true,
|
|
message: 'Veicolo predefinito impostato',
|
|
});
|
|
} catch (error) {
|
|
console.error('Errore impostazione default:', error);
|
|
res.status(500).json({
|
|
success: false,
|
|
message: "Errore durante l'operazione",
|
|
error: error.message,
|
|
});
|
|
}
|
|
});
|
|
|
|
// ============================================================
|
|
// 📊 CONTRIB TYPES - Tipi di Contributo
|
|
// ============================================================
|
|
|
|
/**
|
|
* @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.idapp;
|
|
|
|
const contribTypes = await Contribtype.find({ idapp });
|
|
|
|
res.status(200).json({
|
|
success: true,
|
|
data: contribTypes,
|
|
});
|
|
} catch (error) {
|
|
console.error('Errore recupero contrib types:', error);
|
|
res.status(500).json({
|
|
success: false,
|
|
message: 'Errore durante il recupero',
|
|
error: error.message,
|
|
});
|
|
}
|
|
});
|
|
|
|
const vehiclePhotoStorage = multer.diskStorage({
|
|
destination: (req, file, cb) => {
|
|
const webServerDir = tools.getdirByIdApp(req.user.idapp) + '/upload/vehicles';
|
|
if (!fs.existsSync(webServerDir)) {
|
|
fs.mkdirSync(webServerDir, { recursive: true });
|
|
}
|
|
console.log('📁 Destinazione foto veicolo:', webServerDir);
|
|
cb(null, webServerDir);
|
|
},
|
|
filename: (req, file, cb) => {
|
|
const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1e9);
|
|
const ext = path.extname(file.originalname);
|
|
const filename = 'vehicle-' + uniqueSuffix + ext;
|
|
console.log('📝 Nome file generato:', filename);
|
|
cb(null, filename);
|
|
},
|
|
});
|
|
|
|
const uploadVehiclePhoto = multer({
|
|
storage: vehiclePhotoStorage,
|
|
limits: {
|
|
fileSize: 5 * 1024 * 1024, // 5MB
|
|
},
|
|
fileFilter: (req, file, cb) => {
|
|
console.log('🔍 File ricevuto:', {
|
|
fieldname: file.fieldname,
|
|
originalname: file.originalname,
|
|
mimetype: file.mimetype,
|
|
});
|
|
|
|
const allowedMimes = ['image/jpeg', 'image/jpg', 'image/png', 'image/webp', 'image/gif'];
|
|
if (allowedMimes.includes(file.mimetype)) {
|
|
cb(null, true);
|
|
} else {
|
|
cb(new Error('Solo immagini sono ammesse'), false);
|
|
}
|
|
},
|
|
});
|
|
|
|
/**
|
|
* @route POST /api/viaggi/upload/vehicle-photos
|
|
* @desc Upload multiple foto veicolo (max 5)
|
|
* @access Private
|
|
*/
|
|
router.post(
|
|
'/upload/vehicle-photos',
|
|
authenticate,
|
|
(req, res, next) => {
|
|
console.log('🚀 Request ricevuta per upload multiple foto veicolo');
|
|
next();
|
|
},
|
|
uploadVehiclePhoto.array('photos', 5),
|
|
(err, req, res, next) => {
|
|
if (err) {
|
|
console.error('❌ Errore multer:', err);
|
|
return res.status(400).json({
|
|
success: false,
|
|
message: err.message || 'Errore durante upload',
|
|
});
|
|
}
|
|
next();
|
|
},
|
|
async (req, res) => {
|
|
try {
|
|
console.log('✅ Files ricevuti:', req.files);
|
|
|
|
if (!req.files || req.files.length === 0) {
|
|
return res.status(400).json({
|
|
success: false,
|
|
message: 'Nessuna foto caricata',
|
|
});
|
|
}
|
|
|
|
const photoUrls = req.files.map((file) => `/upload/vehicles/${file.filename}`);
|
|
|
|
res.status(200).json({
|
|
success: true,
|
|
message: `${req.files.length} foto caricate con successo`,
|
|
data: {
|
|
urls: photoUrls,
|
|
count: req.files.length,
|
|
},
|
|
});
|
|
} catch (error) {
|
|
console.error('❌ Errore upload foto veicolo:', error);
|
|
|
|
// Elimina tutti i file in caso di errore
|
|
if (req.files && req.files.length > 0) {
|
|
req.files.forEach((file) => {
|
|
if (file.path) {
|
|
fs.unlinkSync(file.path);
|
|
}
|
|
});
|
|
}
|
|
|
|
res.status(500).json({
|
|
success: false,
|
|
message: "Errore durante l'upload delle foto",
|
|
error: error.message,
|
|
});
|
|
}
|
|
}
|
|
);
|
|
|
|
/**
|
|
* @route POST /api/viaggi/upload/vehicle-photo
|
|
* @desc Upload foto veicolo
|
|
* @access Private
|
|
*/
|
|
router.post(
|
|
'/upload/vehicle-photo',
|
|
authenticate,
|
|
(req, res, next) => {
|
|
console.log('🚀 Request ricevuta per upload foto veicolo');
|
|
console.log('📋 Headers:', req.headers);
|
|
console.log('📦 Body type:', typeof req.body);
|
|
next();
|
|
},
|
|
uploadVehiclePhoto.single('photo'),
|
|
(err, req, res, next) => {
|
|
// Gestione errori multer
|
|
if (err) {
|
|
console.error('❌ Errore multer:', err);
|
|
return res.status(400).json({
|
|
success: false,
|
|
message: err.message || 'Errore durante upload',
|
|
});
|
|
}
|
|
next();
|
|
},
|
|
async (req, res) => {
|
|
try {
|
|
console.log('✅ File ricevuto:', req.file);
|
|
console.log('📄 Body:', req.body);
|
|
|
|
if (!req.file) {
|
|
return res.status(400).json({
|
|
success: false,
|
|
message: 'Nessuna foto caricata - req.file è undefined',
|
|
});
|
|
}
|
|
|
|
const photoUrl = `/upload/vehicles/${req.file.filename}`;
|
|
|
|
res.status(200).json({
|
|
success: true,
|
|
message: 'Foto caricata con successo',
|
|
data: {
|
|
url: photoUrl,
|
|
filename: req.file.filename,
|
|
},
|
|
});
|
|
} catch (error) {
|
|
console.error('❌ Errore upload foto veicolo:', error);
|
|
|
|
if (req.file && req.file.path) {
|
|
fs.unlinkSync(req.file.path);
|
|
}
|
|
|
|
res.status(500).json({
|
|
success: false,
|
|
message: "Errore durante l'upload della foto",
|
|
error: error.message,
|
|
});
|
|
}
|
|
}
|
|
);
|
|
|
|
/**
|
|
* @route DELETE /api/viaggi/upload/vehicle-photo
|
|
* @desc Elimina foto veicolo
|
|
* @access Private
|
|
*/
|
|
router.delete('/upload/vehicle-photo', authenticate, async (req, res) => {
|
|
try {
|
|
const { photoUrl } = req.body;
|
|
|
|
if (!photoUrl) {
|
|
return res.status(400).json({
|
|
success: false,
|
|
message: 'URL foto non fornito',
|
|
});
|
|
}
|
|
|
|
// Estrai il filename dall'URL
|
|
const filename = photoUrl.split('/').pop();
|
|
const filepath = path.join(vehiclesDir, filename);
|
|
|
|
// Verifica che il file esista
|
|
if (fs.existsSync(filepath)) {
|
|
fs.unlinkSync(filepath);
|
|
}
|
|
|
|
res.status(200).json({
|
|
success: true,
|
|
message: 'Foto eliminata con successo',
|
|
});
|
|
} catch (error) {
|
|
console.error('❌ Errore eliminazione foto:', error);
|
|
res.status(500).json({
|
|
success: false,
|
|
message: "Errore durante l'eliminazione della foto",
|
|
error: error.message,
|
|
});
|
|
}
|
|
});
|
|
|
|
// ============================================
|
|
// EXPORT ROUTES
|
|
// ============================================
|
|
|
|
|
|
module.exports = router;
|