341 lines
9.9 KiB
TypeScript
341 lines
9.9 KiB
TypeScript
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 "غير محدد";
|
|
}
|
|
} |