User Log and Persistent Faction Information
This commit is contained in:
211
static/users_log.js
Normal file
211
static/users_log.js
Normal file
@@ -0,0 +1,211 @@
|
||||
// users_log.js - Activity log and active users display
|
||||
|
||||
let logsPollingInterval = null;
|
||||
let usersPollingInterval = null;
|
||||
|
||||
async function fetchActiveUsers() {
|
||||
try {
|
||||
const res = await fetch("/api/active_users", { cache: "no-store" });
|
||||
if (!res.ok) {
|
||||
console.error("Failed to fetch active users:", res.status);
|
||||
return;
|
||||
}
|
||||
|
||||
const data = await res.json();
|
||||
displayActiveUsers(data.users || []);
|
||||
} catch (err) {
|
||||
console.error("Error fetching active users:", err);
|
||||
}
|
||||
}
|
||||
|
||||
function displayActiveUsers(users) {
|
||||
const container = document.getElementById("active-users-list");
|
||||
|
||||
if (users.length === 0) {
|
||||
container.innerHTML = '<div class="no-users">No active users</div>';
|
||||
return;
|
||||
}
|
||||
|
||||
container.innerHTML = users.map(username => `
|
||||
<div class="user-item">
|
||||
<span class="user-indicator"></span>
|
||||
<span class="user-name">${escapeHtml(username)}</span>
|
||||
</div>
|
||||
`).join('');
|
||||
}
|
||||
|
||||
async function fetchActivityLogs() {
|
||||
try {
|
||||
const res = await fetch("/api/activity_logs?limit=200", { cache: "no-store" });
|
||||
if (!res.ok) {
|
||||
console.error("Failed to fetch activity logs:", res.status);
|
||||
return;
|
||||
}
|
||||
|
||||
const data = await res.json();
|
||||
displayActivityLogs(data.logs || []);
|
||||
} catch (err) {
|
||||
console.error("Error fetching activity logs:", err);
|
||||
}
|
||||
}
|
||||
|
||||
function displayActivityLogs(logs) {
|
||||
const container = document.getElementById("activity-log-container");
|
||||
|
||||
if (logs.length === 0) {
|
||||
container.innerHTML = '<div class="no-logs">No activity logs yet</div>';
|
||||
return;
|
||||
}
|
||||
|
||||
container.innerHTML = logs.map(log => {
|
||||
const time = formatTimestamp(log.timestamp);
|
||||
const details = log.details ? ` - ${escapeHtml(log.details)}` : '';
|
||||
|
||||
return `
|
||||
<div class="log-entry">
|
||||
<span class="log-time">${time}</span>
|
||||
<span class="log-user">${escapeHtml(log.username)}</span>
|
||||
<span class="log-action">${escapeHtml(log.action)}</span>
|
||||
${details ? `<span class="log-details">${details}</span>` : ''}
|
||||
</div>
|
||||
`;
|
||||
}).join('');
|
||||
|
||||
// Auto-scroll to bottom on first load
|
||||
if (!container.hasAttribute('data-scrolled')) {
|
||||
container.scrollTop = container.scrollHeight;
|
||||
container.setAttribute('data-scrolled', 'true');
|
||||
}
|
||||
}
|
||||
|
||||
function formatTimestamp(isoString) {
|
||||
const date = new Date(isoString);
|
||||
const hours = String(date.getHours()).padStart(2, '0');
|
||||
const minutes = String(date.getMinutes()).padStart(2, '0');
|
||||
const seconds = String(date.getSeconds()).padStart(2, '0');
|
||||
return `${hours}:${minutes}:${seconds}`;
|
||||
}
|
||||
|
||||
function escapeHtml(text) {
|
||||
const div = document.createElement('div');
|
||||
div.textContent = text;
|
||||
return div.innerHTML;
|
||||
}
|
||||
|
||||
async function copyLogsToClipboard() {
|
||||
try {
|
||||
const res = await fetch("/api/activity_logs?limit=1000", { cache: "no-store" });
|
||||
if (!res.ok) {
|
||||
alert("Failed to fetch logs");
|
||||
return;
|
||||
}
|
||||
|
||||
const data = await res.json();
|
||||
const logs = data.logs || [];
|
||||
|
||||
if (logs.length === 0) {
|
||||
alert("No logs to copy");
|
||||
return;
|
||||
}
|
||||
|
||||
// Format logs as plain text
|
||||
const logText = logs.map(log => {
|
||||
const time = formatTimestamp(log.timestamp);
|
||||
const details = log.details ? ` - ${log.details}` : '';
|
||||
return `[${time}] ${log.username}: ${log.action}${details}`;
|
||||
}).join('\n');
|
||||
|
||||
// Copy to clipboard
|
||||
await navigator.clipboard.writeText(logText);
|
||||
|
||||
// Show success feedback
|
||||
const btn = document.getElementById("copy-log-btn");
|
||||
const originalText = btn.textContent;
|
||||
btn.textContent = "Copied!";
|
||||
btn.style.backgroundColor = "#4CAF50";
|
||||
|
||||
setTimeout(() => {
|
||||
btn.textContent = originalText;
|
||||
btn.style.backgroundColor = "";
|
||||
}, 2000);
|
||||
} catch (err) {
|
||||
console.error("Error copying logs:", err);
|
||||
alert("Failed to copy logs to clipboard");
|
||||
}
|
||||
}
|
||||
|
||||
function startPolling() {
|
||||
// Fetch immediately
|
||||
fetchActiveUsers();
|
||||
fetchActivityLogs();
|
||||
|
||||
// Then poll at intervals
|
||||
usersPollingInterval = setInterval(fetchActiveUsers, 10000); // Every 10 seconds
|
||||
logsPollingInterval = setInterval(fetchActivityLogs, 5000); // Every 5 seconds
|
||||
}
|
||||
|
||||
function stopPolling() {
|
||||
if (usersPollingInterval) {
|
||||
clearInterval(usersPollingInterval);
|
||||
usersPollingInterval = null;
|
||||
}
|
||||
if (logsPollingInterval) {
|
||||
clearInterval(logsPollingInterval);
|
||||
logsPollingInterval = null;
|
||||
}
|
||||
}
|
||||
|
||||
async function handleLogout() {
|
||||
console.log("handleLogout called");
|
||||
try {
|
||||
console.log("Sending logout request to /auth/logout");
|
||||
const response = await fetch("/auth/logout", {
|
||||
method: "POST"
|
||||
});
|
||||
console.log("Logout response status:", response.status);
|
||||
|
||||
if (response.ok) {
|
||||
console.log("Logout successful, redirecting to /login");
|
||||
window.location.href = "/login";
|
||||
} else {
|
||||
console.error("Logout failed with status:", response.status);
|
||||
window.location.href = "/login";
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error during logout:", error);
|
||||
window.location.href = "/login";
|
||||
}
|
||||
}
|
||||
|
||||
function wireUp() {
|
||||
// Attach copy button handler
|
||||
const copyBtn = document.getElementById("copy-log-btn");
|
||||
if (copyBtn) {
|
||||
copyBtn.addEventListener("click", copyLogsToClipboard);
|
||||
}
|
||||
|
||||
// Attach logout handler
|
||||
const logoutBtn = document.getElementById("logout-btn");
|
||||
if (logoutBtn) {
|
||||
logoutBtn.addEventListener("click", handleLogout);
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize when page loads
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
wireUp();
|
||||
startPolling();
|
||||
});
|
||||
|
||||
// Stop polling when page is hidden/unloaded
|
||||
document.addEventListener("visibilitychange", () => {
|
||||
if (document.hidden) {
|
||||
stopPolling();
|
||||
} else {
|
||||
startPolling();
|
||||
}
|
||||
});
|
||||
|
||||
window.addEventListener("beforeunload", () => {
|
||||
stopPolling();
|
||||
});
|
||||
Reference in New Issue
Block a user