index.php 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. <?php
  2. define("CONFIG", 1);
  3. // Настройки
  4. $page_title = 'OpenVPN Status';
  5. // Подключаем конфигурационный файл
  6. $config_file = __DIR__ . '/config.php';
  7. if (!file_exists($config_file)) {
  8. die("Configuration file not found: $config_file");
  9. }
  10. $servers = require_once $config_file;
  11. session_start();
  12. $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
  13. // Проверяем и инициализируем массив, если его нет
  14. if (!isset($_SESSION['last_request_time']) || !is_array($_SESSION['last_request_time'])) {
  15. $_SESSION['last_request_time'] = []; // Создаем пустой массив
  16. }
  17. ?>
  18. <!DOCTYPE html>
  19. <html>
  20. <head>
  21. <title><?= htmlspecialchars($page_title) ?></title>
  22. <style>
  23. body { font-family: Arial, sans-serif; margin: 20px; }
  24. table { border-collapse: collapse; width: 100%; margin-bottom: 20px; }
  25. th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
  26. th { background-color: #f2f2f2; }
  27. .banned { background-color: #ffeeee; }
  28. .actions { white-space: nowrap; }
  29. .btn { padding: 3px 8px; margin: 2px; cursor: pointer; border: 1px solid #ccc; border-radius: 3px; }
  30. .kick-btn { background-color: #ffcccc; }
  31. .ban-btn { background-color: #ff9999; }
  32. .unban-btn { background-color: #ccffcc; }
  33. .section { margin-bottom: 30px; }
  34. .status-badge { padding: 2px 5px; border-radius: 3px; font-size: 0.8em; }
  35. .status-active { background-color: #ccffcc; }
  36. .status-banned { background-color: #ff9999; }
  37. .server-section { border: 1px solid #ddd; padding: 15px; margin-bottom: 20px; border-radius: 5px; }
  38. .spoiler { margin-top: 10px; }
  39. .spoiler-title {
  40. cursor: pointer;
  41. color: #0066cc;
  42. padding: 5px;
  43. background-color: #f0f0f0;
  44. border: 1px solid #ddd;
  45. border-radius: 3px;
  46. display: inline-block;
  47. margin-bottom: 5px;
  48. }
  49. .spoiler-title:after { content: " ▼"; }
  50. .spoiler-title.collapsed:after { content: " ►"; }
  51. .spoiler-content {
  52. display: none;
  53. padding: 10px;
  54. border: 1px solid #ddd;
  55. margin-top: 5px;
  56. background-color: #f9f9f9;
  57. border-radius: 3px;
  58. }
  59. .loading { color: #666; font-style: italic; }
  60. .last-update { font-size: 0.8em; color: #666; margin-top: 5px; }
  61. </style>
  62. </head>
  63. <body>
  64. <h1><?= htmlspecialchars($page_title) ?></h1>
  65. <div id="server-container">
  66. <?php foreach ($servers as $server_name => $server): ?>
  67. <div class="server-section" id="server-<?= htmlspecialchars($server_name) ?>">
  68. <h2><?= htmlspecialchars($server['title']) ?></h2>
  69. <div class="loading">Loading data...</div>
  70. </div>
  71. <?php endforeach; ?>
  72. </div>
  73. <script>
  74. // Функция для загрузки данных сервера
  75. function loadServerData(serverName) {
  76. const serverElement = document.getElementById(`server-${serverName}`);
  77. fetch(`get_server_data.php?server=${serverName}&csrf=<?= $_SESSION['csrf_token'] ?>`,{
  78. headers: {
  79. 'X-Requested-With': 'XMLHttpRequest'
  80. }
  81. })
  82. .then(response => response.text())
  83. .then(html => {
  84. serverElement.innerHTML = html;
  85. // Обновляем данные каждые 60 секунд
  86. setTimeout(() => loadServerData(serverName), 60000);
  87. })
  88. .catch(error => {
  89. serverElement.querySelector('.loading').textContent = 'Error loading data';
  90. console.error('Error:', error);
  91. // Повторяем попытку через 10 секунд при ошибке
  92. setTimeout(() => loadServerData(serverName), 10000);
  93. });
  94. }
  95. // Загружаем данные для всех серверов
  96. document.addEventListener('DOMContentLoaded', function() {
  97. <?php foreach ($servers as $server_name => $server): ?>
  98. loadServerData('<?= $server_name ?>');
  99. <?php endforeach; ?>
  100. });
  101. // Функция для обработки действий (ban/unban)
  102. function handleAction(serverName, action, clientName) {
  103. const params = new URLSearchParams();
  104. params.append('server', serverName);
  105. params.append('action', action);
  106. params.append('client', clientName);
  107. fetch('handle_action.php', {
  108. method: 'POST',
  109. headers: {
  110. 'Content-Type': 'application/x-www-form-urlencoded',
  111. 'X-Requested-With': 'XMLHttpRequest'
  112. },
  113. body: params
  114. })
  115. .then(response => {
  116. // 2. Проверяем статус ответа
  117. if (!response.ok) {
  118. throw new Error(`Server returned ${response.status} status`);
  119. }
  120. return response.json();
  121. })
  122. .then(data => {
  123. // 3. Проверяем структуру ответа
  124. if (!data || typeof data.success === 'undefined') {
  125. throw new Error('Invalid server response');
  126. }
  127. if (data.success) {
  128. loadServerData(serverName);
  129. } else {
  130. console.error('Server error:', data.message);
  131. alert(`Error: ${data.message || 'Operation failed'}`);
  132. }
  133. })
  134. .catch(error => {
  135. // 4. Правильное отображение ошибки
  136. console.error('Request failed:', error);
  137. alert(`Request failed: ${error.message}`);
  138. });
  139. }
  140. // Функция для переключения спойлера
  141. function toggleSpoiler(button) {
  142. const content = button.nextElementSibling;
  143. if (content.style.display === "block") {
  144. content.style.display = "none";
  145. button.classList.add('collapsed');
  146. } else {
  147. content.style.display = "block";
  148. button.classList.remove('collapsed');
  149. }
  150. }
  151. </script>
  152. </body>
  153. </html>