option('dry-run'); $this->info('Đang quét Filament Resources...'); $resourcesPath = app_path('Filament/Resources'); if (! is_dir($resourcesPath)) { $this->error('Không tìm thấy thư mục Filament/Resources'); return self::FAILURE; } $modules = []; $finder = new Finder(); $finder->files()->in($resourcesPath)->name('*Resource.php'); foreach ($finder as $file) { $class = $this->getClassFromFile($file->getRealPath()); if (! $class || ! class_exists($class)) { continue; } $ref = new ReflectionClass($class); if (! $ref->hasProperty('permissionActions')) { continue; } $prop = $ref->getProperty('permissionActions'); $prop->setAccessible(true); $actions = $prop->getDefaultValue(); $label = 'Resource'; if ($ref->hasProperty('pluralModelLabel')) { $labelProp = $ref->getProperty('pluralModelLabel'); $labelProp->setAccessible(true); $label = $labelProp->getDefaultValue() ?? $label; } $moduleName = Str::snake(class_basename($class)); $moduleName = str_replace('_resource', '', $moduleName); $modules[] = [ 'module' => $moduleName, 'label' => $label, 'actions' => $actions ?? [], ]; } if (empty($modules)) { $this->warn('Không tìm thấy Resource nào có $permissionActions.'); return self::SUCCESS; } $this->info('Tìm thấy ' . count($modules) . ' module(s):'); foreach ($modules as $m) { $this->line(" - {$m['module']}: " . implode(', ', $m['actions'])); } if ($dryRun) { $this->info('Dry-run: Không lưu thay đổi.'); return self::SUCCESS; } foreach ($modules as $m) { $existing = PermissionModule::where('module', $m['module'])->first(); if ($existing) { $oldActions = $existing->actions ?? []; $newActions = $m['actions']; if ($oldActions === $newActions) { continue; } $added = array_diff($newActions, $oldActions); $removed = array_diff($oldActions, $newActions); $existing->update([ 'label' => $m['label'], 'actions' => $newActions, ]); if (! empty($added)) { $this->warn(" [{$m['module']}] Thêm actions: " . implode(', ', $added)); // Action mới mặc định TẮT cho tất cả role $this->disableNewActionsForAllRoles($m['module'], $added); } if (! empty($removed)) { $this->warn(" [{$m['module']}] Xóa actions: " . implode(', ', $removed)); $this->removeActionsFromAllRoles($m['module'], $removed); } } else { PermissionModule::create([ 'module' => $m['module'], 'label' => $m['label'], 'actions' => $m['actions'], ]); $this->info(" [{$m['module']}] Tạo mới module."); // Module mới mặc định TẮT cho tất cả role $this->disableNewActionsForAllRoles($m['module'], $m['actions']); } } $this->info('Đồng bộ hoàn tất.'); return self::SUCCESS; } private function getClassFromFile(string $path): ?string { $contents = file_get_contents($path); // Tìm namespace $namespace = null; if (preg_match('/namespace\s+([^;]+);/', $contents, $matches)) { $namespace = $matches[1]; } // Tìm class name $class = null; if (preg_match('/class\s+(\w+)/', $contents, $matches)) { $class = $matches[1]; } if ($namespace && $class) { return $namespace . '\\' . $class; } return null; } private function disableNewActionsForAllRoles(string $module, array $actions): void { $roles = RoleTemplate::all(); foreach ($roles as $role) { $perms = $role->permissions ?? []; foreach ($actions as $action) { if (isset($perms[$module]) && in_array($action, $perms[$module])) { continue; // Đã có thì giữ nguyên } // Không thêm vào = mặc định TẮT } // Không cần update vì JSONB không lưu action mới = TẮT } } private function removeActionsFromAllRoles(string $module, array $actions): void { $roles = RoleTemplate::all(); foreach ($roles as $role) { $perms = $role->permissions ?? []; if (isset($perms[$module])) { $perms[$module] = array_values(array_diff($perms[$module], $actions)); if (empty($perms[$module])) { unset($perms[$module]); } $role->update(['permissions' => $perms]); } } } }