424 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
			
		
		
	
	
			424 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
| <!DOCTYPE html>
 | ||
| <html lang="ru">
 | ||
| 
 | ||
| <head>
 | ||
|   <meta charset="UTF-8">
 | ||
|   <meta name="viewport" content="width=device-width, initial-scale=1.0">
 | ||
|   <title>Система мониторинга утечек данных</title>
 | ||
|   <link rel="stylesheet" href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css" />
 | ||
|   <style>
 | ||
|     body {
 | ||
|       background-color: #1e1e1e;
 | ||
|       color: #ffffff;
 | ||
|       font-family: 'Arial', sans-serif;
 | ||
|       margin: 0;
 | ||
|       padding: 0;
 | ||
|       display: flex;
 | ||
|       transition: margin-left 0.3s ease;
 | ||
|       min-height: 100vh;
 | ||
|       overflow: hidden;
 | ||
|     }
 | ||
| 
 | ||
|     .sidebar {
 | ||
|       width: 250px;
 | ||
|       background-color: #2e2e2e;
 | ||
|       padding: 20px;
 | ||
|       box-shadow: 2px 0 5px rgba(0, 0, 0, 0.3);
 | ||
|       transition: width 0.3s ease;
 | ||
|       overflow-y: auto;
 | ||
|       position: fixed;
 | ||
|       top: 0;
 | ||
|       left: 0;
 | ||
|       bottom: 0;
 | ||
|       z-index: 1;
 | ||
|     }
 | ||
| 
 | ||
|     .sidebar.collapsed {
 | ||
|       width: 60px;
 | ||
|     }
 | ||
| 
 | ||
|     .sidebar.collapsed h2,
 | ||
|     .sidebar.collapsed ul li a span {
 | ||
|       display: none;
 | ||
|     }
 | ||
| 
 | ||
|     .sidebar h2 {
 | ||
|       color: #3399FF;
 | ||
|       text-align: center;
 | ||
|       margin-bottom: 20px;
 | ||
|       font-size: 24px;
 | ||
|       transition: opacity 0.3s ease;
 | ||
|     }
 | ||
| 
 | ||
|     .sidebar ul {
 | ||
|       list-style: none;
 | ||
|       padding: 0;
 | ||
|     }
 | ||
| 
 | ||
|     .sidebar ul li {
 | ||
|       margin: 15px 0;
 | ||
|     }
 | ||
| 
 | ||
|     .sidebar ul li a {
 | ||
|       color: #ffffff;
 | ||
|       text-decoration: none;
 | ||
|       font-size: 16px;
 | ||
|       transition: color 0.3s ease;
 | ||
|       display: flex;
 | ||
|       align-items: center;
 | ||
|     }
 | ||
| 
 | ||
|     .sidebar ul li a:hover {
 | ||
|       color: #3399FF;
 | ||
|     }
 | ||
| 
 | ||
|     .sidebar ul li a i {
 | ||
|       margin-right: 10px;
 | ||
|       font-size: 20px;
 | ||
|     }
 | ||
| 
 | ||
|     .sidebar ul li a span {
 | ||
|       transition: opacity 0.3s ease;
 | ||
|     }
 | ||
| 
 | ||
|     .toggle-btn {
 | ||
|       position: fixed;
 | ||
|       left: 10px;
 | ||
|       top: 10px;
 | ||
|       background-color: #3399FF;
 | ||
|       border: none;
 | ||
|       color: #fff;
 | ||
|       padding: 10px;
 | ||
|       border-radius: 5px;
 | ||
|       cursor: pointer;
 | ||
|       z-index: 1000;
 | ||
|     }
 | ||
| 
 | ||
|     .container {
 | ||
|       flex: 1;
 | ||
|       padding: 20px;
 | ||
|       margin-left: 250px;
 | ||
|       transition: margin-left 0.3s ease;
 | ||
|       overflow-y: auto;
 | ||
|       height: 100vh;
 | ||
|       position: relative;
 | ||
|       z-index: 0;
 | ||
|     }
 | ||
| 
 | ||
|     .container.collapsed {
 | ||
|       margin-left: 60px;
 | ||
|     }
 | ||
| 
 | ||
|     h1 {
 | ||
|       color: #3399FF;
 | ||
|       text-align: center;
 | ||
|       margin-bottom: 20px;
 | ||
|     }
 | ||
| 
 | ||
|     .grid {
 | ||
|       display: grid;
 | ||
|       grid-template-columns: repeat(2, 1fr);
 | ||
|       gap: 20px;
 | ||
|       margin-top: 20px;
 | ||
|     }
 | ||
| 
 | ||
|     .card {
 | ||
|       background-color: #2e2e2e;
 | ||
|       padding: 20px;
 | ||
|       border-radius: 8px;
 | ||
|       box-shadow: 0 0 5px rgba(0, 0, 0, 0.2);
 | ||
|     }
 | ||
| 
 | ||
|     #map {
 | ||
|       height: 400px;
 | ||
|       border-radius: 8px;
 | ||
|       grid-column: span 2;
 | ||
|     }
 | ||
| 
 | ||
|     .screenshots {
 | ||
|       display: flex;
 | ||
|       overflow-x: auto;
 | ||
|       gap: 10px;
 | ||
|       padding: 10px;
 | ||
|     }
 | ||
| 
 | ||
|     .screenshots img {
 | ||
|       max-height: 200px;
 | ||
|       border-radius: 5px;
 | ||
|       border: 2px solid #3399FF;
 | ||
|     }
 | ||
| 
 | ||
|     .logs {
 | ||
|       background-color: #2e2e2e;
 | ||
|       padding: 15px;
 | ||
|       border-radius: 8px;
 | ||
|       height: 200px;
 | ||
|       overflow-y: auto;
 | ||
|       font-family: 'Courier New', monospace;
 | ||
|       font-size: 14px;
 | ||
|       color: #3399FF;
 | ||
|     }
 | ||
| 
 | ||
|     .logs p {
 | ||
|       margin: 5px 0;
 | ||
|     }
 | ||
| 
 | ||
|     .leaflet-marker-icon {
 | ||
|       background-color: #3399FF;
 | ||
|       border: 2px solid #ffffff;
 | ||
|       border-radius: 50%;
 | ||
|       width: 20px !important;
 | ||
|       height: 20px !important;
 | ||
|       box-shadow: 0 0 10px rgba(51, 153, 255, 0.5);
 | ||
|     }
 | ||
| 
 | ||
|     .leaflet-popup-content {
 | ||
|       color: #1e1e1e;
 | ||
|       font-size: 14px;
 | ||
|     }
 | ||
| 
 | ||
|     .leaflet-popup-content-wrapper {
 | ||
|       border-radius: 8px;
 | ||
|       box-shadow: 0 0 10px rgba(0, 0, 0, 0.3);
 | ||
|     }
 | ||
|   </style>
 | ||
|   <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
 | ||
| </head>
 | ||
| 
 | ||
| <body>
 | ||
|   <!-- Боковое меню -->
 | ||
|   <div class="sidebar" id="sidebar">
 | ||
|     <h2>Меню</h2>
 | ||
|     <ul>
 | ||
|       <li><a href="#"><i class="fas fa-home"></i><span>Главная</span></a></li>
 | ||
|       <li><a href="#"><i class="fas fa-cog"></i><span>Настройки</span></a></li>
 | ||
|       <li><a href="#"><i class="fas fa-chart-line"></i><span>Отчеты</span></a></li>
 | ||
|       <li><a href="#"><i class="fas fa-code"></i><span>Парсеры</span></a></li>
 | ||
|       <li><a href="logs.html"><i class="fas fa-file-alt"></i><span>Логи</span></a></li>
 | ||
|     </ul>
 | ||
|   </div>
 | ||
| 
 | ||
|   <!-- Кнопка для сворачивания/разворачивания меню -->
 | ||
|   <button class="toggle-btn" id="toggle-btn">
 | ||
|     <i class="fas fa-bars"></i>
 | ||
|   </button>
 | ||
| 
 | ||
|   <!-- Основной контент -->
 | ||
|   <div class="container" id="main-content">
 | ||
|     <h1>Система мониторинга утечек данных</h1>
 | ||
| 
 | ||
|     <!-- Карта -->
 | ||
|     <div class="card">
 | ||
|       <h2>Карта утечек</h2>
 | ||
|       <div id="map"></div>
 | ||
|     </div>
 | ||
| 
 | ||
|     <!-- Графики, скриншоты и логи -->
 | ||
|     <div class="grid">
 | ||
|       <div class="card">
 | ||
|         <h2>Утечки за месяц</h2>
 | ||
|         <canvas id="monthlyLeaksChart"></canvas>
 | ||
|       </div>
 | ||
|       <div class="card">
 | ||
|         <h2>Состояние парсеров</h2>
 | ||
|         <div class="parsers-status">
 | ||
|           <p>TG Node 0:
 | ||
|             <span class="status-{% if parser_status['tg-node-0'] %}active{% else %}error{% endif %}">
 | ||
|               {% if parser_status['tg-node-0'] %}Активен{% else %}Ошибка{% endif %}
 | ||
|             </span>
 | ||
|           </p>
 | ||
|           <p>TG Node 1:
 | ||
|             <span class="status-{% if parser_status['tg-node-1'] %}active{% else %}error{% endif %}">
 | ||
|               {% if parser_status['tg-node-1'] %}Активен{% else %}Ошибка{% endif %}
 | ||
|             </span>
 | ||
|           </p>
 | ||
|           <canvas id="parsersStatusChart"></canvas>
 | ||
|         </div>
 | ||
|       </div>
 | ||
|       <div class="card">
 | ||
|         <h2>Источники утечек</h2>
 | ||
|         <canvas id="sourcesChart"></canvas>
 | ||
|       </div>
 | ||
|       <div class="card">
 | ||
|         <h2>Скриншоты с форумов</h2>
 | ||
|         <div class="screenshots">
 | ||
|           <img src="https://via.placeholder.com/300x200.png?text=Скриншот+1" alt="Скриншот 1">
 | ||
|           <img src="https://via.placeholder.com/300x200.png?text=Скриншот+2" alt="Скриншот 2">
 | ||
|           <img src="https://via.placeholder.com/300x200.png?text=Скриншот+3" alt="Скриншот 3">
 | ||
|           <img src="https://via.placeholder.com/300x200.png?text=Скриншот+4" alt="Скриншот 4">
 | ||
|           <img src="https://via.placeholder.com/300x200.png?text=Скриншот+5" alt="Скриншот 5">
 | ||
|         </div>
 | ||
|       </div>
 | ||
|       <div class="card" style="grid-column: span 2;">
 | ||
|         <h2>Логи системы</h2>
 | ||
|         <div class="logs">
 | ||
|           <p>[2023-10-01 12:34] Парсер форума запущен.</p>
 | ||
|           <p>[2023-10-01 12:35] Найдена новая утечка на форуме X.</p>
 | ||
|           <p>[2023-10-01 12:36] Ошибка парсера Telegram: timeout.</p>
 | ||
|           <p>[2023-10-01 12:37] Утечка данных в Москве зафиксирована.</p>
 | ||
|           <p>[2023-10-01 12:38] Парсер даркнета завершил работу.</p>
 | ||
|           <p>[2023-10-01 12:39] Новый скриншот добавлен в базу.</p>
 | ||
|         </div>
 | ||
|       </div>
 | ||
|     </div>
 | ||
|   </div>
 | ||
| 
 | ||
|   <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
 | ||
|   <script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js"></script>
 | ||
|   <script>
 | ||
|     const monthlyLeaksCtx = document.getElementById('monthlyLeaksChart').getContext('2d');
 | ||
|     new Chart(monthlyLeaksCtx, {
 | ||
|       type: 'line',
 | ||
|       data: {
 | ||
|         labels: [
 | ||
|           {% for day in leaks_stats['leaks_by_day'] %}
 | ||
|             "{{ day[0].strftime('%d.%m') }}"{% if not loop.last %}, {% endif %}
 | ||
|           {% endfor %}
 | ||
|         ],
 | ||
|         datasets: [{
 | ||
|           label: 'Утечки',
 | ||
|           data: [
 | ||
|             {% for day in leaks_stats['leaks_by_day'] %}
 | ||
|               {{ day[1] }}{% if not loop.last %}, {% endif %}
 | ||
|             {% endfor %}
 | ||
|           ],
 | ||
|           borderColor: '#3399FF',
 | ||
|           tension: 0.1,
 | ||
|           fill: true,
 | ||
|           backgroundColor: 'rgba(51, 153, 255, 0.1)'
 | ||
|         }]
 | ||
|       },
 | ||
|       options: {
 | ||
|         responsive: true,
 | ||
|         plugins: {
 | ||
|           legend: {
 | ||
|             labels: {
 | ||
|               color: '#fff'
 | ||
|             }
 | ||
|           }
 | ||
|         },
 | ||
|         scales: {
 | ||
|           y: {
 | ||
|             beginAtZero: true,
 | ||
|             grid: {
 | ||
|               color: '#444'
 | ||
|             },
 | ||
|             ticks: {
 | ||
|               color: '#fff'
 | ||
|             }
 | ||
|           },
 | ||
|           x: {
 | ||
|             grid: {
 | ||
|               color: '#444'
 | ||
|             },
 | ||
|             ticks: {
 | ||
|               color: '#fff'
 | ||
|             }
 | ||
|           }
 | ||
|         }
 | ||
|       }
 | ||
|     });
 | ||
|     // График состояния парсеров
 | ||
|     const parsersStatusCtx = document.getElementById('parsersStatusChart').getContext('2d');
 | ||
|     new Chart(parsersStatusCtx, {
 | ||
|       type: 'doughnut',
 | ||
|       data: {
 | ||
|         labels: ['Активны', 'Ошибки', 'Неактивны'],
 | ||
|         datasets: [{
 | ||
|           label: 'Состояние',
 | ||
|           data: [
 | ||
|             {{ parser_status['active'] }},
 | ||
|             {{ parser_status['errors'] }},
 | ||
|             0  // Неактивных нет в текущей логике
 | ||
|           ],
 | ||
|           backgroundColor: ['#3399FF', '#ff4444', '#666666'],
 | ||
|         }]
 | ||
|       },
 | ||
|       options: {
 | ||
|         responsive: true,
 | ||
|         plugins: {
 | ||
|           legend: {
 | ||
|             labels: {
 | ||
|               color: '#fff',
 | ||
|             }
 | ||
|           }
 | ||
|         }
 | ||
|       }
 | ||
|     });
 | ||
| 
 | ||
|     // График источников утечек
 | ||
|     const sourcesCtx = document.getElementById('sourcesChart').getContext('2d');
 | ||
|     new Chart(sourcesCtx, {
 | ||
|       type: 'bar',
 | ||
|       data: {
 | ||
|         labels: [
 | ||
|           {% for source in leaks_stats['leaks_by_source'] %}
 | ||
|             '{{ source[0] }}'{% if not loop.last %},{% endif %}
 | ||
|           {% endfor %}
 | ||
|         ],
 | ||
|         datasets: [{
 | ||
|           label: 'Количество утечек',
 | ||
|           data: [
 | ||
|             {% for source in leaks_stats['leaks_by_source'] %}
 | ||
|               {{ source[1] }}{% if not loop.last %},{% endif %}
 | ||
|             {% endfor %}
 | ||
|           ],
 | ||
|           backgroundColor: ['#3399FF', '#00cc99', '#0099cc'],
 | ||
|         }]
 | ||
|       },
 | ||
|       options: {
 | ||
|         responsive: true,
 | ||
|         plugins: {
 | ||
|           legend: {
 | ||
|             labels: {
 | ||
|               color: '#fff',
 | ||
|             }
 | ||
|           }
 | ||
|         },
 | ||
|         scales: {
 | ||
|           y: {
 | ||
|             beginAtZero: true,
 | ||
|             grid: {
 | ||
|               color: '#444',
 | ||
|             },
 | ||
|             ticks: {
 | ||
|               color: '#fff',
 | ||
|             }
 | ||
|           },
 | ||
|           x: {
 | ||
|             grid: {
 | ||
|               color: '#444',
 | ||
|             },
 | ||
|             ticks: {
 | ||
|               color: '#fff',
 | ||
|             }
 | ||
|           }
 | ||
|         }
 | ||
|       }
 | ||
|     });
 | ||
| 
 | ||
|     const map = L.map('map').setView([55.7558, 37.6176], 5);
 | ||
|     L.tileLayer('https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png', {
 | ||
|       attribution: '© OpenStreetMap contributors, © CARTO'
 | ||
|     }).addTo(map);
 | ||
| 
 | ||
|     const marker1 = L.marker([55.7558, 37.6176], {
 | ||
|       icon: L.divIcon({ className: 'leaflet-marker-icon' })
 | ||
|     }).addTo(map).bindPopup('Утечка данных в Москве');
 | ||
| 
 | ||
|     const marker2 = L.marker([59.9343, 30.3351], {
 | ||
|       icon: L.divIcon({ className: 'leaflet-marker-icon' })
 | ||
|     }).addTo(map).bindPopup('Утечка данных в Санкт-Петербурге');
 | ||
| 
 | ||
|     const sidebar = document.getElementById('sidebar');
 | ||
|     const mainContent = document.getElementById('main-content');
 | ||
|     const toggleBtn = document.getElementById('toggle-btn');
 | ||
| 
 | ||
|     toggleBtn.addEventListener('click', () => {
 | ||
|       sidebar.classList.toggle('collapsed');
 | ||
|       mainContent.classList.toggle('collapsed');
 | ||
|     });
 | ||
|   </script>
 | ||
| </body>
 | ||
| 
 | ||
| </html> |