chore(release): 0.5.0 #13

Merged
pufereq merged 33 commits from release/0.5.0 into main 2026-02-28 22:48:50 +00:00
13 changed files with 769 additions and 403 deletions
Showing only changes of commit c5771dc371 - Show all commits

View File

@@ -13,7 +13,6 @@
<script> <script>
$(document).ready(function () { $(document).ready(function () {
const socket = io(); const socket = io();
const dataElement = $("#data");
const showNotify = (message) => { const showNotify = (message) => {
$("#notify").stop().fadeIn(); $("#notify").stop().fadeIn();
@@ -23,47 +22,161 @@
}, 3000); }, 3000);
}; };
const loadClientDetails = (clientId) => {
fetch(`/client/${clientId}`)
.then((response) => response.text())
.then((html) => {
$("#content").html(html);
})
.catch((error) => {
console.error("Error fetching client details:", error);
});
};
// load client_details for the client specified in the URL hash
const hash = window.location.hash;
if (hash) {
const clientId = hash.substring(1);
loadClientDetails(clientId);
}
$("#notify").hide(); $("#notify").hide();
socket.on("connect", () => { socket.on("connect", () => {
console.log("Connected to server"); console.log("Connected to server");
$("#no-connection-icon").hide(); $("#no-connection-message").hide();
showNotify("Connected to server"); showNotify("Connected to server");
}); });
socket.on("disconnect", () => { socket.on("disconnect", () => {
console.log("Disconnected from server"); console.log("Disconnected from server");
$("#no-connection-icon").show(); $("#no-connection-message").show();
showNotify("Disconnected from server"); showNotify("Disconnected from server");
}); });
socket.on("update_data", (data) => { socket.on("update_data", (data) => {
console.log("Received data:", data);
document.getElementById("data").innerHTML = JSON.stringify(
data,
null,
2,
);
// Update client list
const clientList = $("#client-list"); const clientList = $("#client-list");
const existingItems = {};
clientList.empty(); // Index current <li> by clientId
Object.entries(data).forEach(([clientId, client]) => { clientList.children("li").each(function () {
const listItem = document.createElement("li"); const a = $(this).find("a");
if (a.length) {
const iconElement = document.createElement("i"); const clientId = a.attr("href").substring(1);
iconElement.classList.add("fi", "fi-sr-play"); existingItems[clientId] = $(this);
iconElement.style.color = "var(--ctp-green)"; }
listItem.appendChild(iconElement);
const spanElement = document.createElement("a");
spanElement.appendChild(document.createTextNode(clientId));
spanElement.href = `#${clientId}`;
listItem.appendChild(spanElement);
clientList.append(listItem);
}); });
// Track which clientIds are still present
const seen = new Set();
Object.entries(data).forEach(([clientId, client]) => {
seen.add(clientId);
// Build icon
const iconElement = document.createElement("i");
switch (client.status) {
case "online":
iconElement.className = "fi fi-sr-play";
iconElement.style.color = "var(--ctp-green)";
break;
case "offline":
iconElement.className = "fi fi-sr-stop";
iconElement.style.color = "var(--ctp-red)";
break;
case "pending":
iconElement.className = "fi fi-sr-pending";
iconElement.style.color = "var(--ctp-yellow)";
break;
case "stale":
iconElement.className = "fi fi-sr-skull";
iconElement.style.color = "var(--ctp-orange)";
break;
default:
iconElement.className = "fi fi-rr-question";
iconElement.style.color = "var(--ctp-gray)";
}
// Time since last seen
const lastSeen = new Date(client.last_seen * 1000);
const now = new Date();
const timeSinceLastSeen = Math.floor((now - lastSeen) / 1000);
const days = Math.floor(timeSinceLastSeen / 86400);
const hours = Math.floor((timeSinceLastSeen % 86400) / 3600);
const minutes = Math.floor((timeSinceLastSeen % 3600) / 60);
const seconds = timeSinceLastSeen % 60;
let timeSinceLastSeenText = "";
if (days > 0) timeSinceLastSeenText += `${days}d `;
if (hours > 0) timeSinceLastSeenText += `${hours}h `;
if (minutes > 0) timeSinceLastSeenText += `${minutes}m `;
if (seconds > 0) timeSinceLastSeenText += `${seconds}s`;
timeSinceLastSeenText = timeSinceLastSeenText.trim() || "0s";
let statusText = `${client.id} (${timeSinceLastSeenText})`;
// check if <li> exists
if (existingItems[clientId]) {
// update if needed
const li = existingItems[clientId];
const a = li.find("a");
if (a.text() !== statusText) {
a.text(statusText);
}
li.attr(
"title",
`Status: ${client.status}\nLast Seen: ${lastSeen.toISOString()}`,
);
// update icon
const icon = li.find("i")[0];
if (icon) {
icon.className = iconElement.className;
icon.style.color = iconElement.style.color;
}
} else {
// add new <li>
const li = $("<li></li>");
li.append(iconElement);
const a = $("<a></a>")
.text(statusText)
.attr("href", `#${clientId}`);
li.attr(
"title",
`Status: ${client.status}\nLast Seen: ${lastSeen.toISOString()}`,
);
li.append(a);
clientList.append(li);
}
});
// Remove <li> for clients no longer present
Object.keys(existingItems).forEach((clientId) => {
if (!seen.has(clientId)) {
existingItems[clientId].remove();
}
});
// Re-bind click handlers
$("#client-list li > a")
.off("click")
.on("click", function (e) {
const href = $(this).attr("href");
if (href.startsWith("#")) {
const clientId = href.substring(1);
loadClientDetails(clientId);
$("#client-list li > a").removeClass("active");
$(this).addClass("active");
}
});
if (window.location.hash) {
const clientId = window.location.hash.substring(1);
loadClientDetails(clientId);
}
}); });
}); });
</script> </script>
@@ -72,21 +185,18 @@
<div id="wrapper"> <div id="wrapper">
<header> <header>
<h2><a href="{{ url_for('index.index') }}">judas</a></h2> <h2><a href="{{ url_for('index.index') }}">judas</a></h2>
<p> <p id="no-connection-message">
<span id="no-connection-icon" style="display: none"> <i class="fi fi-rr-link-slash"></i>
<i class="fi fi-rr-link-slash"></i> <span> No connection to server </span>
</span>
<a class="button" href="{{ url_for('auth.logout') }}">Logout</a>
</p> </p>
<a class="button" href="{{ url_for('auth.logout') }}">Logout</a>
</header> </header>
<div id="notify"></div> <div id="notify"></div>
<main> <main>
<aside> <aside>
<ul id="client-list"></ul> <ul id="client-list"></ul>
</aside> </aside>
<div id="content"> <div id="content"></div>
<pre id="data"></pre>
</div>
</main> </main>
</div> </div>
</body> </body>