uup
This commit is contained in:
341
app/lib/user-management.server.ts
Normal file
341
app/lib/user-management.server.ts
Normal file
@@ -0,0 +1,341 @@
|
||||
import { prisma } from "./db.server";
|
||||
import { hashPassword } from "./auth.server";
|
||||
import type { User } from "@prisma/client";
|
||||
import type { CreateUserData, UpdateUserData, UserWithoutPassword } from "~/types/database";
|
||||
import { AUTH_LEVELS, USER_STATUS } from "~/types/auth";
|
||||
|
||||
// Get all users with role-based filtering
|
||||
export async function getUsers(
|
||||
currentUserAuthLevel: number,
|
||||
searchQuery?: string,
|
||||
page: number = 1,
|
||||
limit: number = 10
|
||||
): Promise<{
|
||||
users: UserWithoutPassword[];
|
||||
total: number;
|
||||
totalPages: number;
|
||||
}> {
|
||||
const offset = (page - 1) * limit;
|
||||
|
||||
// Build where clause based on current user's auth level
|
||||
const whereClause: any = {};
|
||||
|
||||
// Admins cannot see superadmin accounts
|
||||
if (currentUserAuthLevel === AUTH_LEVELS.ADMIN) {
|
||||
whereClause.authLevel = {
|
||||
gt: AUTH_LEVELS.SUPERADMIN,
|
||||
};
|
||||
}
|
||||
|
||||
// Add search functionality
|
||||
if (searchQuery) {
|
||||
const searchLower = searchQuery.toLowerCase();
|
||||
whereClause.OR = [
|
||||
{ name: { contains: searchLower } },
|
||||
{ username: { contains: searchLower } },
|
||||
{ email: { contains: searchLower } },
|
||||
];
|
||||
}
|
||||
|
||||
const [users, total] = await Promise.all([
|
||||
prisma.user.findMany({
|
||||
where: whereClause,
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
username: true,
|
||||
email: true,
|
||||
status: true,
|
||||
authLevel: true,
|
||||
createdDate: true,
|
||||
editDate: true,
|
||||
},
|
||||
orderBy: { createdDate: 'desc' },
|
||||
skip: offset,
|
||||
take: limit,
|
||||
}),
|
||||
prisma.user.count({ where: whereClause }),
|
||||
]);
|
||||
|
||||
return {
|
||||
users,
|
||||
total,
|
||||
totalPages: Math.ceil(total / limit),
|
||||
};
|
||||
}
|
||||
|
||||
// Get user by ID with role-based access control
|
||||
export async function getUserById(
|
||||
id: number,
|
||||
currentUserAuthLevel: number
|
||||
): Promise<UserWithoutPassword | null> {
|
||||
const user = await prisma.user.findUnique({
|
||||
where: { id },
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
username: true,
|
||||
email: true,
|
||||
status: true,
|
||||
authLevel: true,
|
||||
createdDate: true,
|
||||
editDate: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!user) return null;
|
||||
|
||||
// Admins cannot access superadmin accounts
|
||||
if (currentUserAuthLevel === AUTH_LEVELS.ADMIN && user.authLevel === AUTH_LEVELS.SUPERADMIN) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
// Create new user
|
||||
export async function createUser(
|
||||
userData: CreateUserData,
|
||||
currentUserAuthLevel: number
|
||||
): Promise<{ success: boolean; user?: UserWithoutPassword; error?: string }> {
|
||||
try {
|
||||
// Validate that current user can create users with the specified auth level
|
||||
if (currentUserAuthLevel === AUTH_LEVELS.ADMIN && userData.authLevel === AUTH_LEVELS.SUPERADMIN) {
|
||||
return { success: false, error: "لا يمكن للمدير إنشاء حساب مدير عام" };
|
||||
}
|
||||
|
||||
// Check if username or email already exists
|
||||
const existingUser = await prisma.user.findFirst({
|
||||
where: {
|
||||
OR: [
|
||||
{ username: userData.username },
|
||||
{ email: userData.email },
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
if (existingUser) {
|
||||
if (existingUser.username === userData.username) {
|
||||
return { success: false, error: "اسم المستخدم موجود بالفعل" };
|
||||
}
|
||||
if (existingUser.email === userData.email) {
|
||||
return { success: false, error: "البريد الإلكتروني موجود بالفعل" };
|
||||
}
|
||||
}
|
||||
|
||||
// Hash password
|
||||
const hashedPassword = await hashPassword(userData.password);
|
||||
|
||||
// Create user
|
||||
const user = await prisma.user.create({
|
||||
data: {
|
||||
name: userData.name,
|
||||
username: userData.username,
|
||||
email: userData.email,
|
||||
password: hashedPassword,
|
||||
authLevel: userData.authLevel,
|
||||
status: userData.status || USER_STATUS.ACTIVE,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
username: true,
|
||||
email: true,
|
||||
status: true,
|
||||
authLevel: true,
|
||||
createdDate: true,
|
||||
editDate: true,
|
||||
},
|
||||
});
|
||||
|
||||
return { success: true, user };
|
||||
} catch (error) {
|
||||
console.error("Error creating user:", error);
|
||||
return { success: false, error: "حدث خطأ أثناء إنشاء المستخدم" };
|
||||
}
|
||||
}
|
||||
|
||||
// Update user
|
||||
export async function updateUser(
|
||||
id: number,
|
||||
userData: UpdateUserData,
|
||||
currentUserAuthLevel: number
|
||||
): Promise<{ success: boolean; user?: UserWithoutPassword; error?: string }> {
|
||||
try {
|
||||
// Get existing user to check permissions
|
||||
const existingUser = await getUserById(id, currentUserAuthLevel);
|
||||
if (!existingUser) {
|
||||
return { success: false, error: "المستخدم غير موجود أو لا يمكن الوصول إليه" };
|
||||
}
|
||||
|
||||
// Validate auth level changes
|
||||
if (userData.authLevel !== undefined) {
|
||||
if (currentUserAuthLevel === AUTH_LEVELS.ADMIN && userData.authLevel === AUTH_LEVELS.SUPERADMIN) {
|
||||
return { success: false, error: "لا يمكن للمدير تعيين مستوى مدير عام" };
|
||||
}
|
||||
}
|
||||
|
||||
// Check for username/email conflicts
|
||||
if (userData.username || userData.email) {
|
||||
const conflictUser = await prisma.user.findFirst({
|
||||
where: {
|
||||
AND: [
|
||||
{ id: { not: id } },
|
||||
{
|
||||
OR: [
|
||||
userData.username ? { username: userData.username } : {},
|
||||
userData.email ? { email: userData.email } : {},
|
||||
].filter(condition => Object.keys(condition).length > 0),
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
if (conflictUser) {
|
||||
if (conflictUser.username === userData.username) {
|
||||
return { success: false, error: "اسم المستخدم موجود بالفعل" };
|
||||
}
|
||||
if (conflictUser.email === userData.email) {
|
||||
return { success: false, error: "البريد الإلكتروني موجود بالفعل" };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Prepare update data
|
||||
const updateData: any = {};
|
||||
if (userData.name !== undefined) updateData.name = userData.name;
|
||||
if (userData.username !== undefined) updateData.username = userData.username;
|
||||
if (userData.email !== undefined) updateData.email = userData.email;
|
||||
if (userData.authLevel !== undefined) updateData.authLevel = userData.authLevel;
|
||||
if (userData.status !== undefined) updateData.status = userData.status;
|
||||
|
||||
// Hash new password if provided
|
||||
if (userData.password) {
|
||||
updateData.password = await hashPassword(userData.password);
|
||||
}
|
||||
|
||||
// Update user
|
||||
const user = await prisma.user.update({
|
||||
where: { id },
|
||||
data: updateData,
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
username: true,
|
||||
email: true,
|
||||
status: true,
|
||||
authLevel: true,
|
||||
createdDate: true,
|
||||
editDate: true,
|
||||
},
|
||||
});
|
||||
|
||||
return { success: true, user };
|
||||
} catch (error) {
|
||||
console.error("Error updating user:", error);
|
||||
return { success: false, error: "حدث خطأ أثناء تحديث المستخدم" };
|
||||
}
|
||||
}
|
||||
|
||||
// Delete user
|
||||
export async function deleteUser(
|
||||
id: number,
|
||||
currentUserAuthLevel: number
|
||||
): Promise<{ success: boolean; error?: string }> {
|
||||
try {
|
||||
// Get existing user to check permissions
|
||||
const existingUser = await getUserById(id, currentUserAuthLevel);
|
||||
if (!existingUser) {
|
||||
return { success: false, error: "المستخدم غير موجود أو لا يمكن الوصول إليه" };
|
||||
}
|
||||
|
||||
// Prevent deletion of superadmin by admin
|
||||
if (currentUserAuthLevel === AUTH_LEVELS.ADMIN && existingUser.authLevel === AUTH_LEVELS.SUPERADMIN) {
|
||||
return { success: false, error: "لا يمكن للمدير حذف حساب مدير عام" };
|
||||
}
|
||||
|
||||
// Check if this is the last superadmin
|
||||
if (existingUser.authLevel === AUTH_LEVELS.SUPERADMIN) {
|
||||
const superadminCount = await prisma.user.count({
|
||||
where: { authLevel: AUTH_LEVELS.SUPERADMIN },
|
||||
});
|
||||
|
||||
if (superadminCount <= 1) {
|
||||
return { success: false, error: "لا يمكن حذف آخر مدير عام في النظام" };
|
||||
}
|
||||
}
|
||||
|
||||
// Delete user
|
||||
await prisma.user.delete({
|
||||
where: { id },
|
||||
});
|
||||
|
||||
return { success: true };
|
||||
} catch (error) {
|
||||
console.error("Error deleting user:", error);
|
||||
return { success: false, error: "حدث خطأ أثناء حذف المستخدم" };
|
||||
}
|
||||
}
|
||||
|
||||
// Toggle user status (active/inactive)
|
||||
export async function toggleUserStatus(
|
||||
id: number,
|
||||
currentUserAuthLevel: number
|
||||
): Promise<{ success: boolean; user?: UserWithoutPassword; error?: string }> {
|
||||
try {
|
||||
const existingUser = await getUserById(id, currentUserAuthLevel);
|
||||
if (!existingUser) {
|
||||
return { success: false, error: "المستخدم غير موجود أو لا يمكن الوصول إليه" };
|
||||
}
|
||||
|
||||
const newStatus = existingUser.status === USER_STATUS.ACTIVE
|
||||
? USER_STATUS.INACTIVE
|
||||
: USER_STATUS.ACTIVE;
|
||||
|
||||
const user = await prisma.user.update({
|
||||
where: { id },
|
||||
data: { status: newStatus },
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
username: true,
|
||||
email: true,
|
||||
status: true,
|
||||
authLevel: true,
|
||||
createdDate: true,
|
||||
editDate: true,
|
||||
},
|
||||
});
|
||||
|
||||
return { success: true, user };
|
||||
} catch (error) {
|
||||
console.error("Error toggling user status:", error);
|
||||
return { success: false, error: "حدث خطأ أثناء تغيير حالة المستخدم" };
|
||||
}
|
||||
}
|
||||
|
||||
// Get auth level display name
|
||||
export function getAuthLevelName(authLevel: number): string {
|
||||
switch (authLevel) {
|
||||
case AUTH_LEVELS.SUPERADMIN:
|
||||
return "مدير عام";
|
||||
case AUTH_LEVELS.ADMIN:
|
||||
return "مدير";
|
||||
case AUTH_LEVELS.USER:
|
||||
return "مستخدم";
|
||||
default:
|
||||
return "غير محدد";
|
||||
}
|
||||
}
|
||||
|
||||
// Get status display name
|
||||
export function getStatusName(status: string): string {
|
||||
switch (status) {
|
||||
case USER_STATUS.ACTIVE:
|
||||
return "نشط";
|
||||
case USER_STATUS.INACTIVE:
|
||||
return "غير نشط";
|
||||
default:
|
||||
return "غير محدد";
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user