mirror of
https://github.com/swissmakers/fail2ban-ui.git
synced 2026-04-17 05:53:15 +02:00
Fix loading wrong filter problem, implement creation and deletion of filters and jails, fix some css mismatches, update the handlers and routes
This commit is contained in:
@@ -24,11 +24,13 @@ function loadFilters() {
|
||||
}
|
||||
}
|
||||
select.innerHTML = '';
|
||||
const deleteBtn = document.getElementById('deleteFilterBtn');
|
||||
if (!data.filters || data.filters.length === 0) {
|
||||
const opt = document.createElement('option');
|
||||
opt.value = '';
|
||||
opt.textContent = 'No Filters Found';
|
||||
select.appendChild(opt);
|
||||
if (deleteBtn) deleteBtn.disabled = true;
|
||||
} else {
|
||||
data.filters.forEach(f => {
|
||||
const opt = document.createElement('option');
|
||||
@@ -36,6 +38,14 @@ function loadFilters() {
|
||||
opt.textContent = f;
|
||||
select.appendChild(opt);
|
||||
});
|
||||
// Add change listener if not already added
|
||||
if (!select.hasAttribute('data-listener-added')) {
|
||||
select.setAttribute('data-listener-added', 'true');
|
||||
select.addEventListener('change', function() {
|
||||
if (deleteBtn) deleteBtn.disabled = !select.value;
|
||||
});
|
||||
}
|
||||
if (deleteBtn) deleteBtn.disabled = !select.value;
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
@@ -122,11 +132,115 @@ function showFilterSection() {
|
||||
document.getElementById('logLinesTextarea').value = '';
|
||||
testResultsEl.innerHTML = '';
|
||||
testResultsEl.classList.add('hidden');
|
||||
document.getElementById('deleteFilterBtn').disabled = true;
|
||||
return;
|
||||
}
|
||||
loadFilters();
|
||||
testResultsEl.innerHTML = '';
|
||||
testResultsEl.classList.add('hidden');
|
||||
document.getElementById('logLinesTextarea').value = '';
|
||||
// Add change listener to enable/disable delete button
|
||||
const filterSelect = document.getElementById('filterSelect');
|
||||
const deleteBtn = document.getElementById('deleteFilterBtn');
|
||||
filterSelect.addEventListener('change', function() {
|
||||
deleteBtn.disabled = !filterSelect.value;
|
||||
});
|
||||
}
|
||||
|
||||
function openCreateFilterModal() {
|
||||
document.getElementById('newFilterName').value = '';
|
||||
document.getElementById('newFilterContent').value = '';
|
||||
openModal('createFilterModal');
|
||||
}
|
||||
|
||||
function createFilter() {
|
||||
const filterName = document.getElementById('newFilterName').value.trim();
|
||||
const content = document.getElementById('newFilterContent').value.trim();
|
||||
|
||||
if (!filterName) {
|
||||
showToast('Filter name is required', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
showLoading(true);
|
||||
fetch(withServerParam('/api/filters'), {
|
||||
method: 'POST',
|
||||
headers: serverHeaders({ 'Content-Type': 'application/json' }),
|
||||
body: JSON.stringify({
|
||||
filterName: filterName,
|
||||
content: content
|
||||
})
|
||||
})
|
||||
.then(function(res) {
|
||||
if (!res.ok) {
|
||||
return res.json().then(function(data) {
|
||||
throw new Error(data.error || 'Server returned ' + res.status);
|
||||
});
|
||||
}
|
||||
return res.json();
|
||||
})
|
||||
.then(function(data) {
|
||||
if (data.error) {
|
||||
showToast('Error creating filter: ' + data.error, 'error');
|
||||
return;
|
||||
}
|
||||
closeModal('createFilterModal');
|
||||
showToast(data.message || 'Filter created successfully', 'success');
|
||||
// Reload filters
|
||||
loadFilters();
|
||||
})
|
||||
.catch(function(err) {
|
||||
console.error('Error creating filter:', err);
|
||||
showToast('Error creating filter: ' + (err.message || err), 'error');
|
||||
})
|
||||
.finally(function() {
|
||||
showLoading(false);
|
||||
});
|
||||
}
|
||||
|
||||
function deleteFilter() {
|
||||
const filterName = document.getElementById('filterSelect').value;
|
||||
if (!filterName) {
|
||||
showToast('Please select a filter to delete', 'info');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!confirm('Are you sure you want to delete the filter "' + escapeHtml(filterName) + '"? This action cannot be undone.')) {
|
||||
return;
|
||||
}
|
||||
|
||||
showLoading(true);
|
||||
fetch(withServerParam('/api/filters/' + encodeURIComponent(filterName)), {
|
||||
method: 'DELETE',
|
||||
headers: serverHeaders()
|
||||
})
|
||||
.then(function(res) {
|
||||
if (!res.ok) {
|
||||
return res.json().then(function(data) {
|
||||
throw new Error(data.error || 'Server returned ' + res.status);
|
||||
});
|
||||
}
|
||||
return res.json();
|
||||
})
|
||||
.then(function(data) {
|
||||
if (data.error) {
|
||||
showToast('Error deleting filter: ' + data.error, 'error');
|
||||
return;
|
||||
}
|
||||
showToast(data.message || 'Filter deleted successfully', 'success');
|
||||
// Reload filters
|
||||
loadFilters();
|
||||
// Clear test results
|
||||
document.getElementById('testResults').innerHTML = '';
|
||||
document.getElementById('testResults').classList.add('hidden');
|
||||
document.getElementById('logLinesTextarea').value = '';
|
||||
})
|
||||
.catch(function(err) {
|
||||
console.error('Error deleting filter:', err);
|
||||
showToast('Error deleting filter: ' + (err.message || err), 'error');
|
||||
})
|
||||
.finally(function() {
|
||||
showLoading(false);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -56,6 +56,22 @@ function openJailConfigModal(jailName) {
|
||||
filterTextArea.value = data.filter || '';
|
||||
jailTextArea.value = data.jailConfig || '';
|
||||
|
||||
// Display file paths if available
|
||||
var filterFilePathEl = document.getElementById('filterFilePath');
|
||||
var jailFilePathEl = document.getElementById('jailFilePath');
|
||||
if (filterFilePathEl && data.filterFilePath) {
|
||||
filterFilePathEl.textContent = data.filterFilePath;
|
||||
filterFilePathEl.style.display = 'block';
|
||||
} else if (filterFilePathEl) {
|
||||
filterFilePathEl.style.display = 'none';
|
||||
}
|
||||
if (jailFilePathEl && data.jailFilePath) {
|
||||
jailFilePathEl.textContent = data.jailFilePath;
|
||||
jailFilePathEl.style.display = 'block';
|
||||
} else if (jailFilePathEl) {
|
||||
jailFilePathEl.style.display = 'none';
|
||||
}
|
||||
|
||||
// Check if logpath is set in jail config and show test button
|
||||
updateLogpathButtonVisibility();
|
||||
|
||||
@@ -267,9 +283,17 @@ function openManageJailsModal() {
|
||||
+ ' onclick="openJailConfigModal(\'' + jsEscapedJailName + '\')"'
|
||||
+ ' class="text-xs px-3 py-1.5 bg-blue-500 text-white rounded hover:bg-blue-600 transition-colors whitespace-nowrap"'
|
||||
+ ' data-i18n="modal.filter_config_edit"'
|
||||
+ ' title="' + escapeHtml(t('modal.filter_config_edit', 'Edit Filter')) + '"'
|
||||
+ ' title="' + escapeHtml(t('modal.filter_config_edit', 'Edit Filter / Jail')) + '"'
|
||||
+ ' >'
|
||||
+ escapeHtml(t('modal.filter_config_edit', 'Edit Filter'))
|
||||
+ escapeHtml(t('modal.filter_config_edit', 'Edit Filter / Jail'))
|
||||
+ ' </button>'
|
||||
+ ' <button'
|
||||
+ ' type="button"'
|
||||
+ ' onclick="deleteJail(\'' + jsEscapedJailName + '\')"'
|
||||
+ ' class="text-xs px-3 py-1.5 bg-red-500 text-white rounded hover:bg-red-600 transition-colors whitespace-nowrap"'
|
||||
+ ' title="' + escapeHtml(t('modal.delete_jail', 'Delete Jail')) + '"'
|
||||
+ ' >'
|
||||
+ ' <i class="fas fa-trash"></i>'
|
||||
+ ' </button>'
|
||||
+ ' <label class="inline-flex relative items-center cursor-pointer">'
|
||||
+ ' <input'
|
||||
@@ -428,3 +452,152 @@ function saveManageJailsSingle(checkbox) {
|
||||
});
|
||||
}
|
||||
|
||||
function openCreateJailModal() {
|
||||
document.getElementById('newJailName').value = '';
|
||||
document.getElementById('newJailContent').value = '';
|
||||
const filterSelect = document.getElementById('newJailFilter');
|
||||
if (filterSelect) {
|
||||
filterSelect.value = '';
|
||||
}
|
||||
|
||||
// Load filters into dropdown
|
||||
showLoading(true);
|
||||
fetch(withServerParam('/api/filters'), {
|
||||
headers: serverHeaders()
|
||||
})
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
if (filterSelect) {
|
||||
filterSelect.innerHTML = '<option value="">-- Select a filter --</option>';
|
||||
if (data.filters && data.filters.length > 0) {
|
||||
data.filters.forEach(filter => {
|
||||
const opt = document.createElement('option');
|
||||
opt.value = filter;
|
||||
opt.textContent = filter;
|
||||
filterSelect.appendChild(opt);
|
||||
});
|
||||
}
|
||||
}
|
||||
openModal('createJailModal');
|
||||
})
|
||||
.catch(err => {
|
||||
console.error('Error loading filters:', err);
|
||||
openModal('createJailModal');
|
||||
})
|
||||
.finally(() => showLoading(false));
|
||||
}
|
||||
|
||||
function updateJailConfigFromFilter() {
|
||||
const filterSelect = document.getElementById('newJailFilter');
|
||||
const jailNameInput = document.getElementById('newJailName');
|
||||
const contentTextarea = document.getElementById('newJailContent');
|
||||
|
||||
if (!filterSelect || !contentTextarea) return;
|
||||
|
||||
const selectedFilter = filterSelect.value;
|
||||
|
||||
if (!selectedFilter) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Auto-fill jail name if empty
|
||||
if (jailNameInput && !jailNameInput.value.trim()) {
|
||||
jailNameInput.value = selectedFilter;
|
||||
}
|
||||
|
||||
// Auto-populate jail config
|
||||
const jailName = (jailNameInput && jailNameInput.value.trim()) || selectedFilter;
|
||||
const config = `[${jailName}]
|
||||
enabled = false
|
||||
filter = ${selectedFilter}
|
||||
logpath = /var/log/auth.log
|
||||
maxretry = 5
|
||||
bantime = 3600
|
||||
findtime = 600`;
|
||||
|
||||
contentTextarea.value = config;
|
||||
}
|
||||
|
||||
function createJail() {
|
||||
const jailName = document.getElementById('newJailName').value.trim();
|
||||
const content = document.getElementById('newJailContent').value.trim();
|
||||
|
||||
if (!jailName) {
|
||||
showToast('Jail name is required', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
showLoading(true);
|
||||
fetch(withServerParam('/api/jails'), {
|
||||
method: 'POST',
|
||||
headers: serverHeaders({ 'Content-Type': 'application/json' }),
|
||||
body: JSON.stringify({
|
||||
jailName: jailName,
|
||||
content: content
|
||||
})
|
||||
})
|
||||
.then(function(res) {
|
||||
if (!res.ok) {
|
||||
return res.json().then(function(data) {
|
||||
throw new Error(data.error || 'Server returned ' + res.status);
|
||||
});
|
||||
}
|
||||
return res.json();
|
||||
})
|
||||
.then(function(data) {
|
||||
if (data.error) {
|
||||
showToast('Error creating jail: ' + data.error, 'error');
|
||||
return;
|
||||
}
|
||||
closeModal('createJailModal');
|
||||
showToast(data.message || 'Jail created successfully', 'success');
|
||||
// Reload the manage jails modal
|
||||
openManageJailsModal();
|
||||
})
|
||||
.catch(function(err) {
|
||||
console.error('Error creating jail:', err);
|
||||
showToast('Error creating jail: ' + (err.message || err), 'error');
|
||||
})
|
||||
.finally(function() {
|
||||
showLoading(false);
|
||||
});
|
||||
}
|
||||
|
||||
function deleteJail(jailName) {
|
||||
if (!confirm('Are you sure you want to delete the jail "' + escapeHtml(jailName) + '"? This action cannot be undone.')) {
|
||||
return;
|
||||
}
|
||||
|
||||
showLoading(true);
|
||||
fetch(withServerParam('/api/jails/' + encodeURIComponent(jailName)), {
|
||||
method: 'DELETE',
|
||||
headers: serverHeaders()
|
||||
})
|
||||
.then(function(res) {
|
||||
if (!res.ok) {
|
||||
return res.json().then(function(data) {
|
||||
throw new Error(data.error || 'Server returned ' + res.status);
|
||||
});
|
||||
}
|
||||
return res.json();
|
||||
})
|
||||
.then(function(data) {
|
||||
if (data.error) {
|
||||
showToast('Error deleting jail: ' + data.error, 'error');
|
||||
return;
|
||||
}
|
||||
showToast(data.message || 'Jail deleted successfully', 'success');
|
||||
// Reload the manage jails modal
|
||||
openManageJailsModal();
|
||||
// Refresh dashboard
|
||||
refreshData({ silent: true });
|
||||
})
|
||||
.catch(function(err) {
|
||||
console.error('Error deleting jail:', err);
|
||||
showToast('Error deleting jail: ' + (err.message || err), 'error');
|
||||
})
|
||||
.finally(function() {
|
||||
showLoading(false);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user