GNU/_PAGE/backup/backup_daemon.php
<?php
/**
 * file_manager_v2.php
 * - 구조 변경 없음
 * - ZIP 파일인 경우: [삭제] 버튼으로 동작
 */

$root_dir = '/home/www/DATA/UPBIT/daemon';
$req_dir = isset($_GET['dir']) ? $_GET['dir'] : '';

// 상위 경로 이동 방지
if (strpos($req_dir, '..') !== false) $req_dir = '';
$current_path = realpath($root_dir . '/' . $req_dir);

// 경로 유효성 및 생성
if (!$current_path || strpos($current_path, realpath($root_dir)) !== 0) {
    if (!file_exists($root_dir)) @mkdir($root_dir, 0707, true);
    $current_path = realpath($root_dir);
    $req_dir = '';
}

// ---------------------------------------------------------
// [1] 다운로드 처리
// ---------------------------------------------------------
if (isset($_GET['download'])) {
    $file = basename($_GET['download']);
    $file_path = $current_path . '/' . $file;

    if (file_exists($file_path) && is_file($file_path)) {
        if (ob_get_level()) ob_end_clean();
        header('Content-Description: File Transfer');
        header('Content-Type: application/octet-stream');
        header('Content-Disposition: attachment; filename="' . $file . '"');
        header('Expires: 0');
        header('Cache-Control: must-revalidate');
        header('Pragma: public');
        header('Content-Length: ' . filesize($file_path));
        readfile($file_path);
        exit;
    } else {
        echo "<script>alert('파일이 없습니다.'); history.back();</script>";
        exit;
    }
}

// ---------------------------------------------------------
// [2] 액션 처리 (압축 & 삭제)
// ---------------------------------------------------------
$message = "";

if (isset($_POST['action']) && $_POST['action'] === 'zip') {
    $target = $_POST['target'];
    $source_path = $current_path . '/' . $target;
    
    if (!class_exists('ZipArchive')) {
        $message = "❌ PHP Zip 모듈 미설치";
    } elseif (!is_writable($current_path)) {
        $message = "❌ 쓰기 권한 없음";
    } else {
        $zip_name = $target . '_' . date('Ymd_His') . '.zip';
        $zip_full_path = $current_path . '/' . $zip_name;

        $zip = new ZipArchive();
        if ($zip->open($zip_full_path, ZipArchive::CREATE | ZipArchive::OVERWRITE) === TRUE) {
            if (is_dir($source_path)) {
                $files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($source_path), RecursiveIteratorIterator::LEAVES_ONLY);
                foreach ($files as $file) {
                    if (!$file->isDir()) {
                        $filePath = $file->getRealPath();
                        $relativePath = substr($filePath, strlen($source_path) + 1);
                        $zip->addFile($filePath, $target . '/' . $relativePath);
                    }
                }
            } else {
                $zip->addFile($source_path, basename($source_path));
            }
            $zip->close();
            $message = "✅ 압축 성공: $zip_name";
        } else {
            $message = "❌ 압축 실패";
        }
    }
}

if (isset($_POST['action']) && $_POST['action'] === 'delete') {
    $target = $_POST['target'];
    $target_path = $current_path . '/' . $target;
    if (file_exists($target_path) && is_file($target_path)) {
        if (unlink($target_path)) {
            $message = "🗑️ 삭제 완료: $target";
        } else {
            $message = "❌ 삭제 실패";
        }
    }
}

// 리스트 읽기
$list = [];
if (is_dir($current_path)) {
    $items = scandir($current_path);
    $dirs = [];
    $files = [];
    foreach ($items as $item) {
        if ($item === '.') continue;
        if ($item === '..' && $req_dir === '') continue;
        $full_path = $current_path . '/' . $item;
        if (is_dir($full_path)) $dirs[] = $item;
        else $files[] = $item;
    }
    $list = array_merge($dirs, $files);
}
require_once '/home/www/GNU/_PAGE/head.php';
?>
    <!-- 웹 폰트 및 아이콘 -->
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/orioncactus/pretendard/dist/web/static/pretendard.css">
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
    <style>
        :root {
            --bg: #0b0e11;
            --card-bg: #181a20;
            --border: #2b3139;
            --text-main: #eaecef;
            --text-dim: #848e9c;
            --melon: #CBFF75;
            --binance-yellow: #f0b90b;
            --danger: #f6465d;
            --blue: #38bdf8;
        }

        /* 로딩 애니메이션 */
        @keyframes fadeInUp {
            from { opacity: 0; transform: translateY(15px); }
            to { opacity: 1; transform: translateY(0); }
        }
        @keyframes progress {
            0% { width: 0; }
            100% { width: 100%; }
        }

        #loader-bar {
            position: fixed; top: 0; left: 0; height: 3px; background: var(--melon);
            z-index: 9999; animation: progress 0.8s ease-in-out forwards;
        }

        body {
            background-color: var(--bg);
            color: var(--text-main);
            font-family: 'Pretendard', sans-serif;
            margin: 0;
            line-height: 1.5;
        }

        .container {
            max-width: 100%;
            margin: 50px;
            animation: fadeInUp 0.8s ease-out;
        }

        header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 30px;
        }

        h2 {
            margin: 0;
            font-size: 2.3rem;
            color: var(--melon);
            display: flex;
            align-items: center;
            gap: 12px;
        }

        .breadcrumb {
            background: var(--card-bg);
            padding: 15px 20px;
            border-radius: 5px;
            border: 1px solid var(--border);
            margin-bottom: 20px;
            font-size: 1.2rem;
            color: var(--text-dim);
        }

        .msg {
            background: rgba(203, 255, 117, 0.1);
            border-left: 4px solid var(--melon);
            padding: 15px 20px;
            margin-bottom: 25px;
            color: var(--melon);
            border-radius: 4px;
            animation: fadeInUp 0.5s ease-out;
        }

        .file-card {
            background: var(--card-bg);
            border-radius: 5px;
            border: 1px solid var(--border);
            overflow: hidden;
            box-shadow: 0 10px 30px rgba(0,0,0,0.3);
        }

        table {
            width: 100%;
            border-collapse: collapse;
        }

        th {
            background: rgba(0,0,0,0.2);
            padding: 20px;
            text-align: left;
            font-size: 1rem;
            color: var(--text-dim);
            text-transform: uppercase;
            letter-spacing: 1px;
            border-bottom: 1px solid var(--border);
        }

        td {
            padding: 14px 15px;
            border-bottom: 1px solid var(--border);
            transition: background 0.2s;
        }

        tr:hover td {
            background: rgba(255, 255, 255, 0.02);
        }

        /* 아이콘 색상 */
        .fa-folder { color: var(--binance-yellow); }
        .fa-file-code { color: var(--blue); }
        .fa-file-zipper { color: var(--melon); }
        .fa-arrow-up { color: var(--text-dim); }

        a { color: inherit; text-decoration: none; transition: 0.2s; }
        a.dir-link:hover { color: var(--binance-yellow); }

        .size-info {
            font-family: 'Consolas', monospace;
            font-size: 0.85rem;
            color: var(--text-dim);
        }

        /* 버튼 그룹 */
        .action-group {
            display: flex;
            gap: 8px;
        }

        .btn {
            padding: 6px 12px;
            border-radius: 5px;
            border: none;
            cursor: pointer;
            font-size: 0.75rem;
            font-weight: 600;
            display: flex;
            align-items: center;
            gap: 6px;
            transition: all 0.2s;
            color: #fff;
        }

        .btn-zip { background: #474d57; }
        .btn-zip:hover { background: #5a6270; }
        .btn-down { background: var(--blue); }
        .btn-down:hover { opacity: 0.8; }
        .btn-del { background: var(--danger); }
        .btn-del:hover { opacity: 0.8; }

        @media (max-width: 768px) {
            body { padding: 20px; }
            th:nth-child(2), td:nth-child(2) { display: none; }
        }

         /*---------| 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>

<div id="loader-bar"></div>

<div class="container">
    <header>
        <h2><i class="fa-solid fa-folder-tree"></i> DAEMON FILE EXPLORER</h2>
        <div style="font-size: 0.8rem; color: var(--text-dim);">Root: /DATA/UPBIT/daemon</div>
    </header>

    <div class="breadcrumb">
        <i class="fa-solid fa-house" style="margin-right:8px;"></i>
        <?php echo h($req_dir ? str_replace('/', ' <i class="fa-solid fa-chevron-right" style="font-size:0.7rem; margin:0 5px;"></i> ', $req_dir) : 'ROOT'); ?>
    </div>

    <?php if ($message): ?>
        <div class="msg"><i class="fa-solid fa-circle-info" style="margin-right:10px;"></i> <?php echo $message; ?></div>
    <?php endif; ?>

    <div class="file-card">
        <table>
            <thead>
                <tr>
                    <th>이름</th>
                    <th>크기</th>
                    <th style="text-align:right;">작업</th>
                </tr>
            </thead>
            <tbody>
                <?php foreach ($list as $index => $item): 
                    if ($item === '..') {
                        $link = "?dir=" . urlencode(dirname($req_dir) === '.' ? '' : dirname($req_dir));
                        echo "<tr><td colspan='3'><a href='$link' class='dir-link'><i class='fa-solid fa-arrow-up'></i> <span style='margin-left:10px;'>상위 폴더로 이동</span></a></td></tr>";
                        continue;
                    }
                    $full_path = $current_path . '/' . $item;
                    $is_dir = is_dir($full_path);
                    $new_dir = $req_dir ? $req_dir . '/' . $item : $item;
                    $ext = strtolower(pathinfo($item, PATHINFO_EXTENSION));
                    $size = $is_dir ? '-' : number_format(filesize($full_path)) . ' B';
                    
                    // 아이콘 결정
                    $icon = $is_dir ? 'fa-folder' : ($ext === 'zip' ? 'fa-file-zipper' : 'fa-file-code');
                ?>
                <tr style="animation: fadeInUp 0.4s ease-out <?php echo $index * 0.05; ?>s both;">
                    <td>
                        <i class="fa-solid <?php echo $icon; ?>" style="width: 20px;"></i>
                        <?php if ($is_dir): ?>
                            <a href="?dir=<?php echo urlencode($new_dir); ?>" class="dir-link" style="margin-left:8px;"><?php echo $item; ?></a>
                        <?php else: ?>
                            <span style="margin-left:8px;"><?php echo $item; ?></span>
                        <?php endif; ?>
                    </td>
                    <td class="size-info"><?php echo $size; ?></td>
                    <td>
                        <div class="action-group" style="justify-content: flex-end;">
                            <?php if ($ext !== 'zip'): ?>
                            <form method="post" style="margin:0;">
                                <input type="hidden" name="target" value="<?php echo h($item); ?>">
                                <button type="submit" name="action" value="zip" class="btn btn-zip">
                                    <i class="fa-solid fa-file-archive"></i> 압축
                                </button>
                            </form>
                            <?php endif; ?>

                            <?php if (!$is_dir && $ext === 'zip'): ?>
                            <form method="post" style="margin:0;" onsubmit="return confirm('정말 삭제하시겠습니까?');">
                                <input type="hidden" name="target" value="<?php echo h($item); ?>">
                                <button type="submit" name="action" value="delete" class="btn btn-del">
                                    <i class="fa-solid fa-trash-can"></i> 삭제
                                </button>
                            </form>
                            <?php endif; ?>

                            <?php if (!$is_dir): ?>
                                <a href="?dir=<?php echo urlencode($req_dir); ?>&download=<?php echo urlencode($item); ?>" class="btn btn-down">
                                    <i class="fa-solid fa-download"></i> 다운
                                </a>
                            <?php endif; ?>
                        </div>
                    </td>
                </tr>
                <?php endforeach; ?>
            </tbody>
        </table>
    </div>
</div>

<script>
    // 페이지 로드 시 로더 바 제거
    window.onload = function() {
        const loader = document.getElementById('loader-bar');
        setTimeout(() => {
            loader.style.opacity = '0';
            setTimeout(() => loader.remove(), 500);
        }, 300);
    };
</script>

<?php
function h($s) { return htmlspecialchars((string)$s, ENT_QUOTES, 'UTF-8'); }
?>
</body>
<?php require_once '/home/www/GNU/_PAGE/tail.php'; ?>