Skip to content

Commit

Permalink
overhauling backend to use userId instead of _id
Browse files Browse the repository at this point in the history
  • Loading branch information
singharaj-usai committed Oct 16, 2024
1 parent 0ff9376 commit 19e3249
Show file tree
Hide file tree
Showing 12 changed files with 140 additions and 82 deletions.
13 changes: 10 additions & 3 deletions client/js/admin/auth.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,22 @@ function checkAdminAuth() {
},
success: function (response) {
if (response.isAdmin) {
localStorage.setItem('adminLevel', response.adminLevel);
if (response.userId) {
localStorage.setItem('userId', response.userId);
} else {
console.error('userId not provided in admin check response');
}
loadDashboard();
} else {
alert('You do not have admin privileges.');
window.location.href = '/';
}
},
error: function () {
alert('Authentication failed. Please log in again.');
logout();
error: function (xhr) {
console.error('Admin auth check failed:', xhr.responseText);
alert('Authentication failed. Please try again.');
window.location.href = '/login';
},
});
}
Expand Down
41 changes: 24 additions & 17 deletions client/js/admin/users.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
function loadUsers() {
const currentUserId = localStorage.getItem('userId');
const contentArea = $('#content-area');
contentArea.html(
'<h2 class="text-primary">User Management</h2><div id="user-management" class="row"></div>'
Expand All @@ -10,8 +11,8 @@ function loadUsers() {
headers: {
Authorization: `Bearer ${localStorage.getItem('token')}`,
},
success: function (users) {
displayUsers(users);
success: function (response) {
displayUsers(response.users, response.currentAdminLevel, currentUserId);
},
error: function () {
contentArea.html(
Expand All @@ -21,12 +22,11 @@ function loadUsers() {
});
}

function displayUsers(users) {
function displayUsers(users, currentAdminLevel) {
const userManagement = $('#user-management');
userManagement.empty();

const currentAdminId = localStorage.getItem('userId');
const currentAdminLevel = localStorage.getItem('adminLevel');

users.forEach((user) => {
const panel = $(`
Expand All @@ -51,20 +51,20 @@ function displayUsers(users) {
</div>
<div class="user-actions mt-3">
${currentAdminLevel === 'admin' ? `
<button class="btn btn-sm btn-${user.isBanned ? 'success' : 'warning'} ban-user" data-user-id="${user._id}" data-is-banned="${user.isBanned}" ${user.adminLevel === 'admin' ? 'disabled' : ''}>
<button class="btn btn-sm btn-${user.isBanned ? 'success' : 'warning'} ban-user" data-user-id="${user.userId}" data-is-banned="${user.isBanned}" ${user.adminLevel === 'admin' ? 'disabled' : ''}>
<i class="fa fa-${user.isBanned ? 'unlock' : 'ban'}"></i> ${user.isBanned ? 'Unban User' : 'Ban User'}
</button>
${user.adminLevel === 'user' ? `
<button class="btn btn-sm btn-info promote-moderator" data-user-id="${user._id}"><i class="fa fa-level-up"></i> Promote to Moderator</button>
<button class="btn btn-sm btn-info promote-moderator" data-user-id="${user.userId}"><i class="fa fa-level-up"></i> Promote to Moderator</button>
` : user.adminLevel === 'moderator' ? `
<button class="btn btn-sm btn-primary promote-admin" data-user-id="${user._id}"><i class="fa fa-level-up"></i> Promote to Admin</button>
<button class="btn btn-sm btn-primary promote-admin" data-user-id="${user.userId}"><i class="fa fa-level-up"></i> Promote to Admin</button>
` : ''}
${user.adminLevel !== 'user' && user._id !== currentAdminId ? `
<button class="btn btn-sm btn-danger demote-user" data-user-id="${user._id}"><i class="fa fa-level-down"></i> Demote User</button>
${user.adminLevel !== 'user' ? `
<button class="btn btn-sm btn-danger demote-user" data-user-id="${user.userId}" ${user.userId == currentAdminId ? 'disabled' : ''}><i class="fa fa-level-down"></i> Demote User</button>
` : ''}
<button class="btn btn-sm btn-danger delete-user" data-user-id="${user._id}" ${user.adminLevel !== 'user' ? 'disabled' : ''}><i class="fa fa-trash"></i> Delete User</button>
<button class="btn btn-sm btn-danger delete-user" data-user-id="${user.userId}" ${user.adminLevel !== 'user' ? 'disabled' : ''}><i class="fa fa-trash"></i> Delete User</button>
` : ''}
<button class="btn btn-sm btn-info view-messages" data-user-id="${user._id}"><i class="fa fa-envelope"></i> View Messages</button>
<button class="btn btn-sm btn-info view-messages" data-user-id="${user.userId}"><i class="fa fa-envelope"></i> View Messages</button>
</div>
</div>
</div>
Expand Down Expand Up @@ -237,6 +237,12 @@ function promoteToModerator(userId) {
}

function demoteUser(userId) {
const currentUserId = localStorage.getItem('userId');
if (userId === currentUserId) {
showAlert('warning', 'You cannot demote yourself.');
return;
}

if (confirm('Are you sure you want to demote this user?')) {
$.ajax({
url: `/api/admin/demote/${userId}`,
Expand Down Expand Up @@ -284,11 +290,12 @@ function loadUserMessages(userId) {
success: function (messages) {
displayUserMessages(messages);
},
error: function () {
showAlert('danger', 'Error loading user messages. Please try again.');
error: function (xhr) {
showAlert('danger', `Error loading user messages: ${xhr.responseJSON ? xhr.responseJSON.error : 'Unknown error'}`);
},
});
}

function displayUserMessages(messages) {
const modal = $(`
<div class="modal fade" id="userMessagesModal" tabindex="-1" role="dialog">
Expand Down Expand Up @@ -319,14 +326,14 @@ function displayUserMessages(messages) {
<tr>
<td>
<div class="d-flex align-items-center justify-content-center">
<img src="${message.sender.profilePicture || 'https://www.nicepng.com/png/full/146-1466409_roblox-bacon-hair-png-roblox-bacon-hair-head.png'}" class="img-circle" style="width: 30px; height: 30px; margin-right: 5px;">
<strong>${escapeHtml(message.sender.username)}</strong>
<img src="${message.sender && message.sender.profilePicture || 'https://www.nicepng.com/png/full/146-1466409_roblox-bacon-hair-png-roblox-bacon-hair-head.png'}" class="img-circle" style="width: 30px; height: 30px; margin-right: 5px;">
<strong>${message.sender ? escapeHtml(message.sender.username) : 'Unknown'}</strong>
</div>
</td>
<td>
<div class="d-flex align-items-center justify-content-center">
<img src="${message.recipient.profilePicture || 'https://www.nicepng.com/png/full/146-1466409_roblox-bacon-hair-png-roblox-bacon-hair-head.png'}" class="img-circle" style="width: 30px; height: 30px; margin-right: 5px;">
<strong>${escapeHtml(message.recipient.username)}</strong>
<img src="${message.recipient && message.recipient.profilePicture || 'https://www.nicepng.com/png/full/146-1466409_roblox-bacon-hair-png-roblox-bacon-hair-head.png'}" class="img-circle" style="width: 30px; height: 30px; margin-right: 5px;">
<strong>${message.recipient ? escapeHtml(message.recipient.username) : 'Unknown'}</strong>
</div>
</td>
<td>${escapeHtml(message.message)}</td>
Expand Down
1 change: 1 addition & 0 deletions client/js/auth/auth.js
Original file line number Diff line number Diff line change
Expand Up @@ -431,6 +431,7 @@ const App = {
headers: { Authorization: `Bearer ${token}` },
success: () => {
localStorage.removeItem('userId');
localStorage.removeItem('adminLevel');
localStorage.removeItem('username');
localStorage.removeItem('token');
localStorage.removeItem('isBanned');
Expand Down
20 changes: 11 additions & 9 deletions server/functions/api/middleware/adminAuth.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
const User = require('../models/User');

module.exports = async function isAdmin(req, res, next) {
const isAdmin = async (req, res, next) => {
try {
const user = await User.findById(req.user.id);
if (!user || !user.isAdmin) {
return res
.status(403)
.json({ error: 'Access denied. Admin privileges required.' });
const userId = req.user.userId; // Ensure this is a number
const user = await User.findOne({ userId: userId }); // Use findOne with userId

if (user && user.isAdmin && (user.adminLevel === 'admin' || user.adminLevel === 'moderator')) {
next();
} else {
res.status(403).json({ error: 'Access denied. Admin privileges required.' });
}
req.adminUser = user;
next();
} catch (error) {
console.error('Error checking admin status:', error);
console.error('Error in isAdmin middleware:', error);
res.status(500).json({ error: 'Internal server error' });
}
};

module.exports = isAdmin;
2 changes: 1 addition & 1 deletion server/functions/api/middleware/auth.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const isAuthenticated = async (req, res, next) => {
}

const decoded = jwt.verify(token, process.env.JWT_SECRET);
const user = await User.findById(decoded.userId);
const user = await User.findOne({ userId: decoded.userId });

if (!user) {
return res.status(401).json({ message: 'User not found' });
Expand Down
12 changes: 7 additions & 5 deletions server/functions/api/middleware/authenticateToken.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
const jwt = require('jsonwebtoken');

function authenticateToken(req, res, next) {
const authHeader = req.headers.authorization;
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1];

if (!token) {
return res.status(401).json({ error: 'Access token missing' });
return res.status(401).json({ error: 'No token provided.' });
}

jwt.verify(token, process.env.JWT_SECRET, (err, user) => {
jwt.verify(token, process.env.JWT_SECRET, (err, decoded) => {
if (err) {
return res.status(403).json({ error: 'Invalid token' });
console.error('Token verification error:', err);
return res.status(403).json({ error: 'Invalid token.' });
}
req.user = user;
req.user = { userId: decoded.userId }; // Ensure userId is correctly set
next();
});
}
Expand Down
2 changes: 1 addition & 1 deletion server/functions/api/middleware/updateUserStatus.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ const User = require('../models/User');

const updateUserStatus = async (req, res, next) => {
if (req.user) {
await User.findByIdAndUpdate(req.user._id, {
await User.findOneAndUpdate(req.user.userId, {
isOnline: true,
lastActiveAt: new Date(),
});
Expand Down
1 change: 1 addition & 0 deletions server/functions/api/models/User.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ const userSchema = new mongoose.Schema({
userId: {
type: Number,
unique: true,
required: true,
},
username: {
type: String,
Expand Down
94 changes: 64 additions & 30 deletions server/functions/api/routes/admin.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,26 @@ router.use(isAuthenticated);
router.use(isAdmin);

// Check admin authentication
router.get('/check-auth', (req, res) => {
res.json({ isAdmin: true });
router.get('/check-auth', isAdmin, async (req, res) => {
try {
const userId = req.user.userId;
const user = await User.findOne({ userId: userId }).select('isAdmin adminLevel');

if (user) {
res.json({ isAdmin: user.isAdmin, adminLevel: user.adminLevel, userId: user.userId });
} else {
res.status(404).json({ error: 'User not found.' });
}
} catch (error) {
console.error('Error in /check-auth:', error);
res.status(500).json({ error: 'Internal server error.' });
}
});

// Promote moderator
router.post('/promote-moderator/:id', authenticateToken, isAdmin, async (req, res) => {
try {
const userToPromote = await User.findById(req.params.id);
const userToPromote = await User.findOne({ userId: req.params.id });
if (!userToPromote) {
return res.status(404).json({ error: 'User not found' });
}
Expand All @@ -46,7 +58,7 @@ router.post('/promote-moderator/:id', authenticateToken, isAdmin, async (req, re
// Promote user to admin
router.post('/promote-admin/:id', authenticateToken, isAdmin, async (req, res) => {
try {
const userToPromote = await User.findById(req.params.id);
const userToPromote = await User.findOne({ userId: req.params.id });
if (!userToPromote) {
return res.status(404).json({ error: 'User not found' });
}
Expand All @@ -67,7 +79,7 @@ router.post('/promote-admin/:id', authenticateToken, isAdmin, async (req, res) =

router.post('/demote/:id', authenticateToken, isAdmin, async (req, res) => {
try {
const userToDemote = await User.findById(req.params.id);
const userToDemote = await User.findOne({ userId: req.params.id });
if (!userToDemote) {
return res.status(404).json({ error: 'User not found' });
}
Expand All @@ -76,7 +88,7 @@ router.post('/demote/:id', authenticateToken, isAdmin, async (req, res) => {
return res.status(400).json({ error: 'User is already a regular user' });
}

if (userToDemote._id.toString() === req.user.id) {
if (userToDemote.userId === req.user.userId) {
return res.status(400).json({ error: 'You cannot demote yourself' });
}

Expand Down Expand Up @@ -349,37 +361,55 @@ router.post('/reset-forum-post-count', authenticateToken, async (req, res) => {
});

// Get all users
router.get('/users', authenticateToken, async (req, res) => {
router.get('/users', async (req, res) => {
try {
const users = await User.find({}, '-password').select('username email signupDate isBanned isAdmin currency');
res.json(users);
const users = await User.find().select('-password');
const currentUser = await User.findOne({ userId: req.user.userId }).select('adminLevel');

if (!currentUser) {
return res.status(404).json({ error: 'Current user not found' });
}

res.json({ users, currentAdminLevel: currentUser.adminLevel });
} catch (error) {
console.error('Error fetching users:', error);
res.status(500).json({ error: 'Internal server error' });
res.status(500).json({ error: 'Error fetching users' });
}
});

// ban the user (does not work yet, needs to be worked on more on the server side)
router.post('/users/:userId/ban', authenticateToken, async (req, res) => {
router.post('/users/:id/ban', authenticateToken, isAdmin, async (req, res) => {
try {
const { userId } = req.params;
const { ban, banReason } = req.body;
const userToBan = await User.findOne({ userId: req.params.id });

const user = await User.findById(userId);
if (!user) {
return res.status(404).json({ error: 'User not found' });
if (!userToBan) {
return res.status(404).json({ error: 'User not found.' });
}

user.isBanned = ban;
user.banReason = ban ? banReason : null;
await user.save();
if (userToBan.isAdmin) {
return res.status(403).json({ error: 'Cannot ban an admin user.' });
}

res.json({
message: ban ? 'User banned successfully' : 'User unbanned successfully',
});
if (userToBan.userId === req.user.userId) {
return res.status(403).json({ error: 'You cannot ban yourself.' });
}

if (ban && (!banReason || banReason.trim() === '')) {
return res.status(400).json({ error: 'Ban reason is required when banning a user.' });
}

const updateFields = {
isBanned: ban,
banReason: ban ? banReason.trim() : null,
};

const user = await User.findOneAndUpdate({ userId: req.params.id }, updateFields, { new: true });

return res.json({ message: ban ? 'User banned successfully.' : 'User unbanned successfully.' });
} catch (error) {
console.error('Error banning/unbanning user:', error);
res.status(500).json({ error: 'Internal server error' });
console.error('Error updating user ban status:', error);
return res.status(500).json({ error: 'Internal server error.' });
}
});

Expand Down Expand Up @@ -447,8 +477,8 @@ router.post('/users/:id/ban', authenticateToken, isAdmin, async (req, res) => {
// Delete a user (ONLY USE AS LAST RESORT, THIS IS DESTRUCTIVE)
router.delete('/users/:id', authenticateToken, async (req, res) => {
try {
const userToDelete = await User.findById(req.params.id);
const currentUser = await User.findById(req.user.id);
const userToDelete = await User.findOne({ userId: req.params.id });
const currentUser = await User.findOne({ userId: req.user.userId });

if (!userToDelete) {
return res.status(404).json({ error: 'User not found' });
Expand All @@ -462,11 +492,11 @@ router.delete('/users/:id', authenticateToken, async (req, res) => {
return res.status(403).json({ error: 'Cannot delete an admin user.' });
}

if (userToDelete._id.toString() === req.user.id) {
if (userToDelete.userId === req.user.userId) {
return res.status(403).json({ error: 'You cannot delete yourself.' });
}

await User.findByIdAndDelete(req.params.id);
await User.findOneAndDelete({ userId: req.params.id });
res.json({ message: 'User deleted successfully' });
} catch (error) {
console.error('Error deleting user:', error);
Expand All @@ -475,14 +505,18 @@ router.delete('/users/:id', authenticateToken, async (req, res) => {
});

// Get user messages
router.get('/users/:userId/messages', authenticateToken, async (req, res) => {
router.get('/users/:id/messages', authenticateToken, async (req, res) => {
try {
const user = await User.findOne({ userId: req.params.id });
if (!user) {
return res.status(404).json({ error: 'User not found' });
}
const messages = await Message.find({
$or: [{ sender: req.params.userId }, { recipient: req.params.userId }]
$or: [{ sender: user._id }, { recipient: user._id }]
})
.populate('sender', 'username profilePicture')
.populate('recipient', 'username profilePicture')
.sort({ sentAt: -1 })
.sort({ sentAt: -1 });

res.json(messages);
} catch (error) {
Expand Down
Loading

0 comments on commit 19e3249

Please sign in to comment.