mirror of
https://github.com/samanhappy/mcphub.git
synced 2025-12-24 02:39:19 -05:00
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: samanhappy <2755122+samanhappy@users.noreply.github.com>
294 lines
7.3 KiB
TypeScript
294 lines
7.3 KiB
TypeScript
import { Request, Response } from 'express';
|
|
import { ApiResponse } from '../types/index.js';
|
|
import {
|
|
getAllUsers,
|
|
getUserByUsername,
|
|
createNewUser,
|
|
updateUser,
|
|
deleteUser,
|
|
getUserCount,
|
|
getAdminCount,
|
|
} from '../services/userService.js';
|
|
import { loadSettings } from '../config/index.js';
|
|
import { validatePasswordStrength } from '../utils/passwordValidation.js';
|
|
|
|
// Admin permission check middleware function
|
|
const requireAdmin = (req: Request, res: Response): boolean => {
|
|
const settings = loadSettings();
|
|
if (settings.systemConfig?.routing?.skipAuth) {
|
|
return true;
|
|
}
|
|
|
|
const user = (req as any).user;
|
|
if (!user || !user.isAdmin) {
|
|
res.status(403).json({
|
|
success: false,
|
|
message: 'Admin privileges required',
|
|
});
|
|
return false;
|
|
}
|
|
return true;
|
|
};
|
|
|
|
// Get all users (admin only)
|
|
export const getUsers = (req: Request, res: Response): void => {
|
|
if (!requireAdmin(req, res)) return;
|
|
|
|
try {
|
|
const users = getAllUsers().map(({ password: _, ...user }) => user); // Remove password from response
|
|
const response: ApiResponse = {
|
|
success: true,
|
|
data: users,
|
|
};
|
|
res.json(response);
|
|
} catch (error) {
|
|
res.status(500).json({
|
|
success: false,
|
|
message: 'Failed to get users information',
|
|
});
|
|
}
|
|
};
|
|
|
|
// Get a specific user by username (admin only)
|
|
export const getUser = (req: Request, res: Response): void => {
|
|
if (!requireAdmin(req, res)) return;
|
|
|
|
try {
|
|
const { username } = req.params;
|
|
if (!username) {
|
|
res.status(400).json({
|
|
success: false,
|
|
message: 'Username is required',
|
|
});
|
|
return;
|
|
}
|
|
|
|
const user = getUserByUsername(username);
|
|
if (!user) {
|
|
res.status(404).json({
|
|
success: false,
|
|
message: 'User not found',
|
|
});
|
|
return;
|
|
}
|
|
|
|
const { password: _, ...userData } = user; // Remove password from response
|
|
const response: ApiResponse = {
|
|
success: true,
|
|
data: userData,
|
|
};
|
|
res.json(response);
|
|
} catch (error) {
|
|
res.status(500).json({
|
|
success: false,
|
|
message: 'Failed to get user information',
|
|
});
|
|
}
|
|
};
|
|
|
|
// Create a new user (admin only)
|
|
export const createUser = async (req: Request, res: Response): Promise<void> => {
|
|
if (!requireAdmin(req, res)) return;
|
|
|
|
try {
|
|
const { username, password, isAdmin } = req.body;
|
|
|
|
if (!username || !password) {
|
|
res.status(400).json({
|
|
success: false,
|
|
message: 'Username and password are required',
|
|
});
|
|
return;
|
|
}
|
|
|
|
// Validate password strength
|
|
const validationResult = validatePasswordStrength(password);
|
|
if (!validationResult.isValid) {
|
|
res.status(400).json({
|
|
success: false,
|
|
message: 'Password does not meet security requirements',
|
|
errors: validationResult.errors,
|
|
});
|
|
return;
|
|
}
|
|
|
|
const newUser = await createNewUser(username, password, isAdmin || false);
|
|
if (!newUser) {
|
|
res.status(400).json({
|
|
success: false,
|
|
message: 'Failed to create user or username already exists',
|
|
});
|
|
return;
|
|
}
|
|
|
|
const { password: _, ...userData } = newUser; // Remove password from response
|
|
const response: ApiResponse = {
|
|
success: true,
|
|
data: userData,
|
|
message: 'User created successfully',
|
|
};
|
|
res.status(201).json(response);
|
|
} catch (error) {
|
|
res.status(500).json({
|
|
success: false,
|
|
message: 'Internal server error',
|
|
});
|
|
}
|
|
};
|
|
|
|
// Update an existing user (admin only)
|
|
export const updateExistingUser = async (req: Request, res: Response): Promise<void> => {
|
|
if (!requireAdmin(req, res)) return;
|
|
|
|
try {
|
|
const { username } = req.params;
|
|
const { isAdmin, newPassword } = req.body;
|
|
|
|
if (!username) {
|
|
res.status(400).json({
|
|
success: false,
|
|
message: 'Username is required',
|
|
});
|
|
return;
|
|
}
|
|
|
|
// Check if trying to change admin status
|
|
if (isAdmin !== undefined) {
|
|
const currentUser = getUserByUsername(username);
|
|
if (!currentUser) {
|
|
res.status(404).json({
|
|
success: false,
|
|
message: 'User not found',
|
|
});
|
|
return;
|
|
}
|
|
|
|
// Prevent removing admin status from the last admin
|
|
if (currentUser.isAdmin && !isAdmin && getAdminCount() === 1) {
|
|
res.status(400).json({
|
|
success: false,
|
|
message: 'Cannot remove admin status from the last admin user',
|
|
});
|
|
return;
|
|
}
|
|
}
|
|
|
|
const updateData: any = {};
|
|
if (isAdmin !== undefined) updateData.isAdmin = isAdmin;
|
|
if (newPassword) {
|
|
// Validate new password strength
|
|
const validationResult = validatePasswordStrength(newPassword);
|
|
if (!validationResult.isValid) {
|
|
res.status(400).json({
|
|
success: false,
|
|
message: 'Password does not meet security requirements',
|
|
errors: validationResult.errors,
|
|
});
|
|
return;
|
|
}
|
|
updateData.newPassword = newPassword;
|
|
}
|
|
|
|
if (Object.keys(updateData).length === 0) {
|
|
res.status(400).json({
|
|
success: false,
|
|
message: 'At least one field (isAdmin or newPassword) is required to update',
|
|
});
|
|
return;
|
|
}
|
|
|
|
const updatedUser = await updateUser(username, updateData);
|
|
if (!updatedUser) {
|
|
res.status(404).json({
|
|
success: false,
|
|
message: 'User not found or update failed',
|
|
});
|
|
return;
|
|
}
|
|
|
|
const { password: _, ...userData } = updatedUser; // Remove password from response
|
|
const response: ApiResponse = {
|
|
success: true,
|
|
data: userData,
|
|
message: 'User updated successfully',
|
|
};
|
|
res.json(response);
|
|
} catch (error) {
|
|
res.status(500).json({
|
|
success: false,
|
|
message: 'Internal server error',
|
|
});
|
|
}
|
|
};
|
|
|
|
// Delete a user (admin only)
|
|
export const deleteExistingUser = (req: Request, res: Response): void => {
|
|
if (!requireAdmin(req, res)) return;
|
|
|
|
try {
|
|
const { username } = req.params;
|
|
if (!username) {
|
|
res.status(400).json({
|
|
success: false,
|
|
message: 'Username is required',
|
|
});
|
|
return;
|
|
}
|
|
|
|
// Check if trying to delete the current admin user
|
|
const currentUser = (req as any).user;
|
|
if (currentUser.username === username) {
|
|
res.status(400).json({
|
|
success: false,
|
|
message: 'Cannot delete your own account',
|
|
});
|
|
return;
|
|
}
|
|
|
|
const success = deleteUser(username);
|
|
if (!success) {
|
|
res.status(400).json({
|
|
success: false,
|
|
message: 'User not found, failed to delete, or cannot delete the last admin',
|
|
});
|
|
return;
|
|
}
|
|
|
|
res.json({
|
|
success: true,
|
|
message: 'User deleted successfully',
|
|
});
|
|
} catch (error) {
|
|
res.status(500).json({
|
|
success: false,
|
|
message: 'Internal server error',
|
|
});
|
|
}
|
|
};
|
|
|
|
// Get user statistics (admin only)
|
|
export const getUserStats = (req: Request, res: Response): void => {
|
|
if (!requireAdmin(req, res)) return;
|
|
|
|
try {
|
|
const totalUsers = getUserCount();
|
|
const adminUsers = getAdminCount();
|
|
const regularUsers = totalUsers - adminUsers;
|
|
|
|
const response: ApiResponse = {
|
|
success: true,
|
|
data: {
|
|
totalUsers,
|
|
adminUsers,
|
|
regularUsers,
|
|
},
|
|
};
|
|
res.json(response);
|
|
} catch (error) {
|
|
res.status(500).json({
|
|
success: false,
|
|
message: 'Failed to get user statistics',
|
|
});
|
|
}
|
|
};
|