samoulla-backend / controllers /visitController.js
Samoulla Sync Bot
Auto-deploy Samoulla Backend: b68e45770de26ed39feb4b1c0925e5345eb3a61d
634b9bb
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,
});
}
};