$exitCode === 0, 'output' => $cleanOutput, 'code' => $exitCode ]); } catch (\Exception $e) { echo json_encode([ 'success' => false, 'output' => '❌ ' . $e->getMessage(), 'code' => 1 ]); } exit; } // 템플릿 삭제 (Manager 직접 사용 - 확인 프롬프트 우회) if ($action === 'template_uninstall') { $identifier = $_GET['identifier'] ?? ''; try { $manager = app(\App\Extension\TemplateManager::class); $manager->loadTemplates(); $result = $manager->uninstallTemplate($identifier); echo json_encode([ 'success' => $result, 'output' => $result ? "✅ 템플릿 '{$identifier}' 삭제 완료" : "❌ 삭제 실패", 'code' => $result ? 0 : 1 ]); } catch (\Exception $e) { echo json_encode(['success' => false, 'output' => '❌ ' . $e->getMessage(), 'code' => 1]); } exit; } // 모듈 삭제 (Manager 직접 사용) if ($action === 'module_uninstall') { $identifier = $_GET['identifier'] ?? ''; try { $manager = app(\App\Extension\ModuleManager::class); $manager->loadModules(); $result = $manager->uninstallModule($identifier); echo json_encode([ 'success' => $result, 'output' => $result ? "✅ 모듈 '{$identifier}' 삭제 완료" : "❌ 삭제 실패", 'code' => $result ? 0 : 1 ]); } catch (\Exception $e) { echo json_encode(['success' => false, 'output' => '❌ ' . $e->getMessage(), 'code' => 1]); } exit; } // 플러그인 삭제 (Manager 직접 사용) if ($action === 'plugin_uninstall') { $identifier = $_GET['identifier'] ?? ''; try { $manager = app(\App\Extension\PluginManager::class); $manager->loadPlugins(); $result = $manager->uninstallPlugin($identifier); echo json_encode([ 'success' => $result, 'output' => $result ? "✅ 플러그인 '{$identifier}' 삭제 완료" : "❌ 삭제 실패", 'code' => $result ? 0 : 1 ]); } catch (\Exception $e) { echo json_encode(['success' => false, 'output' => '❌ ' . $e->getMessage(), 'code' => 1]); } exit; } // 템플릿 빌드 (npm run build) if ($action === 'template_build') { $identifier = $_GET['identifier'] ?? ''; try { $templatePath = base_path('templates/' . $identifier); if (!is_dir($templatePath)) { echo json_encode([ 'success' => false, 'output' => "❌ 템플릿 '{$identifier}' 디렉토리를 찾을 수 없습니다.", 'code' => 1 ]); exit; } // package.json 존재 확인 if (!file_exists($templatePath . '/package.json')) { echo json_encode([ 'success' => false, 'output' => "❌ package.json 파일이 없습니다.", 'code' => 1 ]); exit; } // Windows 환경에서 npm run build 실행 $command = "cd " . escapeshellarg($templatePath) . " && npm run build 2>&1"; if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') { $command = "cd /d " . escapeshellarg($templatePath) . " && npm run build 2>&1"; } $output = []; $returnCode = 0; exec($command, $output, $returnCode); // ANSI 색상 코드 제거 및 로그 필터링 $filteredOutput = []; foreach ($output as $line) { // ANSI 색상 코드 제거 $cleanLine = preg_replace('/\x1b\[[0-9;]*m/', '', $line); // 필터링할 패턴 (상세 진행 로그 제외) $skipPatterns = [ '/^transforming\.\.\./', // transforming... '/^✓ \d+ modules transformed/', // ✓ 194 modules transformed '/^rendering chunks\.\.\./', // rendering chunks... '/^computing gzip size\.\.\./', // computing gzip size... '/^You are using Node\.js/', // Node.js 버전 경고 '/^vite v[\d\.]+ building/', // vite 버전 정보 ]; $shouldSkip = false; foreach ($skipPatterns as $pattern) { if (preg_match($pattern, $cleanLine)) { $shouldSkip = true; break; } } if (!$shouldSkip && trim($cleanLine) !== '') { $filteredOutput[] = $cleanLine; } } $outputText = implode("\n", $filteredOutput); // 성공/실패 여부 표시 if ($returnCode === 0) { $outputText = "✅ 빌드 완료\n\n" . $outputText; } else { $outputText = "❌ 빌드 실패\n\n" . $outputText; } echo json_encode([ 'success' => $returnCode === 0, 'output' => $outputText, 'code' => $returnCode ]); } catch (\Exception $e) { echo json_encode(['success' => false, 'output' => '❌ ' . $e->getMessage(), 'code' => 1]); } exit; } // 인스톨러 초기화 if ($action === 'reset') { $results = []; $deleteVendor = isset($_GET['delete_vendor']) && $_GET['delete_vendor'] === '1'; // DB 테이블 전체 삭제 (.env 삭제 전에 실행해야 DB 연결 유지) $dropTables = isset($_GET['drop_tables']) && $_GET['drop_tables'] === '1'; if ($dropTables) { try { $tables = \Illuminate\Support\Facades\DB::select('SHOW TABLES'); if (empty($tables)) { $results[] = ['name' => 'DB 테이블', 'status' => 'notfound']; } else { // SHOW TABLES 결과의 첫 번째 컬럼명을 동적으로 가져옴 $columnKey = array_key_first((array) $tables[0]); $tableNames = array_map(fn($t) => ((array) $t)[$columnKey], $tables); \Illuminate\Support\Facades\DB::statement('SET FOREIGN_KEY_CHECKS = 0'); $droppedCount = 0; foreach ($tableNames as $tableName) { \Illuminate\Support\Facades\DB::statement("DROP TABLE IF EXISTS `{$tableName}`"); $droppedCount++; } \Illuminate\Support\Facades\DB::statement('SET FOREIGN_KEY_CHECKS = 1'); $results[] = [ 'name' => "DB 테이블 ({$droppedCount}개 삭제)", 'status' => 'deleted' ]; } } catch (\Exception $e) { $results[] = ['name' => 'DB 테이블 (' . $e->getMessage() . ')', 'status' => 'failed']; } } // 삭제 대상 파일 목록 (표시명 => 경로) $filesToDelete = [ '.env' => base_path('.env'), 'storage/app/g7_installed' => storage_path('app/g7_installed'), 'storage/installer-state.json' => storage_path('installer-state.json') ]; foreach ($filesToDelete as $displayName => $file) { if (file_exists($file)) { if (@unlink($file)) { $results[] = ['name' => $displayName, 'status' => 'deleted']; } else { $results[] = ['name' => $displayName, 'status' => 'failed']; } } else { $results[] = ['name' => $displayName, 'status' => 'notfound']; } } if ($deleteVendor) { $vendorPath = base_path('vendor'); if (is_dir($vendorPath)) { // .gitkeep 보존: vendor 내부 항목만 개별 삭제 $vendorItems = array_diff(scandir($vendorPath), ['.', '..', '.gitkeep']); $deletedCount = 0; $failedCount = 0; foreach ($vendorItems as $item) { $itemPath = $vendorPath . DIRECTORY_SEPARATOR . $item; $escapedItem = escapeshellarg($itemPath); if (is_dir($itemPath)) { if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') { exec("rmdir /s /q $escapedItem 2>nul", $output, $returnCode); } else { exec("rm -rf $escapedItem", $output, $returnCode); } is_dir($itemPath) ? $failedCount++ : $deletedCount++; } else { @unlink($itemPath) ? $deletedCount++ : $failedCount++; } } $results[] = [ 'name' => 'vendor/ (.gitkeep 보존)', 'status' => $failedCount === 0 ? 'deleted' : 'failed' ]; } else { $results[] = ['name' => 'vendor/', 'status' => 'notfound']; } } // 확장 설치 디렉토리 삭제 (_bundled, _pending 보존) $deleteExtensions = isset($_GET['delete_extensions']) && $_GET['delete_extensions'] === '1'; if ($deleteExtensions) { $extensionTypes = ['modules', 'plugins', 'templates']; $protectedDirs = ['_bundled', '_pending']; foreach ($extensionTypes as $type) { $basePath = base_path($type); if (!is_dir($basePath)) { $results[] = ['name' => "$type/", 'status' => 'notfound']; continue; } $dirs = array_filter(glob($basePath . '/*'), 'is_dir'); $deletedDirs = []; $failedDirs = []; foreach ($dirs as $dir) { $dirName = basename($dir); if (in_array($dirName, $protectedDirs)) { continue; // _bundled, _pending 보호 } $escapedDir = escapeshellarg($dir); if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') { exec("rmdir /s /q $escapedDir 2>nul", $output, $returnCode); } else { exec("rm -rf $escapedDir", $output, $returnCode); } is_dir($dir) ? $failedDirs[] = $dirName : $deletedDirs[] = $dirName; } if (empty($deletedDirs) && empty($failedDirs)) { $results[] = ['name' => "$type/", 'status' => 'notfound']; } else { $status = empty($failedDirs) ? 'deleted' : 'failed'; $detail = implode(', ', $deletedDirs); $results[] = ['name' => "$type/ ($detail)", 'status' => $status]; } } } echo json_encode(['success' => true, 'results' => $results]); exit; } } ?> {{ config('app.name', '그누보드7') }}
G7

{{ config('app.name', '그누보드7') }}

개발 환경 대시보드

📦 모듈 관리
🎨 템플릿 관리
🔌 플러그인 관리
🌐 언어팩 관리
🔨 코어 빌드
🔗 확장 공통
🧹 캐시 & DB
🔄 코어 업데이트
🔍 SEO
🛠️ 유지보수

개발 도구

🔄
인스톨러 초기화

.env, storage 파일 삭제하여 설치 상태 초기화

vendor 폴더 삭제
확장 설치 디렉토리 삭제 (_bundled, _pending 보존)
DB 테이블 전체 삭제 (migrations 포함)