diff --git a/api/index.js b/api/index.js index 6cae1cf..cfedeb5 100644 --- a/api/index.js +++ b/api/index.js @@ -6206,7 +6206,7 @@ let ipBansCachedResults = null; // Cache last fetched results for client-side fi // Load IP bans from database async function loadIpBans() { - const query = "SELECT ip, failure_count, ban_count, banned_until, first_failure, has_authed_successfully, last_success_at, total_connections, total_failures, total_successes, first_seen FROM ip_bans ORDER BY banned_until DESC, total_failures DESC"; + const query = "SELECT ip, failure_count, ban_count, banned_until, first_failure, has_authed_successfully, last_success_at, total_connections, total_failures, total_successes, first_seen, idle_failure_count, idle_ban_count, idle_banned_until FROM ip_bans ORDER BY MAX(banned_until, idle_banned_until) DESC, total_failures DESC"; await executeSqlQueryRaw(query, 'ip_bans_load'); } @@ -6255,20 +6255,22 @@ function handleIpBansResponse(responseData) { let currentlyBanned = 0; let totalBans = 0; + // Helper: is this row currently banned (auth or idle) + const isRowBanned = row => (row.banned_until > now) || (row.idle_banned_until > now); + const isRowExpired = row => !isRowBanned(row) && ((row.ban_count > 0) || (row.idle_ban_count > 0)); + // Filter results based on current filter let filteredResults = results; if (ipBansFilter === 'banned') { - filteredResults = results.filter(row => row.banned_until > now); + filteredResults = results.filter(isRowBanned); } else if (ipBansFilter === 'expired') { - filteredResults = results.filter(row => row.banned_until <= now && row.banned_until > 0); + filteredResults = results.filter(isRowExpired); } - // Calculate stats + // Calculate stats (include both auth and idle bans) results.forEach(row => { - totalBans += parseInt(row.ban_count || 0); - if (row.banned_until > now) { - currentlyBanned++; - } + totalBans += parseInt(row.ban_count || 0) + parseInt(row.idle_ban_count || 0); + if (isRowBanned(row)) currentlyBanned++; }); // Update stats @@ -6286,17 +6288,28 @@ function handleIpBansResponse(responseData) { // Render table const tbody = document.getElementById('ip-bans-tbody'); tbody.innerHTML = filteredResults.map(row => { - const isBanned = row.banned_until > now; + const isBanned = isRowBanned(row); + const isIdleBanned = row.idle_banned_until > now; + const isAuthBanned = row.banned_until > now; const isWhitelisted = whitelistedIPs.has(row.ip); - const status = isWhitelisted ? '⭐ Whitelisted' : (isBanned ? '🔴 Banned' : (row.ban_count > 0 ? '🟡 Expired' : '🟢 Clean')); - const bannedUntil = row.banned_until > 0 ? new Date(row.banned_until * 1000).toLocaleString() : '-'; + let statusLabel; + if (isWhitelisted) statusLabel = '⭐ Whitelisted'; + else if (isAuthBanned && isIdleBanned) statusLabel = '🔴 Banned (auth+idle)'; + else if (isAuthBanned) statusLabel = '🔴 Banned (auth)'; + else if (isIdleBanned) statusLabel = '🔴 Banned (idle)'; + else if ((row.ban_count > 0) || (row.idle_ban_count > 0)) statusLabel = '🟡 Expired'; + else statusLabel = '🟢 Clean'; + + // Show the later of the two ban expiry times + const effectiveBannedUntil = Math.max(row.banned_until || 0, row.idle_banned_until || 0); + const bannedUntil = effectiveBannedUntil > 0 ? new Date(effectiveBannedUntil * 1000).toLocaleString() : '-'; const failures = row.total_failures || 0; const authedSuccessfully = row.has_authed_successfully ? '✅ Yes' : '❌ No'; const connectionAttempts = row.total_connections || 0; return `