added server tracking plus server metrics

This commit is contained in:
erik 2025-06-20 07:17:01 +00:00
parent 80a0a16bab
commit ca12f4807b
5 changed files with 567 additions and 6 deletions

View file

@ -14,10 +14,23 @@
<!-- Sidebar for active players list and filters -->
<aside id="sidebar">
<!-- Container for sort and filter controls -->
<div id="sortButtons" class="sort-buttons"></div>
<h2 id="activePlayersHeader">Active Mosswart Enjoyers</h2>
<!-- Server Status -->
<div id="serverStatus" class="server-status-container">
<h3>Coldeve Server Status</h3>
<div class="status-indicator">
<span class="status-dot" id="statusDot"></span>
<span id="statusText">Checking...</span>
</div>
<div class="status-details">
<div>Players: <span id="playerCount">-</span></div>
<div>Latency: <span id="latencyMs">-</span> ms</div>
<div>Uptime: <span id="uptime">-</span></div>
<div>Last Restart: <span id="lastRestart">-</span></div>
</div>
</div>
<!-- Total rares counter -->
<div id="totalRaresCounter" class="total-rares-counter">
🔥 Total Rares: <span id="totalRaresCount">Loading...</span>
@ -60,6 +73,9 @@
</a>
</div>
<!-- Container for sort and filter controls -->
<div id="sortButtons" class="sort-buttons"></div>
<!-- Text input to filter active players by name -->
<input type="text" id="playerFilter" class="player-filter" placeholder="Filter players..." />

View file

@ -1067,13 +1067,87 @@ function updateTotalRaresDisplay(data) {
}
}
async function pollServerHealth() {
try {
const response = await fetch(`${API_BASE}/server-health`);
const data = await response.json();
updateServerStatusDisplay(data);
} catch (e) {
console.error('Server health fetch failed:', e);
updateServerStatusDisplay({ status: 'error' });
}
}
function updateServerStatusDisplay(data) {
const statusDot = document.getElementById('statusDot');
const statusText = document.getElementById('statusText');
const playerCount = document.getElementById('playerCount');
const latencyMs = document.getElementById('latencyMs');
const uptime = document.getElementById('uptime');
const lastRestart = document.getElementById('lastRestart');
if (!statusDot || !statusText) return;
// Update status indicator
const status = data.status || 'unknown';
statusDot.className = `status-dot status-${status}`;
statusText.textContent = status.charAt(0).toUpperCase() + status.slice(1);
// Update player count
if (playerCount) {
playerCount.textContent = data.player_count !== null && data.player_count !== undefined ? data.player_count : '-';
}
// Update latency
if (latencyMs) {
latencyMs.textContent = data.latency_ms ? Math.round(data.latency_ms) : '-';
}
// Update uptime
if (uptime) {
uptime.textContent = data.uptime || '-';
}
// Update last restart with Stockholm timezone (24h format, no year)
if (lastRestart) {
if (data.last_restart) {
const restartDate = new Date(data.last_restart);
const formattedDate = restartDate.toLocaleString('sv-SE', {
timeZone: 'Europe/Stockholm',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
hour12: false
});
lastRestart.textContent = formattedDate;
} else {
lastRestart.textContent = 'Unknown';
}
}
}
function handleServerStatusUpdate(msg) {
// Handle real-time server status updates via WebSocket
if (msg.status === 'up' && msg.message) {
// Show notification for server coming back online
console.log(`Server Status: ${msg.message}`);
}
// Trigger an immediate server health poll to refresh the display
pollServerHealth();
}
function startPolling() {
if (pollID !== null) return;
pollLive();
pollTotalRares(); // Initial fetch
pollServerHealth(); // Initial server health check
pollID = setInterval(pollLive, POLL_MS);
// Poll total rares every 5 minutes (300,000 ms)
setInterval(pollTotalRares, 300000);
// Poll server health every 30 seconds (30,000 ms)
setInterval(pollServerHealth, 30000);
}
img.onload = () => {
@ -1091,6 +1165,12 @@ img.onload = () => {
initHeatMap();
};
// Ensure server health polling starts regardless of image loading
document.addEventListener('DOMContentLoaded', () => {
// Start server health polling immediately on DOM ready
pollServerHealth();
});
/* ---------- rendering sorted list & dots ------------------------ */
/**
* Filter and sort the currentPlayers, then render them.
@ -1293,6 +1373,8 @@ function initWebSocket() {
updateVitalsDisplay(msg);
} else if (msg.type === 'rare') {
triggerEpicRareNotification(msg.character_name, msg.name);
} else if (msg.type === 'server_status') {
handleServerStatusUpdate(msg);
}
});
socket.addEventListener('close', () => setTimeout(initWebSocket, 2000));

View file

@ -203,6 +203,98 @@ body {
to { text-shadow: 0 0 18px rgba(255, 255, 255, 0.9), 0 0 25px rgba(136, 102, 255, 0.5); }
}
/* Server Status Styling */
.server-status-container {
margin: 0 0 16px 0;
padding: 12px;
background: linear-gradient(135deg, #2a4a2a, #1a3a1a);
border: 2px solid #44aa44;
border-radius: 8px;
box-shadow: 0 3px 8px rgba(0, 0, 0, 0.4);
}
.server-status-container h3 {
margin: 0 0 10px 0;
font-size: 1.1rem;
color: #aaffaa;
text-align: center;
font-weight: 600;
}
.status-indicator {
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 8px;
font-weight: 600;
font-size: 1rem;
}
.status-dot {
width: 12px;
height: 12px;
border-radius: 50%;
margin-right: 8px;
box-shadow: 0 0 6px rgba(0, 0, 0, 0.3);
}
.status-dot.status-up {
background-color: #44ff44;
box-shadow: 0 0 8px rgba(68, 255, 68, 0.6);
animation: status-pulse-up 2s ease-in-out infinite;
}
.status-dot.status-down {
background-color: #ff4444;
box-shadow: 0 0 8px rgba(255, 68, 68, 0.6);
animation: status-pulse-down 2s ease-in-out infinite;
}
.status-dot.status-unknown,
.status-dot.status-error {
background-color: #ffaa44;
box-shadow: 0 0 8px rgba(255, 170, 68, 0.6);
}
@keyframes status-pulse-up {
0%, 100% {
box-shadow: 0 0 8px rgba(68, 255, 68, 0.6);
}
50% {
box-shadow: 0 0 16px rgba(68, 255, 68, 0.9);
}
}
@keyframes status-pulse-down {
0%, 100% {
box-shadow: 0 0 8px rgba(255, 68, 68, 0.6);
}
50% {
box-shadow: 0 0 16px rgba(255, 68, 68, 0.9);
}
}
.status-details {
font-size: 0.85rem;
color: #ccc;
line-height: 1.6;
display: grid;
grid-template-columns: 1fr 1fr;
gap: 8px 16px;
}
.status-details div {
display: flex;
align-items: center;
white-space: nowrap;
}
.status-details span {
color: #fff;
font-weight: 500;
margin-left: 6px;
}
.total-kills-counter {
margin: 0 0 12px 0;
padding: 8px 12px;