generated from corrad-software/corrad-af-2024
490 lines
12 KiB
JavaScript
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;
|