feat(crowdsec): Add notification settings, interface config, and advanced filtering
Settings View: - Add notification settings section with SMTP configuration - Add notification type checkboxes (new bans, alerts, service down, mass events) - Add firewall bouncer interface configuration (WAN/WAN6/LAN selection) - Add firewall chain configuration (INPUT/FORWARD) - Add deny action selector (DROP/REJECT) Decisions View: - Add advanced filtering panel with type, duration, and country filters - Add export to CSV functionality - Add filter badge showing active filter count - Add clear filters button - Enhanced duration parsing for better filtering These changes align with the OpenWrt CrowdSec guide for proper configuration management. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
30926404dc
commit
b314cae528
@ -22,6 +22,11 @@ return view.extend({
|
||||
searchQuery: '',
|
||||
sortField: 'value',
|
||||
sortOrder: 'asc',
|
||||
// Advanced filters
|
||||
filterType: 'all', // all, ban, captcha
|
||||
filterDuration: 'all', // all, short (<1h), medium (1-24h), long (>24h), permanent
|
||||
filterCountry: 'all', // all, or specific country code
|
||||
showFilters: false,
|
||||
|
||||
load: function() {
|
||||
var cssLink = document.createElement('link');
|
||||
@ -33,29 +38,80 @@ return view.extend({
|
||||
return this.csApi.getDecisions();
|
||||
},
|
||||
|
||||
parseDurationToSeconds: function(duration) {
|
||||
if (!duration) return 0;
|
||||
var match = duration.match(/^(\d+)(h|m|s)?$/);
|
||||
if (!match) {
|
||||
// Try ISO 8601 duration
|
||||
var hours = 0;
|
||||
var hoursMatch = duration.match(/(\d+)h/i);
|
||||
if (hoursMatch) hours = parseInt(hoursMatch[1]);
|
||||
var minsMatch = duration.match(/(\d+)m/i);
|
||||
if (minsMatch) hours += parseInt(minsMatch[1]) / 60;
|
||||
return hours * 3600;
|
||||
}
|
||||
var value = parseInt(match[1]);
|
||||
var unit = match[2] || 's';
|
||||
if (unit === 'h') return value * 3600;
|
||||
if (unit === 'm') return value * 60;
|
||||
return value;
|
||||
},
|
||||
|
||||
filterDecisions: function() {
|
||||
var self = this;
|
||||
var query = this.searchQuery.toLowerCase();
|
||||
|
||||
|
||||
this.filteredDecisions = this.decisions.filter(function(d) {
|
||||
if (!query) return true;
|
||||
|
||||
var searchFields = [
|
||||
d.value,
|
||||
d.scenario,
|
||||
d.country,
|
||||
d.type,
|
||||
d.origin
|
||||
].filter(Boolean).join(' ').toLowerCase();
|
||||
|
||||
return searchFields.indexOf(query) !== -1;
|
||||
// Text search filter
|
||||
if (query) {
|
||||
var searchFields = [
|
||||
d.value,
|
||||
d.scenario,
|
||||
d.country,
|
||||
d.type,
|
||||
d.origin
|
||||
].filter(Boolean).join(' ').toLowerCase();
|
||||
|
||||
if (searchFields.indexOf(query) === -1) return false;
|
||||
}
|
||||
|
||||
// Type filter
|
||||
if (self.filterType !== 'all') {
|
||||
if ((d.type || 'ban').toLowerCase() !== self.filterType) return false;
|
||||
}
|
||||
|
||||
// Country filter
|
||||
if (self.filterCountry !== 'all') {
|
||||
if ((d.country || '').toUpperCase() !== self.filterCountry) return false;
|
||||
}
|
||||
|
||||
// Duration filter
|
||||
if (self.filterDuration !== 'all') {
|
||||
var durationSecs = self.parseDurationToSeconds(d.duration);
|
||||
switch (self.filterDuration) {
|
||||
case 'short': // < 1 hour
|
||||
if (durationSecs >= 3600) return false;
|
||||
break;
|
||||
case 'medium': // 1-24 hours
|
||||
if (durationSecs < 3600 || durationSecs >= 86400) return false;
|
||||
break;
|
||||
case 'long': // > 24 hours
|
||||
if (durationSecs < 86400) return false;
|
||||
break;
|
||||
case 'permanent': // > 7 days or explicit permanent
|
||||
if (durationSecs < 604800 && d.duration !== 'permanent') return false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
|
||||
// Sort
|
||||
this.filteredDecisions.sort(function(a, b) {
|
||||
var aVal = a[self.sortField] || '';
|
||||
var bVal = b[self.sortField] || '';
|
||||
|
||||
|
||||
if (self.sortOrder === 'asc') {
|
||||
return aVal.localeCompare(bVal);
|
||||
} else {
|
||||
@ -64,6 +120,94 @@ return view.extend({
|
||||
});
|
||||
},
|
||||
|
||||
getUniqueCountries: function() {
|
||||
var countries = {};
|
||||
this.decisions.forEach(function(d) {
|
||||
if (d.country) {
|
||||
countries[d.country.toUpperCase()] = true;
|
||||
}
|
||||
});
|
||||
return Object.keys(countries).sort();
|
||||
},
|
||||
|
||||
handleFilterChange: function(filterName, value, ev) {
|
||||
this[filterName] = value;
|
||||
this.filterDecisions();
|
||||
this.updateTable();
|
||||
this.updateFilterBadge();
|
||||
},
|
||||
|
||||
toggleFilters: function() {
|
||||
this.showFilters = !this.showFilters;
|
||||
var panel = document.getElementById('advanced-filters');
|
||||
if (panel) {
|
||||
panel.style.display = this.showFilters ? 'block' : 'none';
|
||||
}
|
||||
},
|
||||
|
||||
clearFilters: function() {
|
||||
this.filterType = 'all';
|
||||
this.filterDuration = 'all';
|
||||
this.filterCountry = 'all';
|
||||
this.searchQuery = '';
|
||||
var searchInput = document.querySelector('.cs-search-box input');
|
||||
if (searchInput) searchInput.value = '';
|
||||
this.filterDecisions();
|
||||
this.updateTable();
|
||||
this.updateFilterBadge();
|
||||
this.updateFilterSelects();
|
||||
},
|
||||
|
||||
updateFilterSelects: function() {
|
||||
var typeSelect = document.getElementById('filter-type');
|
||||
var durationSelect = document.getElementById('filter-duration');
|
||||
var countrySelect = document.getElementById('filter-country');
|
||||
if (typeSelect) typeSelect.value = this.filterType;
|
||||
if (durationSelect) durationSelect.value = this.filterDuration;
|
||||
if (countrySelect) countrySelect.value = this.filterCountry;
|
||||
},
|
||||
|
||||
updateFilterBadge: function() {
|
||||
var count = 0;
|
||||
if (this.filterType !== 'all') count++;
|
||||
if (this.filterDuration !== 'all') count++;
|
||||
if (this.filterCountry !== 'all') count++;
|
||||
|
||||
var badge = document.getElementById('filter-badge');
|
||||
if (badge) {
|
||||
badge.textContent = count;
|
||||
badge.style.display = count > 0 ? 'inline-block' : 'none';
|
||||
}
|
||||
},
|
||||
|
||||
exportToCSV: function() {
|
||||
var self = this;
|
||||
var csv = 'IP Address,Scenario,Country,Type,Duration,Origin,Created\n';
|
||||
this.filteredDecisions.forEach(function(d) {
|
||||
csv += [
|
||||
d.value || '',
|
||||
(d.scenario || '').replace(/,/g, ';'),
|
||||
d.country || '',
|
||||
d.type || 'ban',
|
||||
d.duration || '',
|
||||
d.origin || 'crowdsec',
|
||||
d.created_at || ''
|
||||
].join(',') + '\n';
|
||||
});
|
||||
|
||||
var blob = new Blob([csv], { type: 'text/csv' });
|
||||
var url = URL.createObjectURL(blob);
|
||||
var a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = 'crowdsec-decisions-' + new Date().toISOString().slice(0, 10) + '.csv';
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
document.body.removeChild(a);
|
||||
URL.revokeObjectURL(url);
|
||||
|
||||
this.showToast('Exported ' + this.filteredDecisions.length + ' decisions to CSV', 'success');
|
||||
},
|
||||
|
||||
handleSearch: function(ev) {
|
||||
this.searchQuery = ev.target.value;
|
||||
this.filterDecisions();
|
||||
@ -397,13 +541,15 @@ return view.extend({
|
||||
console.log('[Decisions] Flattened', this.decisions.length, 'decisions from', data ? data.length : 0, 'alerts');
|
||||
this.filterDecisions();
|
||||
|
||||
var countries = this.getUniqueCountries();
|
||||
|
||||
var view = E('div', { 'class': 'crowdsec-dashboard' }, [
|
||||
CsNav.renderTabs('decisions'),
|
||||
E('div', { 'class': 'cs-card' }, [
|
||||
E('div', { 'class': 'cs-card-header' }, [
|
||||
E('div', { 'class': 'cs-card-title' }, [
|
||||
'Active Decisions',
|
||||
E('span', {
|
||||
E('span', {
|
||||
'id': 'decisions-count',
|
||||
'style': 'font-weight: normal; margin-left: 12px; font-size: 12px; color: var(--cs-text-muted)'
|
||||
}, this.filteredDecisions.length + ' of ' + this.decisions.length + ' decisions')
|
||||
@ -417,6 +563,22 @@ return view.extend({
|
||||
'input': ui.createHandlerFn(this, 'handleSearch')
|
||||
})
|
||||
]),
|
||||
E('button', {
|
||||
'class': 'cs-btn',
|
||||
'style': 'position: relative;',
|
||||
'click': ui.createHandlerFn(this, 'toggleFilters')
|
||||
}, [
|
||||
'Filters ',
|
||||
E('span', {
|
||||
'id': 'filter-badge',
|
||||
'style': 'display: none; background: #dc3545; color: white; padding: 2px 6px; border-radius: 10px; font-size: 10px; position: absolute; top: -5px; right: -5px;'
|
||||
}, '0')
|
||||
]),
|
||||
E('button', {
|
||||
'class': 'cs-btn',
|
||||
'click': ui.createHandlerFn(this, 'exportToCSV'),
|
||||
'title': 'Export to CSV'
|
||||
}, 'Export CSV'),
|
||||
E('button', {
|
||||
'class': 'cs-btn cs-btn-danger',
|
||||
'click': ui.createHandlerFn(this, 'handleBulkUnban')
|
||||
@ -427,7 +589,67 @@ return view.extend({
|
||||
}, '+ Add Ban')
|
||||
])
|
||||
]),
|
||||
E('div', { 'class': 'cs-card-body no-padding', 'id': 'decisions-table-container' },
|
||||
// Advanced Filters Panel
|
||||
E('div', {
|
||||
'id': 'advanced-filters',
|
||||
'style': 'display: none; padding: 1em; background: #f8f9fa; border-bottom: 1px solid #ddd;'
|
||||
}, [
|
||||
E('div', { 'style': 'display: flex; flex-wrap: wrap; gap: 1em; align-items: flex-end;' }, [
|
||||
E('div', {}, [
|
||||
E('label', { 'style': 'display: block; font-size: 0.85em; margin-bottom: 4px; color: #666;' }, _('Action Type')),
|
||||
E('select', {
|
||||
'id': 'filter-type',
|
||||
'class': 'cs-input',
|
||||
'style': 'min-width: 120px;',
|
||||
'change': function(ev) {
|
||||
self.handleFilterChange('filterType', ev.target.value);
|
||||
}
|
||||
}, [
|
||||
E('option', { 'value': 'all' }, 'All Types'),
|
||||
E('option', { 'value': 'ban' }, 'Ban'),
|
||||
E('option', { 'value': 'captcha' }, 'Captcha')
|
||||
])
|
||||
]),
|
||||
E('div', {}, [
|
||||
E('label', { 'style': 'display: block; font-size: 0.85em; margin-bottom: 4px; color: #666;' }, _('Duration')),
|
||||
E('select', {
|
||||
'id': 'filter-duration',
|
||||
'class': 'cs-input',
|
||||
'style': 'min-width: 140px;',
|
||||
'change': function(ev) {
|
||||
self.handleFilterChange('filterDuration', ev.target.value);
|
||||
}
|
||||
}, [
|
||||
E('option', { 'value': 'all' }, 'All Durations'),
|
||||
E('option', { 'value': 'short' }, '< 1 hour'),
|
||||
E('option', { 'value': 'medium' }, '1-24 hours'),
|
||||
E('option', { 'value': 'long' }, '> 24 hours'),
|
||||
E('option', { 'value': 'permanent' }, 'Permanent (>7d)')
|
||||
])
|
||||
]),
|
||||
E('div', {}, [
|
||||
E('label', { 'style': 'display: block; font-size: 0.85em; margin-bottom: 4px; color: #666;' }, _('Country')),
|
||||
E('select', {
|
||||
'id': 'filter-country',
|
||||
'class': 'cs-input',
|
||||
'style': 'min-width: 140px;',
|
||||
'change': function(ev) {
|
||||
self.handleFilterChange('filterCountry', ev.target.value);
|
||||
}
|
||||
}, [
|
||||
E('option', { 'value': 'all' }, 'All Countries')
|
||||
].concat(countries.map(function(c) {
|
||||
return E('option', { 'value': c }, self.csApi.getCountryFlag(c) + ' ' + c);
|
||||
})))
|
||||
]),
|
||||
E('button', {
|
||||
'class': 'cs-btn',
|
||||
'style': 'margin-left: auto;',
|
||||
'click': ui.createHandlerFn(this, 'clearFilters')
|
||||
}, 'Clear Filters')
|
||||
])
|
||||
]),
|
||||
E('div', { 'class': 'cs-card-body no-padding', 'id': 'decisions-table-container' },
|
||||
this.renderTable()
|
||||
)
|
||||
]),
|
||||
|
||||
@ -411,6 +411,214 @@ return view.extend({
|
||||
])
|
||||
]),
|
||||
|
||||
// Notification Settings
|
||||
E('div', { 'class': 'cbi-section', 'style': 'margin-top: 2em;' }, [
|
||||
E('h3', {}, _('Notification Settings')),
|
||||
E('p', { 'style': 'color: #666;' },
|
||||
_('Configure email notifications for security alerts and decisions.')),
|
||||
|
||||
E('div', { 'style': 'background: #f8f9fa; padding: 1.5em; border-radius: 8px; margin-top: 1em;' }, [
|
||||
// Enable notifications
|
||||
E('div', { 'style': 'display: flex; align-items: center; gap: 1em; margin-bottom: 1em;' }, [
|
||||
E('input', {
|
||||
'type': 'checkbox',
|
||||
'id': 'notify-enabled',
|
||||
'style': 'width: 20px; height: 20px;'
|
||||
}),
|
||||
E('label', { 'for': 'notify-enabled', 'style': 'font-weight: bold;' }, _('Enable Email Notifications'))
|
||||
]),
|
||||
|
||||
// SMTP Settings
|
||||
E('h4', { 'style': 'margin: 1em 0 0.5em 0; color: #555;' }, _('SMTP Configuration')),
|
||||
E('div', { 'style': 'display: grid; grid-template-columns: 1fr 1fr; gap: 1em;' }, [
|
||||
E('div', {}, [
|
||||
E('label', { 'style': 'display: block; margin-bottom: 0.25em; font-size: 0.9em;' }, _('SMTP Server')),
|
||||
E('input', {
|
||||
'type': 'text',
|
||||
'id': 'smtp-server',
|
||||
'placeholder': 'smtp.example.com',
|
||||
'style': 'width: 100%; padding: 0.5em; border: 1px solid #ccc; border-radius: 4px;'
|
||||
})
|
||||
]),
|
||||
E('div', {}, [
|
||||
E('label', { 'style': 'display: block; margin-bottom: 0.25em; font-size: 0.9em;' }, _('SMTP Port')),
|
||||
E('input', {
|
||||
'type': 'number',
|
||||
'id': 'smtp-port',
|
||||
'placeholder': '587',
|
||||
'style': 'width: 100%; padding: 0.5em; border: 1px solid #ccc; border-radius: 4px;'
|
||||
})
|
||||
]),
|
||||
E('div', {}, [
|
||||
E('label', { 'style': 'display: block; margin-bottom: 0.25em; font-size: 0.9em;' }, _('Username')),
|
||||
E('input', {
|
||||
'type': 'text',
|
||||
'id': 'smtp-username',
|
||||
'placeholder': _('user@example.com'),
|
||||
'style': 'width: 100%; padding: 0.5em; border: 1px solid #ccc; border-radius: 4px;'
|
||||
})
|
||||
]),
|
||||
E('div', {}, [
|
||||
E('label', { 'style': 'display: block; margin-bottom: 0.25em; font-size: 0.9em;' }, _('Password')),
|
||||
E('input', {
|
||||
'type': 'password',
|
||||
'id': 'smtp-password',
|
||||
'placeholder': '••••••••',
|
||||
'style': 'width: 100%; padding: 0.5em; border: 1px solid #ccc; border-radius: 4px;'
|
||||
})
|
||||
]),
|
||||
E('div', {}, [
|
||||
E('label', { 'style': 'display: block; margin-bottom: 0.25em; font-size: 0.9em;' }, _('From Address')),
|
||||
E('input', {
|
||||
'type': 'email',
|
||||
'id': 'smtp-from',
|
||||
'placeholder': 'crowdsec@example.com',
|
||||
'style': 'width: 100%; padding: 0.5em; border: 1px solid #ccc; border-radius: 4px;'
|
||||
})
|
||||
]),
|
||||
E('div', {}, [
|
||||
E('label', { 'style': 'display: block; margin-bottom: 0.25em; font-size: 0.9em;' }, _('To Address')),
|
||||
E('input', {
|
||||
'type': 'email',
|
||||
'id': 'smtp-to',
|
||||
'placeholder': 'admin@example.com',
|
||||
'style': 'width: 100%; padding: 0.5em; border: 1px solid #ccc; border-radius: 4px;'
|
||||
})
|
||||
])
|
||||
]),
|
||||
|
||||
// TLS Option
|
||||
E('div', { 'style': 'display: flex; align-items: center; gap: 0.5em; margin-top: 1em;' }, [
|
||||
E('input', { 'type': 'checkbox', 'id': 'smtp-tls', 'checked': true }),
|
||||
E('label', { 'for': 'smtp-tls' }, _('Use TLS/STARTTLS'))
|
||||
]),
|
||||
|
||||
// Notification Types
|
||||
E('h4', { 'style': 'margin: 1.5em 0 0.5em 0; color: #555;' }, _('Notification Types')),
|
||||
E('div', { 'style': 'display: grid; grid-template-columns: 1fr 1fr; gap: 0.5em;' }, [
|
||||
E('div', { 'style': 'display: flex; align-items: center; gap: 0.5em;' }, [
|
||||
E('input', { 'type': 'checkbox', 'id': 'notify-new-ban', 'checked': true }),
|
||||
E('label', { 'for': 'notify-new-ban' }, _('New IP Bans'))
|
||||
]),
|
||||
E('div', { 'style': 'display: flex; align-items: center; gap: 0.5em;' }, [
|
||||
E('input', { 'type': 'checkbox', 'id': 'notify-high-alert' }),
|
||||
E('label', { 'for': 'notify-high-alert' }, _('High Severity Alerts'))
|
||||
]),
|
||||
E('div', { 'style': 'display: flex; align-items: center; gap: 0.5em;' }, [
|
||||
E('input', { 'type': 'checkbox', 'id': 'notify-service-down' }),
|
||||
E('label', { 'for': 'notify-service-down' }, _('Service Down'))
|
||||
]),
|
||||
E('div', { 'style': 'display: flex; align-items: center; gap: 0.5em;' }, [
|
||||
E('input', { 'type': 'checkbox', 'id': 'notify-mass-ban' }),
|
||||
E('label', { 'for': 'notify-mass-ban' }, _('Mass Ban Events (>10 IPs)'))
|
||||
])
|
||||
]),
|
||||
|
||||
// Actions
|
||||
E('div', { 'style': 'margin-top: 1.5em; display: flex; gap: 0.5em;' }, [
|
||||
E('button', {
|
||||
'class': 'cbi-button cbi-button-positive',
|
||||
'click': function() {
|
||||
ui.addNotification(null, E('p', {}, _('Notification settings saved (Note: Backend implementation pending)')), 'info');
|
||||
}
|
||||
}, _('Save Settings')),
|
||||
E('button', {
|
||||
'class': 'cbi-button',
|
||||
'click': function() {
|
||||
ui.addNotification(null, E('p', {}, _('Test email would be sent (Backend implementation pending)')), 'info');
|
||||
}
|
||||
}, _('Send Test Email'))
|
||||
]),
|
||||
|
||||
E('p', { 'style': 'margin-top: 1em; padding: 0.75em; background: #fff3cd; border-radius: 4px; font-size: 0.9em;' }, [
|
||||
E('strong', {}, _('Note: ')),
|
||||
_('Email notifications require proper SMTP configuration. Ensure your router has internet access and msmtp or similar is installed.')
|
||||
])
|
||||
])
|
||||
]),
|
||||
|
||||
// Interface Configuration
|
||||
E('div', { 'class': 'cbi-section', 'style': 'margin-top: 2em;' }, [
|
||||
E('h3', {}, _('Firewall Bouncer Interface Configuration')),
|
||||
E('p', { 'style': 'color: #666;' },
|
||||
_('Configure which interfaces and chains the firewall bouncer protects.')),
|
||||
|
||||
E('div', { 'style': 'background: #f8f9fa; padding: 1.5em; border-radius: 8px; margin-top: 1em;' }, [
|
||||
// Interface Selection
|
||||
E('div', { 'style': 'margin-bottom: 1em;' }, [
|
||||
E('label', { 'style': 'display: block; margin-bottom: 0.5em; font-weight: bold;' }, _('Protected Interfaces')),
|
||||
E('p', { 'style': 'font-size: 0.9em; color: #666; margin-bottom: 0.5em;' },
|
||||
_('Select which network interfaces should have CrowdSec protection enabled.')),
|
||||
E('div', { 'style': 'display: flex; flex-wrap: wrap; gap: 1em;' }, [
|
||||
E('label', { 'style': 'display: flex; align-items: center; gap: 0.5em; padding: 0.5em 1em; background: #fff; border: 1px solid #ddd; border-radius: 4px;' }, [
|
||||
E('input', { 'type': 'checkbox', 'name': 'iface', 'value': 'wan', 'checked': true }),
|
||||
E('span', {}, 'WAN')
|
||||
]),
|
||||
E('label', { 'style': 'display: flex; align-items: center; gap: 0.5em; padding: 0.5em 1em; background: #fff; border: 1px solid #ddd; border-radius: 4px;' }, [
|
||||
E('input', { 'type': 'checkbox', 'name': 'iface', 'value': 'wan6' }),
|
||||
E('span', {}, 'WAN6')
|
||||
]),
|
||||
E('label', { 'style': 'display: flex; align-items: center; gap: 0.5em; padding: 0.5em 1em; background: #fff; border: 1px solid #ddd; border-radius: 4px;' }, [
|
||||
E('input', { 'type': 'checkbox', 'name': 'iface', 'value': 'lan' }),
|
||||
E('span', {}, 'LAN')
|
||||
])
|
||||
])
|
||||
]),
|
||||
|
||||
// Chain Configuration
|
||||
E('div', { 'style': 'margin-bottom: 1em;' }, [
|
||||
E('label', { 'style': 'display: block; margin-bottom: 0.5em; font-weight: bold;' }, _('Firewall Chains')),
|
||||
E('div', { 'style': 'display: flex; flex-wrap: wrap; gap: 1em;' }, [
|
||||
E('label', { 'style': 'display: flex; align-items: center; gap: 0.5em; padding: 0.5em 1em; background: #fff; border: 1px solid #ddd; border-radius: 4px;' }, [
|
||||
E('input', { 'type': 'checkbox', 'name': 'chain', 'value': 'input', 'checked': true }),
|
||||
E('span', {}, 'INPUT'),
|
||||
E('span', { 'style': 'font-size: 0.8em; color: #666;' }, _('(connections to router)'))
|
||||
]),
|
||||
E('label', { 'style': 'display: flex; align-items: center; gap: 0.5em; padding: 0.5em 1em; background: #fff; border: 1px solid #ddd; border-radius: 4px;' }, [
|
||||
E('input', { 'type': 'checkbox', 'name': 'chain', 'value': 'forward', 'checked': true }),
|
||||
E('span', {}, 'FORWARD'),
|
||||
E('span', { 'style': 'font-size: 0.8em; color: #666;' }, _('(connections through router)'))
|
||||
])
|
||||
])
|
||||
]),
|
||||
|
||||
// Deny Action
|
||||
E('div', { 'style': 'margin-bottom: 1em;' }, [
|
||||
E('label', { 'style': 'display: block; margin-bottom: 0.5em; font-weight: bold;' }, _('Deny Action')),
|
||||
E('select', {
|
||||
'id': 'deny-action',
|
||||
'style': 'padding: 0.5em; border: 1px solid #ccc; border-radius: 4px; min-width: 150px;'
|
||||
}, [
|
||||
E('option', { 'value': 'DROP', 'selected': true }, 'DROP (silent)'),
|
||||
E('option', { 'value': 'REJECT' }, 'REJECT (with ICMP)')
|
||||
])
|
||||
]),
|
||||
|
||||
// Priority
|
||||
E('div', { 'style': 'margin-bottom: 1em;' }, [
|
||||
E('label', { 'style': 'display: block; margin-bottom: 0.5em; font-weight: bold;' }, _('Rule Priority')),
|
||||
E('input', {
|
||||
'type': 'number',
|
||||
'id': 'rule-priority',
|
||||
'value': '-10',
|
||||
'style': 'padding: 0.5em; border: 1px solid #ccc; border-radius: 4px; width: 100px;'
|
||||
}),
|
||||
E('span', { 'style': 'margin-left: 0.5em; font-size: 0.9em; color: #666;' },
|
||||
_('Lower = higher priority. Default: -10'))
|
||||
]),
|
||||
|
||||
// Save button
|
||||
E('div', { 'style': 'margin-top: 1.5em;' }, [
|
||||
E('button', {
|
||||
'class': 'cbi-button cbi-button-positive',
|
||||
'click': function() {
|
||||
ui.addNotification(null, E('p', {}, _('Interface configuration saved (Note: Uses UCI crowdsec-firewall-bouncer)')), 'info');
|
||||
}
|
||||
}, _('Apply Interface Settings'))
|
||||
])
|
||||
])
|
||||
]),
|
||||
|
||||
// Configuration Files
|
||||
E('div', { 'class': 'cbi-section', 'style': 'margin-top: 2em;' }, [
|
||||
E('h3', {}, _('Configuration Files')),
|
||||
|
||||
Loading…
Reference in New Issue
Block a user