EDMS/server/utils/userUtils.js
2025-05-31 16:58:30 +08:00

490 lines
12 KiB
JavaScript

import prisma from './prisma';
import sha256 from 'crypto-js/sha256';
/**
* Collection of utility functions for working with users
*/
export const UserUtils = {
/**
* Create a new user
*
* @param {Object} data User data
* @param {string} data.userUsername Username
* @param {string} data.userPassword Password (will be hashed)
* @param {string} data.userFullName Full name
* @param {string} data.userEmail Email (optional)
* @param {string} data.userPhone Phone (optional)
* @param {string} data.userStatus Status (optional, defaults to "active")
* @param {number} data.dp_id Department ID (optional)
* @returns {Promise<Object>} Created user (without password)
*/
async create(data) {
// Validate required fields
if (!data.userUsername) {
throw new Error('Username is required');
}
if (!data.userPassword) {
throw new Error('Password is required');
}
if (!data.userFullName) {
throw new Error('Full name is required');
}
// Check if username already exists
const existingUser = await prisma.user.findFirst({
where: {
userUsername: data.userUsername
}
});
if (existingUser) {
throw new Error('Username already exists');
}
// Check if department exists if provided
if (data.dp_id) {
const department = await prisma.department.findUnique({
where: {
dp_id: parseInt(data.dp_id)
}
});
if (!department) {
throw new Error(`Department with ID ${data.dp_id} not found`);
}
}
// Create random secret key
const secretKey = this.generateRandomKey(32);
// Encrypt password with SHA256
const hashedPassword = sha256(data.userPassword).toString();
// Create user
const user = await prisma.user.create({
data: {
userSecretKey: secretKey,
userUsername: data.userUsername,
userPassword: hashedPassword,
userFullName: data.userFullName,
userEmail: data.userEmail || null,
userPhone: data.userPhone || null,
userStatus: data.userStatus ? data.userStatus.toUpperCase() : "ACTIVE",
dp_id: data.dp_id ? parseInt(data.dp_id) : null,
userCreatedDate: new Date(),
userModifiedDate: new Date()
}
});
// Return user without password
const { userPassword, ...userWithoutPassword } = user;
return userWithoutPassword;
},
/**
* Get all users with pagination and filtering
*
* @param {Object} options Query options
* @param {number} options.page Page number
* @param {number} options.limit Items per page
* @param {string} options.search Search term
* @param {number} options.departmentId Filter by department ID
* @param {string} options.status Filter by status
* @returns {Promise<Object>} Users and pagination metadata
*/
async getAll({ page = 1, limit = 10, search = '', departmentId = null, status = null }) {
const skip = (page - 1) * limit;
// Define filters
const filters = {
where: {}
};
// Filter by department if provided
if (departmentId) {
filters.where.dp_id = parseInt(departmentId);
}
// Filter by status if provided
if (status) {
filters.where.userStatus = status;
}
// Add search filter if provided
if (search) {
filters.where = {
...filters.where,
OR: [
{ userUsername: { contains: search } },
{ userFullName: { contains: search } },
{ userEmail: { contains: search } }
]
};
}
// Get users with pagination
const [users, total] = await Promise.all([
prisma.user.findMany({
...filters,
skip,
take: limit,
orderBy: {
userFullName: 'asc'
},
select: {
userID: true,
userUsername: true,
userFullName: true,
userEmail: true,
userPhone: true,
userStatus: true,
dp_id: true,
userCreatedDate: true,
userModifiedDate: true,
department: {
select: {
dp_id: true,
dp_name: true,
organization: {
select: {
org_id: true,
org_name: true
}
}
}
},
userrole: {
select: {
userRoleID: true,
role: {
select: {
roleID: true,
roleName: true
}
}
}
}
}
}),
prisma.user.count(filters)
]);
// Calculate pagination metadata
const totalPages = Math.ceil(total / limit);
return {
users,
pagination: {
page,
limit,
total,
totalPages,
hasNextPage: page < totalPages,
hasPrevPage: page > 1
}
};
},
/**
* Get user by ID
*
* @param {number} id User ID
* @returns {Promise<Object|null>} User or null if not found
*/
async getById(id) {
if (isNaN(parseInt(id))) {
throw new Error('Invalid user ID');
}
const user = await prisma.user.findUnique({
where: {
userID: parseInt(id)
},
select: {
userID: true,
userUsername: true,
userFullName: true,
userEmail: true,
userPhone: true,
userStatus: true,
dp_id: true,
userCreatedDate: true,
userModifiedDate: true,
department: {
select: {
dp_id: true,
dp_name: true,
organization: {
select: {
org_id: true,
org_name: true
}
}
}
},
userrole: {
select: {
userRoleID: true,
role: {
select: {
roleID: true,
roleName: true,
roleDescription: true
}
}
}
}
}
});
if (!user) {
return null;
}
return user;
},
/**
* Update a user
*
* @param {number} id User ID
* @param {Object} data User data to update
* @returns {Promise<Object>} Updated user (without password)
*/
async update(id, data) {
if (isNaN(parseInt(id))) {
throw new Error('Invalid user ID');
}
// Check if user exists
const existingUser = await prisma.user.findUnique({
where: {
userID: parseInt(id)
}
});
if (!existingUser) {
throw new Error('User not found');
}
// Check if department exists if provided
if (data.dp_id !== undefined) {
if (data.dp_id !== null) {
const department = await prisma.department.findUnique({
where: {
dp_id: parseInt(data.dp_id)
}
});
if (!department) {
throw new Error(`Department with ID ${data.dp_id} not found`);
}
}
}
// Check if username is taken by another user
if (data.userUsername && data.userUsername !== existingUser.userUsername) {
const usernameExists = await prisma.user.findFirst({
where: {
userUsername: data.userUsername,
userID: {
not: parseInt(id)
}
}
});
if (usernameExists) {
throw new Error('Username is already taken by another user');
}
}
// Prepare update data
const updateData = {};
// Only update fields that are provided
if (data.userUsername !== undefined) updateData.userUsername = data.userUsername;
if (data.userFullName !== undefined) updateData.userFullName = data.userFullName;
if (data.userEmail !== undefined) updateData.userEmail = data.userEmail;
if (data.userPhone !== undefined) updateData.userPhone = data.userPhone;
if (data.userStatus !== undefined) updateData.userStatus = data.userStatus ? data.userStatus.toUpperCase() : null;
if (data.dp_id !== undefined) updateData.dp_id = data.dp_id ? parseInt(data.dp_id) : null;
// Hash password if provided
if (data.userPassword) {
updateData.userPassword = sha256(data.userPassword).toString();
}
// Update modified date
updateData.userModifiedDate = new Date();
// Update user
const user = await prisma.user.update({
where: {
userID: parseInt(id)
},
data: updateData
});
// Return user without password
const { userPassword, ...userWithoutPassword } = user;
return userWithoutPassword;
},
/**
* Delete a user
*
* @param {number} id User ID
* @returns {Promise<Object>} Deleted user (without password)
*/
async delete(id) {
if (isNaN(parseInt(id))) {
throw new Error('Invalid user ID');
}
// Check if user exists
const existingUser = await prisma.user.findUnique({
where: {
userID: parseInt(id)
},
include: {
userrole: {
select: {
userRoleID: true
}
}
}
});
if (!existingUser) {
throw new Error('User not found');
}
// Check if user has any roles assigned
if (existingUser.userrole && existingUser.userrole.length > 0) {
// Delete all associated user roles first
await prisma.userrole.deleteMany({
where: {
userRoleUserID: parseInt(id)
}
});
}
// Delete user
const user = await prisma.user.delete({
where: {
userID: parseInt(id)
}
});
// Return user without password
const { userPassword, ...userWithoutPassword } = user;
return userWithoutPassword;
},
/**
* Get users by department ID
*
* @param {number} departmentId Department ID
* @returns {Promise<Array>} List of users
*/
async getByDepartment(departmentId) {
if (isNaN(parseInt(departmentId))) {
throw new Error('Invalid department ID');
}
// Check if department exists
const department = await prisma.department.findUnique({
where: {
dp_id: parseInt(departmentId)
}
});
if (!department) {
throw new Error('Department not found');
}
// Get users for this department
return prisma.user.findMany({
where: {
dp_id: parseInt(departmentId)
},
orderBy: {
userFullName: 'asc'
},
select: {
userID: true,
userUsername: true,
userFullName: true,
userEmail: true,
userPhone: true,
userStatus: true,
userCreatedDate: true,
userModifiedDate: true,
userrole: {
select: {
userRoleID: true,
role: {
select: {
roleID: true,
roleName: true
}
}
}
}
}
});
},
/**
* Verify user credentials
*
* @param {string} username Username
* @param {string} password Raw password
* @returns {Promise<Object|null>} User data if credentials are valid, null otherwise
*/
async verifyCredentials(username, password) {
if (!username || !password) {
return null;
}
// Get user by username
const user = await prisma.user.findFirst({
where: {
userUsername: username
}
});
if (!user) {
return null;
}
// Check password
const hashedPassword = sha256(password).toString();
if (user.userPassword !== hashedPassword) {
return null;
}
// Return user without password
const { userPassword, ...userWithoutPassword } = user;
return userWithoutPassword;
},
/**
* Generate a random key
*
* @param {number} length Length of the key
* @returns {string} Random key
*/
generateRandomKey(length) {
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
let result = '';
for (let i = 0; i < length; i++) {
result += chars.charAt(Math.floor(Math.random() * chars.length));
}
return result;
}
};
export default UserUtils;