Files
boursecu_server/app/Services/AdminPageAccess.php
T
2026-10-20 10:26:45 +03:30

251 lines
7.0 KiB
PHP

<?php
namespace App\Services;
use App\Models\User;
class AdminPageAccess
{
public const DEPLOY_ADMIN_MOBILE_CORE = '9128081392';
/** کلید برگ‌ها؛ با ADMIN_PAGE_TREE در ادمین یکی بماند. */
public const LEAF_KEYS = [
'dashboardMain',
'customerMain_limitedOrder',
'customerMain_archive',
'customerMain_online',
'customerMain_specialPrice',
'customerMain_goldCredit',
'productsMain_limitedOrder',
'productsMain_archive',
'productsMain_categories',
'ordersMain',
'transactionsMain',
'accountsMain',
'walletMain',
'physicalWithdrawMain',
'withdrawMain',
'pricingMain',
'priceTriggers',
'conditionalOrders',
'colleagueOrdersMain',
'accountingMain',
'supportMain',
'websiteLandingSite',
'websiteLandingHeader',
'websiteLandingHero',
'websiteLandingLivePrice',
'websiteLandingCalculator',
'websiteLandingFeature',
'websiteLandingSteps',
'websiteLandingTrust',
'websiteLandingFaq',
'websiteLandingBottomCta',
'websiteLandingFooter',
'configuration',
'captureSitesMain',
'advancedConfiguration',
'smsPanelMain',
'tradingHours',
'banners',
'appNotifications',
'automations',
'smsMain',
'giftCardMain',
'discounts',
'usersMain',
];
private const ALIASES = [
'dashboard' => '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;
}
}