This commit is contained in:
user committed 2025-07-02 19:25:20 +03:00
1 parent 21964fa986
commit 797f379648
68 files changed
+23871 -1271

No files matched your search

+620
View File
@@ -0,0 +1,620 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>MY Network Monitor - Distributed Protocol v2.0</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background: linear-gradient(135deg, #0a0a0a 0%, #1a1a2e 50%, #16213e 100%);
color: #00ff41;
font-family: 'Courier New', 'Lucida Console', monospace;
min-height: 100vh;
overflow-x: auto;
animation: backgroundPulse 10s ease-in-out infinite alternate;
}
@keyframes backgroundPulse {
0% { background: linear-gradient(135deg, #0a0a0a 0%, #1a1a2e 50%, #16213e 100%); }
100% { background: linear-gradient(135deg, #0f0f0f 0%, #1f1f3e 50%, #1b274e 100%); }
}
.container {
max-width: 1400px;
margin: 0 auto;
padding: 20px;
}
.header {
text-align: center;
margin-bottom: 30px;
padding: 20px;
border: 2px solid #00ff41;
border-radius: 10px;
background: rgba(0, 255, 65, 0.05);
box-shadow: 0 0 20px rgba(0, 255, 65, 0.3);
}
.header h1 {
font-size: 2.5em;
text-shadow: 0 0 10px #00ff41;
animation: glow 2s ease-in-out infinite alternate;
}
@keyframes glow {
from { text-shadow: 0 0 10px #00ff41, 0 0 20px #00ff41; }
to { text-shadow: 0 0 20px #00ff41, 0 0 30px #00ff41, 0 0 40px #00ff41; }
}
.status-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(350px, 1fr));
gap: 20px;
margin-bottom: 30px;
}
.status-card {
background: rgba(0, 0, 0, 0.7);
border: 1px solid #00ff41;
border-radius: 8px;
padding: 20px;
box-shadow: 0 0 15px rgba(0, 255, 65, 0.2);
transition: all 0.3s ease;
}
.status-card:hover {
transform: translateY(-5px);
box-shadow: 0 5px 25px rgba(0, 255, 65, 0.4);
}
.status-card h3 {
color: #00ff41;
margin-bottom: 15px;
font-size: 1.2em;
text-transform: uppercase;
border-bottom: 1px solid #00ff41;
padding-bottom: 5px;
}
.status-item {
display: flex;
justify-content: space-between;
margin: 10px 0;
padding: 5px 0;
}
.status-value {
color: #ffffff;
font-weight: bold;
}
.status-online { color: #00ff00; }
.status-offline { color: #ff0000; }
.status-warning { color: #ffff00; }
.ascii-display {
background: rgba(0, 0, 0, 0.9);
border: 2px solid #00ff41;
border-radius: 8px;
padding: 20px;
margin: 20px 0;
font-family: 'Courier New', monospace;
font-size: 11px;
line-height: 1.1;
white-space: pre;
overflow-x: auto;
box-shadow: inset 0 0 20px rgba(0, 255, 65, 0.1);
}
.control-panel {
display: flex;
gap: 15px;
margin: 20px 0;
flex-wrap: wrap;
}
.btn {
background: linear-gradient(45deg, #00ff41, #00cc33);
color: #000;
border: none;
padding: 12px 24px;
font-family: inherit;
font-weight: bold;
cursor: pointer;
border-radius: 5px;
text-transform: uppercase;
transition: all 0.3s ease;
box-shadow: 0 0 10px rgba(0, 255, 65, 0.3);
}
.btn:hover {
background: linear-gradient(45deg, #00cc33, #00ff41);
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(0, 255, 65, 0.5);
}
.btn:active {
transform: translateY(1px);
}
.network-topology {
background: rgba(0, 0, 0, 0.8);
border: 1px solid #00ff41;
border-radius: 8px;
padding: 20px;
margin: 20px 0;
min-height: 300px;
}
.node {
display: inline-block;
background: rgba(0, 255, 65, 0.1);
border: 2px solid #00ff41;
border-radius: 50%;
width: 80px;
height: 80px;
line-height: 76px;
text-align: center;
margin: 10px;
position: relative;
animation: pulse 3s ease-in-out infinite;
}
@keyframes pulse {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.05); }
}
.node.this-node {
background: rgba(0, 255, 65, 0.3);
animation: strongPulse 2s ease-in-out infinite;
}
@keyframes strongPulse {
0%, 100% { box-shadow: 0 0 10px #00ff41; }
50% { box-shadow: 0 0 30px #00ff41, 0 0 40px #00ff41; }
}
.logs-panel {
background: rgba(0, 0, 0, 0.9);
border: 1px solid #00ff41;
border-radius: 8px;
padding: 15px;
margin: 20px 0;
height: 200px;
overflow-y: auto;
font-size: 12px;
}
.log-entry {
margin: 5px 0;
padding: 2px 0;
}
.log-timestamp {
color: #888;
margin-right: 10px;
}
.log-info { color: #00ff41; }
.log-warning { color: #ffff00; }
.log-error { color: #ff0000; }
.footer {
text-align: center;
margin-top: 40px;
padding: 20px;
border-top: 1px solid #00ff41;
color: #888;
}
.loading {
display: inline-block;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.metrics-bar {
background: rgba(0, 0, 0, 0.5);
height: 20px;
border-radius: 10px;
margin: 10px 0;
overflow: hidden;
border: 1px solid #00ff41;
}
.metrics-fill {
height: 100%;
background: linear-gradient(90deg, #00ff41, #00cc33);
transition: width 0.5s ease;
}
/* Responsive design */
@media (max-width: 768px) {
.status-grid {
grid-template-columns: 1fr;
}
.header h1 {
font-size: 1.8em;
}
.ascii-display {
font-size: 9px;
}
.control-panel {
justify-content: center;
}
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>MY NETWORK MONITOR</h1>
<p>Distributed Content Protocol v2.0</p>
<p id="last-update">Last Update: <span id="timestamp">Loading...</span></p>
</div>
<div class="control-panel">
<button class="btn" onclick="refreshData()">
<span id="refresh-icon">🔄</span> REFRESH
</button>
<button class="btn" onclick="toggleASCII()">
📊 ASCII VIEW
</button>
<button class="btn" onclick="startSync()">
⚡ START SYNC
</button>
<button class="btn" onclick="showLogs()">
📋 LOGS
</button>
</div>
<div class="status-grid">
<div class="status-card">
<h3>🖥️ Node Status</h3>
<div class="status-item">
<span>Node ID:</span>
<span class="status-value" id="node-id">Loading...</span>
</div>
<div class="status-item">
<span>Status:</span>
<span class="status-value" id="node-status">Loading...</span>
</div>
<div class="status-item">
<span>Uptime:</span>
<span class="status-value" id="node-uptime">Loading...</span>
</div>
<div class="status-item">
<span>Version:</span>
<span class="status-value" id="node-version">Loading...</span>
</div>
</div>
<div class="status-card">
<h3>🌐 Network Status</h3>
<div class="status-item">
<span>Connected Peers:</span>
<span class="status-value" id="peer-count">Loading...</span>
</div>
<div class="status-item">
<span>Known Nodes:</span>
<span class="status-value" id="known-nodes">Loading...</span>
</div>
<div class="status-item">
<span>Network Health:</span>
<span class="status-value" id="network-health">Loading...</span>
</div>
<div class="metrics-bar">
<div class="metrics-fill" id="network-bar" style="width: 0%"></div>
</div>
</div>
<div class="status-card">
<h3>⚡ Sync Engine</h3>
<div class="status-item">
<span>Sync Status:</span>
<span class="status-value" id="sync-status">Loading...</span>
</div>
<div class="status-item">
<span>Active Syncs:</span>
<span class="status-value" id="active-syncs">Loading...</span>
</div>
<div class="status-item">
<span>Queue Size:</span>
<span class="status-value" id="queue-size">Loading...</span>
</div>
<div class="status-item">
<span>Workers:</span>
<span class="status-value" id="workers-count">Loading...</span>
</div>
</div>
<div class="status-card">
<h3>📊 Content Stats</h3>
<div class="status-item">
<span>Total Items:</span>
<span class="status-value" id="content-items">Loading...</span>
</div>
<div class="status-item">
<span>Total Size:</span>
<span class="status-value" id="content-size">Loading...</span>
</div>
<div class="status-item">
<span>Replicated:</span>
<span class="status-value" id="replicated-count">Loading...</span>
</div>
<div class="metrics-bar">
<div class="metrics-fill" id="storage-bar" style="width: 0%"></div>
</div>
</div>
</div>
<div id="ascii-container" class="ascii-display" style="display: none;">
<div id="ascii-content">Loading ASCII status...</div>
</div>
<div class="network-topology" id="topology-view">
<h3>🔗 Network Topology</h3>
<div id="topology-nodes">
<div class="node this-node">THIS<br>NODE</div>
</div>
</div>
<div class="logs-panel" id="logs-panel" style="display: none;">
<h3>📋 System Logs</h3>
<div id="logs-content">
<div class="log-entry log-info">
<span class="log-timestamp">{{ monitoring_data.timestamp[:19] if monitoring_data.timestamp else 'N/A' }}</span>
<span>[INFO] MY Network Monitor initialized</span>
</div>
</div>
</div>
<div class="footer">
<p>MY Network Protocol - Decentralized Content Distribution System</p>
<p>Real-time monitoring dashboard with live updates every 15 seconds</p>
</div>
</div>
<script>
let asciiVisible = false;
let logsVisible = false;
let updateInterval;
// Инициализация данных
let monitoringData = {};
function setMonitoringData(data) {
monitoringData = data;
}
function initializeData() {
if (monitoringData.status === 'online') {
updateNodeInfo(monitoringData.node_info);
updatePeersInfo(monitoringData.peers_info);
updateSyncStatus(monitoringData.sync_status);
} else {
showOfflineStatus();
}
updateTimestamp();
}
function updateNodeInfo(nodeInfo) {
document.getElementById('node-id').textContent =
nodeInfo.node_id ? nodeInfo.node_id.substring(0, 16) + '...' : 'Unknown';
const statusElement = document.getElementById('node-status');
statusElement.textContent = nodeInfo.status ? nodeInfo.status.toUpperCase() : 'UNKNOWN';
statusElement.className = 'status-value ' +
(nodeInfo.status === 'running' ? 'status-online' : 'status-offline');
const uptime = nodeInfo.uptime || 0;
const hours = Math.floor(uptime / 3600);
const minutes = Math.floor((uptime % 3600) / 60);
document.getElementById('node-uptime').textContent = `${hours}h ${minutes}m`;
document.getElementById('node-version').textContent = nodeInfo.version || 'MY Network 2.0';
}
function updatePeersInfo(peersInfo) {
const peerCount = peersInfo.peer_count || 0;
document.getElementById('peer-count').textContent = peerCount;
document.getElementById('known-nodes').textContent = peersInfo.peers ? peersInfo.peers.length : 0;
const healthElement = document.getElementById('network-health');
const health = peerCount > 0 ? 'CONNECTED' : 'ISOLATED';
healthElement.textContent = health;
healthElement.className = 'status-value ' +
(peerCount > 0 ? 'status-online' : 'status-warning');
// Обновить индикатор сети
const networkBar = document.getElementById('network-bar');
const healthPercent = Math.min(peerCount * 20, 100);
networkBar.style.width = healthPercent + '%';
// Обновить топологию
updateTopology(peersInfo.peers || []);
}
function updateSyncStatus(syncStatus) {
const isRunning = syncStatus.is_running || false;
const statusElement = document.getElementById('sync-status');
statusElement.textContent = isRunning ? 'RUNNING' : 'STOPPED';
statusElement.className = 'status-value ' +
(isRunning ? 'status-online' : 'status-offline');
document.getElementById('active-syncs').textContent = syncStatus.active_syncs || 0;
document.getElementById('queue-size').textContent = syncStatus.queue_size || 0;
document.getElementById('workers-count').textContent = syncStatus.workers_count || 0;
}
function updateTopology(peers) {
const topologyNodes = document.getElementById('topology-nodes');
// Очистить существующие ноды (кроме центральной)
const existingNodes = topologyNodes.querySelectorAll('.node:not(.this-node)');
existingNodes.forEach(node => node.remove());
// Добавить ноды пиров
peers.slice(0, 8).forEach((peer, index) => {
const node = document.createElement('div');
node.className = 'node';
node.innerHTML = peer.node_id ? peer.node_id.substring(0, 4).toUpperCase() : 'PEER';
node.style.animationDelay = (index * 0.2) + 's';
topologyNodes.appendChild(node);
});
}
function showOfflineStatus() {
document.getElementById('node-status').textContent = 'OFFLINE';
document.getElementById('node-status').className = 'status-value status-offline';
document.getElementById('network-health').textContent = 'DISCONNECTED';
document.getElementById('network-health').className = 'status-value status-offline';
document.getElementById('sync-status').textContent = 'STOPPED';
document.getElementById('sync-status').className = 'status-value status-offline';
}
function updateTimestamp() {
const now = new Date();
document.getElementById('timestamp').textContent =
now.toISOString().substring(0, 19).replace('T', ' ') + ' UTC';
}
async function refreshData() {
const refreshIcon = document.getElementById('refresh-icon');
refreshIcon.className = 'loading';
refreshIcon.textContent = '⏳';
try {
const response = await fetch('/api/my/monitor/live');
const data = await response.json();
if (data.success) {
updateNodeInfo(data.data.node_info);
updatePeersInfo({
peer_count: data.data.network_stats.connected_peers,
peers: data.data.peers
});
updateSyncStatus(data.data.sync_status);
updateTimestamp();
addLogEntry('info', 'Data refreshed successfully');
}
} catch (error) {
addLogEntry('error', 'Failed to refresh data: ' + error.message);
} finally {
refreshIcon.className = '';
refreshIcon.textContent = '🔄';
}
}
async function toggleASCII() {
asciiVisible = !asciiVisible;
const container = document.getElementById('ascii-container');
if (asciiVisible) {
container.style.display = 'block';
try {
const response = await fetch('/api/my/monitor/ascii');
const data = await response.json();
document.getElementById('ascii-content').textContent = data.ascii;
} catch (error) {
document.getElementById('ascii-content').textContent = 'Error loading ASCII view';
}
} else {
container.style.display = 'none';
}
}
async function startSync() {
try {
const response = await fetch('/api/my/sync/start', { method: 'POST' });
const data = await response.json();
if (data.success) {
addLogEntry('info', 'Network sync started');
setTimeout(refreshData, 1000); // Обновить через секунду
} else {
addLogEntry('error', 'Failed to start sync');
}
} catch (error) {
addLogEntry('error', 'Sync request failed: ' + error.message);
}
}
function showLogs() {
logsVisible = !logsVisible;
const logsPanel = document.getElementById('logs-panel');
logsPanel.style.display = logsVisible ? 'block' : 'none';
}
function addLogEntry(level, message) {
const logsContent = document.getElementById('logs-content');
const logEntry = document.createElement('div');
logEntry.className = `log-entry log-${level}`;
const timestamp = new Date().toISOString().substring(11, 19);
logEntry.innerHTML = `
<span class="log-timestamp">${timestamp}</span>
<span>[${level.toUpperCase()}] ${message}</span>
`;
logsContent.appendChild(logEntry);
logsContent.scrollTop = logsContent.scrollHeight;
// Ограничить количество логов
if (logsContent.children.length > 50) {
logsContent.removeChild(logsContent.firstChild);
}
}
// Автоматическое обновление каждые 15 секунд
function startAutoUpdate() {
updateInterval = setInterval(refreshData, 15000);
}
function stopAutoUpdate() {
if (updateInterval) {
clearInterval(updateInterval);
}
}
// Инициализация при загрузке страницы
document.addEventListener('DOMContentLoaded', function() {
// Получить данные из data-атрибута
const dataElement = document.getElementById('monitoring-data');
if (dataElement) {
try {
const data = JSON.parse(dataElement.textContent);
setMonitoringData(data);
} catch (e) {
console.error('Error parsing monitoring data:', e);
setMonitoringData({status: 'offline', error: 'Data parsing failed'});
}
}
initializeData();
startAutoUpdate();
addLogEntry('info', 'MY Network Monitor loaded');
});
// Остановить обновления при уходе со страницы
window.addEventListener('beforeunload', stopAutoUpdate);
</script>
<!-- Данные мониторинга -->
<script type="application/json" id="monitoring-data">{{ monitoring_data | tojson }}</script>
</body>
</html>