{"id":105,"url":"https:\/\/web.daaee.cn\/.\/TVbox\/bak_sender.php","title":"\u6587\u4ef6\u5907\u4efd\u7cfb\u7edf - \u540c\u6b65\u7248\u672c","content":"<!DOCTYPE html>\n<html>\n<head>\n    <meta charset=\"UTF-8\">\n    <title>\u6587\u4ef6\u5907\u4efd\u7cfb\u7edf - \u540c\u6b65\u7248\u672c<\/title>\n    <style>\n        body { font-family: Arial, sans-serif; margin: 20px; background: #f5f5f5; }\n        .container { max-width: 1200px; margin: 0 auto; background: white; padding: 20px; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); }\n        h1 { color: #333; border-bottom: 2px solid #0073aa; padding-bottom: 10px; }\n        .form-group { margin-bottom: 15px; }\n        label { display: block; margin-bottom: 5px; font-weight: bold; }\n        input[type=\"text\"], input[type=\"url\"], textarea { \n            width: 100%; \n            padding: 8px; \n            box-sizing: border-box; \n            border: 1px solid #ddd;\n            border-radius: 4px;\n        }\n        .btn { \n            background: #0073aa; \n            color: white; \n            padding: 10px 20px; \n            border: none; \n            border-radius: 4px;\n            cursor: pointer; \n            font-size: 16px;\n            margin-right: 10px;\n        }\n        .btn:hover { background: #005a87; }\n        .btn-danger { background: #dc3545; }\n        .btn-danger:hover { background: #c82333; }\n        .btn-success { background: #28a745; }\n        .btn-success:hover { background: #218838; }\n        .status-panel { \n            margin-top: 20px; \n            padding: 15px; \n            background: #f8f9fa; \n            border: 1px solid #dee2e6;\n            border-radius: 4px;\n        }\n        .progress-container { margin: 20px 0; }\n        .progress-bar { \n            width: 100%; \n            height: 30px; \n            background: #e9ecef; \n            border-radius: 4px;\n            overflow: hidden;\n        }\n        .progress-fill { \n            height: 100%; \n            background: #28a745; \n            width: 0%;\n            transition: width 0.3s;\n            text-align: center;\n            line-height: 30px;\n            color: white;\n            font-weight: bold;\n        }\n        .stats-grid { \n            display: grid; \n            grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); \n            gap: 10px;\n            margin-top: 15px;\n        }\n        .stat-box { \n            padding: 10px; \n            background: white; \n            border: 1px solid #ddd;\n            border-radius: 4px;\n            text-align: center;\n        }\n        .stat-value { \n            font-size: 24px; \n            font-weight: bold; \n            color: #0073aa;\n        }\n        .stat-label { \n            font-size: 12px; \n            color: #666; \n            margin-top: 5px;\n        }\n        .log-panel {\n            margin-top: 20px;\n            padding: 15px;\n            background: #f8f9fa;\n            border: 1px solid #dee2e6;\n            border-radius: 4px;\n            max-height: 300px;\n            overflow-y: auto;\n            font-family: monospace;\n            font-size: 12px;\n        }\n        .log-entry { margin-bottom: 5px; padding: 3px; }\n        .log-time { color: #666; }\n        .log-info { color: #0073aa; }\n        .log-success { color: #28a745; }\n        .log-error { color: #dc3545; }\n        .log-warning { color: #ffc107; }\n        .tab-container { margin-top: 20px; }\n        .tab-header { display: flex; border-bottom: 1px solid #ddd; }\n        .tab { padding: 10px 20px; cursor: pointer; }\n        .tab.active { background: #0073aa; color: white; border-bottom: 2px solid #005a87; }\n        .tab-content { display: none; padding: 20px 0; }\n        .tab-content.active { display: block; }\n        .config-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; }\n        .file-list { max-height: 200px; overflow-y: auto; }\n        .current-file { \n            padding: 10px; \n            background: #e7f3ff; \n            border-left: 4px solid #0073aa;\n            margin: 10px 0;\n        }\n    <\/style>\n<\/head>\n<body>\n    <div class=\"container\">\n        <h1>\ud83d\udcc1 \u6587\u4ef6\u5907\u4efd\u7cfb\u7edf - \u540c\u6b65\u7248\u672c<\/h1>\n        \n        <div class=\"tab-container\">\n            <div class=\"tab-header\">\n                <div class=\"tab active\" onclick=\"switchTab('backup')\">\u5907\u4efd\u7ba1\u7406<\/div>\n                <div class=\"tab\" onclick=\"switchTab('config')\">\u6392\u9664\u914d\u7f6e<\/div>\n                <div class=\"tab\" onclick=\"switchTab('history')\">\u5907\u4efd\u5386\u53f2<\/div>\n            <\/div>\n            \n            <!-- \u5907\u4efd\u7ba1\u7406\u6807\u7b7e\u9875 -->\n            <div id=\"backup-tab\" class=\"tab-content active\">\n                <div class=\"form-group\">\n                    <label for=\"api_url\">\u5907\u4efdAPI URL:<\/label>\n                    <input type=\"url\" id=\"api_url\" name=\"api_url\" \n                           value=\"http:\/\/your-domain.com\/backup_receiver.php\"\n                           placeholder=\"http:\/\/your-domain.com\/backup_receiver.php\" required>\n                <\/div>\n                <div class=\"form-group\">\n                    <label for=\"backup_dir\">\u5907\u4efd\u76ee\u5f55 (\u53ef\u9009\uff0c\u9ed8\u8ba4\u5f53\u524d\u76ee\u5f55):<\/label>\n                    <input type=\"text\" id=\"backup_dir\" name=\"backup_dir\" \n                           value=\"\/mnt\/sda1\/www\/TVbox\"\n                           placeholder=\"\/mnt\/sda1\/www\/TVbox\">\n                <\/div>\n                \n                <button class=\"btn btn-success\" onclick=\"initBackup()\">\u521d\u59cb\u5316\u5907\u4efd\u4efb\u52a1<\/button>\n                <button class=\"btn\" onclick=\"startBackup()\" id=\"startBtn\">\u5f00\u59cb\u5907\u4efd<\/button>\n                <button class=\"btn btn-danger\" onclick=\"resetBackup()\">\u91cd\u7f6e\u4efb\u52a1<\/button>\n                <button class=\"btn\" onclick=\"pauseBackup()\" id=\"pauseBtn\">\u6682\u505c<\/button>\n                \n                <div class=\"status-panel\">\n                    <h3>\u5907\u4efd\u72b6\u6001<\/h3>\n                    <div id=\"backupStatus\">\n                        <p>\u7b49\u5f85\u5f00\u59cb\u5907\u4efd...<\/p>\n                    <\/div>\n                    \n                    <div class=\"progress-container\">\n                        <div class=\"progress-bar\">\n                            <div class=\"progress-fill\" id=\"progressFill\">0%<\/div>\n                        <\/div>\n                    <\/div>\n                    \n                    <div id=\"currentFile\" class=\"current-file\"><\/div>\n                    \n                    <div class=\"stats-grid\" id=\"statsGrid\">\n                        <div class=\"stat-box\">\n                            <div class=\"stat-value\" id=\"statTotal\">0<\/div>\n                            <div class=\"stat-label\">\u603b\u6587\u4ef6\u6570<\/div>\n                        <\/div>\n                        <div class=\"stat-box\">\n                            <div class=\"stat-value\" id=\"statPending\">0<\/div>\n                            <div class=\"stat-label\">\u5f85\u5907\u4efd<\/div>\n                        <\/div>\n                        <div class=\"stat-box\">\n                            <div class=\"stat-value\" id=\"statCompleted\">0<\/div>\n                            <div class=\"stat-label\">\u5df2\u5907\u4efd<\/div>\n                        <\/div>\n                        <div class=\"stat-box\">\n                            <div class=\"stat-value\" id=\"statFailed\">0<\/div>\n                            <div class=\"stat-label\">\u5931\u8d25\u6570<\/div>\n                        <\/div>\n                        <div class=\"stat-box\">\n                            <div class=\"stat-value\" id=\"statExcluded\">0<\/div>\n                            <div class=\"stat-label\">\u5df2\u6392\u9664<\/div>\n                        <\/div>\n                        <div class=\"stat-box\">\n                            <div class=\"stat-value\" id=\"statSize\">0 B<\/div>\n                            <div class=\"stat-label\">\u603b\u5927\u5c0f<\/div>\n                        <\/div>\n                    <\/div>\n                    \n                    <div class=\"log-panel\" id=\"logPanel\"><\/div>\n                <\/div>\n            <\/div>\n            \n            <!-- \u6392\u9664\u914d\u7f6e\u6807\u7b7e\u9875 -->\n            <div id=\"config-tab\" class=\"tab-content\">\n                <div class=\"config-grid\">\n                    <div>\n                        <h3>\u6392\u9664\u76ee\u5f55<\/h3>\n                        <textarea id=\"exclude_dirs\" rows=\"10\" placeholder=\"\u6bcf\u884c\u4e00\u4e2a\u76ee\u5f55&#10;\u4f8b\u5982\uff1a&#10;.git&#10;node_modules&#10;vendor\"><\/textarea>\n                    <\/div>\n                    <div>\n                        <h3>\u6392\u9664\u6587\u4ef6<\/h3>\n                        <textarea id=\"exclude_files\" rows=\"5\" placeholder=\"\u6bcf\u884c\u4e00\u4e2a\u6587\u4ef6\u540d&#10;\u4f8b\u5982\uff1a&#10;config.php&#10;backup.php\"><\/textarea>\n                    <\/div>\n                    <div>\n                        <h3>\u6392\u9664\u6269\u5c55\u540d<\/h3>\n                        <textarea id=\"exclude_exts\" rows=\"5\" placeholder=\"\u6bcf\u884c\u4e00\u4e2a\u6269\u5c55\u540d&#10;\u4f8b\u5982\uff1a&#10;.log&#10;.tmp&#10;.cache\"><\/textarea>\n                    <\/div>\n                    <div>\n                        <h3>\u8bf4\u660e<\/h3>\n                        <p>\u6392\u9664\u89c4\u5219\u8bf4\u660e\uff1a<\/p>\n                        <ul>\n                            <li>\u6392\u9664\u76ee\u5f55\uff1a\u4e0d\u626b\u63cf\u6307\u5b9a\u76ee\u5f55\u4e0b\u7684\u4efb\u4f55\u6587\u4ef6<\/li>\n                            <li>\u6392\u9664\u6587\u4ef6\uff1a\u5b8c\u5168\u5339\u914d\u6587\u4ef6\u540d\uff08\u5305\u542b\u8def\u5f84\uff09<\/li>\n                            <li>\u6392\u9664\u6269\u5c55\u540d\uff1a\u5339\u914d\u6587\u4ef6\u6269\u5c55\u540d<\/li>\n                        <\/ul>\n                        <button class=\"btn btn-success\" onclick=\"saveConfig()\">\u4fdd\u5b58\u914d\u7f6e<\/button>\n                    <\/div>\n                <\/div>\n            <\/div>\n            \n            <!-- \u5907\u4efd\u5386\u53f2\u6807\u7b7e\u9875 -->\n            <div id=\"history-tab\" class=\"tab-content\">\n                <h3>\u5907\u4efd\u5386\u53f2\u8bb0\u5f55<\/h3>\n                <div id=\"historyList\"><\/div>\n            <\/div>\n        <\/div>\n    <\/div>\n    \n    <script>\n        let backupInterval = null;\n        let isBackupRunning = false;\n        let isPaused = false;\n        \n        function switchTab(tabName) {\n            \/\/ \u9690\u85cf\u6240\u6709\u6807\u7b7e\u9875\n            document.querySelectorAll('.tab-content').forEach(tab => {\n                tab.classList.remove('active');\n            });\n            document.querySelectorAll('.tab').forEach(tab => {\n                tab.classList.remove('active');\n            });\n            \n            \/\/ \u663e\u793a\u9009\u4e2d\u7684\u6807\u7b7e\u9875\n            document.getElementById(tabName + '-tab').classList.add('active');\n            event.target.classList.add('active');\n            \n            \/\/ \u5982\u679c\u662f\u5386\u53f2\u6807\u7b7e\u9875\uff0c\u52a0\u8f7d\u5386\u53f2\u8bb0\u5f55\n            if (tabName === 'history') {\n                loadHistory();\n            }\n        }\n        \n        function initBackup() {\n            const apiUrl = document.getElementById('api_url').value;\n            const backupDir = document.getElementById('backup_dir').value || '.';\n            \n            if (!apiUrl) {\n                alert('\u8bf7\u8f93\u5165API URL');\n                return;\n            }\n            \n            addLog('\u6b63\u5728\u521d\u59cb\u5316\u5907\u4efd\u4efb\u52a1...', 'info');\n            \n            fetch('', {\n                method: 'POST',\n                headers: {\n                    'Content-Type': 'application\/x-www-form-urlencoded',\n                    'X-Requested-With': 'XMLHttpRequest'\n                },\n                body: `action=init&api_url=${encodeURIComponent(apiUrl)}&backup_dir=${encodeURIComponent(backupDir)}`\n            })\n            .then(response => response.json())\n            .then(data => {\n                if (data.status) {\n                    updateStatus(data.status);\n                    addLog('\u5907\u4efd\u4efb\u52a1\u521d\u59cb\u5316\u5b8c\u6210', 'success');\n                    addLog(`\u68c0\u6d4b\u5230 ${data.status.stats.excluded} \u4e2a\u88ab\u6392\u9664\u6587\u4ef6`, 'warning');\n                    addLog(`\u68c0\u6d4b\u5230 ${data.status.stats.total} \u4e2a\u5f85\u5907\u4efd\u6587\u4ef6`, 'info');\n                    addLog('\u7cfb\u7edf\u5c31\u7eea\uff0c\u53ef\u4ee5\u5f00\u59cb\u5907\u4efd', 'success');\n                } else {\n                    addLog('\u521d\u59cb\u5316\u5931\u8d25', 'error');\n                }\n            })\n            .catch(error => {\n                addLog('\u521d\u59cb\u5316\u5931\u8d25: ' + error, 'error');\n            });\n        }\n        \n        function startBackup() {\n            if (isBackupRunning) return;\n            \n            const apiUrl = document.getElementById('api_url').value;\n            if (!apiUrl) {\n                alert('\u8bf7\u5148\u8bbe\u7f6eAPI URL');\n                return;\n            }\n            \n            isBackupRunning = true;\n            document.getElementById('startBtn').disabled = true;\n            document.getElementById('pauseBtn').textContent = '\u6682\u505c';\n            \n            addLog('\u5f00\u59cb\u540c\u6b65\u5907\u4efd...', 'info');\n            \n            backupInterval = setInterval(processNextFile, 1000); \/\/ \u6bcf\u79d2\u5904\u7406\u4e00\u4e2a\u6587\u4ef6\n        }\n        \n        function pauseBackup() {\n            if (!isBackupRunning) return;\n            \n            if (isPaused) {\n                \/\/ \u6062\u590d\u5907\u4efd\n                isPaused = false;\n                document.getElementById('pauseBtn').textContent = '\u6682\u505c';\n                addLog('\u6062\u590d\u5907\u4efd', 'info');\n            } else {\n                \/\/ \u6682\u505c\u5907\u4efd\n                isPaused = true;\n                document.getElementById('pauseBtn').textContent = '\u7ee7\u7eed';\n                addLog('\u5907\u4efd\u5df2\u6682\u505c', 'warning');\n            }\n        }\n        \n        function resetBackup() {\n            if (isBackupRunning) {\n                clearInterval(backupInterval);\n                isBackupRunning = false;\n            }\n            \n            const apiUrl = document.getElementById('api_url').value;\n            \n            fetch('', {\n                method: 'POST',\n                headers: {\n                    'Content-Type': 'application\/x-www-form-urlencoded',\n                    'X-Requested-With': 'XMLHttpRequest'\n                },\n                body: `action=reset&api_url=${encodeURIComponent(apiUrl)}`\n            })\n            .then(response => response.json())\n            .then(data => {\n                addLog('\u5907\u4efd\u4efb\u52a1\u5df2\u91cd\u7f6e', 'info');\n                document.getElementById('backupStatus').innerHTML = '<p>\u7b49\u5f85\u5f00\u59cb\u5907\u4efd...<\/p>';\n                document.getElementById('progressFill').style.width = '0%';\n                document.getElementById('progressFill').textContent = '0%';\n                resetStats();\n            });\n        }\n        \n        function processNextFile() {\n            if (isPaused) return;\n            \n            const apiUrl = document.getElementById('api_url').value;\n            \n            fetch('', {\n                method: 'POST',\n                headers: {\n                    'Content-Type': 'application\/x-www-form-urlencoded',\n                    'X-Requested-With': 'XMLHttpRequest'\n                },\n                body: `action=next&api_url=${encodeURIComponent(apiUrl)}`\n            })\n            .then(response => response.json())\n            .then(data => {\n                if (data.error) {\n                    addLog('\u9519\u8bef: ' + data.error, 'error');\n                    return;\n                }\n                \n                if (data.completed) {\n                    clearInterval(backupInterval);\n                    isBackupRunning = false;\n                    document.getElementById('startBtn').disabled = false;\n                    addLog(data.message, 'success');\n                    return;\n                }\n                \n                \/\/ \u66f4\u65b0\u5f53\u524d\u6587\u4ef6\n                const currentFileDiv = document.getElementById('currentFile');\n                currentFileDiv.innerHTML = `\n                    <strong>\u6b63\u5728\u5907\u4efd:<\/strong> ${data.current_file}<br>\n                    <small>\u5927\u5c0f: ${data.current_size}<\/small>\n                `;\n                \n                \/\/ \u66f4\u65b0\u8fdb\u5ea6\n                document.getElementById('progressFill').style.width = data.progress + '%';\n                document.getElementById('progressFill').textContent = data.progress.toFixed(1) + '%';\n                \n                \/\/ \u66f4\u65b0\u7edf\u8ba1\n                if (data.stats) {\n                    updateStats(data.stats);\n                }\n                \n                \/\/ \u8bb0\u5f55\u65e5\u5fd7\n                if (data.result && data.result.success) {\n                    addLog(`\u5df2\u5907\u4efd: ${data.current_file}`, 'success');\n                } else if (data.result) {\n                    addLog(`\u5907\u4efd\u5931\u8d25: ${data.current_file} (${data.result.error || '\u672a\u77e5\u9519\u8bef'})`, 'error');\n                }\n            })\n            .catch(error => {\n                addLog('\u8bf7\u6c42\u5931\u8d25: ' + error, 'error');\n            });\n        }\n        \n        function updateStatus(status) {\n            const statusDiv = document.getElementById('backupStatus');\n            statusDiv.innerHTML = `\n                <p><strong>\u5907\u4efd\u76ee\u5f55:<\/strong> ${status.scan_dir}<\/p>\n                <p><strong>\u5907\u4efdID:<\/strong> ${status.backup_id}<\/p>\n                <p><strong>\u5f00\u59cb\u65f6\u95f4:<\/strong> ${status.start_time}<\/p>\n                <p><strong>\u6700\u540e\u66f4\u65b0:<\/strong> ${status.last_update}<\/p>\n            `;\n            \n            \/\/ \u66f4\u65b0\u8fdb\u5ea6\n            document.getElementById('progressFill').style.width = status.progress + '%';\n            document.getElementById('progressFill').textContent = status.progress.toFixed(1) + '%';\n            \n            \/\/ \u66f4\u65b0\u7edf\u8ba1\n            updateStats(status.stats);\n            \n            \/\/ \u663e\u793a\u6392\u9664\u6587\u4ef6\n            if (status.excluded_files && status.excluded_files.length > 0) {\n                addLog('\u88ab\u6392\u9664\u7684\u6587\u4ef6:', 'warning');\n                status.excluded_files.forEach(file => {\n                    addLog(`  - ${file}`, 'info');\n                });\n            }\n        }\n        \n        function updateStats(stats) {\n            document.getElementById('statTotal').textContent = stats.total;\n            document.getElementById('statPending').textContent = stats.pending;\n            document.getElementById('statCompleted').textContent = stats.completed;\n            document.getElementById('statFailed').textContent = stats.failed;\n            document.getElementById('statExcluded').textContent = stats.excluded;\n            document.getElementById('statSize').textContent = stats.total_size;\n        }\n        \n        function resetStats() {\n            document.getElementById('statTotal').textContent = '0';\n            document.getElementById('statPending').textContent = '0';\n            document.getElementById('statCompleted').textContent = '0';\n            document.getElementById('statFailed').textContent = '0';\n            document.getElementById('statExcluded').textContent = '0';\n            document.getElementById('statSize').textContent = '0 B';\n        }\n        \n        function addLog(message, type = 'info') {\n            const logPanel = document.getElementById('logPanel');\n            const now = new Date();\n            const timeStr = now.toTimeString().split(' ')[0];\n            \n            const logEntry = document.createElement('div');\n            logEntry.className = 'log-entry';\n            logEntry.innerHTML = `\n                <span class=\"log-time\">[${timeStr}]<\/span>\n                <span class=\"log-${type}\"> ${message}<\/span>\n            `;\n            \n            logPanel.appendChild(logEntry);\n            logPanel.scrollTop = logPanel.scrollHeight;\n        }\n        \n        function saveConfig() {\n            const directories = document.getElementById('exclude_dirs').value;\n            const files = document.getElementById('exclude_files').value;\n            const extensions = document.getElementById('exclude_exts').value;\n            const apiUrl = document.getElementById('api_url').value;\n            \n            fetch('', {\n                method: 'POST',\n                headers: {\n                    'Content-Type': 'application\/x-www-form-urlencoded',\n                    'X-Requested-With': 'XMLHttpRequest'\n                },\n                body: `action=save_config&api_url=${encodeURIComponent(apiUrl)}&exclude_dirs=${encodeURIComponent(directories)}&exclude_files=${encodeURIComponent(files)}&exclude_exts=${encodeURIComponent(extensions)}`\n            })\n            .then(response => response.json())\n            .then(data => {\n                if (data.success) {\n                    alert('\u914d\u7f6e\u4fdd\u5b58\u6210\u529f');\n                } else {\n                    alert('\u914d\u7f6e\u4fdd\u5b58\u5931\u8d25');\n                }\n            });\n        }\n        \n        function loadHistory() {\n            const apiUrl = document.getElementById('api_url').value;\n            if (!apiUrl) return;\n            \n            \/\/ \u8fd9\u91cc\u53ef\u4ee5\u6dfb\u52a0\u52a0\u8f7d\u5386\u53f2\u8bb0\u5f55\u7684AJAX\u8c03\u7528\n            \/\/ \u7531\u4e8e\u65f6\u95f4\u5173\u7cfb\uff0c\u8fd9\u91cc\u53ea\u662f\u663e\u793a\u4e00\u4e2a\u63d0\u793a\n            document.getElementById('historyList').innerHTML = `\n                <p>\u5907\u4efd\u5386\u53f2\u8bb0\u5f55\u529f\u80fd\u9700\u8981\u4ece\u670d\u52a1\u5668\u52a0\u8f7d\u6570\u636e\u3002<\/p>\n                <p>\u8bf7\u786e\u4fdd backup_history.json \u6587\u4ef6\u5b58\u5728\u4e14\u53ef\u8bfb\u3002<\/p>\n            `;\n        }\n        \n        \/\/ \u9875\u9762\u52a0\u8f7d\u65f6\u68c0\u67e5\u73b0\u6709\u4efb\u52a1\n        window.onload = function() {\n            const apiUrl = document.getElementById('api_url').value;\n            if (apiUrl) {\n                fetch('', {\n                    method: 'POST',\n                    headers: {\n                        'Content-Type': 'application\/x-www-form-urlencoded',\n                        'X-Requested-With': 'XMLHttpRequest'\n                    },\n                    body: `action=status&api_url=${encodeURIComponent(apiUrl)}`\n                })\n                .then(response => response.json())\n                .then(data => {\n                    if (data.status) {\n                        updateStatus(data.status);\n                        addLog('\u68c0\u6d4b\u5230\u73b0\u6709\u5907\u4efd\u4efb\u52a1', 'info');\n                    }\n                });\n            }\n        };\n    <\/script>\n<\/body>\n<\/html>\n"}