🚀 مُصادَق: تحديث برمجي جديد 2026-05-03 02:51

This commit is contained in:
Hamza-Ayed
2026-05-03 02:51:50 +03:00
parent 7b86fa717d
commit 392f6dbd9b
3 changed files with 65 additions and 7 deletions

View File

@@ -13,6 +13,12 @@ final class UsersController
public function list(Request $request): void
{
$currentUserRole = $request->user->role ?? 'viewer';
if (!in_array($currentUserRole, ['super_admin', 'admin'])) {
Response::error('ليس لديك صلاحية لعرض المستخدمين', 'FORBIDDEN', 403);
return;
}
try {
$tenantId = $request->tenantId;
$db = Database::getInstance();
@@ -31,11 +37,30 @@ final class UsersController
public function create(Request $request): void
{
$currentUserRole = $request->user->role ?? 'viewer';
if (!in_array($currentUserRole, ['super_admin', 'admin'])) {
Response::error('ليس لديك صلاحية لإضافة مستخدمين', 'FORBIDDEN', 403);
return;
}
$name = $request->input('name');
$email = $request->input('email');
$password = $request->input('password');
$role = $request->input('role', 'accountant');
// Admin can only create accountants and employees. Only super_admin can create admins.
if ($currentUserRole === 'admin' && in_array($role, ['admin', 'super_admin'])) {
Response::error('لا تملك الصلاحية لإضافة مدراء', 'FORBIDDEN', 403);
return;
}
// Validate valid roles
$validRoles = ['super_admin', 'admin', 'accountant', 'employee', 'viewer'];
if (!in_array($role, $validRoles)) {
Response::error('صلاحية غير صالحة', 'VALIDATION_ERROR', 422);
return;
}
if (!$name || !$email || !$password) {
Response::error('Name, email, and password are required', 'VALIDATION_ERROR', 422);
return;

View File

@@ -12,7 +12,8 @@ final class FileStorageService
public function __construct()
{
$this->storagePath = $_ENV['STORAGE_PATH'] ?? dirname(__DIR__, 2) . '/storage';
// Use dynamic path to avoid issues if Mac .env is deployed to Linux server
$this->storagePath = dirname(__DIR__, 2) . '/storage';
}
public function store(array $file, string $tenantId, string $companyId): string

View File

@@ -150,6 +150,7 @@
function logout() {
localStorage.removeItem('access_token');
localStorage.removeItem('user_role');
API.accessToken = null;
initApp();
}
@@ -170,6 +171,14 @@
// ── Users View ───────────────────────────────────────────
async function renderUsers() {
document.getElementById('page-title').textContent = 'إدارة المستخدمين';
// Check RBAC
const role = localStorage.getItem('user_role');
if (role !== 'super_admin' && role !== 'admin') {
contentDiv.innerHTML = `<div class="text-red-400 p-8 glass-panel rounded-3xl text-center">عذراً، ليس لديك صلاحية للوصول إلى هذه الصفحة.</div>`;
return;
}
try {
const res = await API.get('/users');
const users = res.data;
@@ -187,8 +196,14 @@
html += `<div class="col-span-full text-center py-12 text-slate-500 glass-panel rounded-3xl">لا يوجد مستخدمين مسجلين.</div>`;
} else {
users.forEach(user => {
const roleColor = user.role === 'admin' ? 'text-primary' : (user.role === 'manager' ? 'text-blue-400' : 'text-slate-400');
const roleLabel = user.role === 'admin' ? 'سوبر أدمن' : (user.role === 'manager' ? 'مدير' : 'محاسب');
let roleColor = 'text-slate-400';
let roleLabel = 'مستخدم';
if (user.role === 'super_admin') { roleColor = 'text-primary'; roleLabel = 'سوبر أدمن'; }
else if (user.role === 'admin') { roleColor = 'text-blue-400'; roleLabel = 'مدير النظام'; }
else if (user.role === 'accountant') { roleColor = 'text-purple-400'; roleLabel = 'محاسب'; }
else if (user.role === 'employee') { roleColor = 'text-orange-400'; roleLabel = 'موظف'; }
html += `
<div class="glass-panel p-6 rounded-3xl flex flex-col h-full border-t-4 border-t-primary">
<div class="flex items-center gap-4 mb-4">
@@ -218,11 +233,20 @@
html += `</div>`;
contentDiv.innerHTML = html;
} catch(err) {
contentDiv.innerHTML = `<div class="text-red-400">خطأ في جلب المستخدمين</div>`;
contentDiv.innerHTML = `<div class="text-red-400 p-8 glass-panel rounded-3xl">خطأ في جلب المستخدمين: ${err.error?.message_ar || err.message}</div>`;
}
}
function showAddUserModal() {
const currentRole = localStorage.getItem('user_role');
let optionsHtml = `
<option value="accountant">محاسب</option>
<option value="employee">موظف</option>
`;
if (currentRole === 'super_admin') {
optionsHtml += `<option value="admin">مدير نظام</option>`;
}
const modals = document.getElementById('modals');
modals.innerHTML = `
<div class="fixed inset-0 bg-black/60 backdrop-blur-sm z-[100] flex items-center justify-center p-4 overflow-y-auto" id="user-modal">
@@ -233,9 +257,7 @@
<input type="email" id="usr-email" class="w-full bg-black/20 border border-white/10 rounded-xl px-4 py-3 text-white focus:border-primary outline-none" placeholder="البريد الإلكتروني" required>
<input type="password" id="usr-password" class="w-full bg-black/20 border border-white/10 rounded-xl px-4 py-3 text-white focus:border-primary outline-none" placeholder="كلمة المرور" required>
<select id="usr-role" class="w-full bg-black/20 border border-white/10 rounded-xl px-4 py-3 text-white focus:border-primary outline-none" required>
<option value="accountant">محاسب</option>
<option value="admin">مدير</option>
<option value="employee">موظف</option>
${optionsHtml}
</select>
<div class="flex gap-3 mt-6 pt-4 border-t border-white/10">
@@ -301,6 +323,7 @@
const password = document.getElementById('login-password').value;
const res = await API.post('/auth/login', { email, password });
localStorage.setItem('access_token', res.data.access_token);
localStorage.setItem('user_role', res.data.user.role);
API.accessToken = res.data.access_token;
initApp();
} catch (err) {
@@ -677,6 +700,15 @@
document.getElementById('sidebar').classList.add('flex');
document.getElementById('header').classList.add('flex');
// Hide Users menu if not admin or super_admin
const role = localStorage.getItem('user_role');
const usersNav = document.getElementById('nav-users');
if (role !== 'super_admin' && role !== 'admin') {
if (usersNav) usersNav.style.display = 'none';
} else {
if (usersNav) usersNav.style.display = 'flex';
}
// AI Chat Listener
document.getElementById('ai-query').onkeydown = async (e) => {
if (e.key === 'Enter') {