mkcart / backend /controllers /adminController.js
Kumar
updated
c2efbe6
const Admin = require('../models/adminModel');
const User = require('../models/userModel');
const ProductModel = require('../models/productModel');
const asyncHandler = require('express-async-handler');
const jwt = require('jsonwebtoken');
const bcrypt = require('bcryptjs');
const multer = require('multer');
const path = require('path');
const fs = require('fs');
const { getNextImageNumber, isValidImageFile } = require('../utils/imageNumbering');
const generateAdminToken = (res, adminId) => {
const token = jwt.sign({ id: adminId }, process.env.JWT_SECRET_KEY, {
expiresIn: '24h',
});
const isProduction = process.env.NODE_ENV === 'production';
res.cookie('adminToken', token, {
httpOnly: true,
secure: isProduction,
sameSite: isProduction ? 'none' : 'lax',
maxAge: 24 * 60 * 60 * 1000,
...(isProduction && process.env.COOKIE_DOMAIN && {
domain: process.env.COOKIE_DOMAIN
})
});
return token;
};
const adminLogin = asyncHandler(async (req, res) => {
const { username, password } = req.body;
if (!username || !password) {
res.status(400);
throw new Error('Please provide username and password');
}
const admin = await Admin.findOne({
$or: [{ username }, { email: username }]
});
if (!admin) {
res.status(401);
throw new Error('Invalid credentials');
}
if (!admin.isActive) {
res.status(401);
throw new Error('Account is deactivated');
}
const isPasswordValid = await admin.checkPassword(password);
if (!isPasswordValid) {
res.status(401);
throw new Error('Invalid credentials');
}
await Admin.findByIdAndUpdate(admin._id, { lastLogin: new Date() });
const token = generateAdminToken(res, admin._id);
res.status(200).json({
_id: admin._id,
username: admin.username,
email: admin.email,
firstName: admin.firstName,
lastName: admin.lastName,
role: admin.role,
permissions: admin.permissions,
avatar: admin.avatar,
token
});
});
const adminLogout = asyncHandler(async (req, res) => {
const isProduction = process.env.NODE_ENV === 'production';
res.cookie('adminToken', '', {
httpOnly: true,
expires: new Date(0),
secure: isProduction,
sameSite: isProduction ? 'none' : 'lax',
...(isProduction && process.env.COOKIE_DOMAIN && {
domain: process.env.COOKIE_DOMAIN
})
});
res.status(200).json({ message: 'Logged out successfully' });
});
const getAdminProfile = asyncHandler(async (req, res) => {
const admin = await Admin.findById(req.admin._id).select('-password');
res.status(200).json(admin);
});
const updateAdminProfile = asyncHandler(async (req, res) => {
const { firstName, lastName, email, avatar, preferences } = req.body;
const admin = await Admin.findById(req.admin._id);
if (admin) {
admin.firstName = firstName || admin.firstName;
admin.lastName = lastName || admin.lastName;
admin.email = email || admin.email;
admin.avatar = avatar || admin.avatar;
admin.preferences = { ...admin.preferences, ...preferences };
const updatedAdmin = await admin.save();
res.status(200).json({
_id: updatedAdmin._id,
username: updatedAdmin.username,
email: updatedAdmin.email,
firstName: updatedAdmin.firstName,
lastName: updatedAdmin.lastName,
role: updatedAdmin.role,
permissions: updatedAdmin.permissions,
avatar: updatedAdmin.avatar,
preferences: updatedAdmin.preferences
});
} else {
res.status(404);
throw new Error('Admin not found');
}
});
const getAllUsers = asyncHandler(async (req, res) => {
const page = parseInt(req.query.page) || 1;
const limit = parseInt(req.query.limit) || 10;
const search = req.query.search || '';
const filter = req.query.filter || 'all';
const skip = (page - 1) * limit;
const andConditions = [];
if (search) {
andConditions.push({
$or: [
{ name: { $regex: search, $options: 'i' } },
{ email: { $regex: search, $options: 'i' } }
]
});
}
if (filter === 'active') {
andConditions.push({
$or: [
{ isActive: true },
{ isActive: { $exists: false } }
]
});
} else if (filter === 'inactive') {
andConditions.push({ isActive: false });
}
const query = andConditions.length > 0 ? { $and: andConditions } : {};
const users = await User.find(query)
.select('-password')
.sort({ createdAt: -1 })
.skip(skip)
.limit(limit);
const usersWithDetails = users.map(user => {
let recentAddress = '', recentCity = '', recentPhone = '';
if (user.orders && user.orders.length > 0) {
const latestOrder = user.orders.reduce((latest, curr) => {
return (!latest || (curr.createdAt > latest.createdAt)) ? curr : latest;
}, null);
if (latestOrder && latestOrder.shippingAddress) {
recentAddress = latestOrder.shippingAddress.address || latestOrder.shippingAddress.fullName || '';
recentCity = latestOrder.shippingAddress.city || '';
recentPhone = latestOrder.shippingAddress.phone || '';
}
}
return {
_id: user._id,
name: user.name,
email: user.email,
isActive: typeof user.isActive === 'boolean' ? user.isActive : true,
createdAt: user.createdAt,
recentAddress,
recentCity,
recentPhone,
country: user.country || '',
gender: user.gender || '',
dateOfBirth: user.dateOfBirth || ''
};
});
const total = await User.countDocuments(query);
res.status(200).json({
users: usersWithDetails,
pagination: {
page,
limit,
total,
pages: Math.ceil(total / limit)
}
});
});
const getUserById = asyncHandler(async (req, res) => {
const user = await User.findById(req.params.id).select('-password');
let recentAddress = '', recentCity = '', recentPhone = '';
if (user && user.orders && user.orders.length > 0) {
const latestOrder = user.orders.reduce((latest, curr) => {
return (!latest || (curr.createdAt > latest.createdAt)) ? curr : latest;
}, null);
if (latestOrder && latestOrder.shippingAddress) {
recentAddress = latestOrder.shippingAddress.address || latestOrder.shippingAddress.fullName || '';
recentCity = latestOrder.shippingAddress.city || '';
recentPhone = latestOrder.shippingAddress.phone || '';
}
}
if (user) {
res.status(200).json({
_id: user._id,
name: user.name,
email: user.email,
isActive: typeof user.isActive === 'boolean' ? user.isActive : true,
createdAt: user.createdAt,
recentAddress,
recentCity,
recentPhone,
country: user.country || '',
gender: user.gender || '',
dateOfBirth: user.dateOfBirth || ''
});
} else {
res.status(404);
throw new Error('User not found');
}
});
const updateUser = asyncHandler(async (req, res) => {
const { name, email, isActive } = req.body;
const user = await User.findById(req.params.id);
if (user) {
user.name = name || user.name;
user.email = email || user.email;
if (typeof isActive === 'boolean') {
user.isActive = isActive;
}
const updatedUser = await user.save();
res.status(200).json({
_id: updatedUser._id,
name: updatedUser.name,
email: updatedUser.email,
isActive: updatedUser.isActive
});
} else {
res.status(404);
throw new Error('User not found');
}
});
const deleteUser = asyncHandler(async (req, res) => {
const user = await User.findById(req.params.id);
if (user) {
await User.findByIdAndDelete(req.params.id);
res.status(200).json({ message: 'User deleted successfully' });
} else {
res.status(404);
throw new Error('User not found');
}
});
const createUser = asyncHandler(async (req, res) => {
const { name, email, password, isActive = true } = req.body;
if (!name || !email || !password) {
res.status(400);
throw new Error('Please provide name, email, and password');
}
const existingUser = await User.findOne({ email });
if (existingUser) {
res.status(400);
throw new Error('User with this email already exists');
}
const user = await User.create({
name,
email,
password,
isActive,
createdAt: new Date(),
});
res.status(201).json({
_id: user._id,
name: user.name,
email: user.email,
isActive: user.isActive,
createdAt: user.createdAt
});
});
const getAllProducts = asyncHandler(async (req, res) => {
const page = parseInt(req.query.page) || 1;
const limit = parseInt(req.query.limit) || 10;
const search = req.query.search || '';
const category = req.query.category || '';
const sortBy = req.query.sortBy || 'createdAt';
const sortOrder = req.query.sortOrder || 'desc';
const skip = (page - 1) * limit;
let query = {};
if (search) {
query.name = { $regex: search, $options: 'i' };
}
if (category) {
query.category = category;
}
const sortOptions = {};
sortOptions[sortBy] = sortOrder === 'desc' ? -1 : 1;
const products = await ProductModel.find(query)
.sort(sortOptions)
.skip(skip)
.limit(limit);
const total = await ProductModel.countDocuments(query);
res.status(200).json({
products,
pagination: {
page,
limit,
total,
pages: Math.ceil(total / limit)
}
});
});
const createProduct = asyncHandler(async (req, res) => {
const {
name,
price,
description,
category,
seller,
stock,
images,
specifications,
warrantyDetails,
afterSalesSupport,
ratings,
numOfReviews,
PremiumBadge,
reviews
} = req.body;
if (!name || !price || !category) {
res.status(400);
throw new Error('Please provide name, price, and category');
}
await ensureCategoryExists(category);
console.log('Creating product with images:', images);
const product = await ProductModel.create({
name,
price,
description,
category,
seller,
stock: stock || 0,
images: images || [],
specifications,
warrantyDetails,
afterSalesSupport,
ratings: ratings ,
numOfReviews: numOfReviews || 0,
PremiumBadge,
reviews: reviews || []
});
console.log('Product created successfully:', product._id);
res.status(201).json(product);
});
const updateProduct = asyncHandler(async (req, res) => {
const {
name,
price,
description,
category,
seller,
stock,
images,
specifications,
warrantyDetails,
afterSalesSupport,
ratings,
numOfReviews,
PremiumBadge,
reviews
} = req.body;
const product = await ProductModel.findById(req.params.id);
if (product) {
console.log('Updating product with images:', images);
if (category && category !== product.category) {
await ensureCategoryExists(category);
}
product.name = name || product.name;
product.price = price || product.price;
product.description = description || product.description;
product.category = category || product.category;
product.seller = seller || product.seller;
product.stock = stock !== undefined ? stock : product.stock;
product.images = images || product.images;
product.specifications = specifications || product.specifications;
product.warrantyDetails = warrantyDetails || product.warrantyDetails;
product.afterSalesSupport = afterSalesSupport || product.afterSalesSupport;
product.ratings = ratings ||product.ratings;
product.numOfReviews = numOfReviews !== undefined ? numOfReviews : product.numOfReviews;
product.PremiumBadge = PremiumBadge !== undefined ? PremiumBadge : product.PremiumBadge;
product.reviews = reviews || product.reviews;
const updatedProduct = await product.save();
console.log('Product updated successfully:', updatedProduct._id);
res.status(200).json(updatedProduct);
} else {
res.status(404);
throw new Error('Product not found');
}
});
const deleteProduct = asyncHandler(async (req, res) => {
const product = await ProductModel.findById(req.params.id);
if (product) {
await ProductModel.findByIdAndDelete(req.params.id);
res.status(200).json({ message: 'Product deleted successfully' });
} else {
res.status(404);
throw new Error('Product not found');
}
});
const uploadProductImages = asyncHandler(async (req, res) => {
try {
console.log('Upload request received:', req.files);
if (!req.files || req.files.length === 0) {
res.status(400);
throw new Error('No files uploaded');
}
const uploadedImages = req.files.map(file => {
const imagePath = `/images/products/${file.filename}`;
console.log('Storing image path in database:', imagePath);
return {
image: imagePath
};
});
console.log('Uploaded images:', uploadedImages);
res.status(200).json({
success: true,
images: uploadedImages,
message: `${uploadedImages.length} image(s) uploaded successfully`
});
} catch (error) {
console.error('Upload error:', error);
res.status(500).json({
success: false,
message: 'Error uploading images',
error: error.message
});
}
});
const getDashboardStats = asyncHandler(async (req, res) => {
const today = new Date();
const startOfDay = new Date(today.setHours(0, 0, 0, 0));
const startOfWeek = new Date(today.setDate(today.getDate() - today.getDay()));
const startOfMonth = new Date(today.getFullYear(), today.getMonth(), 1);
const startOfYear = new Date(today.getFullYear(), 0, 1);
const [
totalUsers,
activeUsers,
newUsersToday,
newUsersThisWeek,
newUsersThisMonth,
newUsersThisYear,
totalProducts,
lowStockProducts,
outOfStockProducts,
highRatedProducts,
topProducts
] = await Promise.all([
User.countDocuments(),
User.countDocuments({ $or: [{ isActive: true }, { isActive: { $exists: false } }] }),
User.countDocuments({ createdAt: { $gte: startOfDay } }),
User.countDocuments({ createdAt: { $gte: startOfWeek } }),
User.countDocuments({ createdAt: { $gte: startOfMonth } }),
User.countDocuments({ createdAt: { $gte: startOfYear } }),
ProductModel.countDocuments(),
ProductModel.countDocuments({ stock: { $lt: 10, $gt: 0 } }),
ProductModel.countDocuments({ stock: 0 }),
ProductModel.countDocuments({ ratings: { $gte: 4 } }),
ProductModel.aggregate([
{ $sort: { ratings: -1, numOfReviews: -1 } },
{ $limit: 8 },
{
$project: {
name: 1,
price: 1,
ratings: 1,
numOfReviews: 1,
stock: 1,
category: 1,
images: 1
}
}
])
]);
const orderStats = await User.aggregate([
{ $unwind: '$orders' },
{
$group: {
_id: null,
totalOrders: { $sum: 1 },
totalRevenue: { $sum: '$orders.amount' },
todayOrders: {
$sum: {
$cond: [
{ $gte: ['$orders.createdAt', startOfDay] },
1,
0
]
}
},
todayRevenue: {
$sum: {
$cond: [
{ $gte: ['$orders.createdAt', startOfDay] },
'$orders.amount',
0
]
}
},
weekOrders: {
$sum: {
$cond: [
{ $gte: ['$orders.createdAt', startOfWeek] },
1,
0
]
}
},
weekRevenue: {
$sum: {
$cond: [
{ $gte: ['$orders.createdAt', startOfWeek] },
'$orders.amount',
0
]
}
},
monthOrders: {
$sum: {
$cond: [
{ $gte: ['$orders.createdAt', startOfMonth] },
1,
0
]
}
},
monthRevenue: {
$sum: {
$cond: [
{ $gte: ['$orders.createdAt', startOfMonth] },
'$orders.amount',
0
]
}
},
pendingOrders: {
$sum: {
$cond: [
{ $eq: [{ $toLower: '$orders.status' }, 'placed'] },
1,
0
]
}
},
processingOrders: {
$sum: {
$cond: [
{ $eq: [{ $toLower: '$orders.status' }, 'processing'] },
1,
0
]
}
},
shippedOrders: {
$sum: {
$cond: [
{ $eq: [{ $toLower: '$orders.status' }, 'shipped'] },
1,
0
]
}
},
deliveredOrders: {
$sum: {
$cond: [
{ $eq: [{ $toLower: '$orders.status' }, 'delivered'] },
1,
0
]
}
}
}
}
]);
const stats = orderStats[0] || {
totalOrders: 0,
totalRevenue: 0,
todayOrders: 0,
todayRevenue: 0,
weekOrders: 0,
weekRevenue: 0,
monthOrders: 0,
monthRevenue: 0,
pendingOrders: 0,
processingOrders: 0,
shippedOrders: 0,
deliveredOrders: 0
};
const recentOrders = await User.aggregate([
{ $unwind: '$orders' },
{ $sort: { 'orders.createdAt': -1 } },
{ $limit: 10 },
{
$project: {
orderId: '$orders._id',
userId: '$_id',
userName: '$name',
userEmail: '$email',
amount: '$orders.amount',
status: '$orders.status',
createdAt: '$orders.createdAt',
items: '$orders.orderItems'
}
}
]);
const salesTrends = await User.aggregate([
{ $unwind: '$orders' },
{
$match: {
'orders.createdAt': {
$gte: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000)
}
}
},
{
$group: {
_id: {
$dateToString: {
format: '%Y-%m-%d',
date: '$orders.createdAt'
}
},
orders: { $sum: 1 },
revenue: { $sum: '$orders.amount' }
}
},
{ $sort: { _id: 1 } }
]);
const categoryStats = await ProductModel.aggregate([
{ $group: { _id: '$category', count: { $sum: 1 } } },
{ $sort: { count: -1 } }
]);
res.status(200).json({
users: {
total: totalUsers,
active: activeUsers,
newToday: newUsersToday,
newThisWeek: newUsersThisWeek,
newThisMonth: newUsersThisMonth,
newThisYear: newUsersThisYear,
growthRate: totalUsers > 0 ? ((newUsersThisMonth / totalUsers) * 100).toFixed(2) : 0
},
products: {
total: totalProducts,
lowStock: lowStockProducts,
outOfStock: outOfStockProducts,
highRated: highRatedProducts,
categories: categoryStats
},
orders: {
total: stats.totalOrders,
totalRevenue: stats.totalRevenue,
todayOrders: stats.todayOrders,
todayRevenue: stats.todayRevenue,
weekOrders: stats.weekOrders,
weekRevenue: stats.weekRevenue,
monthOrders: stats.monthOrders,
monthRevenue: stats.monthRevenue,
statusBreakdown: {
pending: stats.pendingOrders,
processing: stats.processingOrders,
shipped: stats.shippedOrders,
delivered: stats.deliveredOrders
},
averageOrderValue: stats.totalOrders > 0 ? (stats.totalRevenue / stats.totalOrders).toFixed(2) : 0
},
topProducts,
latestOrders: recentOrders,
salesTrends,
systemHealth: {
database: 'healthy',
uptime: process.uptime(),
memory: process.memoryUsage(),
timestamp: new Date().toISOString()
}
});
});
const getAllOrders = asyncHandler(async (req, res) => {
const page = parseInt(req.query.page) || 1;
const limit = parseInt(req.query.limit) || 10;
const status = req.query.status || 'all';
const period = req.query.period || '30d';
const skip = (page - 1) * limit;
let startDate;
const endDate = new Date();
switch (period) {
case '7d':
startDate = new Date(endDate.getTime() - 7 * 24 * 60 * 60 * 1000);
break;
case '30d':
startDate = new Date(endDate.getTime() - 30 * 24 * 60 * 60 * 1000);
break;
case '90d':
startDate = new Date(endDate.getTime() - 90 * 24 * 60 * 60 * 1000);
break;
default:
startDate = new Date(endDate.getTime() - 30 * 24 * 60 * 60 * 1000);
}
const users = await User.find({
'orders.createdAt': { $gte: startDate, $lte: endDate }
});
let allOrders = [];
users.forEach(user => {
user.orders.forEach(order => {
if (order.createdAt >= startDate && order.createdAt <= endDate) {
allOrders.push({
_id: order._id,
orderId: order._id,
userId: user._id,
userName: user.name,
userEmail: user.email,
amount: order.amount,
status: order.status,
createdAt: order.createdAt,
items: order.orderItems || [],
shippingAddress: order.shippingAddress,
paymentMethod: order.paymentMethod,
estimatedDelivery: order.estimatedDelivery,
phone: order.shippingAddress?.phone || '',
city: order.shippingAddress?.city || ''
});
}
});
});
if (status !== 'all') {
allOrders = allOrders.filter(order => order.status === status);
}
allOrders.sort((a, b) => new Date(b.createdAt) - new Date(a.createdAt));
const total = allOrders.length;
const orders = allOrders.slice(skip, skip + limit);
res.status(200).json({
orders,
pagination: {
page,
limit,
total,
pages: Math.ceil(total / limit)
}
});
});
const getOrderById = asyncHandler(async (req, res) => {
const { id } = req.params;
const user = await User.findOne({ 'orders._id': id });
if (!user) {
res.status(404);
throw new Error('Order not found');
}
const order = user.orders.find(order => order._id.toString() === id);
if (order) {
res.status(200).json({
order: {
...order.toObject(),
userName: user.name,
userEmail: user.email,
shippingAddress: order.shippingAddress,
paymentMethod: order.paymentMethod,
estimatedDelivery: order.estimatedDelivery,
phone: order.shippingAddress?.phone || '',
city: order.shippingAddress?.city || ''
}
});
} else {
res.status(404);
throw new Error('Order not found');
}
});
const updateOrder = asyncHandler(async (req, res) => {
const { id } = req.params;
const { status } = req.body;
const user = await User.findOne({ 'orders._id': id });
if (!user) {
res.status(404);
throw new Error('Order not found');
}
const orderIndex = user.orders.findIndex(order => order._id.toString() === id);
if (orderIndex === -1) {
res.status(404);
throw new Error('Order not found');
}
user.orders[orderIndex].status = status;
await user.save();
res.status(200).json({
message: 'Order updated successfully',
order: user.orders[orderIndex]
});
});
const deleteOrder = asyncHandler(async (req, res) => {
const { id } = req.params;
const user = await User.findOne({ 'orders._id': id });
if (!user) {
res.status(404);
throw new Error('Order not found');
}
user.orders = user.orders.filter(order => order._id.toString() !== id);
await user.save();
res.status(200).json({ message: 'Order deleted successfully' });
});
const getAnalytics = asyncHandler(async (req, res) => {
const { period = '30d' } = req.query;
let startDate;
const endDate = new Date();
switch (period) {
case '7d':
startDate = new Date(endDate.getTime() - 7 * 24 * 60 * 60 * 1000);
break;
case '30d':
startDate = new Date(endDate.getTime() - 30 * 24 * 60 * 60 * 1000);
break;
case '90d':
startDate = new Date(endDate.getTime() - 90 * 24 * 60 * 60 * 1000);
break;
default:
startDate = new Date(endDate.getTime() - 30 * 24 * 60 * 60 * 1000);
}
const userGrowth = await User.aggregate([
{
$match: {
createdAt: { $gte: startDate, $lte: endDate }
}
},
{
$group: {
_id: {
year: { $year: '$createdAt' },
month: { $month: '$createdAt' },
day: { $dayOfMonth: '$createdAt' }
},
count: { $sum: 1 }
}
},
{ $sort: { '_id.year': 1, '_id.month': 1, '_id.day': 1 } }
]);
const allUsers = await User.find({
'orders.createdAt': { $gte: startDate, $lte: endDate }
});
const revenueData = [];
const orderData = [];
const categoryRevenue = {};
const productPerformance = {};
allUsers.forEach(user => {
user.orders.forEach(order => {
if (order.createdAt >= startDate && order.createdAt <= endDate) {
const dateKey = order.createdAt.toISOString().split('T')[0];
const orderAmount = order.amount || 0;
const existingRevenue = revenueData.find(item => item.date === dateKey);
if (existingRevenue) {
existingRevenue.revenue += orderAmount;
} else {
revenueData.push({
date: dateKey,
revenue: orderAmount
});
}
const existingOrder = orderData.find(item => item.date === dateKey);
if (existingOrder) {
existingOrder.orders += 1;
} else {
orderData.push({
date: dateKey,
orders: 1
});
}
order.orderItems?.forEach(item => {
if (item.category) {
categoryRevenue[item.category] = (categoryRevenue[item.category] || 0) + (item.price * item.qty);
}
if (item.productId) {
productPerformance[item.productId] = (productPerformance[item.productId] || 0) + (item.price * item.qty);
}
});
}
});
});
revenueData.sort((a, b) => new Date(a.date) - new Date(b.date));
orderData.sort((a, b) => new Date(a.date) - new Date(b.date));
const topCategories = Object.entries(categoryRevenue)
.map(([category, revenue]) => ({ category, revenue }))
.sort((a, b) => b.revenue - a.revenue)
.slice(0, 10);
const topProductIds = Object.entries(productPerformance)
.sort((a, b) => b[1] - a[1])
.slice(0, 10)
.map(([productId]) => productId);
const topProducts = await ProductModel.find({ _id: { $in: topProductIds } })
.select('name price category ratings numOfReviews');
const userEngagement = await User.aggregate([
{
$match: {
createdAt: { $gte: startDate, $lte: endDate }
}
},
{
$project: {
hasOrders: { $gt: [{ $size: '$orders' }, 0] },
orderCount: { $size: '$orders' },
totalSpent: { $sum: '$orders.amount' },
lastOrderDate: { $max: '$orders.createdAt' }
}
},
{
$group: {
_id: null,
totalUsers: { $sum: 1 },
usersWithOrders: { $sum: { $cond: ['$hasOrders', 1, 0] } },
avgOrdersPerUser: { $avg: '$orderCount' },
avgSpendingPerUser: { $avg: '$totalSpent' }
}
}
]);
const geographicData = await User.aggregate([
{
$match: {
createdAt: { $gte: startDate, $lte: endDate }
}
},
{
$group: {
_id: '$shippingAddress.state',
count: { $sum: 1 }
}
},
{ $sort: { count: -1 } }
]);
const conversionFunnel = {
totalVisitors: userEngagement[0]?.totalUsers || 0,
registeredUsers: userEngagement[0]?.totalUsers || 0,
usersWithOrders: userEngagement[0]?.usersWithOrders || 0,
conversionRate: userEngagement[0]?.totalUsers > 0
? ((userEngagement[0]?.usersWithOrders / userEngagement[0]?.totalUsers) * 100).toFixed(2)
: 0
};
res.status(200).json({
period,
userGrowth,
revenueData,
orderData,
topCategories,
topProducts,
userEngagement: userEngagement[0] || {},
geographicData,
conversionFunnel,
summary: {
totalRevenue: revenueData.reduce((sum, item) => sum + item.revenue, 0),
totalOrders: orderData.reduce((sum, item) => sum + item.orders, 0),
avgOrderValue: orderData.reduce((sum, item) => sum + item.orders, 0) > 0
? (revenueData.reduce((sum, item) => sum + item.revenue, 0) / orderData.reduce((sum, item) => sum + item.orders, 0)).toFixed(2)
: 0
}
});
});
const getSystemInfo = asyncHandler(async (req, res) => {
const systemInfo = {
nodeVersion: process.version,
platform: process.platform,
memory: process.memoryUsage(),
uptime: process.uptime(),
environment: process.env.NODE_ENV,
database: {
connected: mongoose.connection.readyState === 1,
host: mongoose.connection.host,
name: mongoose.connection.name
}
};
res.status(200).json(systemInfo);
});
const getAdminActivityLog = asyncHandler(async (req, res) => {
const page = parseInt(req.query.page) || 1;
const limit = parseInt(req.query.limit) || 50;
const adminId = req.query.adminId;
const skip = (page - 1) * limit;
let query = {};
if (adminId) {
query._id = adminId;
}
const admins = await Admin.find(query)
.select('firstName lastName username activityLog')
.sort({ 'activityLog.timestamp': -1 })
.skip(skip)
.limit(limit);
const activityLogs = [];
admins.forEach(admin => {
admin.activityLog.forEach(log => {
activityLogs.push({
adminName: admin.getFullName(),
adminUsername: admin.username,
...log.toObject()
});
});
});
activityLogs.sort((a, b) => new Date(b.timestamp) - new Date(a.timestamp));
res.status(200).json({
activityLogs: activityLogs.slice(0, limit),
pagination: {
page,
limit,
total: activityLogs.length
}
});
});
const getAllCategories = asyncHandler(async (req, res) => {
const categories = await ProductModel.distinct('category');
res.status(200).json({
success: true,
categories: categories.sort()
});
});
const createCategory = asyncHandler(async (req, res) => {
const { name } = req.body;
if (!name || name.trim() === '') {
res.status(400);
throw new Error('Category name is required');
}
const trimmedName = name.trim();
const existingCategory = await ProductModel.findOne({ category: trimmedName });
if (existingCategory) {
res.status(400);
throw new Error('Category already exists');
}
res.status(201).json({
success: true,
message: 'Category created successfully',
category: trimmedName
});
});
const ensureCategoryExists = async (categoryName) => {
if (!categoryName || categoryName.trim() === '') {
return false;
}
const trimmedName = categoryName.trim();
const existingCategory = await ProductModel.findOne({ category: trimmedName });
if (existingCategory) {
return true;
}
return true;
};
module.exports = {
adminLogin,
adminLogout,
getAdminProfile,
updateAdminProfile,
getAllUsers,
getUserById,
updateUser,
deleteUser,
createUser,
getAllProducts,
createProduct,
updateProduct,
deleteProduct,
uploadProductImages,
getAllOrders,
getOrderById,
updateOrder,
deleteOrder,
getDashboardStats,
getAnalytics,
getSystemInfo,
getAdminActivityLog,
getAllCategories,
createCategory
};