const Visit = require('../models/visitModel'); // Track a visit exports.trackVisit = async (req, res) => { try { const { sessionId, page, userId } = req.body; if (!sessionId) { return res.status(400).json({ status: 'fail', message: 'Session ID is required', }); } // Get IP address from request const xForwardedFor = req.headers['x-forwarded-for']; const ipAddress = (xForwardedFor ? xForwardedFor.split(',')[0] : null) || req.connection.remoteAddress || req.socket.remoteAddress || req.ip; // Get user agent const userAgent = req.headers['user-agent'] || ''; // Grouping priority: // 1. If we have a userId, we find the ONE global record for this user // 2. If no userId (guest), we find a recent record for this sessionId const today = new Date(); today.setHours(0, 0, 0, 0); const query = { createdAt: { $gte: today } }; if (userId) { query.user = userId; } else { query.sessionId = sessionId; } let visit = await Visit.findOne(query).populate('user', 'name email'); if (visit) { // Grouping: Increment count and update last active time visit.count += 1; visit.lastVisitedAt = new Date(); // Update user ID if it was null before (guest just logged in) if (!visit.user && userId) { visit.user = userId; } // Update IP and User Agent to latest visit.ipAddress = ipAddress; visit.userAgent = userAgent; visit.page = page || '/'; await visit.save(); } else { // Create new visit document visit = await Visit.create({ user: userId || null, ipAddress, userAgent, page: page || '/', sessionId, count: 1, lastVisitedAt: new Date(), }); // Populate if it was a user if (userId) { await visit.populate('user', 'name email'); } } res.status(200).json({ status: 'success', data: { visit }, }); } catch (err) { res.status(500).json({ status: 'fail', message: err.message, }); } }; // Get visit statistics exports.getVisitStats = async (req, res) => { try { const now = new Date(); // Today's start const todayStart = new Date( now.getFullYear(), now.getMonth(), now.getDate(), ); // Current month boundaries const currentMonthStart = new Date(now.getFullYear(), now.getMonth(), 1); const currentMonthEnd = new Date( now.getFullYear(), now.getMonth() + 1, 0, 23, 59, 59, ); // Previous month boundaries const previousMonthStart = new Date( now.getFullYear(), now.getMonth() - 1, 1, ); const previousMonthEnd = new Date( now.getFullYear(), now.getMonth(), 0, 23, 59, 59, ); // Helper for summing counts and counting documents const getStats = async (filter = {}) => { const uniqueVisits = await Visit.countDocuments(filter); const totalResult = await Visit.aggregate([ { $match: filter }, { $group: { _id: null, total: { $sum: '$count' } } }, ]); const totalVisits = totalResult.length > 0 ? totalResult[0].total : 0; return { uniqueVisits, totalVisits }; }; // Get all time stats const allTime = await getStats(); // Get today's stats const today = await getStats({ createdAt: { $gte: todayStart } }); // Get monthly stats for growth calculation const currentMonth = await getStats({ createdAt: { $gte: currentMonthStart, $lte: currentMonthEnd }, }); const previousMonth = await getStats({ createdAt: { $gte: previousMonthStart, $lte: previousMonthEnd }, }); // Calculate monthly growth percentage based on TOTAL visits let monthlyGrowth = 0; if (previousMonth.totalVisits > 0) { monthlyGrowth = ((currentMonth.totalVisits - previousMonth.totalVisits) / previousMonth.totalVisits) * 100; } else if (currentMonth.totalVisits > 0) { monthlyGrowth = 100; } res.status(200).json({ status: 'success', data: { totalVisits: allTime.totalVisits, uniqueVisits: allTime.uniqueVisits, todayVisits: today.totalVisits, todayUnique: today.uniqueVisits, currentMonthVisits: currentMonth.totalVisits, previousMonthVisits: previousMonth.totalVisits, monthlyGrowth: parseFloat(monthlyGrowth.toFixed(1)), }, }); } catch (err) { res.status(500).json({ status: 'fail', message: err.message, }); } }; // Get all visits (admin only) exports.getAllVisits = async (req, res) => { try { const { page = 1, limit = 50, startDate, endDate } = req.query; // Build filter const filter = {}; if (startDate || endDate) { filter.createdAt = {}; if (startDate) filter.createdAt.$gte = new Date(startDate); if (endDate) filter.createdAt.$lte = new Date(endDate); } const visits = await Visit.find(filter) .populate('user', 'name email') .sort({ createdAt: -1 }) .limit(limit * 1) .skip((page - 1) * limit); const count = await Visit.countDocuments(filter); res.status(200).json({ status: 'success', results: visits.length, totalPages: Math.ceil(count / limit), currentPage: page, data: { visits }, }); } catch (err) { res.status(500).json({ status: 'fail', message: err.message, }); } };