Added new clear function-calls for the recent-stored-events and as well the permanent-block list

This commit is contained in:
2026-02-21 17:23:33 +01:00
parent c85d51edd6
commit 3e06cfc1ab
12 changed files with 138 additions and 11 deletions

View File

@@ -555,6 +555,16 @@ func BanInsightsHandler(c *gin.Context) {
})
}
// Deletes all stored ban event records.
func ClearBanEventsHandler(c *gin.Context) {
deleted, err := storage.ClearBanEvents(c.Request.Context())
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{"deleted": deleted})
}
// =========================================================================
// Fail2ban Servers Management
// =========================================================================
@@ -1631,6 +1641,16 @@ func ListPermanentBlocksHandler(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"blocks": records})
}
// Deletes all permanent block records.
func ClearPermanentBlocksHandler(c *gin.Context) {
deleted, err := storage.ClearPermanentBlocks(c.Request.Context())
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{"deleted": deleted})
}
// Allows manual block/unblock against the configured integration.
func AdvancedActionsTestHandler(c *gin.Context) {
var req struct {

View File

@@ -80,6 +80,7 @@ func RegisterRoutes(r *gin.Engine, hub *Hub) {
// Internal API calls for advanced actions
api.GET("/advanced-actions/blocks", ListPermanentBlocksHandler)
api.DELETE("/advanced-actions/blocks", ClearPermanentBlocksHandler)
api.POST("/advanced-actions/test", AdvancedActionsTestHandler)
// Internal API calls for Fail2ban-UI server management
@@ -95,6 +96,7 @@ func RegisterRoutes(r *gin.Engine, hub *Hub) {
// Internal API calls to get the stats of the bans
api.GET("/events/bans", ListBanEventsHandler)
api.DELETE("/events/bans", ClearBanEventsHandler)
api.GET("/events/bans/stats", BanStatisticsHandler)
api.GET("/events/bans/insights", BanInsightsHandler)

View File

@@ -483,7 +483,7 @@ function renderLogOverviewContent() {
var todayCount = totalBansToday();
var weekCount = totalBansWeek();
if (statsKeys.length === 0 && totalStored === 0) {
html += '<p class="text-gray-500" data-i18n="logs.overview.empty">No ban events recorded yet.</p>';
//html += '<p class="text-gray-500" data-i18n="logs.overview.empty">No ban events recorded yet.</p>';
} else {
html += ''
+ '<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-6">'
@@ -531,7 +531,10 @@ function renderLogOverviewContent() {
}
html += ' </tbody></table></div></div>';
}
html += '<h4 class="text-md font-semibold text-gray-800 mb-3" data-i18n="logs.overview.recent_events_title">Recent stored events</h4>';
html += '<div class="flex items-center justify-between mb-3">'
+ '<h4 class="text-md font-semibold text-gray-800" data-i18n="logs.overview.recent_events_title">Recent stored events</h4>'
+ '<button type="button" class="px-3 py-1.5 text-xs rounded border border-red-300 text-red-600 hover:bg-red-50" onclick="clearStoredBanEvents()" data-i18n="logs.overview.clear_events">Clear</button>'
+ '</div>';
var countries = getBanEventCountries();
var recurringMap = getRecurringIPMap();
var searchQuery = (banEventsFilterText || '').trim();
@@ -649,6 +652,28 @@ function loadMoreBanEvents() {
});
}
function clearStoredBanEvents() {
var msg = t('logs.overview.clear_events_confirm',
'This will permanently delete all stored ban events. Statistics, insights, and the event history will be reset to zero.\n\nThis action cannot be undone. Continue?');
if (!confirm(msg)) return;
fetch('/api/events/bans', { method: 'DELETE', headers: serverHeaders() })
.then(function(res) { return res.json(); })
.then(function(data) {
if (data.error) {
showToast(data.error, 'error');
return;
}
showToast(t('logs.overview.clear_events_success', 'All stored ban events cleared.'), 'success');
latestBanEvents = [];
latestBanStats = {};
latestBanInsights = null;
banEventsTotal = 0;
banEventsHasMore = false;
renderLogOverviewSection();
})
.catch(function(err) { showToast(String(err), 'error'); });
}
// Filtering function for the banned IPs for the dashboard.
function filterIPs() {
const input = document.getElementById("ipSearch");

View File

@@ -479,6 +479,23 @@ function refreshPermanentBlockLog() {
loadPermanentBlockLog();
}
function clearPermanentBlockLog() {
var msg = t('settings.advanced.clear_log_confirm',
'This will permanently delete the entire block log. Fail2ban UI will assume that no IPs are currently blocked on the external firewall.\n\nThis action cannot be undone. Continue?');
if (!confirm(msg)) return;
fetch('/api/advanced-actions/blocks', { method: 'DELETE', headers: serverHeaders() })
.then(function(res) { return res.json(); })
.then(function(data) {
if (data.error) {
showToast(data.error, 'error');
return;
}
showToast(t('settings.advanced.clear_log_success', 'Permanent block log cleared.'), 'success');
loadPermanentBlockLog();
})
.catch(function(err) { showToast(String(err), 'error'); });
}
// =========================================================================
// Advanced Test
// =========================================================================

View File

@@ -454,7 +454,10 @@
</div>
</div>
<div class="mt-6">
<h4 class="text-md font-semibold text-gray-800 mb-2" data-i18n="settings.advanced.log_title">Permanent Block Log</h4>
<div class="flex items-center justify-between mb-2">
<h4 class="text-md font-semibold text-gray-800" data-i18n="settings.advanced.log_title">Permanent Block Log</h4>
<button type="button" class="px-3 py-1.5 text-xs rounded border border-red-300 text-red-600 hover:bg-red-50" onclick="clearPermanentBlockLog()" data-i18n="settings.advanced.clear_log">Clear</button>
</div>
<div id="permanentBlockLog" class="overflow-x-auto border border-gray-200 rounded-md">
<p class="text-sm text-gray-500 p-4" data-i18n="settings.advanced.log_empty">No permanent blocks recorded yet.</p>
</div>