'dashboardMain', 'main' => 'dashboardMain', 'orders' => 'ordersMain', 'transactions' => 'transactionsMain', 'support' => 'supportMain', 'factor' => 'withdrawMain', ]; public static function hasFullAccess(?User $user): bool { if (!$user) { return false; } if (self::isDeployAdmin($user)) { return true; } $access = $user->page_access; return $access === null; } public static function isDeployAdmin(?User $user): bool { if (!$user) { return false; } return self::normalizeMobile($user->mobile ?? '') === self::DEPLOY_ADMIN_MOBILE_CORE; } /** * null = دسترسی کامل. آرایه = فقط کلیدهای شناخته‌شده. */ public static function normalize($value): ?array { if ($value === null || $value === '' || $value === false) { return null; } if (is_string($value)) { $decoded = json_decode($value, true); $value = is_array($decoded) ? $decoded : null; if ($value === null) { return null; } } if (!is_array($value)) { return null; } $out = []; foreach (self::LEAF_KEYS as $key) { $row = $value[$key] ?? null; if (!is_array($row)) { continue; } $view = self::toBool($row['view'] ?? false); $edit = self::toBool($row['edit'] ?? false); $delete = self::toBool($row['delete'] ?? false); if ($edit || $delete) { $view = true; } if (!$view) { $edit = false; $delete = false; } if (!$view && !$edit && !$delete) { continue; } $out[$key] = [ 'view' => $view, 'edit' => $edit, 'delete' => $delete, ]; } return $out; } public static function hasAnyView(?array $access): bool { if ($access === null) { return true; } foreach ($access as $row) { if (!empty($row['view'])) { return true; } } return false; } public static function allows(?User $user, string $routeName, string $action): bool { if (self::hasFullAccess($user)) { return true; } $leaf = self::ALIASES[$routeName] ?? $routeName; if (!in_array($leaf, self::LEAF_KEYS, true)) { return false; } $access = $user->page_access; if (!is_array($access)) { return false; } $row = $access[$leaf] ?? null; if (!is_array($row)) { return false; } if ($action === 'delete') { return !empty($row['delete']); } if ($action === 'edit') { return !empty($row['edit']); } return !empty($row['view']); } public static function isReadPost(string $path): bool { $path = strtolower(trim($path, '/')); if (str_contains($path, '/table') || substr($path, -5) === 'table') { return true; } if (str_contains($path, 'preview')) { return true; } if (str_contains($path, 'get_customer_list') || str_contains($path, 'products-by-categories')) { return true; } if (str_contains($path, 'mande-by-codes')) { return true; } $exact = [ 'getbalance', 'prices', 'prices/chart', 'gateway', 'finance', 'check_card', 'dashboard', 'dashboard/finance', 'dashboard/customer/all_sell', 'dashboard/customer/single/all_sell', 'dashboard/customer/flow', 'dashboard/customer/order-peak-hours', 'dashboard/profit-conversions', 'dashboard/montzar-reconcile', ]; if (in_array($path, $exact, true)) { return true; } if (preg_match('#^customers/\d+/(referrals|funds|piggy-funds)$#', $path)) { return true; } if (preg_match('#^customers/\d+/assets/\d+/(piggy-funds|transactions)$#', $path)) { return true; } if (preg_match('#^customers/\d+/piggy-funds/\d+/transactions$#', $path)) { return true; } if (preg_match('#^customers/\d+/gold_credit/installments$#', $path)) { return true; } if (preg_match('#^chat/(focus|unfocus|ensure-webhook)$#', $path)) { return true; } return false; } private static function toBool($value): bool { if (is_bool($value)) { return $value; } if (is_numeric($value)) { return (int) $value === 1; } $text = strtolower(trim((string) $value)); return in_array($text, ['1', 'true', 'yes', 'on'], true); } private static function normalizeMobile(?string $mobile): string { $digits = preg_replace('/\D+/', '', (string) $mobile) ?? ''; if (str_starts_with($digits, '98') && strlen($digits) >= 12) { $digits = substr($digits, 2); } if (strlen($digits) === 11 && str_starts_with($digits, '0')) { $digits = substr($digits, 1); } return $digits; } }