index.php 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. <?php
  2. /**
  3. * Admin page: generate tracking links and view all captured clicks.
  4. */
  5. $dataDir = __DIR__ . '/data';
  6. $logFile = $dataDir . '/tracks.jsonl';
  7. $keysFile = $dataDir . '/keys.json';
  8. $baseUrl = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? 'https' : 'http')
  9. . '://' . ($_SERVER['HTTP_HOST'] ?? 'localhost')
  10. . rtrim(dirname($_SERVER['SCRIPT_NAME'] ?? '/'), '/') . '/';
  11. $trackingLink = $baseUrl . 'track.php';
  12. $trackedKeys = [];
  13. if (is_file($keysFile) && is_readable($keysFile)) {
  14. $raw = file_get_contents($keysFile);
  15. $decoded = json_decode($raw, true);
  16. if (is_array($decoded)) {
  17. $trackedKeys = $decoded;
  18. }
  19. }
  20. if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['action'] === 'create_key') {
  21. $existing = array_flip($trackedKeys);
  22. do {
  23. $newKey = bin2hex(random_bytes(8));
  24. } while (isset($existing[$newKey]));
  25. $trackedKeys[] = $newKey;
  26. if (!is_dir($dataDir)) {
  27. mkdir($dataDir, 0755, true);
  28. }
  29. file_put_contents($keysFile, json_encode($trackedKeys, JSON_PRETTY_PRINT), LOCK_EX);
  30. }
  31. $maxTracks = 100;
  32. $tracks = [];
  33. if (is_file($logFile) && is_readable($logFile)) {
  34. $lines = file($logFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
  35. if ($lines !== false) {
  36. if (count($lines) > $maxTracks) {
  37. $lines = array_slice($lines, -$maxTracks);
  38. file_put_contents($logFile, implode("\n", $lines) . "\n", LOCK_EX);
  39. }
  40. $reversed = array_reverse($lines);
  41. foreach ($reversed as $line) {
  42. $decoded = json_decode($line, true);
  43. if (is_array($decoded)) {
  44. $tracks[] = $decoded;
  45. }
  46. }
  47. }
  48. }
  49. ?>
  50. <!DOCTYPE html>
  51. <html lang="en">
  52. <head>
  53. <meta charset="UTF-8">
  54. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  55. <title>Click Tracker – Admin</title>
  56. <style>
  57. body { font-family: system-ui, sans-serif; margin: 1rem 2rem; max-width: 1200px; }
  58. h1 { margin-top: 0; }
  59. .form-group { margin-bottom: 1rem; }
  60. .form-group label { display: block; margin-bottom: 0.25rem; font-weight: 500; }
  61. .form-group input[type="url"] { width: 100%; max-width: 480px; padding: 0.5rem; box-sizing: border-box; }
  62. .generated { margin: 1rem 0; padding: 0.75rem; background: #e8f5e9; border-radius: 4px; word-break: break-all; }
  63. .generated a { color: #2e7d32; }
  64. .error { color: #c62828; margin: 1rem 0; }
  65. table { border-collapse: collapse; width: 100%; margin-top: 1.5rem; font-size: 0.9rem; }
  66. th, td { border: 1px solid #ccc; padding: 0.5rem 0.75rem; text-align: left; vertical-align: top; }
  67. th { background: #f5f5f5; font-weight: 600; }
  68. tr:nth-child(even) { background: #fafafa; }
  69. .col-time { white-space: nowrap; }
  70. .col-ua, .col-referrer, .col-target { max-width: 280px; word-break: break-all; }
  71. .empty { color: #666; font-style: italic; margin-top: 1rem; }
  72. </style>
  73. </head>
  74. <body>
  75. <h1>Click Tracker – Admin</h1>
  76. <section>
  77. <h2>Tracking link</h2>
  78. <p class="generated">Use this link to capture clicks (redirects here after): <a href="<?php echo htmlspecialchars($trackingLink); ?>"><?php echo htmlspecialchars($trackingLink); ?></a></p>
  79. </section>
  80. <section>
  81. <h2>Tracked links (by key)</h2>
  82. <p>Create a unique key to get a link that identifies which link was clicked. Clicks are logged with the key.</p>
  83. <form method="post" action="">
  84. <input type="hidden" name="action" value="create_key">
  85. <button type="submit">Create unique key</button>
  86. </form>
  87. <?php if (!empty($trackedKeys)): ?>
  88. <?php $linkTemplate = $baseUrl . 'track.php?key=KEY'; ?>
  89. <ul style="margin-top: 1rem; list-style: none; padding: 0;">
  90. <?php foreach (array_reverse($trackedKeys) as $k): ?>
  91. <li style="margin-bottom: 0.5rem; padding: 0.5rem; background: #f5f5f5; border-radius: 4px;">
  92. <code style="font-size: 0.85em;"><?php echo htmlspecialchars($k); ?></code>
  93. <span style="margin-left: 0.5rem; word-break: break-all; color: #555;"><?php echo htmlspecialchars($linkTemplate); ?></span>
  94. </li>
  95. <?php endforeach; ?>
  96. </ul>
  97. <p class="empty" style="margin-top: 0.5rem;">Replace <code>KEY</code> in the URL with the key above to construct your link.</p>
  98. <?php else: ?>
  99. <p class="empty">No tracked keys yet. Click the button above to create one.</p>
  100. <?php endif; ?>
  101. </section>
  102. <section>
  103. <h2>Past tracks</h2>
  104. <?php if (empty($tracks)): ?>
  105. <p class="empty">No tracks yet.</p>
  106. <?php else: ?>
  107. <table>
  108. <thead>
  109. <tr>
  110. <th class="col-time">Time</th>
  111. <th>Key</th>
  112. <th>IP</th>
  113. <th class="col-ua">User-Agent</th>
  114. <th class="col-referrer">Referrer</th>
  115. <th class="col-target">Target URL</th>
  116. <th>Method</th>
  117. <th>Request URI</th>
  118. </tr>
  119. </thead>
  120. <tbody>
  121. <?php foreach ($tracks as $t): ?>
  122. <tr>
  123. <td class="col-time"><?php echo htmlspecialchars($t['timestamp'] ?? '-'); ?></td>
  124. <td><?php echo htmlspecialchars($t['key'] ?? '-'); ?></td>
  125. <td><?php echo htmlspecialchars($t['ip'] ?? '-'); ?><?php if (!empty($t['ip_forwarded'])) echo ' <small>(X-Forwarded: ' . htmlspecialchars($t['ip_forwarded']) . ')</small>'; ?></td>
  126. <td class="col-ua"><?php echo htmlspecialchars($t['user_agent'] ?? '-'); ?></td>
  127. <td class="col-referrer"><?php echo htmlspecialchars($t['referrer'] ?? '-'); ?></td>
  128. <td class="col-target"><?php echo htmlspecialchars($t['target_url'] ?? '-'); ?></td>
  129. <td><?php echo htmlspecialchars($t['request_method'] ?? '-'); ?></td>
  130. <td><?php echo htmlspecialchars($t['request_uri'] ?? '-'); ?></td>
  131. </tr>
  132. <?php endforeach; ?>
  133. </tbody>
  134. </table>
  135. <?php endif; ?>
  136. </section>
  137. </body>
  138. </html>