car_mms/app/lib/user-management.server.ts
2025-09-11 14:22:27 +03:00

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 "غير محدد";
}
}