GNU/_PAGE/backup/backup_delete.php
<?php
/**
 * Data Directory Explorer (PHP Version) - Multi Volume Support with Batch Delete Feature
 */

// 1. 볼륨 설정 (기존 코드 유지)
$volumes = [
    'maria'  => [
        'name' => '마리아 (Maria Volume)',
        'path' => '/data/backup'
    ],
    'oldboy' => [
        'name' => '올드보이 (Oldboy Volume)',
        'path' => '/home/www/DATA/UPBIT/log'
    ]
];

$current_vol_id = isset($_GET['vol']) && isset($volumes[$_GET['vol']]) ? $_GET['vol'] : 'maria';
$root_path = $volumes[$current_vol_id]['path'];
$current_sub_dir = isset($_GET['dir']) ? $_GET['dir'] : '';

// 2. 경로 계산 및 보안 검사 (기존 코드 유지)
$target_dir = $root_path . ($current_sub_dir ? DIRECTORY_SEPARATOR . $current_sub_dir : '');
$real_target = realpath($target_dir);
$real_root = realpath($root_path);

if ($real_target === false || ($real_root !== false && strpos($real_target, $real_root) !== 0)) {
    $target_dir = $real_root ?: $root_path;
    $current_sub_dir = '';
} else {
    $target_dir = $real_target;
}

// [기존 코드 유지] 디렉토리 용량 계산 함수
function getDirSize($path) {
    $size = 0;
    try {
        if (is_dir($path)) {
            foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path, FilesystemIterator::SKIP_DOTS)) as $file) {
                $size += $file->getSize();
            }
        }
    } catch (Exception $e) { $size = 0; }
    return $size;
}

// 3. 파일 삭제 로직 처리 (기존 코드 유지)
$messages = [];
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) {
    if ($_POST['action'] === 'delete') {
        $files_to_delete = [$_POST['file_name']];
    } elseif ($_POST['action'] === 'delete_batch' && isset($_POST['selected_files'])) {
        $files_to_delete = $_POST['selected_files'];
    } else {
        $files_to_delete = [];
    }

    $deleted_count = 0;
    foreach ($files_to_delete as $file_name) {
        $delete_path = $target_dir . DIRECTORY_SEPARATOR . $file_name;
        $real_delete_path = realpath($delete_path);
        if ($real_delete_path && is_file($real_delete_path) && strpos($real_delete_path, $real_root) === 0) {
            if (unlink($real_delete_path)) {
                $deleted_count++;
            }
        }
    }
    if ($deleted_count > 0) {
        $messages[] = "성공적으로 " . $deleted_count . "개의 파일을 삭제했습니다.";
    } elseif (!empty($files_to_delete)) {
        $messages[] = "파일 삭제에 실패했거나 권한이 없습니다.";
    }
}

// 4. 파일 목록 읽기 및 사이즈 계산 (기존 구조 유지)
$result = [];
$total_dir_bytes = 0;

if (is_dir($target_dir)) {
    $files = array_diff(scandir($target_dir), ['.', '..']);
    foreach ($files as $file) {
        $path = $target_dir . DIRECTORY_SEPARATOR . $file;
        $rel = ltrim(substr($path, strlen($real_root)), DIRECTORY_SEPARATOR);
        if (file_exists($path)) {
            $is_dir = is_dir($path);
            $bytes = $is_dir ? getDirSize($path) : @filesize($path);
            $total_dir_bytes += $bytes;

            $result[] = [
                'name'   => $file,
                'path'   => $rel,
                'is_dir' => $is_dir,
                'size'   => formatSize($bytes),
                'ctime'  => date("Y-m-d H:i:s", filectime($path)),
                'mtime'  => date("Y-m-d H:i:s", filemtime($path)),
            ];
        }
    }
    usort($result, function($a, $b) {
        if ($a['is_dir'] != $b['is_dir']) return $b['is_dir'] <=> $a['is_dir'];
        return strcasecmp($a['name'], $b['name']);
    });
}

function formatSize($bytes) {
    if (!$bytes || $bytes <= 0) return "0 B";
    $units = ['B', 'KB', 'MB', 'GB', 'TB'];
    $i = floor(log($bytes, 1024));
    return round($bytes / pow(1024, $i), 2) . ' ' . $units[$i];
}
require_once '/home/www/GNU/_PAGE/head.php';
?>
    <title>Explorer - <?php echo $volumes[$current_vol_id]['name']; ?></title>
    <script src="https://unpkg.com/lucide@latest"></script>
    <style>
        @import url('https://fonts.googleapis.com/css2?family=Pretendard:wght@400;500;600;700&display=swap');
        
        /* 기본 설정 */
        :root {
            --bg-deep: #020617;
            --bg-side: #0f172a;
            --slate-200: #e2e8f0;
            --slate-400: #94a3b8;
            --slate-500: #64748b;
            --slate-800: rgba(30, 41, 59, 0.5);
            --indigo-400: #818cf8;
            --indigo-500: #6366f1;
            --amber-500: #f59e0b;
            --red-400: #f87171;
            --emerald-400: #34d399;
        }

        body { font-family: 'Pretendard', sans-serif; overflow: hidden; font-size: 16px; margin: 0; background-color: var(--bg-deep); color: var(--slate-200); }
        
        /* 레이아웃 구조 */
        #layout-wrapper { display: flex; width: 100vw; height: 100vh; overflow: hidden; }
        
        /* 사이드바 스타일 */
        .sidebar { width: 320px; flex-shrink: 0; background-color: var(--bg-side); border-right: 1px solid var(--slate-800); display: flex; flex-direction: column; z-index: 20; }
        .sidebar-header { padding: 2rem; border-bottom: 1px solid var(--slate-800); }
        .logo-area { display: flex; align-items: center; gap: 0.75rem; color: var(--indigo-400); font-weight: 700; font-size: 1.5rem; letter-spacing: -0.025em; }
        .logo-icon { padding: 0.625rem; background: rgba(99, 102, 241, 0.1); border-radius: 6px; }
        
        .sidebar-nav { flex: 1; padding: 1.25rem; overflow-y: auto; }
        .nav-label { font-size: 0.75rem; font-bold: 700; color: var(--slate-500); text-transform: uppercase; letter-spacing: 0.2em; padding: 0 1rem; margin-bottom: 1.25rem; opacity: 0.5; }
        .nav-item { display: flex; align-items: center; gap: 1rem; padding: 1rem 1.25rem; border-radius: 7px; transition: all 0.3s; color: var(--slate-400); text-decoration: none; margin-bottom: 0.625rem; }
        .nav-item:hover { background: rgba(30, 41, 59, 0.5); color: var(--slate-200); }
        .nav-item.active { background: rgba(79, 70, 229, 0.2); color: var(--indigo-400); border: 1px solid rgba(99, 102, 241, 0.3); box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1); }
        
        .sidebar-footer { padding: 1.5rem; border-top: 1px solid var(--slate-800); }
        .mount-info { background: rgba(2, 6, 23, 0.5); border-radius: 6px; padding: 1.25rem; border: 1px solid var(--slate-800); }
        .mount-label { font-size: 0.75rem; color: var(--slate-500); font-weight: 700; text-transform: uppercase; margin-bottom: 0.625rem; letter-spacing: 0.05em; }
        .mount-path { font-family: monospace; font-size: 13px; color: rgba(129, 140, 248, 0.7); word-break: break-all; line-height: 1.6; }

        /* 메인 영역 스타일 */
        .main-container { flex: 1; display: flex; flex-direction: column; overflow: hidden; position: relative; }
        
        .top-header { height: 6rem; background: rgba(2, 6, 23, 0.8); backdrop-filter: blur(12px); border-bottom: 1px solid var(--slate-800); display: flex; align-items: center; justify-content: space-between; padding: 0 2.5rem; z-index: 10; }
        .path-box { display: flex; align-items: center; gap: 1rem; flex: 1; min-width: 0; }
        .path-display { font-family: monospace; font-size: 0.875rem; color: #cbd5e1; background: rgba(15, 23, 42, 0.8); padding: 0.75rem 1.5rem; border-radius: 6px; border: 1px solid #1e293b; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 1; }
        .path-label { color: var(--indigo-500); font-weight: 700; text-transform: uppercase; margin-right: 0.75rem; }
        
        .size-badge { display: flex; align-items: center; gap: 0.75rem; background: rgba(99, 102, 241, 0.1); border: 1px solid rgba(99, 102, 241, 0.2); padding: 0.75rem 1.25rem; border-radius: 6px; flex-shrink: 0; }
        .size-label { font-size: 0.75rem; font-weight: 700; color: var(--slate-500); text-transform: uppercase; letter-spacing: 0.1em; }
        .size-value { font-family: monospace; font-size: 0.875rem; color: var(--indigo-400); font-weight: 700; }
        
        .refresh-btn { padding: 0.75rem; border-radius: 9999px; transition: all 0.7s; color: var(--slate-400); margin-left: 1rem; }
        .refresh-btn:hover { background: #1e293b; color: #fff; border-color: #334155; transform: rotate(180deg); }

        /* 스크롤 영역 및 테이블 */
        .scroll-area { flex: 1; padding: 2.5rem; overflow-y: auto; }
        .alert-box { margin-bottom: 2rem; padding: 1.25rem; background: rgba(16, 185, 129, 0.1); border: 1px solid rgba(16, 185, 129, 0.2); color: var(--emerald-400); border-radius: 7px; display: flex; align-items: center; justify-content: space-between; }
        
        .action-bar { margin-bottom: 2rem; display: flex; align-items: center; justify-content: space-between; }
        .breadcrumb { display: flex; align-items: center; gap: 0.75rem; font-size: 1rem; }
        .breadcrumb a { text-decoration: none; color: var(--slate-500); transition: color 0.2s; font-weight: 500; }
        .breadcrumb a:hover { color: var(--indigo-400); }
        .breadcrumb .separator { color: #334155; }
        
        .btn-batch { display: flex; align-items: center; gap: 0.625rem; padding: 0.75rem 1.5rem; background: rgba(239, 68, 68, 0.1); color: var(--red-400); border: 1px solid rgba(239, 68, 68, 0.2); border-radius: 6px; font-weight: 700; font-size: 0.875rem; transition: all 0.3s; box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1); }
        .btn-batch:hover { background: #ef4444; color: #fff; }

        .glass-panel { background: rgba(30, 41, 59, 0.7); backdrop-filter: blur(12px); border: 1px solid rgba(255, 255, 255, 0.05); border-radius: 10px; overflow: hidden; box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5); }
        
        .explorer-table { width: 100%; border-collapse: separate; border-spacing: 0; text-align: left; font-size: 15px; }
        .explorer-table thead tr { background: rgba(30, 41, 59, 0.3); color: var(--slate-500); font-weight: 700; text-transform: uppercase; font-size: 0.75rem; letter-spacing: 0.15em; }
        .explorer-table th { padding: 1.75rem 1.5rem; border-bottom: 1px solid var(--slate-800); }
        .explorer-table td { padding: 1.5rem; border-bottom: 1px solid var(--slate-800); }
        
        .row-item { opacity: 0; transform: translateX(-10px); transition: all 0.3s ease; cursor: pointer; }
        .row-item.visible { opacity: 1; transform: translateX(0); }
        .row-item:hover { background-color: rgba(99, 102, 241, 0.08) !important; box-shadow: inset 5px 0 0 0 var(--indigo-500); }
        
        .file-icon-box { padding: 0.75rem; border-radius: 7px; transition: transform 0.3s; display: flex; align-items: center; justify-content: center; }
        .is-dir .file-icon-box { background: rgba(245, 158, 11, 0.1); color: var(--amber-500); }
        .is-file .file-icon-box { background: rgba(51, 65, 85, 0.3); color: var(--slate-400); }
        .row-item:hover .file-icon-box { transform: scale(1.1); }
        
        .name-text { font-weight: 700; font-size: 1rem; transition: color 0.2s; }
        .is-dir .name-text { color: var(--slate-200); }
        .is-file .name-text { color: var(--slate-400); }
        .row-item:hover .name-text { color: #fff; }
        
        .size-text { font-family: monospace; font-size: 0.875rem; }
        .is-dir .size-text { color: rgba(129, 140, 248, 0.8); }
        .is-file .size-text { color: var(--slate-500); }
        
        .time-text { font-size: 0.875rem; font-variant-numeric: tabular-nums; color: var(--slate-500); }
        .mtime-text { font-size: 0.875rem; font-variant-numeric: tabular-nums; font-weight: 600; color: rgba(129, 140, 248, 0.7); }
        
        .btn-delete { padding: 0.75rem; border-radius: 7px; color: var(--slate-500); transition: all 0.2s; }
        .btn-delete:hover { color: var(--red-400); background: rgba(239, 68, 68, 0.1); }

        /* 공통 애니메이션 및 기타 */
        .animate-fadeInUp { animation: fadeInUp 0.5s ease forwards; opacity: 0; }
        @keyframes fadeInUp { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } }
        .delay-1 { animation-delay: 0.1s; }
        .delay-2 { animation-delay: 0.2s; }
        .custom-scrollbar::-webkit-scrollbar { width: 8px; }
        .custom-scrollbar::-webkit-scrollbar-track { background: var(--bg-side); }
        .custom-scrollbar::-webkit-scrollbar-thumb { background: #334155; border-radius: 7px; }
        input[type="checkbox"] { width: 1.25rem; height: 1.25rem; border-radius: 4px; background: #0f172a; border: 1px solid #334155; cursor: pointer; accent-color: var(--indigo-500); }
        /*---------| BODY SCROLLBAR |---------*/
        body::-webkit-scrollbar                                            { width:10px; }
        body::-webkit-scrollbar-thumb                                      { background-color:#333; border:1px solid #222; }
        body::-webkit-scrollbar-thumb:hover                                { background-color:#555; cursor:default; }
        body::-webkit-scrollbar-track                                      { background-color:#0b0e11; }
    </style>
</head>
<body class="bg-[#020617] text-slate-200">

<div id="layout-wrapper">

    <!-- 사이드바 -->
    <aside class="sidebar">
        <div class="sidebar-header">
            <h1 class="logo-area animate-fadeInUp">
                <div class="logo-icon">
                    <i data-lucide="layers" class="w-7 h-7"></i>
                </div>
                <span>DATA CORE</span>
            </h1>
        </div>
        <nav class="sidebar-nav">
            <div class="nav-label">Storage Volumes</div>
            <?php foreach ($volumes as $id => $vol): ?>
                <a href="?vol=<?php echo $id; ?>" 
                   class="nav-item <?php echo $current_vol_id === $id ? 'active' : ''; ?>">
                    <i data-lucide="<?php echo $id === 'maria' ? 'database' : 'hard-drive'; ?>" class="w-6 h-6"></i>
                    <span style="font-weight: 600; font-size: 15px;"><?php echo $vol['name']; ?></span>
                </a>
            <?php endforeach; ?>
        </nav>
        <div class="sidebar-footer">
            <div class="mount-info">
                <p class="mount-label">Base Mount Point</p>
                <p class="mount-path"><?php echo $root_path; ?></p>
            </div>
        </div>
    </aside>

    <!-- 메인 컨텐츠 -->
    <main class="main-container">
        <header class="top-header">
            <div class="path-box animate-fadeInUp">
                <div id="pathDisplay" class="path-display">
                    <span class="path-label">Path:</span>
                    <?php echo $root_path . ($current_sub_dir ? DIRECTORY_SEPARATOR . $current_sub_dir : ''); ?>
                </div>
                <div class="size-badge">
                    <i data-lucide="database" class="w-4 h-4 text-indigo-400"></i>
                    <span class="size-label hidden-md">Total Size:</span>
                    <span class="size-value"><?php echo formatSize($total_dir_bytes); ?></span>
                </div>
            </div>
            <button onclick="location.reload()" class="refresh-btn">
                <i data-lucide="refresh-cw" class="w-6 h-6"></i>
            </button>
        </header>

        <div class="scroll-area custom-scrollbar">
            <?php foreach ($messages as $msg): ?>
                <div class="alert-box animate-fadeInUp">
                    <div style="display: flex; align-items: center; gap: 1rem;">
                        <i data-lucide="check-circle" class="w-6 h-6"></i>
                        <span style="font-weight: 500; font-size: 15px;"><?php echo htmlspecialchars($msg); ?></span>
                    </div>
                </div>
            <?php endforeach; ?>

            <div class="action-bar animate-fadeInUp delay-1">
                <nav class="breadcrumb">
                    <a href="?vol=<?php echo $current_vol_id; ?>" style="display: flex; align-items: center; gap: 0.5rem;">
                        <i data-lucide="home" class="w-5 h-5"></i>
                        <span>root</span>
                    </a>
                    <?php 
                    if ($current_sub_dir) {
                        $parts = explode(DIRECTORY_SEPARATOR, trim($current_sub_dir, DIRECTORY_SEPARATOR));
                        $path_acc = '';
                        foreach ($parts as $part) {
                            if (!$part) continue;
                            $path_acc .= ($path_acc ? DIRECTORY_SEPARATOR : '') . $part;
                            echo '<i data-lucide="chevron-right" class="w-5 h-5 separator"></i>';
                            echo '<a href="?vol=' . $current_vol_id . '&dir=' . urlencode($path_acc) . '">' . htmlspecialchars($part) . '</a>';
                        }
                    }
                    ?>
                </nav>

                <button type="button" onclick="submitBatchDelete()" id="batchDeleteBtn" style="display: none;" class="btn-batch animate-fadeInUp">
                    <i data-lucide="trash-2" class="w-5 h-5"></i> Selected Delete
                </button>
            </div>

            <form id="mainForm" method="POST" class="animate-fadeInUp delay-2">
                <input type="hidden" name="action" id="formAction" value="delete_batch">
                <div class="glass-panel">
                    <table class="explorer-table">
                        <thead>
                            <tr>
                                <th style="width: 4rem; text-align: center;">
                                    <input type="checkbox" id="selectAll">
                                </th>
                                <th>Name</th>
                                <th>Size</th>
                                <th>Created</th>
                                <th>Modified</th>
                                <th style="text-align: center;">Action</th>
                            </tr>
                        </thead>
                        <tbody id="fileTableBody">
                            <?php if ($current_sub_dir !== ''): ?>
                                <?php 
                                $parent = dirname($current_sub_dir);
                                if ($parent === '.' || $parent === DIRECTORY_SEPARATOR) $parent = '';
                                ?>
                                <tr class="row-item" onclick="location.href='?vol=<?php echo $current_vol_id; ?>&dir=<?php echo urlencode($parent); ?>'">
                                    <td style="text-align: center;">
                                        <i data-lucide="arrow-up" style="color: #475569;"></i>
                                    </td>
                                    <td colspan="5" style="color: rgba(129, 140, 248, 0.8); font-weight: 700; font-style: italic;">
                                        Parent Directory
                                    </td>
                                </tr>
                            <?php endif; ?>

                            <?php foreach ($result as $index => $item): ?>
                                <tr class="row-item <?php echo $item['is_dir'] ? 'is-dir' : 'is-file'; ?>">
                                    <td style="text-align: center;" onclick="event.stopPropagation();">
                                        <?php if (!$item['is_dir']): ?>
                                            <input type="checkbox" name="selected_files[]" value="<?php echo htmlspecialchars($item['name']); ?>" class="file-checkbox">
                                        <?php else: ?>
                                            <i data-lucide="folder" style="color: rgba(245, 158, 11, 0.4);"></i>
                                        <?php endif; ?>
                                    </td>
                                    <td style="display: flex; align-items: center; gap: 1.25rem;" onclick="if(<?php echo $item['is_dir']?'true':'false'; ?>) location.href='?vol=<?php echo $current_vol_id; ?>&dir=<?php echo urlencode($item['path']); ?>'">
                                        <div class="file-icon-box">
                                            <i data-lucide="<?php echo $item['is_dir'] ? 'folder' : 'file'; ?>" class="w-6 h-6"></i>
                                        </div>
                                        <span class="name-text"><?php echo htmlspecialchars($item['name']); ?></span>
                                    </td>
                                    <td class="size-text"><?php echo $item['size']; ?></td>
                                    <td class="time-text"><?php echo $item['ctime']; ?></td>
                                    <td class="mtime-text"><?php echo $item['mtime']; ?></td>
                                    <td style="text-align: center;" onclick="event.stopPropagation();">
                                        <?php if (!$item['is_dir']): ?>
                                            <button type="button" onclick="singleDelete('<?php echo addslashes($item['name']); ?>')" class="btn-delete">
                                                <i data-lucide="trash-2" class="w-5 h-5"></i>
                                            </button>
                                        <?php endif; ?>
                                    </td>
                                </tr>
                            <?php endforeach; ?>
                        </tbody>
                    </table>
                </div>
                <input type="hidden" name="file_name" id="singleDeleteFileName">
            </form>
        </div>
    </main>

</div>

    <script>
        document.addEventListener('DOMContentLoaded', () => {
            lucide.createIcons();
            const rows = document.querySelectorAll('.row-item');
            rows.forEach((row, index) => {
                setTimeout(() => { row.classList.add('visible'); }, index * 40);
            });

            const selectAll = document.getElementById('selectAll');
            const checkboxes = document.querySelectorAll('.file-checkbox');
            const batchBtn = document.getElementById('batchDeleteBtn');

            if (selectAll) {
                selectAll.addEventListener('change', () => {
                    checkboxes.forEach(cb => cb.checked = selectAll.checked);
                    toggleBatchButton();
                });
            }
            checkboxes.forEach(cb => {
                cb.addEventListener('change', toggleBatchButton);
            });

            function toggleBatchButton() {
                const checkedCount = document.querySelectorAll('.file-checkbox:checked').length;
                batchBtn.style.display = checkedCount > 0 ? 'flex' : 'none';
            }

            window.submitBatchDelete = function() {
                if (confirm(`선택한 항목들을 영구 삭제하시겠습니까?`)) {
                    document.getElementById('formAction').value = 'delete_batch';
                    document.getElementById('mainForm').submit();
                }
            }
            window.singleDelete = function(fileName) {
                if (confirm(`'${fileName}' 파일을 삭제하시겠습니까?`)) {
                    document.getElementById('formAction').value = 'delete';
                    document.getElementById('singleDeleteFileName').value = fileName;
                    document.getElementById('mainForm').submit();
                }
            }
        });
    </script>
</body>
<?php require_once '/home/www/GNU/_PAGE/tail.php'; ?>