argument('identifier'); // 식별자 형식 검증 $validator = Validator::make( ['identifier' => $identifier], ['identifier' => [new ValidExtensionIdentifier]] ); if ($validator->fails()) { $this->error('❌ '.$validator->errors()->first('identifier')); return Command::FAILURE; } $force = (bool) $this->option('force'); try { // 플러그인 디렉토리 스캔 및 로드 $this->pluginManager->loadPlugins(); // 이미 설치된 플러그인인지 확인 (force 시 스킵하여 재설치/복구 허용) $existingPlugin = $this->pluginRepository->findByIdentifier($identifier); if ($existingPlugin && ! $force) { $this->warn('⚠️ '.__('plugins.commands.install.already_installed', ['plugin' => $identifier])); return Command::FAILURE; } if ($existingPlugin && $force) { $this->warn('⚠️ '.__('plugins.commands.install.force_reinstall', ['plugin' => $identifier])); } // 플러그인 설치 $vendorMode = VendorMode::fromStringOrAuto((string) $this->option('vendor-mode')); $onProgress = $this->createProgressCallback(PluginManager::INSTALL_STEPS); try { $result = $this->pluginManager->installPlugin($identifier, $onProgress, $vendorMode, $force); $this->finishProgress(); } catch (\Exception $e) { $this->finishProgress(); throw $e; } if ($result) { // 설치된 플러그인 정보 조회 $plugin = $this->pluginRepository->findByIdentifier($identifier); // 플러그인 인스턴스에서 생성된 role 개수 조회 $pluginInstance = $this->pluginManager->getPlugin($identifier); $rolesCount = 0; if ($pluginInstance && method_exists($pluginInstance, 'getRoles')) { $rolesCount = count($pluginInstance->getRoles()); } // 권한 개수 조회 $permissionsCount = Permission::byExtension(ExtensionOwnerType::Plugin, $identifier)->count(); // 성공 메시지 $this->info('✅ '.__('plugins.commands.install.success', ['plugin' => $identifier])); $this->info(' - '.__('plugins.commands.install.vendor', ['vendor' => $plugin->vendor])); $this->info(' - '.__('plugins.commands.install.version', ['version' => $plugin->version])); $this->info(' - '.__('plugins.commands.install.roles_created', ['count' => $rolesCount])); $this->info(' - '.__('plugins.commands.install.permissions_created', ['count' => $permissionsCount])); Log::info(__('plugins.commands.install.success', ['plugin' => $identifier])); // 검색 인덱스 재생성은 운영자가 선택했을 때만 수행한다 (인덱스 잠금·재색인 비용) $this->handleSearchIndexRebuild(); return Command::SUCCESS; } return Command::FAILURE; } catch (\Exception $e) { $this->error('❌ '.$e->getMessage()); Log::error('플러그인 설치 실패', [ 'plugin' => $identifier, 'error' => $e->getMessage(), ]); return Command::FAILURE; } } }