mirror of
https://github.com/WenPai-org/wpban.git
synced 2025-08-02 19:58:42 +08:00
完全重写
This commit is contained in:
parent
fa7b00b4f5
commit
0ae1b5b593
13 changed files with 4657 additions and 78 deletions
537
assets/admin.css
Normal file
537
assets/admin.css
Normal file
|
@ -0,0 +1,537 @@
|
|||
/* WPBan Pro Admin Styles - WordPress Native Experience */
|
||||
|
||||
.wpban-wrap {
|
||||
margin-top: 20px;
|
||||
max-width: 1400px;
|
||||
}
|
||||
|
||||
/* Dashboard Grid */
|
||||
.wpban-dashboard {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.wpban-stats-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
|
||||
gap: 20px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.wpban-stat-card {
|
||||
background: #fff;
|
||||
border: 1px solid #c3c4c7;
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
box-shadow: 0 1px 1px rgba(0,0,0,0.04);
|
||||
position: relative;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.wpban-stat-card:hover {
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.wpban-stat-card h3 {
|
||||
margin: 0 0 10px 0;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #1d2327;
|
||||
}
|
||||
|
||||
.wpban-stat-number {
|
||||
font-size: 36px;
|
||||
font-weight: 300;
|
||||
color: #2271b1;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.wpban-stat-trend {
|
||||
margin-top: 10px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.wpban-stat-trend .up {
|
||||
color: #d63638;
|
||||
}
|
||||
|
||||
.wpban-stat-trend .down {
|
||||
color: #00a32a;
|
||||
}
|
||||
|
||||
/* Grid Layout */
|
||||
.wpban-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 20px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
@media (max-width: 1200px) {
|
||||
.wpban-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
/* Cards */
|
||||
.wpban-card {
|
||||
background: #fff;
|
||||
border: 1px solid #c3c4c7;
|
||||
padding: 20px;
|
||||
margin-bottom: 20px;
|
||||
box-shadow: 0 1px 1px rgba(0,0,0,0.04);
|
||||
}
|
||||
|
||||
.wpban-card h2 {
|
||||
margin: 0 0 15px 0;
|
||||
font-size: 1.3em;
|
||||
font-weight: 600;
|
||||
color: #1d2327;
|
||||
}
|
||||
|
||||
.wpban-card h3 {
|
||||
margin: 20px 0 10px 0;
|
||||
font-size: 1.1em;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* Actions */
|
||||
.wpban-actions {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
margin: 20px 0;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
/* Bypass URL */
|
||||
.wpban-bypass-url {
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.wpban-bypass-url label {
|
||||
display: block;
|
||||
margin-bottom: 10px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.wpban-input-group {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.wpban-input-group input {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
/* Templates Grid */
|
||||
.wpban-templates-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.wpban-template-card {
|
||||
background: #f6f7f7;
|
||||
border: 1px solid #dcdcde;
|
||||
padding: 20px;
|
||||
border-radius: 4px;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.wpban-template-card:hover {
|
||||
background: #fff;
|
||||
border-color: #2271b1;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
.wpban-template-card h3 {
|
||||
margin: 0 0 10px 0;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.wpban-template-card p {
|
||||
margin: 0 0 15px 0;
|
||||
color: #50575e;
|
||||
font-size: 13px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
/* Tabs */
|
||||
.wpban-tabs {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.tab-content {
|
||||
display: none;
|
||||
background: #fff;
|
||||
border: 1px solid #c3c4c7;
|
||||
border-top: none;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.tab-content.active {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.tab-content h2 {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
/* Crawler Controls */
|
||||
.wpban-crawler-controls {
|
||||
margin-bottom: 20px;
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
/* Crawler Grid */
|
||||
.wpban-crawler-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
|
||||
gap: 10px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.wpban-crawler-item {
|
||||
display: block;
|
||||
padding: 12px;
|
||||
background: #f6f7f7;
|
||||
border: 1px solid #dcdcde;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.wpban-crawler-item:hover {
|
||||
background: #fff;
|
||||
border-color: #2271b1;
|
||||
}
|
||||
|
||||
.wpban-crawler-item input[type="checkbox"] {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.wpban-crawler-name {
|
||||
font-weight: 600;
|
||||
display: inline-block;
|
||||
margin-right: 8px;
|
||||
font-family: Consolas, Monaco, monospace;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.wpban-crawler-desc {
|
||||
font-size: 12px;
|
||||
color: #50575e;
|
||||
display: inline;
|
||||
}
|
||||
|
||||
/* Notices */
|
||||
.wpban-notice {
|
||||
padding: 12px;
|
||||
margin: 15px 0;
|
||||
border-left: 4px solid;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.wpban-notice-warning {
|
||||
border-left-color: #f0b849;
|
||||
background-color: #fef8ee;
|
||||
}
|
||||
|
||||
.wpban-notice-warning p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* Logs */
|
||||
.wpban-logs-filters {
|
||||
background: #fff;
|
||||
border: 1px solid #c3c4c7;
|
||||
padding: 15px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.wpban-logs-filters form {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.wpban-logs-filters input[type="date"],
|
||||
.wpban-logs-filters input[type="text"],
|
||||
.wpban-logs-filters select {
|
||||
height: 30px;
|
||||
line-height: 28px;
|
||||
}
|
||||
|
||||
/* Badges */
|
||||
.wpban-badge {
|
||||
display: inline-block;
|
||||
padding: 3px 8px;
|
||||
border-radius: 3px;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.wpban-badge-banned {
|
||||
background: #d63638;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.wpban-badge-blocked {
|
||||
background: #f0b849;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.wpban-badge-failed_login {
|
||||
background: #dba617;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.wpban-badge-bypass {
|
||||
background: #00a32a;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* Truncate */
|
||||
.wpban-truncate {
|
||||
display: inline-block;
|
||||
max-width: 200px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
/* Country Selector */
|
||||
.wpban-country-selector select {
|
||||
font-family: Consolas, Monaco, monospace;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
/* Rate Limit Settings */
|
||||
.form-table input[type="number"] {
|
||||
width: 80px;
|
||||
}
|
||||
|
||||
/* Tools Grid */
|
||||
.wpban-tools-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
/* Chart Container */
|
||||
#wpban-country-chart {
|
||||
max-height: 300px;
|
||||
}
|
||||
|
||||
/* Activity List */
|
||||
.wpban-activity-list {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.wpban-activity-list li {
|
||||
padding: 8px 0;
|
||||
border-bottom: 1px solid #f0f0f1;
|
||||
}
|
||||
|
||||
.wpban-activity-list li:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
/* Form Elements */
|
||||
.form-table textarea.code {
|
||||
font-family: Consolas, Monaco, monospace;
|
||||
font-size: 13px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
/* Pagination */
|
||||
.tablenav-pages {
|
||||
margin: 20px 0;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.tablenav-pages .pagination-links {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.tablenav-pages a,
|
||||
.tablenav-pages span {
|
||||
display: inline-block;
|
||||
padding: 3px 8px;
|
||||
margin: 0 2px;
|
||||
background: #f6f7f7;
|
||||
border: 1px solid #dcdcde;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.tablenav-pages .current {
|
||||
background: #2271b1;
|
||||
color: #fff;
|
||||
border-color: #2271b1;
|
||||
}
|
||||
|
||||
/* Responsive Tables */
|
||||
@media (max-width: 782px) {
|
||||
.wp-list-table {
|
||||
display: block;
|
||||
overflow-x: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
|
||||
.wpban-stats-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.wpban-templates-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.wpban-crawler-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.wpban-tools-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.wpban-logs-filters form {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.wpban-logs-filters input,
|
||||
.wpban-logs-filters select,
|
||||
.wpban-logs-filters button {
|
||||
width: 100%;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Loading States */
|
||||
.wpban-loading {
|
||||
opacity: 0.6;
|
||||
pointer-events: none;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.wpban-loading::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
margin: -10px 0 0 -10px;
|
||||
border: 2px solid #f3f3f3;
|
||||
border-top: 2px solid #2271b1;
|
||||
border-radius: 50%;
|
||||
animation: wpban-spin 1s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes wpban-spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
/* Buttons */
|
||||
.button.button-small {
|
||||
padding: 0 8px;
|
||||
line-height: 26px;
|
||||
height: 28px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
/* WP Editor in Settings */
|
||||
.tab-content .wp-editor-wrap {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
/* Empty States */
|
||||
.wpban-empty-state {
|
||||
text-align: center;
|
||||
padding: 40px;
|
||||
color: #646970;
|
||||
}
|
||||
|
||||
.wpban-empty-state p {
|
||||
font-size: 16px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* Success/Error Messages */
|
||||
.wpban-message {
|
||||
padding: 12px;
|
||||
margin: 15px 0;
|
||||
border-left: 4px solid;
|
||||
background: #fff;
|
||||
animation: wpban-fade-in 0.3s ease;
|
||||
}
|
||||
|
||||
.wpban-message.success {
|
||||
border-left-color: #00a32a;
|
||||
background-color: #f0f8f0;
|
||||
}
|
||||
|
||||
.wpban-message.error {
|
||||
border-left-color: #d63638;
|
||||
background-color: #fef1f1;
|
||||
}
|
||||
|
||||
@keyframes wpban-fade-in {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(-10px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Row Actions */
|
||||
.row-actions {
|
||||
font-size: 12px;
|
||||
padding-top: 4px;
|
||||
}
|
||||
|
||||
.row-actions a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* Status Indicators */
|
||||
.wpban-status {
|
||||
display: inline-block;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 50%;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.wpban-status.active {
|
||||
background: #00a32a;
|
||||
}
|
||||
|
||||
.wpban-status.inactive {
|
||||
background: #787c82;
|
||||
}
|
||||
|
||||
/* Code Preview */
|
||||
.wpban-code-preview {
|
||||
background: #f6f7f7;
|
||||
border: 1px solid #dcdcde;
|
||||
padding: 15px;
|
||||
font-family: Consolas, Monaco, monospace;
|
||||
font-size: 13px;
|
||||
overflow-x: auto;
|
||||
white-space: pre;
|
||||
max-height: 300px;
|
||||
overflow-y: auto;
|
||||
}
|
335
assets/admin.js
Normal file
335
assets/admin.js
Normal file
|
@ -0,0 +1,335 @@
|
|||
/**
|
||||
* WPBan Pro Admin JavaScript
|
||||
*/
|
||||
|
||||
(function($) {
|
||||
'use strict';
|
||||
|
||||
// Tab Navigation
|
||||
$(document).on('click', '.nav-tab', function(e) {
|
||||
e.preventDefault();
|
||||
const target = $(this).attr('href');
|
||||
|
||||
$('.nav-tab').removeClass('nav-tab-active');
|
||||
$(this).addClass('nav-tab-active');
|
||||
|
||||
$('.tab-content').removeClass('active');
|
||||
$(target).addClass('active');
|
||||
|
||||
// Save active tab to localStorage
|
||||
localStorage.setItem('wpban_active_tab', target);
|
||||
});
|
||||
|
||||
// Restore active tab
|
||||
$(document).ready(function() {
|
||||
const activeTab = localStorage.getItem('wpban_active_tab');
|
||||
if (activeTab && $(activeTab).length) {
|
||||
$('.nav-tab[href="' + activeTab + '"]').trigger('click');
|
||||
}
|
||||
});
|
||||
|
||||
// Settings Form Handler
|
||||
$('#wpban-settings-form').on('submit', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
const $form = $(this);
|
||||
const $button = $('#wpban-save-settings');
|
||||
const $spinner = $button.next('.spinner');
|
||||
|
||||
$button.prop('disabled', true);
|
||||
$spinner.addClass('is-active');
|
||||
|
||||
$.ajax({
|
||||
url: wpban.ajax_url,
|
||||
type: 'POST',
|
||||
data: {
|
||||
action: 'wpban_save_settings',
|
||||
settings: $form.serialize(),
|
||||
_ajax_nonce: $('#wpban_nonce').val()
|
||||
},
|
||||
success: function(response) {
|
||||
if (response.success) {
|
||||
showMessage('success', response.data.message);
|
||||
} else {
|
||||
showMessage('error', response.data || wpban.i18n.error);
|
||||
}
|
||||
},
|
||||
error: function() {
|
||||
showMessage('error', wpban.i18n.error);
|
||||
},
|
||||
complete: function() {
|
||||
$button.prop('disabled', false);
|
||||
$spinner.removeClass('is-active');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Apply Template
|
||||
window.wpbanApplyTemplate = function(templateId) {
|
||||
if (!confirm(wpban.i18n.confirm_template)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$.post(wpban.ajax_url, {
|
||||
action: 'wpban_apply_template',
|
||||
template: templateId,
|
||||
_ajax_nonce: wpban.nonce
|
||||
}, function(response) {
|
||||
if (response.success) {
|
||||
showMessage('success', response.data.message);
|
||||
setTimeout(() => location.reload(), 1500);
|
||||
} else {
|
||||
showMessage('error', response.data || wpban.i18n.error);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// Copy Text to Clipboard
|
||||
window.wpbanCopyText = function(text) {
|
||||
navigator.clipboard.writeText(text).then(() => {
|
||||
showMessage('success', 'Copied to clipboard!');
|
||||
}).catch(() => {
|
||||
// Fallback for older browsers
|
||||
const $temp = $('<input>');
|
||||
$('body').append($temp);
|
||||
$temp.val(text).select();
|
||||
document.execCommand('copy');
|
||||
$temp.remove();
|
||||
showMessage('success', 'Copied to clipboard!');
|
||||
});
|
||||
};
|
||||
|
||||
// Crawler Selection
|
||||
window.wpbanSelectCrawlers = function(type, select) {
|
||||
if (type === 'all') {
|
||||
$('.wpban-crawler-item input').prop('checked', select);
|
||||
} else {
|
||||
$(`.wpban-crawler-item[data-type="${type}"] input`).prop('checked', select);
|
||||
}
|
||||
};
|
||||
|
||||
// Export Logs
|
||||
window.wpbanExportLogs = function() {
|
||||
window.location.href = wpban.ajax_url + '?action=wpban_export_logs&_ajax_nonce=' + wpban.nonce;
|
||||
};
|
||||
|
||||
// Clear Logs
|
||||
window.wpbanClearLogs = function() {
|
||||
if (!confirm(wpban.i18n.confirm_clear)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$.post(wpban.ajax_url, {
|
||||
action: 'wpban_clear_logs',
|
||||
_ajax_nonce: wpban.nonce
|
||||
}, function(response) {
|
||||
if (response.success) {
|
||||
showMessage('success', response.data.message);
|
||||
setTimeout(() => location.reload(), 1500);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// Test Email
|
||||
window.wpbanTestEmail = function() {
|
||||
const $button = event.target;
|
||||
$button.disabled = true;
|
||||
|
||||
$.post(wpban.ajax_url, {
|
||||
action: 'wpban_test_email',
|
||||
_ajax_nonce: wpban.nonce
|
||||
}, function(response) {
|
||||
if (response.success) {
|
||||
showMessage('success', response.data.message);
|
||||
} else {
|
||||
showMessage('error', response.data || wpban.i18n.error);
|
||||
}
|
||||
}).always(function() {
|
||||
$button.disabled = false;
|
||||
});
|
||||
};
|
||||
|
||||
// Export Settings
|
||||
window.wpbanExportSettings = function() {
|
||||
$.get(wpban.ajax_url, {
|
||||
action: 'wpban_export_settings',
|
||||
_ajax_nonce: wpban.nonce
|
||||
}, function(response) {
|
||||
if (response.success) {
|
||||
const blob = new Blob([response.data], {type: 'application/json'});
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = 'wpban-settings-' + new Date().toISOString().split('T')[0] + '.json';
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
document.body.removeChild(a);
|
||||
URL.revokeObjectURL(url);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// Optimize Database
|
||||
window.wpbanOptimizeDatabase = function() {
|
||||
const $button = event.target;
|
||||
$button.disabled = true;
|
||||
|
||||
$.post(wpban.ajax_url, {
|
||||
action: 'wpban_optimize_database',
|
||||
_ajax_nonce: wpban.nonce
|
||||
}, function(response) {
|
||||
if (response.success) {
|
||||
showMessage('success', 'Database optimized successfully!');
|
||||
}
|
||||
}).always(function() {
|
||||
$button.disabled = false;
|
||||
});
|
||||
};
|
||||
|
||||
// Clear Old Logs
|
||||
window.wpbanClearOldLogs = function() {
|
||||
if (!confirm('This will permanently delete all logs older than 30 days. Continue?')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$.post(wpban.ajax_url, {
|
||||
action: 'wpban_clear_old_logs',
|
||||
_ajax_nonce: wpban.nonce
|
||||
}, function(response) {
|
||||
if (response.success) {
|
||||
showMessage('success', response.data.message);
|
||||
setTimeout(() => location.reload(), 1500);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// Live Search for Crawlers
|
||||
let searchTimeout;
|
||||
$(document).on('input', '#wpban-crawler-search', function() {
|
||||
const query = $(this).val().toLowerCase();
|
||||
clearTimeout(searchTimeout);
|
||||
|
||||
searchTimeout = setTimeout(() => {
|
||||
$('.wpban-crawler-item').each(function() {
|
||||
const $item = $(this);
|
||||
const text = $item.text().toLowerCase();
|
||||
$item.toggle(text.indexOf(query) > -1);
|
||||
});
|
||||
}, 300);
|
||||
});
|
||||
|
||||
// Real-time Log Updates (optional)
|
||||
if ($('#wpban-logs-container').length) {
|
||||
// Auto-refresh logs every 30 seconds
|
||||
setInterval(function() {
|
||||
if (document.visibilityState === 'visible') {
|
||||
refreshLogs();
|
||||
}
|
||||
}, 30000);
|
||||
}
|
||||
|
||||
function refreshLogs() {
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
|
||||
$.get(wpban.ajax_url, {
|
||||
action: 'wpban_get_logs',
|
||||
page: params.get('paged') || 1,
|
||||
date_from: params.get('date_from'),
|
||||
date_to: params.get('date_to'),
|
||||
action_filter: params.get('action_filter'),
|
||||
ip_filter: params.get('ip_filter'),
|
||||
_ajax_nonce: wpban.nonce
|
||||
}, function(response) {
|
||||
if (response.success) {
|
||||
$('#wpban-logs-container tbody').html(response.data.html);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Show Message
|
||||
function showMessage(type, message) {
|
||||
const $message = $('<div>')
|
||||
.addClass('wpban-message ' + type)
|
||||
.text(message)
|
||||
.prependTo('.wpban-wrap')
|
||||
.hide()
|
||||
.fadeIn();
|
||||
|
||||
setTimeout(() => {
|
||||
$message.fadeOut(() => $message.remove());
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
// Geo Blocking Mode Toggle
|
||||
$('input[name="settings[geo_blocking][mode]"]').on('change', function() {
|
||||
const mode = $(this).val();
|
||||
const $label = $('.wpban-country-selector').prev('th').find('label');
|
||||
|
||||
if (mode === 'whitelist') {
|
||||
$label.text('Allowed Countries');
|
||||
} else {
|
||||
$label.text('Blocked Countries');
|
||||
}
|
||||
});
|
||||
|
||||
// Initialize Select2 for country selector (if available)
|
||||
if ($.fn.select2) {
|
||||
$('.wpban-country-selector select').select2({
|
||||
placeholder: 'Select countries...',
|
||||
width: '100%'
|
||||
});
|
||||
}
|
||||
|
||||
// Handle browser restrictions toggle
|
||||
$('input[name="settings[browser_restrictions][wechat_qq][enabled]"]').on('change', function() {
|
||||
const $fields = $(this).closest('table').find('tr').not(':first');
|
||||
$fields.toggle($(this).is(':checked'));
|
||||
}).trigger('change');
|
||||
|
||||
// Email notifications toggle
|
||||
$('input[name="settings[email_notifications][enabled]"]').on('change', function() {
|
||||
const $fields = $(this).closest('table').find('tr').not(':first');
|
||||
$fields.toggle($(this).is(':checked'));
|
||||
}).trigger('change');
|
||||
|
||||
// Geo blocking toggle
|
||||
$('input[name="settings[geo_blocking][enabled]"]').on('change', function() {
|
||||
const $fields = $(this).closest('table').find('tr').not(':first');
|
||||
$fields.toggle($(this).is(':checked'));
|
||||
}).trigger('change');
|
||||
|
||||
// Keyboard shortcuts
|
||||
$(document).on('keydown', function(e) {
|
||||
// Ctrl/Cmd + S to save
|
||||
if ((e.ctrlKey || e.metaKey) && e.key === 's') {
|
||||
e.preventDefault();
|
||||
$('#wpban-save-settings').trigger('click');
|
||||
}
|
||||
});
|
||||
|
||||
// Tooltip initialization
|
||||
$('.wpban-tooltip').tooltip({
|
||||
position: {
|
||||
my: 'center bottom-10',
|
||||
at: 'center top'
|
||||
}
|
||||
});
|
||||
|
||||
// Confirm before leaving with unsaved changes
|
||||
let formChanged = false;
|
||||
$('#wpban-settings-form').on('change', 'input, select, textarea', function() {
|
||||
formChanged = true;
|
||||
});
|
||||
|
||||
window.addEventListener('beforeunload', function(e) {
|
||||
if (formChanged) {
|
||||
e.preventDefault();
|
||||
e.returnValue = 'You have unsaved changes. Are you sure you want to leave?';
|
||||
}
|
||||
});
|
||||
|
||||
$('#wpban-save-settings').on('click', function() {
|
||||
formChanged = false;
|
||||
});
|
||||
|
||||
})(jQuery);
|
994
includes/class-wpban-admin.php
Normal file
994
includes/class-wpban-admin.php
Normal file
|
@ -0,0 +1,994 @@
|
|||
<?php
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
class WPBan_Admin {
|
||||
private $security;
|
||||
|
||||
public function __construct() {
|
||||
$this->security = $GLOBALS['wpban_security'];
|
||||
|
||||
add_action('admin_menu', [$this, 'add_menu']);
|
||||
add_action('admin_enqueue_scripts', [$this, 'enqueue_scripts']);
|
||||
|
||||
// AJAX handlers
|
||||
add_action('wp_ajax_wpban_save_settings', [$this, 'ajax_save_settings']);
|
||||
add_action('wp_ajax_wpban_apply_template', [$this, 'ajax_apply_template']);
|
||||
add_action('wp_ajax_wpban_get_logs', [$this, 'ajax_get_logs']);
|
||||
add_action('wp_ajax_wpban_export_logs', [$this, 'ajax_export_logs']);
|
||||
add_action('wp_ajax_wpban_clear_logs', [$this, 'ajax_clear_logs']);
|
||||
add_action('wp_ajax_wpban_test_email', [$this, 'ajax_test_email']);
|
||||
add_action('wp_ajax_wpban_get_country_stats', [$this, 'ajax_get_country_stats']);
|
||||
}
|
||||
|
||||
public function add_menu() {
|
||||
add_menu_page(
|
||||
__('WPBan Security', 'wpban'),
|
||||
__('WPBan Security', 'wpban'),
|
||||
'manage_options',
|
||||
'wpban',
|
||||
[$this, 'render_dashboard'],
|
||||
'dashicons-shield',
|
||||
30
|
||||
);
|
||||
|
||||
add_submenu_page(
|
||||
'wpban',
|
||||
__('Settings', 'wpban'),
|
||||
__('Settings', 'wpban'),
|
||||
'manage_options',
|
||||
'wpban-settings',
|
||||
[$this, 'render_settings']
|
||||
);
|
||||
|
||||
add_submenu_page(
|
||||
'wpban',
|
||||
__('Security Logs', 'wpban'),
|
||||
__('Logs', 'wpban'),
|
||||
'manage_options',
|
||||
'wpban-logs',
|
||||
[$this, 'render_logs']
|
||||
);
|
||||
|
||||
add_submenu_page(
|
||||
'wpban',
|
||||
__('Tools', 'wpban'),
|
||||
__('Tools', 'wpban'),
|
||||
'manage_options',
|
||||
'wpban-tools',
|
||||
[$this, 'render_tools']
|
||||
);
|
||||
}
|
||||
|
||||
public function enqueue_scripts($hook) {
|
||||
if (strpos($hook, 'wpban') === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
wp_enqueue_style('wpban-admin', WPBAN_URL . 'assets/admin.css', [], WPBAN_VERSION);
|
||||
wp_enqueue_script('wpban-admin', WPBAN_URL . 'assets/admin.js', ['jquery'], WPBAN_VERSION);
|
||||
wp_localize_script('wpban-admin', 'wpban', [
|
||||
'ajax_url' => admin_url('admin-ajax.php'),
|
||||
'nonce' => wp_create_nonce('wpban_admin'),
|
||||
'i18n' => [
|
||||
'confirm_clear' => __('Are you sure you want to clear all logs?', 'wpban'),
|
||||
'confirm_template' => __('This will replace your current settings. Continue?', 'wpban'),
|
||||
'email_sent' => __('Test email sent!', 'wpban'),
|
||||
'error' => __('An error occurred', 'wpban')
|
||||
]
|
||||
]);
|
||||
|
||||
// Add Chart.js for statistics
|
||||
wp_enqueue_script('chartjs', 'https://cdn.jsdelivr.net/npm/chart.js@3.9.1/dist/chart.min.js', [], '3.9.1');
|
||||
}
|
||||
|
||||
public function render_dashboard() {
|
||||
$stats = $this->security->get_stats();
|
||||
$bypass_url = home_url('/?wpban_bypass=' . get_option('wpban_bypass_path'));
|
||||
?>
|
||||
<div class="wrap wpban-wrap">
|
||||
<h1><?php _e('WPBan Security Dashboard', 'wpban'); ?></h1>
|
||||
|
||||
<div class="wpban-dashboard">
|
||||
<!-- Stats Grid -->
|
||||
<div class="wpban-stats-grid">
|
||||
<div class="wpban-stat-card">
|
||||
<h3><?php _e('Total Blocks', 'wpban'); ?></h3>
|
||||
<div class="wpban-stat-number"><?php echo number_format($stats['total_blocks']); ?></div>
|
||||
<div class="wpban-stat-trend">
|
||||
<?php
|
||||
$yesterday = $stats['total_blocks'] - $stats['today_blocks'];
|
||||
$trend = $yesterday > 0 ? round(($stats['today_blocks'] / $yesterday - 1) * 100) : 0;
|
||||
?>
|
||||
<span class="<?php echo $trend > 0 ? 'up' : 'down'; ?>">
|
||||
<?php echo $trend > 0 ? '↑' : '↓'; ?> <?php echo abs($trend); ?>%
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="wpban-stat-card">
|
||||
<h3><?php _e('Unique IPs', 'wpban'); ?></h3>
|
||||
<div class="wpban-stat-number"><?php echo number_format($stats['unique_ips']); ?></div>
|
||||
</div>
|
||||
|
||||
<div class="wpban-stat-card">
|
||||
<h3><?php _e('Today\'s Blocks', 'wpban'); ?></h3>
|
||||
<div class="wpban-stat-number"><?php echo number_format($stats['today_blocks']); ?></div>
|
||||
</div>
|
||||
|
||||
<div class="wpban-stat-card">
|
||||
<h3><?php _e('Active Rules', 'wpban'); ?></h3>
|
||||
<div class="wpban-stat-number"><?php echo number_format($stats['active_rules']); ?></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="wpban-grid">
|
||||
<!-- Quick Actions -->
|
||||
<div class="wpban-card">
|
||||
<h2><?php _e('Quick Actions', 'wpban'); ?></h2>
|
||||
<div class="wpban-bypass-url">
|
||||
<label><?php _e('Emergency Bypass URL:', 'wpban'); ?></label>
|
||||
<div class="wpban-input-group">
|
||||
<input type="text" value="<?php echo esc_attr($bypass_url); ?>" readonly />
|
||||
<button class="button" onclick="wpbanCopyText(this.previousElementSibling.value)">
|
||||
<?php _e('Copy', 'wpban'); ?>
|
||||
</button>
|
||||
</div>
|
||||
<p class="description"><?php _e('Save this URL to access your site if you get locked out.', 'wpban'); ?></p>
|
||||
</div>
|
||||
|
||||
<div class="wpban-actions">
|
||||
<a href="<?php echo admin_url('admin.php?page=wpban-settings'); ?>" class="button button-primary">
|
||||
<?php _e('Configure Settings', 'wpban'); ?>
|
||||
</a>
|
||||
<a href="<?php echo admin_url('admin.php?page=wpban-logs'); ?>" class="button">
|
||||
<?php _e('View Logs', 'wpban'); ?>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Country Stats -->
|
||||
<div class="wpban-card">
|
||||
<h2><?php _e('Top Blocked Countries', 'wpban'); ?></h2>
|
||||
<canvas id="wpban-country-chart" height="200"></canvas>
|
||||
<script>
|
||||
jQuery(document).ready(function($) {
|
||||
const ctx = document.getElementById('wpban-country-chart').getContext('2d');
|
||||
const data = <?php echo json_encode($stats['top_countries']); ?>;
|
||||
|
||||
new Chart(ctx, {
|
||||
type: 'bar',
|
||||
data: {
|
||||
labels: data.map(d => d.country_code || 'Unknown'),
|
||||
datasets: [{
|
||||
label: 'Blocks',
|
||||
data: data.map(d => d.count),
|
||||
backgroundColor: '#2271b1'
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
plugins: {
|
||||
legend: { display: false }
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Templates -->
|
||||
<div class="wpban-card">
|
||||
<h2><?php _e('Security Templates', 'wpban'); ?></h2>
|
||||
<p><?php _e('Quickly apply pre-configured security settings.', 'wpban'); ?></p>
|
||||
<div class="wpban-templates-grid">
|
||||
<?php foreach ($this->security->get_templates() as $id => $template): ?>
|
||||
<div class="wpban-template-card">
|
||||
<h3><?php echo esc_html($template['name']); ?></h3>
|
||||
<p><?php echo esc_html($template['description']); ?></p>
|
||||
<button class="button" onclick="wpbanApplyTemplate('<?php echo esc_attr($id); ?>')">
|
||||
<?php _e('Apply', 'wpban'); ?>
|
||||
</button>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Recent Activity -->
|
||||
<div class="wpban-card">
|
||||
<h2><?php _e('Recent Activity', 'wpban'); ?></h2>
|
||||
<table class="wp-list-table widefat">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><?php _e('Time', 'wpban'); ?></th>
|
||||
<th><?php _e('IP', 'wpban'); ?></th>
|
||||
<th><?php _e('Country', 'wpban'); ?></th>
|
||||
<th><?php _e('Action', 'wpban'); ?></th>
|
||||
<th><?php _e('Details', 'wpban'); ?></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($stats['recent_blocks'] as $log): ?>
|
||||
<tr>
|
||||
<td><?php echo human_time_diff(strtotime($log->timestamp)); ?> ago</td>
|
||||
<td><code><?php echo esc_html($log->ip); ?></code></td>
|
||||
<td><?php echo esc_html($log->country_code ?: '-'); ?></td>
|
||||
<td><span class="wpban-badge wpban-badge-<?php echo esc_attr($log->action); ?>">
|
||||
<?php echo esc_html($log->action); ?></span></td>
|
||||
<td><?php echo esc_html($log->reason); ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
public function render_settings() {
|
||||
$settings = get_option('wpban_settings', []);
|
||||
$countries = wpban_get_country_list();
|
||||
?>
|
||||
<div class="wrap wpban-wrap">
|
||||
<h1><?php _e('WPBan Security Settings', 'wpban'); ?></h1>
|
||||
|
||||
<form id="wpban-settings-form" method="post">
|
||||
<?php wp_nonce_field('wpban_settings', 'wpban_nonce'); ?>
|
||||
|
||||
<div class="wpban-tabs">
|
||||
<nav class="nav-tab-wrapper">
|
||||
<a href="#general" class="nav-tab nav-tab-active"><?php _e('General', 'wpban'); ?></a>
|
||||
<a href="#ip-rules" class="nav-tab"><?php _e('IP Rules', 'wpban'); ?></a>
|
||||
<a href="#advanced" class="nav-tab"><?php _e('Advanced', 'wpban'); ?></a>
|
||||
<a href="#rate-limit" class="nav-tab"><?php _e('Rate Limiting', 'wpban'); ?></a>
|
||||
<a href="#geo-block" class="nav-tab"><?php _e('Geo Blocking', 'wpban'); ?></a>
|
||||
<a href="#crawlers" class="nav-tab"><?php _e('Crawlers', 'wpban'); ?></a>
|
||||
<a href="#notifications" class="nav-tab"><?php _e('Notifications', 'wpban'); ?></a>
|
||||
</nav>
|
||||
|
||||
<!-- General Tab -->
|
||||
<div id="general" class="tab-content active">
|
||||
<table class="form-table">
|
||||
<tr>
|
||||
<th scope="row"><?php _e('Enable Logging', 'wpban'); ?></th>
|
||||
<td>
|
||||
<label>
|
||||
<input type="checkbox" name="settings[enable_logging]" value="1"
|
||||
<?php checked($settings['enable_logging'] ?? true); ?> />
|
||||
<?php _e('Log all security events', 'wpban'); ?>
|
||||
</label>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row"><?php _e('Reverse Proxy', 'wpban'); ?></th>
|
||||
<td>
|
||||
<label>
|
||||
<input type="checkbox" name="settings[reverse_proxy]" value="1"
|
||||
<?php checked($settings['reverse_proxy'] ?? false); ?> />
|
||||
<?php _e('Server is behind a reverse proxy (Cloudflare, nginx, etc.)', 'wpban'); ?>
|
||||
</label>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row"><?php _e('Ban Message', 'wpban'); ?></th>
|
||||
<td>
|
||||
<?php
|
||||
wp_editor(
|
||||
$settings['ban_message'] ?? '<h1>Access Denied</h1><p>Your access has been restricted.</p>',
|
||||
'ban_message',
|
||||
[
|
||||
'textarea_name' => 'settings[ban_message]',
|
||||
'textarea_rows' => 10,
|
||||
'media_buttons' => false
|
||||
]
|
||||
);
|
||||
?>
|
||||
<p class="description"><?php _e('Variables: %IP%, %DATE%, %SITE%', 'wpban'); ?></p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- IP Rules Tab -->
|
||||
<div id="ip-rules" class="tab-content">
|
||||
<table class="form-table">
|
||||
<tr>
|
||||
<th scope="row"><?php _e('Banned IPs', 'wpban'); ?></th>
|
||||
<td>
|
||||
<textarea name="settings[banned_ips]" rows="10" class="large-text code"><?php
|
||||
echo esc_textarea(implode("\n", $settings['banned_ips'] ?? []));
|
||||
?></textarea>
|
||||
<p class="description"><?php _e('One per line. Use * for wildcards (e.g., 192.168.*.*)', 'wpban'); ?></p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row"><?php _e('IP Ranges', 'wpban'); ?></th>
|
||||
<td>
|
||||
<textarea name="settings[banned_ranges]" rows="5" class="large-text code"><?php
|
||||
echo esc_textarea(implode("\n", $settings['banned_ranges'] ?? []));
|
||||
?></textarea>
|
||||
<p class="description"><?php _e('CIDR: 192.168.0.0/24 or Range: 192.168.0.1-192.168.0.255', 'wpban'); ?></p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row"><?php _e('Whitelist IPs', 'wpban'); ?></th>
|
||||
<td>
|
||||
<textarea name="settings[whitelist_ips]" rows="5" class="large-text code"><?php
|
||||
echo esc_textarea(implode("\n", $settings['whitelist_ips'] ?? []));
|
||||
?></textarea>
|
||||
<p class="description"><?php _e('These IPs will never be blocked', 'wpban'); ?></p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row"><?php _e('Login Protection', 'wpban'); ?></th>
|
||||
<td>
|
||||
<textarea name="settings[login_allowed_ips]" rows="5" class="large-text code"><?php
|
||||
echo esc_textarea(implode("\n", $settings['login_allowed_ips'] ?? []));
|
||||
?></textarea>
|
||||
<p class="description"><?php _e('Only these IPs can access wp-login.php (leave empty to allow all)', 'wpban'); ?></p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- Advanced Tab -->
|
||||
<div id="advanced" class="tab-content">
|
||||
<table class="form-table">
|
||||
<tr>
|
||||
<th scope="row"><?php _e('Banned Hosts', 'wpban'); ?></th>
|
||||
<td>
|
||||
<textarea name="settings[banned_hosts]" rows="5" class="large-text code"><?php
|
||||
echo esc_textarea(implode("\n", $settings['banned_hosts'] ?? []));
|
||||
?></textarea>
|
||||
<p class="description"><?php _e('e.g., *.badhost.com', 'wpban'); ?></p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row"><?php _e('Banned Referers', 'wpban'); ?></th>
|
||||
<td>
|
||||
<textarea name="settings[banned_referers]" rows="5" class="large-text code"><?php
|
||||
echo esc_textarea(implode("\n", $settings['banned_referers'] ?? []));
|
||||
?></textarea>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row"><?php _e('Banned User Agents', 'wpban'); ?></th>
|
||||
<td>
|
||||
<textarea name="settings[banned_agents]" rows="5" class="large-text code"><?php
|
||||
echo esc_textarea(implode("\n", $settings['banned_agents'] ?? []));
|
||||
?></textarea>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row"><?php _e('Browser Restrictions', 'wpban'); ?></th>
|
||||
<td>
|
||||
<fieldset>
|
||||
<label>
|
||||
<input type="checkbox" name="settings[browser_restrictions][wechat_qq][enabled]" value="1"
|
||||
<?php checked($settings['browser_restrictions']['wechat_qq']['enabled'] ?? false); ?> />
|
||||
<?php _e('Block WeChat/QQ Browsers', 'wpban'); ?>
|
||||
</label>
|
||||
</fieldset>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- Rate Limiting Tab -->
|
||||
<div id="rate-limit" class="tab-content">
|
||||
<h2><?php _e('Rate Limiting Settings', 'wpban'); ?></h2>
|
||||
<p><?php _e('Protect against floods and brute force attacks by limiting request rates.', 'wpban'); ?></p>
|
||||
|
||||
<table class="form-table">
|
||||
<tr>
|
||||
<th scope="row"><?php _e('General Requests', 'wpban'); ?></th>
|
||||
<td>
|
||||
<input type="number" name="settings[rate_limits][requests_per_minute]"
|
||||
value="<?php echo esc_attr($settings['rate_limits']['requests_per_minute'] ?? 60); ?>"
|
||||
min="10" max="1000" />
|
||||
<span class="description"><?php _e('requests per minute', 'wpban'); ?></span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row"><?php _e('Login Attempts', 'wpban'); ?></th>
|
||||
<td>
|
||||
<input type="number" name="settings[rate_limits][login_per_hour]"
|
||||
value="<?php echo esc_attr($settings['rate_limits']['login_per_hour'] ?? 5); ?>"
|
||||
min="1" max="50" />
|
||||
<span class="description"><?php _e('attempts per hour', 'wpban'); ?></span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row"><?php _e('API Requests', 'wpban'); ?></th>
|
||||
<td>
|
||||
<input type="number" name="settings[rate_limits][api_per_minute]"
|
||||
value="<?php echo esc_attr($settings['rate_limits']['api_per_minute'] ?? 30); ?>"
|
||||
min="5" max="500" />
|
||||
<span class="description"><?php _e('requests per minute', 'wpban'); ?></span>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- Geo Blocking Tab -->
|
||||
<div id="geo-block" class="tab-content">
|
||||
<h2><?php _e('Geographic Blocking', 'wpban'); ?></h2>
|
||||
|
||||
<table class="form-table">
|
||||
<tr>
|
||||
<th scope="row"><?php _e('Enable Geo Blocking', 'wpban'); ?></th>
|
||||
<td>
|
||||
<label>
|
||||
<input type="checkbox" name="settings[geo_blocking][enabled]" value="1"
|
||||
<?php checked($settings['geo_blocking']['enabled'] ?? false); ?> />
|
||||
<?php _e('Block or allow access based on country', 'wpban'); ?>
|
||||
</label>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row"><?php _e('Block Mode', 'wpban'); ?></th>
|
||||
<td>
|
||||
<label>
|
||||
<input type="radio" name="settings[geo_blocking][mode]" value="blacklist"
|
||||
<?php checked(($settings['geo_blocking']['mode'] ?? 'blacklist'), 'blacklist'); ?> />
|
||||
<?php _e('Block selected countries', 'wpban'); ?>
|
||||
</label><br>
|
||||
<label>
|
||||
<input type="radio" name="settings[geo_blocking][mode]" value="whitelist"
|
||||
<?php checked(($settings['geo_blocking']['mode'] ?? 'blacklist'), 'whitelist'); ?> />
|
||||
<?php _e('Allow only selected countries', 'wpban'); ?>
|
||||
</label>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row"><?php _e('Countries', 'wpban'); ?></th>
|
||||
<td>
|
||||
<div class="wpban-country-selector">
|
||||
<select multiple name="settings[geo_blocking][countries][]" size="10" style="width: 100%; max-width: 400px;">
|
||||
<?php
|
||||
$selected = $settings['geo_blocking']['blocked_countries'] ?? [];
|
||||
foreach ($countries as $code => $name): ?>
|
||||
<option value="<?php echo esc_attr($code); ?>"
|
||||
<?php selected(in_array($code, $selected)); ?>>
|
||||
<?php echo esc_html("$name ($code)"); ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<p class="description"><?php _e('Hold Ctrl/Cmd to select multiple countries', 'wpban'); ?></p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- Crawlers Tab -->
|
||||
<div id="crawlers" class="tab-content">
|
||||
<div class="wpban-crawler-controls">
|
||||
<button type="button" class="button" onclick="wpbanSelectCrawlers('ai', true)">
|
||||
<?php _e('Select All AI', 'wpban'); ?>
|
||||
</button>
|
||||
<button type="button" class="button" onclick="wpbanSelectCrawlers('seo', true)">
|
||||
<?php _e('Select All SEO', 'wpban'); ?>
|
||||
</button>
|
||||
<button type="button" class="button" onclick="wpbanSelectCrawlers('all', false)">
|
||||
<?php _e('Deselect All', 'wpban'); ?>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
$crawlers = wpban_get_crawler_list();
|
||||
$blocked = $settings['blocked_crawlers'] ?? [];
|
||||
?>
|
||||
|
||||
<h3><?php _e('AI Crawlers', 'wpban'); ?></h3>
|
||||
<div class="wpban-crawler-grid">
|
||||
<?php foreach ($crawlers['ai'] as $crawler => $info): ?>
|
||||
<label class="wpban-crawler-item" data-type="ai">
|
||||
<input type="checkbox" name="settings[blocked_crawlers][]"
|
||||
value="<?php echo esc_attr($crawler); ?>"
|
||||
<?php checked(in_array($crawler, $blocked)); ?> />
|
||||
<span class="wpban-crawler-name"><?php echo esc_html($crawler); ?></span>
|
||||
<span class="wpban-crawler-desc"><?php echo esc_html($info['description']); ?></span>
|
||||
</label>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
|
||||
<h3><?php _e('SEO Crawlers', 'wpban'); ?></h3>
|
||||
<div class="wpban-notice wpban-notice-warning">
|
||||
<p><?php _e('⚠️ Blocking SEO crawlers may affect your search engine rankings!', 'wpban'); ?></p>
|
||||
</div>
|
||||
<div class="wpban-crawler-grid">
|
||||
<?php foreach ($crawlers['seo'] as $crawler => $info): ?>
|
||||
<label class="wpban-crawler-item" data-type="seo">
|
||||
<input type="checkbox" name="settings[blocked_crawlers][]"
|
||||
value="<?php echo esc_attr($crawler); ?>"
|
||||
<?php checked(in_array($crawler, $blocked)); ?> />
|
||||
<span class="wpban-crawler-name"><?php echo esc_html($crawler); ?></span>
|
||||
<span class="wpban-crawler-desc"><?php echo esc_html($info['description']); ?></span>
|
||||
</label>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Notifications Tab -->
|
||||
<div id="notifications" class="tab-content">
|
||||
<table class="form-table">
|
||||
<tr>
|
||||
<th scope="row"><?php _e('Enable Email Notifications', 'wpban'); ?></th>
|
||||
<td>
|
||||
<label>
|
||||
<input type="checkbox" name="settings[email_notifications][enabled]" value="1"
|
||||
<?php checked($settings['email_notifications']['enabled'] ?? false); ?> />
|
||||
<?php _e('Send email alerts for security events', 'wpban'); ?>
|
||||
</label>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row"><?php _e('Recipient Email', 'wpban'); ?></th>
|
||||
<td>
|
||||
<input type="email" name="settings[email_notifications][recipient]"
|
||||
value="<?php echo esc_attr($settings['email_notifications']['recipient'] ?? get_option('admin_email')); ?>"
|
||||
class="regular-text" />
|
||||
<button type="button" class="button" onclick="wpbanTestEmail()">
|
||||
<?php _e('Send Test Email', 'wpban'); ?>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row"><?php _e('Alert Threshold', 'wpban'); ?></th>
|
||||
<td>
|
||||
<input type="number" name="settings[email_notifications][threshold]"
|
||||
value="<?php echo esc_attr($settings['email_notifications']['threshold'] ?? 10); ?>"
|
||||
min="1" max="100" />
|
||||
<span class="description"><?php _e('Send alert after this many blocks from same IP', 'wpban'); ?></span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row"><?php _e('Alert Events', 'wpban'); ?></th>
|
||||
<td>
|
||||
<?php
|
||||
$events = [
|
||||
'rate_limit' => __('Rate limit exceeded', 'wpban'),
|
||||
'geo_block' => __('Geographic block', 'wpban'),
|
||||
'brute_force' => __('Brute force attempt', 'wpban'),
|
||||
'crawler_block' => __('Crawler blocked', 'wpban')
|
||||
];
|
||||
$selected_events = $settings['email_notifications']['events'] ?? ['rate_limit', 'brute_force'];
|
||||
|
||||
foreach ($events as $event => $label): ?>
|
||||
<label style="display: block; margin-bottom: 5px;">
|
||||
<input type="checkbox" name="settings[email_notifications][events][]"
|
||||
value="<?php echo esc_attr($event); ?>"
|
||||
<?php checked(in_array($event, $selected_events)); ?> />
|
||||
<?php echo esc_html($label); ?>
|
||||
</label>
|
||||
<?php endforeach; ?>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p class="submit">
|
||||
<button type="submit" class="button button-primary" id="wpban-save-settings">
|
||||
<?php _e('Save Settings', 'wpban'); ?>
|
||||
</button>
|
||||
<span class="spinner"></span>
|
||||
</p>
|
||||
</form>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
public function render_logs() {
|
||||
$current_page = isset($_GET['paged']) ? max(1, intval($_GET['paged'])) : 1;
|
||||
$per_page = 50;
|
||||
|
||||
$filters = [
|
||||
'date_from' => sanitize_text_field($_GET['date_from'] ?? ''),
|
||||
'date_to' => sanitize_text_field($_GET['date_to'] ?? ''),
|
||||
'action' => sanitize_text_field($_GET['action_filter'] ?? ''),
|
||||
'ip' => sanitize_text_field($_GET['ip_filter'] ?? ''),
|
||||
'country' => sanitize_text_field($_GET['country_filter'] ?? '')
|
||||
];
|
||||
|
||||
$result = $this->security->get_logs($filters, $current_page, $per_page);
|
||||
?>
|
||||
<div class="wrap wpban-wrap">
|
||||
<h1><?php _e('Security Logs', 'wpban'); ?></h1>
|
||||
|
||||
<!-- Filters -->
|
||||
<div class="wpban-logs-filters">
|
||||
<form method="get" action="">
|
||||
<input type="hidden" name="page" value="wpban-logs" />
|
||||
|
||||
<input type="date" name="date_from" value="<?php echo esc_attr($filters['date_from']); ?>"
|
||||
placeholder="<?php esc_attr_e('From date', 'wpban'); ?>" />
|
||||
|
||||
<input type="date" name="date_to" value="<?php echo esc_attr($filters['date_to']); ?>"
|
||||
placeholder="<?php esc_attr_e('To date', 'wpban'); ?>" />
|
||||
|
||||
<select name="action_filter">
|
||||
<option value=""><?php _e('All Actions', 'wpban'); ?></option>
|
||||
<option value="banned" <?php selected($filters['action'], 'banned'); ?>><?php _e('Banned', 'wpban'); ?></option>
|
||||
<option value="blocked" <?php selected($filters['action'], 'blocked'); ?>><?php _e('Blocked', 'wpban'); ?></option>
|
||||
<option value="failed_login" <?php selected($filters['action'], 'failed_login'); ?>><?php _e('Failed Login', 'wpban'); ?></option>
|
||||
<option value="bypass" <?php selected($filters['action'], 'bypass'); ?>><?php _e('Bypass Used', 'wpban'); ?></option>
|
||||
</select>
|
||||
|
||||
<input type="text" name="ip_filter" value="<?php echo esc_attr($filters['ip']); ?>"
|
||||
placeholder="<?php esc_attr_e('IP Address', 'wpban'); ?>" />
|
||||
|
||||
<input type="text" name="country_filter" value="<?php echo esc_attr($filters['country']); ?>"
|
||||
placeholder="<?php esc_attr_e('Country Code', 'wpban'); ?>" size="5" />
|
||||
|
||||
<button type="submit" class="button"><?php _e('Filter', 'wpban'); ?></button>
|
||||
<a href="<?php echo admin_url('admin.php?page=wpban-logs'); ?>" class="button"><?php _e('Reset', 'wpban'); ?></a>
|
||||
|
||||
<button type="button" class="button" onclick="wpbanExportLogs()">
|
||||
<?php _e('Export CSV', 'wpban'); ?>
|
||||
</button>
|
||||
|
||||
<button type="button" class="button" onclick="wpbanClearLogs()">
|
||||
<?php _e('Clear Logs', 'wpban'); ?>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- Logs Table -->
|
||||
<table class="wp-list-table widefat fixed striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 150px;"><?php _e('Time', 'wpban'); ?></th>
|
||||
<th style="width: 120px;"><?php _e('IP Address', 'wpban'); ?></th>
|
||||
<th style="width: 60px;"><?php _e('Country', 'wpban'); ?></th>
|
||||
<th style="width: 100px;"><?php _e('Action', 'wpban'); ?></th>
|
||||
<th><?php _e('Details', 'wpban'); ?></th>
|
||||
<th><?php _e('User Agent', 'wpban'); ?></th>
|
||||
<th><?php _e('URI', 'wpban'); ?></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php if (empty($result['logs'])): ?>
|
||||
<tr>
|
||||
<td colspan="7"><?php _e('No logs found.', 'wpban'); ?></td>
|
||||
</tr>
|
||||
<?php else: ?>
|
||||
<?php foreach ($result['logs'] as $log): ?>
|
||||
<tr>
|
||||
<td><?php echo esc_html($log->timestamp); ?></td>
|
||||
<td>
|
||||
<code><?php echo esc_html($log->ip); ?></code>
|
||||
<div class="row-actions">
|
||||
<a href="?page=wpban-logs&ip_filter=<?php echo urlencode($log->ip); ?>">
|
||||
<?php _e('Filter', 'wpban'); ?>
|
||||
</a>
|
||||
</div>
|
||||
</td>
|
||||
<td><?php echo esc_html($log->country_code ?: '-'); ?></td>
|
||||
<td>
|
||||
<span class="wpban-badge wpban-badge-<?php echo esc_attr($log->action); ?>">
|
||||
<?php echo esc_html($log->action); ?>
|
||||
</span>
|
||||
</td>
|
||||
<td><?php echo esc_html($log->reason); ?></td>
|
||||
<td>
|
||||
<span class="wpban-truncate" title="<?php echo esc_attr($log->user_agent); ?>">
|
||||
<?php echo esc_html(substr($log->user_agent, 0, 50)); ?>
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<span class="wpban-truncate" title="<?php echo esc_attr($log->uri); ?>">
|
||||
<?php echo esc_html($log->uri); ?>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<!-- Pagination -->
|
||||
<?php if ($result['pages'] > 1): ?>
|
||||
<div class="tablenav bottom">
|
||||
<div class="tablenav-pages">
|
||||
<?php
|
||||
echo paginate_links([
|
||||
'base' => add_query_arg('paged', '%#%'),
|
||||
'format' => '',
|
||||
'prev_text' => '«',
|
||||
'next_text' => '»',
|
||||
'total' => $result['pages'],
|
||||
'current' => $current_page
|
||||
]);
|
||||
?>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
public function render_tools() {
|
||||
?>
|
||||
<div class="wrap wpban-wrap">
|
||||
<h1><?php _e('WPBan Tools', 'wpban'); ?></h1>
|
||||
|
||||
<div class="wpban-tools-grid">
|
||||
<!-- Import/Export -->
|
||||
<div class="wpban-card">
|
||||
<h2><?php _e('Import/Export Settings', 'wpban'); ?></h2>
|
||||
<p><?php _e('Backup your settings or migrate to another site.', 'wpban'); ?></p>
|
||||
|
||||
<h3><?php _e('Export', 'wpban'); ?></h3>
|
||||
<p>
|
||||
<button class="button" onclick="wpbanExportSettings()">
|
||||
<?php _e('Download Settings', 'wpban'); ?>
|
||||
</button>
|
||||
</p>
|
||||
|
||||
<h3><?php _e('Import', 'wpban'); ?></h3>
|
||||
<form method="post" enctype="multipart/form-data" action="<?php echo admin_url('admin-post.php'); ?>">
|
||||
<input type="hidden" name="action" value="wpban_import_settings" />
|
||||
<?php wp_nonce_field('wpban_import', 'wpban_import_nonce'); ?>
|
||||
<p>
|
||||
<input type="file" name="import_file" accept=".json" required />
|
||||
</p>
|
||||
<p>
|
||||
<button type="submit" class="button">
|
||||
<?php _e('Import Settings', 'wpban'); ?>
|
||||
</button>
|
||||
</p>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- Database Maintenance -->
|
||||
<div class="wpban-card">
|
||||
<h2><?php _e('Database Maintenance', 'wpban'); ?></h2>
|
||||
<p><?php _e('Optimize your WPBan database tables.', 'wpban'); ?></p>
|
||||
|
||||
<?php
|
||||
global $wpdb;
|
||||
$logs_count = $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->prefix}wpban_logs");
|
||||
$logs_size = $wpdb->get_var("SELECT ROUND(((data_length + index_length) / 1024 / 1024), 2)
|
||||
FROM information_schema.TABLES
|
||||
WHERE table_schema = '" . DB_NAME . "'
|
||||
AND table_name = '{$wpdb->prefix}wpban_logs'");
|
||||
?>
|
||||
|
||||
<table class="wp-list-table widefat">
|
||||
<tr>
|
||||
<th><?php _e('Total Log Entries', 'wpban'); ?></th>
|
||||
<td><?php echo number_format($logs_count); ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><?php _e('Database Size', 'wpban'); ?></th>
|
||||
<td><?php echo $logs_size; ?> MB</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p>
|
||||
<button class="button" onclick="wpbanOptimizeDatabase()">
|
||||
<?php _e('Optimize Tables', 'wpban'); ?>
|
||||
</button>
|
||||
<button class="button" onclick="wpbanClearOldLogs()">
|
||||
<?php _e('Clear Logs Older Than 30 Days', 'wpban'); ?>
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- System Info -->
|
||||
<div class="wpban-card">
|
||||
<h2><?php _e('System Information', 'wpban'); ?></h2>
|
||||
<textarea readonly class="large-text" rows="10"><?php
|
||||
echo "WordPress Version: " . get_bloginfo('version') . "\n";
|
||||
echo "PHP Version: " . PHP_VERSION . "\n";
|
||||
echo "MySQL Version: " . $wpdb->db_version() . "\n";
|
||||
echo "WPBan Version: " . WPBAN_VERSION . "\n";
|
||||
echo "Active Theme: " . wp_get_theme()->get('Name') . "\n";
|
||||
echo "Active Plugins:\n";
|
||||
foreach (get_option('active_plugins') as $plugin) {
|
||||
$plugin_data = get_plugin_data(WP_PLUGIN_DIR . '/' . $plugin);
|
||||
echo "- " . $plugin_data['Name'] . " " . $plugin_data['Version'] . "\n";
|
||||
}
|
||||
?></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
// AJAX Handlers
|
||||
public function ajax_save_settings() {
|
||||
check_ajax_referer('wpban_settings', '_ajax_nonce');
|
||||
|
||||
if (!current_user_can('manage_options')) {
|
||||
wp_send_json_error('Permission denied');
|
||||
}
|
||||
|
||||
parse_str($_POST['settings'], $data);
|
||||
$settings = $data['settings'] ?? [];
|
||||
|
||||
// Process textarea fields
|
||||
$textarea_fields = [
|
||||
'banned_ips', 'banned_ranges', 'banned_hosts',
|
||||
'banned_referers', 'banned_agents', 'whitelist_ips',
|
||||
'login_allowed_ips'
|
||||
];
|
||||
|
||||
foreach ($textarea_fields as $field) {
|
||||
if (isset($settings[$field])) {
|
||||
$settings[$field] = array_filter(array_map('trim', explode("\n", $settings[$field])));
|
||||
}
|
||||
}
|
||||
|
||||
// Process geo blocking
|
||||
if (isset($settings['geo_blocking']['mode']) && $settings['geo_blocking']['mode'] === 'whitelist') {
|
||||
$settings['geo_blocking']['allowed_countries'] = $settings['geo_blocking']['countries'] ?? [];
|
||||
$settings['geo_blocking']['blocked_countries'] = [];
|
||||
} else {
|
||||
$settings['geo_blocking']['blocked_countries'] = $settings['geo_blocking']['countries'] ?? [];
|
||||
$settings['geo_blocking']['allowed_countries'] = [];
|
||||
}
|
||||
unset($settings['geo_blocking']['countries']);
|
||||
|
||||
// Validate settings
|
||||
if (isset($settings['rate_limits'])) {
|
||||
foreach ($settings['rate_limits'] as $key => $value) {
|
||||
$settings['rate_limits'][$key] = max(1, intval($value));
|
||||
}
|
||||
}
|
||||
|
||||
update_option('wpban_settings', $settings);
|
||||
$this->security->clear_cache();
|
||||
|
||||
wp_send_json_success(['message' => __('Settings saved successfully!', 'wpban')]);
|
||||
}
|
||||
|
||||
public function ajax_apply_template() {
|
||||
check_ajax_referer('wpban_admin', '_ajax_nonce');
|
||||
|
||||
if (!current_user_can('manage_options')) {
|
||||
wp_send_json_error('Permission denied');
|
||||
}
|
||||
|
||||
$template_id = sanitize_key($_POST['template']);
|
||||
$templates = $this->security->get_templates();
|
||||
|
||||
if (!isset($templates[$template_id])) {
|
||||
wp_send_json_error(__('Invalid template', 'wpban'));
|
||||
}
|
||||
|
||||
$current = get_option('wpban_settings', []);
|
||||
$new_settings = array_merge($current, $templates[$template_id]['settings']);
|
||||
|
||||
update_option('wpban_settings', $new_settings);
|
||||
$this->security->clear_cache();
|
||||
|
||||
wp_send_json_success(['message' => __('Template applied successfully!', 'wpban')]);
|
||||
}
|
||||
|
||||
public function ajax_get_logs() {
|
||||
check_ajax_referer('wpban_admin', '_ajax_nonce');
|
||||
|
||||
if (!current_user_can('manage_options')) {
|
||||
wp_send_json_error('Permission denied');
|
||||
}
|
||||
|
||||
$page = intval($_POST['page'] ?? 1);
|
||||
$filters = [
|
||||
'date_from' => sanitize_text_field($_POST['date_from'] ?? ''),
|
||||
'date_to' => sanitize_text_field($_POST['date_to'] ?? ''),
|
||||
'action' => sanitize_text_field($_POST['action'] ?? ''),
|
||||
'ip' => sanitize_text_field($_POST['ip'] ?? '')
|
||||
];
|
||||
|
||||
$result = $this->security->get_logs($filters, $page);
|
||||
|
||||
ob_start();
|
||||
// Render log rows
|
||||
foreach ($result['logs'] as $log) {
|
||||
// ... render table rows ...
|
||||
}
|
||||
$html = ob_get_clean();
|
||||
|
||||
wp_send_json_success([
|
||||
'html' => $html,
|
||||
'pagination' => paginate_links([
|
||||
'total' => $result['pages'],
|
||||
'current' => $page
|
||||
])
|
||||
]);
|
||||
}
|
||||
|
||||
public function ajax_export_logs() {
|
||||
check_ajax_referer('wpban_admin', '_ajax_nonce');
|
||||
|
||||
if (!current_user_can('manage_options')) {
|
||||
wp_die('Permission denied');
|
||||
}
|
||||
|
||||
$logs = $this->security->get_logs([], 1, 10000);
|
||||
|
||||
header('Content-Type: text/csv');
|
||||
header('Content-Disposition: attachment; filename="wpban-logs-' . date('Y-m-d') . '.csv"');
|
||||
|
||||
$output = fopen('php://output', 'w');
|
||||
fputcsv($output, ['Time', 'IP', 'Country', 'Action', 'Reason', 'User Agent', 'Referer', 'URI']);
|
||||
|
||||
foreach ($logs['logs'] as $log) {
|
||||
fputcsv($output, [
|
||||
$log->timestamp,
|
||||
$log->ip,
|
||||
$log->country_code,
|
||||
$log->action,
|
||||
$log->reason,
|
||||
$log->user_agent,
|
||||
$log->referer,
|
||||
$log->uri
|
||||
]);
|
||||
}
|
||||
|
||||
fclose($output);
|
||||
exit;
|
||||
}
|
||||
|
||||
public function ajax_clear_logs() {
|
||||
check_ajax_referer('wpban_admin', '_ajax_nonce');
|
||||
|
||||
if (!current_user_can('manage_options')) {
|
||||
wp_send_json_error('Permission denied');
|
||||
}
|
||||
|
||||
global $wpdb;
|
||||
$wpdb->query("TRUNCATE TABLE {$wpdb->prefix}wpban_logs");
|
||||
|
||||
wp_send_json_success(['message' => __('Logs cleared successfully!', 'wpban')]);
|
||||
}
|
||||
|
||||
public function ajax_test_email() {
|
||||
check_ajax_referer('wpban_admin', '_ajax_nonce');
|
||||
|
||||
if (!current_user_can('manage_options')) {
|
||||
wp_send_json_error('Permission denied');
|
||||
}
|
||||
|
||||
$settings = get_option('wpban_settings', []);
|
||||
$to = $settings['email_notifications']['recipient'] ?? get_option('admin_email');
|
||||
$subject = sprintf('[%s] WPBan Test Email', get_bloginfo('name'));
|
||||
$message = "This is a test email from WPBan Security.\n\n";
|
||||
$message .= "If you received this email, your notifications are working correctly!";
|
||||
|
||||
$sent = wp_mail($to, $subject, $message);
|
||||
|
||||
if ($sent) {
|
||||
wp_send_json_success(['message' => __('Test email sent successfully!', 'wpban')]);
|
||||
} else {
|
||||
wp_send_json_error(__('Failed to send test email. Please check your email settings.', 'wpban'));
|
||||
}
|
||||
}
|
||||
|
||||
public function ajax_get_country_stats() {
|
||||
check_ajax_referer('wpban_admin', '_ajax_nonce');
|
||||
|
||||
if (!current_user_can('manage_options')) {
|
||||
wp_send_json_error('Permission denied');
|
||||
}
|
||||
|
||||
global $wpdb;
|
||||
$stats = $wpdb->get_results(
|
||||
"SELECT country_code, COUNT(*) as count
|
||||
FROM {$wpdb->prefix}wpban_logs
|
||||
WHERE country_code IS NOT NULL
|
||||
GROUP BY country_code
|
||||
ORDER BY count DESC
|
||||
LIMIT 20"
|
||||
);
|
||||
|
||||
wp_send_json_success($stats);
|
||||
}
|
||||
}
|
82
includes/class-wpban-cache.php
Normal file
82
includes/class-wpban-cache.php
Normal file
|
@ -0,0 +1,82 @@
|
|||
<?php
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
class WPBan_Cache {
|
||||
private $cache_group = 'wpban';
|
||||
private $use_transients = false;
|
||||
|
||||
public function __construct() {
|
||||
// Check if object caching is available
|
||||
if (!wp_using_ext_object_cache()) {
|
||||
$this->use_transients = true;
|
||||
}
|
||||
}
|
||||
|
||||
public function get($key, $callback = null, $expiration = 3600) {
|
||||
$settings = get_option('wpban_settings', []);
|
||||
if (empty($settings['enable_caching'])) {
|
||||
return $callback ? $callback() : false;
|
||||
}
|
||||
|
||||
$cache_key = $this->get_cache_key($key);
|
||||
|
||||
if ($this->use_transients) {
|
||||
$value = get_transient($cache_key);
|
||||
} else {
|
||||
$value = wp_cache_get($key, $this->cache_group);
|
||||
}
|
||||
|
||||
if ($value === false && $callback) {
|
||||
$value = $callback();
|
||||
$this->set($key, $value, $expiration);
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
public function set($key, $value, $expiration = 3600) {
|
||||
$settings = get_option('wpban_settings', []);
|
||||
if (empty($settings['enable_caching'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$cache_key = $this->get_cache_key($key);
|
||||
|
||||
if ($this->use_transients) {
|
||||
return set_transient($cache_key, $value, $expiration);
|
||||
} else {
|
||||
return wp_cache_set($key, $value, $this->cache_group, $expiration);
|
||||
}
|
||||
}
|
||||
|
||||
public function delete($key) {
|
||||
$cache_key = $this->get_cache_key($key);
|
||||
|
||||
if ($this->use_transients) {
|
||||
return delete_transient($cache_key);
|
||||
} else {
|
||||
return wp_cache_delete($key, $this->cache_group);
|
||||
}
|
||||
}
|
||||
|
||||
public function clear() {
|
||||
if ($this->use_transients) {
|
||||
// Clear all WPBan transients
|
||||
global $wpdb;
|
||||
$wpdb->query(
|
||||
"DELETE FROM {$wpdb->options}
|
||||
WHERE option_name LIKE '_transient_wpban_%'
|
||||
OR option_name LIKE '_transient_timeout_wpban_%'"
|
||||
);
|
||||
} else {
|
||||
// Clear object cache group
|
||||
wp_cache_delete_group($this->cache_group);
|
||||
}
|
||||
}
|
||||
|
||||
private function get_cache_key($key) {
|
||||
return $this->use_transients ? 'wpban_' . md5($key) : $key;
|
||||
}
|
||||
}
|
300
includes/class-wpban-core.php
Normal file
300
includes/class-wpban-core.php
Normal file
|
@ -0,0 +1,300 @@
|
|||
<?php
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
class WPBan_Core {
|
||||
private $cache;
|
||||
private $logger;
|
||||
private $settings;
|
||||
private $bypass_cookie = false;
|
||||
|
||||
public function __construct($cache, $logger) {
|
||||
$this->cache = $cache;
|
||||
$this->logger = $logger;
|
||||
$this->settings = $this->get_settings();
|
||||
|
||||
// Check bypass first
|
||||
add_action('init', [$this, 'check_bypass'], 1);
|
||||
|
||||
// Main security checks
|
||||
add_action('init', [$this, 'check_ban'], 10);
|
||||
add_action('init', [$this, 'check_login_restriction'], 11);
|
||||
add_action('wp_footer', [$this, 'check_browser_restrictions']);
|
||||
|
||||
// Robots.txt modifications
|
||||
add_filter('robots_txt', [$this, 'modify_robots_txt'], 10, 2);
|
||||
|
||||
// Performance: only load heavy checks if needed
|
||||
if ($this->should_check_crawlers()) {
|
||||
add_action('init', [$this, 'check_crawlers'], 12);
|
||||
}
|
||||
}
|
||||
|
||||
private function get_settings() {
|
||||
return $this->cache->get('settings', function() {
|
||||
return get_option('wpban_settings', [
|
||||
'banned_ips' => [],
|
||||
'banned_ranges' => [],
|
||||
'banned_hosts' => [],
|
||||
'banned_referers' => [],
|
||||
'banned_agents' => [],
|
||||
'whitelist_ips' => [],
|
||||
'login_allowed_ips' => [],
|
||||
'blocked_crawlers' => [],
|
||||
'browser_restrictions' => [],
|
||||
'ban_message' => '<h1>Access Denied</h1><p>Your access to this site has been restricted.</p>',
|
||||
'enable_logging' => true,
|
||||
'enable_caching' => true,
|
||||
'reverse_proxy' => false
|
||||
]);
|
||||
});
|
||||
}
|
||||
|
||||
public function check_bypass() {
|
||||
$bypass_path = get_option('wpban_bypass_path');
|
||||
|
||||
// Check URL bypass
|
||||
if (isset($_GET['wpban_bypass']) && $_GET['wpban_bypass'] === $bypass_path) {
|
||||
setcookie(WPBAN_BYPASS_KEY, $bypass_path, time() + DAY_IN_SECONDS, COOKIEPATH, COOKIE_DOMAIN, is_ssl(), true);
|
||||
$this->bypass_cookie = true;
|
||||
wp_redirect(remove_query_arg('wpban_bypass'));
|
||||
exit;
|
||||
}
|
||||
|
||||
// Check cookie bypass
|
||||
if (isset($_COOKIE[WPBAN_BYPASS_KEY]) && $_COOKIE[WPBAN_BYPASS_KEY] === $bypass_path) {
|
||||
$this->bypass_cookie = true;
|
||||
}
|
||||
}
|
||||
|
||||
public function check_ban() {
|
||||
if ($this->bypass_cookie) {
|
||||
return;
|
||||
}
|
||||
|
||||
$ip = wpban_get_ip($this->settings['reverse_proxy']);
|
||||
$checks = [
|
||||
'ip' => $this->is_ip_banned($ip),
|
||||
'host' => $this->is_host_banned($ip),
|
||||
'referer' => $this->is_referer_banned(),
|
||||
'agent' => $this->is_agent_banned()
|
||||
];
|
||||
|
||||
foreach ($checks as $type => $banned) {
|
||||
if ($banned && !$this->is_whitelisted($ip)) {
|
||||
$this->logger->log($ip, 'banned', $type . '_ban');
|
||||
$this->show_ban_message();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function is_ip_banned($ip) {
|
||||
// Check exact IPs and wildcards
|
||||
$banned_ips = $this->cache->get('banned_ips_compiled', function() {
|
||||
$patterns = [];
|
||||
foreach ($this->settings['banned_ips'] as $pattern) {
|
||||
$patterns[] = $this->compile_wildcard_pattern($pattern);
|
||||
}
|
||||
return $patterns;
|
||||
});
|
||||
|
||||
foreach ($banned_ips as $pattern) {
|
||||
if (preg_match($pattern, $ip)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Check IP ranges
|
||||
foreach ($this->settings['banned_ranges'] as $range) {
|
||||
if ($this->ip_in_range($ip, $range)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function is_host_banned($ip) {
|
||||
if (empty($this->settings['banned_hosts'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$hostname = $this->cache->get('hostname_' . $ip, function() use ($ip) {
|
||||
return gethostbyaddr($ip);
|
||||
}, 3600); // Cache for 1 hour
|
||||
|
||||
foreach ($this->settings['banned_hosts'] as $pattern) {
|
||||
if (wpban_match_wildcard($pattern, $hostname)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function is_referer_banned() {
|
||||
$referer = $_SERVER['HTTP_REFERER'] ?? '';
|
||||
if (empty($referer) || empty($this->settings['banned_referers'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ($this->settings['banned_referers'] as $pattern) {
|
||||
if (wpban_match_wildcard($pattern, $referer)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function is_agent_banned() {
|
||||
$agent = $_SERVER['HTTP_USER_AGENT'] ?? '';
|
||||
if (empty($agent) || empty($this->settings['banned_agents'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ($this->settings['banned_agents'] as $pattern) {
|
||||
if (wpban_match_wildcard($pattern, $agent)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function is_whitelisted($ip) {
|
||||
foreach ($this->settings['whitelist_ips'] as $pattern) {
|
||||
if (wpban_match_wildcard($pattern, $ip) || $this->ip_in_range($ip, $pattern)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function check_login_restriction() {
|
||||
if ($this->bypass_cookie || empty($this->settings['login_allowed_ips'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$login_files = ['wp-login.php', 'wp-register.php'];
|
||||
$current_file = basename($_SERVER['SCRIPT_FILENAME']);
|
||||
|
||||
if (in_array($current_file, $login_files)) {
|
||||
$ip = wpban_get_ip($this->settings['reverse_proxy']);
|
||||
$allowed = false;
|
||||
|
||||
foreach ($this->settings['login_allowed_ips'] as $pattern) {
|
||||
if (wpban_match_wildcard($pattern, $ip) || $this->ip_in_range($ip, $pattern)) {
|
||||
$allowed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$allowed) {
|
||||
$this->logger->log($ip, 'blocked', 'login_restriction');
|
||||
wp_redirect(home_url());
|
||||
exit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function check_browser_restrictions() {
|
||||
if ($this->bypass_cookie || empty($this->settings['browser_restrictions'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$ua = $_SERVER['HTTP_USER_AGENT'] ?? '';
|
||||
|
||||
// WeChat/QQ browser check
|
||||
if (isset($this->settings['browser_restrictions']['wechat_qq']) &&
|
||||
$this->settings['browser_restrictions']['wechat_qq']['enabled'] &&
|
||||
(strpos($ua, 'MQQBrowser') !== false || strpos($ua, 'MicroMessenger') !== false)) {
|
||||
|
||||
$this->logger->log(wpban_get_ip($this->settings['reverse_proxy']), 'blocked', 'browser_restriction');
|
||||
$this->show_browser_block_message($this->settings['browser_restrictions']['wechat_qq']);
|
||||
}
|
||||
}
|
||||
|
||||
public function check_crawlers() {
|
||||
if ($this->bypass_cookie || empty($this->settings['blocked_crawlers'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$ua = $_SERVER['HTTP_USER_AGENT'] ?? '';
|
||||
$ip = wpban_get_ip($this->settings['reverse_proxy']);
|
||||
|
||||
foreach ($this->settings['blocked_crawlers'] as $crawler) {
|
||||
if (stripos($ua, $crawler) !== false) {
|
||||
$this->logger->log($ip, 'blocked', 'crawler_block', $crawler);
|
||||
http_response_code(403);
|
||||
exit('Access denied for crawlers');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function modify_robots_txt($output, $public) {
|
||||
if (!empty($this->settings['blocked_crawlers'])) {
|
||||
$output .= "\n# wpban Rules\n";
|
||||
foreach ($this->settings['blocked_crawlers'] as $crawler) {
|
||||
$output .= "User-agent: $crawler\n";
|
||||
$output .= "Disallow: /\n\n";
|
||||
}
|
||||
}
|
||||
return $output;
|
||||
}
|
||||
|
||||
private function should_check_crawlers() {
|
||||
// Performance optimization: only check if crawlers are configured
|
||||
return !empty($this->settings['blocked_crawlers']);
|
||||
}
|
||||
|
||||
private function compile_wildcard_pattern($pattern) {
|
||||
$pattern = preg_quote($pattern, '/');
|
||||
$pattern = str_replace('\*', '.*', $pattern);
|
||||
return '/^' . $pattern . '$/i';
|
||||
}
|
||||
|
||||
private function ip_in_range($ip, $range) {
|
||||
if (strpos($range, '/') !== false) {
|
||||
// CIDR notation
|
||||
list($subnet, $bits) = explode('/', $range);
|
||||
$ip_long = ip2long($ip);
|
||||
$subnet_long = ip2long($subnet);
|
||||
$mask = -1 << (32 - $bits);
|
||||
return ($ip_long & $mask) == ($subnet_long & $mask);
|
||||
} elseif (strpos($range, '-') !== false) {
|
||||
// Range notation
|
||||
list($start, $end) = explode('-', $range);
|
||||
$ip_long = ip2long($ip);
|
||||
return ($ip_long >= ip2long(trim($start)) && $ip_long <= ip2long(trim($end)));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private function show_ban_message() {
|
||||
$message = $this->settings['ban_message'];
|
||||
$message = str_replace(
|
||||
['%IP%', '%DATE%', '%SITE%'],
|
||||
[wpban_get_ip($this->settings['reverse_proxy']), date('Y-m-d H:i:s'), get_bloginfo('name')],
|
||||
$message
|
||||
);
|
||||
|
||||
wp_die($message, 'Access Denied', ['response' => 403]);
|
||||
}
|
||||
|
||||
private function show_browser_block_message($settings) {
|
||||
?>
|
||||
<div style="position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.8); display: flex; justify-content: center; align-items: center; z-index: 999999;">
|
||||
<div style="background: white; padding: 30px; border-radius: 10px; max-width: 500px; text-align: center;">
|
||||
<h2><?php echo esc_html($settings['title']); ?></h2>
|
||||
<p><?php echo esc_html($settings['message']); ?></p>
|
||||
<button onclick="navigator.clipboard.writeText(window.location.href).then(() => alert('Link copied!'));" style="padding: 10px 20px; background: #2271b1; color: white; border: none; border-radius: 5px; cursor: pointer;">
|
||||
<?php echo esc_html($settings['button_text']); ?>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<script>document.body.style.overflow = 'hidden';</script>
|
||||
<?php
|
||||
}
|
||||
}
|
124
includes/class-wpban-logger.php
Normal file
124
includes/class-wpban-logger.php
Normal file
|
@ -0,0 +1,124 @@
|
|||
<?php
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
class WPBan_Logger {
|
||||
private $table_name;
|
||||
|
||||
public function __construct() {
|
||||
global $wpdb;
|
||||
$this->table_name = $wpdb->prefix . 'wpban_logs';
|
||||
}
|
||||
|
||||
public function log($ip, $action, $reason, $details = '') {
|
||||
$settings = get_option('wpban_settings', []);
|
||||
if (empty($settings['enable_logging'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
global $wpdb;
|
||||
|
||||
$data = [
|
||||
'ip' => $ip,
|
||||
'user_agent' => substr($_SERVER['HTTP_USER_AGENT'] ?? '', 0, 500),
|
||||
'referer' => substr($_SERVER['HTTP_REFERER'] ?? '', 0, 500),
|
||||
'uri' => substr($_SERVER['REQUEST_URI'] ?? '', 0, 500),
|
||||
'action' => $action,
|
||||
'reason' => $reason . ($details ? ' - ' . $details : ''),
|
||||
'timestamp' => current_time('mysql')
|
||||
];
|
||||
|
||||
$wpdb->insert($this->table_name, $data);
|
||||
|
||||
// Clean old logs (keep last 30 days)
|
||||
$this->clean_old_logs();
|
||||
}
|
||||
|
||||
public function get_logs($filters = []) {
|
||||
global $wpdb;
|
||||
|
||||
$where = [];
|
||||
$where_values = [];
|
||||
|
||||
if (!empty($filters['date'])) {
|
||||
$where[] = "DATE(timestamp) = %s";
|
||||
$where_values[] = $filters['date'];
|
||||
}
|
||||
|
||||
if (!empty($filters['action'])) {
|
||||
$where[] = "action = %s";
|
||||
$where_values[] = $filters['action'];
|
||||
}
|
||||
|
||||
if (!empty($filters['ip'])) {
|
||||
$where[] = "ip = %s";
|
||||
$where_values[] = $filters['ip'];
|
||||
}
|
||||
|
||||
$where_clause = $where ? 'WHERE ' . implode(' AND ', $where) : '';
|
||||
$limit = isset($filters['limit']) ? intval($filters['limit']) : 100;
|
||||
|
||||
$query = "SELECT * FROM {$this->table_name} {$where_clause} ORDER BY timestamp DESC LIMIT %d";
|
||||
$where_values[] = $limit;
|
||||
|
||||
return $wpdb->get_results($wpdb->prepare($query, $where_values));
|
||||
}
|
||||
|
||||
public function get_stats() {
|
||||
global $wpdb;
|
||||
|
||||
$settings = get_option('wpban_settings', []);
|
||||
|
||||
// Total blocks
|
||||
$total_blocks = $wpdb->get_var("SELECT COUNT(*) FROM {$this->table_name}");
|
||||
|
||||
// Unique IPs
|
||||
$unique_ips = $wpdb->get_var("SELECT COUNT(DISTINCT ip) FROM {$this->table_name}");
|
||||
|
||||
// Today's blocks
|
||||
$today = current_time('Y-m-d');
|
||||
$today_blocks = $wpdb->get_var($wpdb->prepare(
|
||||
"SELECT COUNT(*) FROM {$this->table_name} WHERE DATE(timestamp) = %s",
|
||||
$today
|
||||
));
|
||||
|
||||
// Count active rules
|
||||
$active_rules = 0;
|
||||
$rule_types = ['banned_ips', 'banned_ranges', 'banned_hosts', 'banned_referers', 'banned_agents', 'blocked_crawlers'];
|
||||
foreach ($rule_types as $type) {
|
||||
if (!empty($settings[$type])) {
|
||||
$active_rules += count($settings[$type]);
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
'total_blocks' => $total_blocks,
|
||||
'unique_ips' => $unique_ips,
|
||||
'today_blocks' => $today_blocks,
|
||||
'active_rules' => $active_rules
|
||||
];
|
||||
}
|
||||
|
||||
public function clear_logs() {
|
||||
global $wpdb;
|
||||
$wpdb->query("TRUNCATE TABLE {$this->table_name}");
|
||||
}
|
||||
|
||||
private function clean_old_logs() {
|
||||
global $wpdb;
|
||||
|
||||
// Run cleanup only 1% of the time to avoid performance impact
|
||||
if (mt_rand(1, 100) > 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
$days_to_keep = 30;
|
||||
$cutoff_date = date('Y-m-d H:i:s', strtotime("-{$days_to_keep} days"));
|
||||
|
||||
$wpdb->query($wpdb->prepare(
|
||||
"DELETE FROM {$this->table_name} WHERE timestamp < %s",
|
||||
$cutoff_date
|
||||
));
|
||||
}
|
||||
}
|
719
includes/class-wpban-security.php
Normal file
719
includes/class-wpban-security.php
Normal file
|
@ -0,0 +1,719 @@
|
|||
<?php
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
class WPBan_Security {
|
||||
private $settings;
|
||||
private $bypass_active = false;
|
||||
private $cache_group = 'wpban';
|
||||
private $ip_address;
|
||||
|
||||
public function __construct() {
|
||||
$this->load_settings();
|
||||
$this->ip_address = wpban_get_ip($this->settings['reverse_proxy'] ?? false);
|
||||
|
||||
// Core hooks
|
||||
add_action('init', [$this, 'check_bypass'], 1);
|
||||
add_action('init', [$this, 'check_rate_limit'], 5);
|
||||
add_action('init', [$this, 'check_geo_block'], 6);
|
||||
add_action('init', [$this, 'check_ban'], 10);
|
||||
add_action('init', [$this, 'check_login_restriction'], 11);
|
||||
add_action('init', [$this, 'check_crawlers'], 12);
|
||||
add_action('wp_footer', [$this, 'check_browser_restrictions']);
|
||||
add_filter('robots_txt', [$this, 'modify_robots_txt'], 10, 2);
|
||||
|
||||
// Login protection
|
||||
add_action('wp_login_failed', [$this, 'handle_login_failed']);
|
||||
add_filter('authenticate', [$this, 'check_login_attempt'], 30, 3);
|
||||
}
|
||||
|
||||
private function load_settings() {
|
||||
$this->settings = wp_cache_get('settings', $this->cache_group);
|
||||
if ($this->settings === false) {
|
||||
$this->settings = get_option('wpban_settings', []);
|
||||
wp_cache_set('settings', $this->settings, $this->cache_group, 3600);
|
||||
}
|
||||
}
|
||||
|
||||
public function check_bypass() {
|
||||
$bypass_path = get_option('wpban_bypass_path');
|
||||
|
||||
// URL bypass
|
||||
if (isset($_GET['wpban_bypass']) && $_GET['wpban_bypass'] === $bypass_path) {
|
||||
setcookie(WPBAN_BYPASS_KEY, $bypass_path, time() + DAY_IN_SECONDS, COOKIEPATH, COOKIE_DOMAIN, is_ssl(), true);
|
||||
$this->bypass_active = true;
|
||||
$this->log('bypass', 'bypass_activated');
|
||||
wp_redirect(remove_query_arg('wpban_bypass'));
|
||||
exit;
|
||||
}
|
||||
|
||||
// Cookie bypass
|
||||
if (isset($_COOKIE[WPBAN_BYPASS_KEY]) && $_COOKIE[WPBAN_BYPASS_KEY] === $bypass_path) {
|
||||
$this->bypass_active = true;
|
||||
}
|
||||
}
|
||||
|
||||
public function check_rate_limit() {
|
||||
if ($this->bypass_active || empty($this->settings['rate_limits'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
global $wpdb;
|
||||
$table = $wpdb->prefix . 'wpban_rate_limits';
|
||||
$limits = $this->settings['rate_limits'];
|
||||
|
||||
// Determine action type
|
||||
$action = 'general';
|
||||
if (strpos($_SERVER['REQUEST_URI'], '/wp-login.php') !== false) {
|
||||
$action = 'login';
|
||||
} elseif (strpos($_SERVER['REQUEST_URI'], '/wp-json/') !== false) {
|
||||
$action = 'api';
|
||||
}
|
||||
|
||||
// Get limit settings
|
||||
$window = 60; // seconds
|
||||
$max_requests = $limits['requests_per_minute'] ?? 60;
|
||||
|
||||
if ($action === 'login') {
|
||||
$window = 3600;
|
||||
$max_requests = $limits['login_per_hour'] ?? 5;
|
||||
} elseif ($action === 'api') {
|
||||
$max_requests = $limits['api_per_minute'] ?? 30;
|
||||
}
|
||||
|
||||
$window_start = date('Y-m-d H:i:s', time() - $window);
|
||||
|
||||
// Check current count
|
||||
$current = $wpdb->get_row($wpdb->prepare(
|
||||
"SELECT count, window_start FROM $table WHERE ip = %s AND action = %s AND window_start > %s",
|
||||
$this->ip_address, $action, $window_start
|
||||
));
|
||||
|
||||
if ($current) {
|
||||
if ($current->count >= $max_requests) {
|
||||
$this->log('blocked', 'rate_limit', $action);
|
||||
$this->send_notification('rate_limit', [
|
||||
'action' => $action,
|
||||
'count' => $current->count
|
||||
]);
|
||||
wp_die(__('Too many requests. Please try again later.', 'wpban'), 429);
|
||||
}
|
||||
|
||||
// Increment counter
|
||||
$wpdb->query($wpdb->prepare(
|
||||
"UPDATE $table SET count = count + 1, last_attempt = %s WHERE ip = %s AND action = %s",
|
||||
current_time('mysql'), $this->ip_address, $action
|
||||
));
|
||||
} else {
|
||||
// Create new record
|
||||
$wpdb->insert($table, [
|
||||
'ip' => $this->ip_address,
|
||||
'action' => $action,
|
||||
'count' => 1,
|
||||
'window_start' => current_time('mysql'),
|
||||
'last_attempt' => current_time('mysql')
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
public function check_geo_block() {
|
||||
if ($this->bypass_active || empty($this->settings['geo_blocking']['enabled'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$country = $this->get_country_code($this->ip_address);
|
||||
$blocked_countries = $this->settings['geo_blocking']['blocked_countries'] ?? [];
|
||||
$allowed_countries = $this->settings['geo_blocking']['allowed_countries'] ?? [];
|
||||
|
||||
// If allowed list is set, only allow those countries
|
||||
if (!empty($allowed_countries) && !in_array($country, $allowed_countries)) {
|
||||
$this->log('blocked', 'geo_block', $country);
|
||||
$this->send_notification('geo_block', ['country' => $country]);
|
||||
wp_die(__('Access denied from your location.', 'wpban'), 403);
|
||||
}
|
||||
|
||||
// Check blocked countries
|
||||
if (in_array($country, $blocked_countries)) {
|
||||
$this->log('blocked', 'geo_block', $country);
|
||||
$this->send_notification('geo_block', ['country' => $country]);
|
||||
wp_die(__('Access denied from your location.', 'wpban'), 403);
|
||||
}
|
||||
}
|
||||
|
||||
public function check_ban() {
|
||||
if ($this->bypass_active) {
|
||||
return;
|
||||
}
|
||||
|
||||
$checks = [
|
||||
'ip' => $this->is_ip_banned(),
|
||||
'host' => $this->is_host_banned(),
|
||||
'referer' => $this->is_referer_banned(),
|
||||
'agent' => $this->is_agent_banned()
|
||||
];
|
||||
|
||||
foreach ($checks as $type => $banned) {
|
||||
if ($banned && !$this->is_whitelisted()) {
|
||||
$this->log('banned', $type . '_ban');
|
||||
$this->show_ban_message();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function check_login_restriction() {
|
||||
if ($this->bypass_active || empty($this->settings['login_allowed_ips'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$login_files = ['wp-login.php', 'wp-register.php', 'xmlrpc.php'];
|
||||
$current_file = basename($_SERVER['SCRIPT_FILENAME']);
|
||||
|
||||
if (in_array($current_file, $login_files)) {
|
||||
$allowed = false;
|
||||
foreach ($this->settings['login_allowed_ips'] as $pattern) {
|
||||
if (wpban_match_pattern($pattern, $this->ip_address)) {
|
||||
$allowed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$allowed) {
|
||||
$this->log('blocked', 'login_restriction');
|
||||
wp_redirect(home_url());
|
||||
exit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function check_crawlers() {
|
||||
if ($this->bypass_active || empty($this->settings['blocked_crawlers'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$ua = $_SERVER['HTTP_USER_AGENT'] ?? '';
|
||||
foreach ($this->settings['blocked_crawlers'] as $crawler) {
|
||||
if (stripos($ua, $crawler) !== false) {
|
||||
$this->log('blocked', 'crawler_block', $crawler);
|
||||
http_response_code(403);
|
||||
exit('Access denied');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function check_browser_restrictions() {
|
||||
if ($this->bypass_active || empty($this->settings['browser_restrictions'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$ua = $_SERVER['HTTP_USER_AGENT'] ?? '';
|
||||
|
||||
if (isset($this->settings['browser_restrictions']['wechat_qq']) &&
|
||||
$this->settings['browser_restrictions']['wechat_qq']['enabled'] &&
|
||||
(strpos($ua, 'MQQBrowser') !== false || strpos($ua, 'MicroMessenger') !== false)) {
|
||||
|
||||
$this->log('blocked', 'browser_restriction', 'WeChat/QQ');
|
||||
$this->show_browser_block_message($this->settings['browser_restrictions']['wechat_qq']);
|
||||
}
|
||||
}
|
||||
|
||||
public function handle_login_failed($username) {
|
||||
$this->log('failed_login', 'login_attempt', $username);
|
||||
|
||||
// Check for brute force
|
||||
global $wpdb;
|
||||
$table = $wpdb->prefix . 'wpban_logs';
|
||||
$count = $wpdb->get_var($wpdb->prepare(
|
||||
"SELECT COUNT(*) FROM $table WHERE ip = %s AND action = 'failed_login' AND timestamp > %s",
|
||||
$this->ip_address,
|
||||
date('Y-m-d H:i:s', strtotime('-1 hour'))
|
||||
));
|
||||
|
||||
if ($count >= 10) {
|
||||
$this->send_notification('brute_force', [
|
||||
'attempts' => $count,
|
||||
'username' => $username
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
public function check_login_attempt($user, $username, $password) {
|
||||
if (empty($username) || empty($password)) {
|
||||
return $user;
|
||||
}
|
||||
|
||||
// Additional login security checks can be added here
|
||||
return $user;
|
||||
}
|
||||
|
||||
public function modify_robots_txt($output, $public) {
|
||||
if (!empty($this->settings['blocked_crawlers'])) {
|
||||
$output .= "\n# WPBan Security Rules\n";
|
||||
foreach ($this->settings['blocked_crawlers'] as $crawler) {
|
||||
$output .= "User-agent: $crawler\n";
|
||||
$output .= "Disallow: /\n\n";
|
||||
}
|
||||
}
|
||||
return $output;
|
||||
}
|
||||
|
||||
// Helper methods
|
||||
private function is_ip_banned() {
|
||||
foreach ($this->settings['banned_ips'] ?? [] as $pattern) {
|
||||
if (wpban_match_pattern($pattern, $this->ip_address)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($this->settings['banned_ranges'] ?? [] as $range) {
|
||||
if (wpban_ip_in_range($this->ip_address, $range)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function is_host_banned() {
|
||||
if (empty($this->settings['banned_hosts'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$hostname = $this->get_cached_data('hostname_' . $this->ip_address, function() {
|
||||
return gethostbyaddr($this->ip_address);
|
||||
}, 3600);
|
||||
|
||||
foreach ($this->settings['banned_hosts'] as $pattern) {
|
||||
if (wpban_match_pattern($pattern, $hostname)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function is_referer_banned() {
|
||||
$referer = $_SERVER['HTTP_REFERER'] ?? '';
|
||||
if (empty($referer) || empty($this->settings['banned_referers'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ($this->settings['banned_referers'] as $pattern) {
|
||||
if (wpban_match_pattern($pattern, $referer)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function is_agent_banned() {
|
||||
$agent = $_SERVER['HTTP_USER_AGENT'] ?? '';
|
||||
if (empty($agent) || empty($this->settings['banned_agents'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ($this->settings['banned_agents'] as $pattern) {
|
||||
if (wpban_match_pattern($pattern, $agent)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function is_whitelisted() {
|
||||
foreach ($this->settings['whitelist_ips'] ?? [] as $pattern) {
|
||||
if (wpban_match_pattern($pattern, $this->ip_address) ||
|
||||
wpban_ip_in_range($this->ip_address, $pattern)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private function get_country_code($ip) {
|
||||
global $wpdb;
|
||||
$table = $wpdb->prefix . 'wpban_geo_cache';
|
||||
|
||||
// Check cache first
|
||||
$cached = $wpdb->get_var($wpdb->prepare(
|
||||
"SELECT country_code FROM $table WHERE ip = %s",
|
||||
$ip
|
||||
));
|
||||
|
||||
if ($cached) {
|
||||
return $cached;
|
||||
}
|
||||
|
||||
// Use IP geolocation service
|
||||
$country = $this->lookup_country($ip);
|
||||
|
||||
// Cache result
|
||||
if ($country) {
|
||||
$wpdb->replace($table, [
|
||||
'ip' => $ip,
|
||||
'country_code' => $country['code'],
|
||||
'country_name' => $country['name'],
|
||||
'city' => $country['city'] ?? null,
|
||||
'cached_at' => current_time('mysql')
|
||||
]);
|
||||
}
|
||||
|
||||
return $country['code'] ?? 'XX';
|
||||
}
|
||||
|
||||
private function lookup_country($ip) {
|
||||
// Try multiple services
|
||||
$services = [
|
||||
'ipapi' => "http://ip-api.com/json/{$ip}?fields=status,country,countryCode,city",
|
||||
'ipinfo' => "https://ipinfo.io/{$ip}/json"
|
||||
];
|
||||
|
||||
foreach ($services as $name => $url) {
|
||||
$response = wp_remote_get($url, ['timeout' => 2]);
|
||||
|
||||
if (!is_wp_error($response)) {
|
||||
$data = json_decode(wp_remote_retrieve_body($response), true);
|
||||
|
||||
if ($name === 'ipapi' && $data['status'] === 'success') {
|
||||
return [
|
||||
'code' => $data['countryCode'],
|
||||
'name' => $data['country'],
|
||||
'city' => $data['city']
|
||||
];
|
||||
} elseif ($name === 'ipinfo' && isset($data['country'])) {
|
||||
return [
|
||||
'code' => $data['country'],
|
||||
'name' => $data['country'],
|
||||
'city' => $data['city'] ?? null
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private function log($action, $reason, $details = '') {
|
||||
if (empty($this->settings['enable_logging'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
global $wpdb;
|
||||
$country = $action === 'geo_block' ? $details : $this->get_country_code($this->ip_address);
|
||||
|
||||
$wpdb->insert($wpdb->prefix . 'wpban_logs', [
|
||||
'ip' => $this->ip_address,
|
||||
'country_code' => $country,
|
||||
'user_agent' => substr($_SERVER['HTTP_USER_AGENT'] ?? '', 0, 500),
|
||||
'referer' => substr($_SERVER['HTTP_REFERER'] ?? '', 0, 500),
|
||||
'uri' => substr($_SERVER['REQUEST_URI'] ?? '', 0, 500),
|
||||
'action' => $action,
|
||||
'reason' => $reason . ($details ? ' - ' . $details : ''),
|
||||
'timestamp' => current_time('mysql')
|
||||
]);
|
||||
}
|
||||
|
||||
private function send_notification($type, $data = []) {
|
||||
if (empty($this->settings['email_notifications']['enabled']) ||
|
||||
!in_array($type, $this->settings['email_notifications']['events'] ?? [])) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check threshold
|
||||
global $wpdb;
|
||||
$count = $wpdb->get_var($wpdb->prepare(
|
||||
"SELECT COUNT(*) FROM {$wpdb->prefix}wpban_logs
|
||||
WHERE ip = %s AND timestamp > %s",
|
||||
$this->ip_address,
|
||||
date('Y-m-d H:i:s', strtotime('-1 hour'))
|
||||
));
|
||||
|
||||
if ($count < ($this->settings['email_notifications']['threshold'] ?? 10)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Send email
|
||||
$to = $this->settings['email_notifications']['recipient'];
|
||||
$subject = sprintf(__('[%s] Security Alert: %s', 'wpban'), get_bloginfo('name'), $type);
|
||||
$message = $this->format_notification_email($type, $data);
|
||||
|
||||
wp_mail($to, $subject, $message);
|
||||
}
|
||||
|
||||
private function format_notification_email($type, $data) {
|
||||
$site = get_bloginfo('name');
|
||||
$time = current_time('mysql');
|
||||
|
||||
$message = "Security alert on {$site}\n\n";
|
||||
$message .= "Event: {$type}\n";
|
||||
$message .= "Time: {$time}\n";
|
||||
$message .= "IP Address: {$this->ip_address}\n";
|
||||
$message .= "Country: " . $this->get_country_code($this->ip_address) . "\n";
|
||||
|
||||
if (!empty($data)) {
|
||||
$message .= "\nDetails:\n";
|
||||
foreach ($data as $key => $value) {
|
||||
$message .= "- {$key}: {$value}\n";
|
||||
}
|
||||
}
|
||||
|
||||
$message .= "\nView logs: " . admin_url('admin.php?page=wpban-logs');
|
||||
|
||||
return $message;
|
||||
}
|
||||
|
||||
private function show_ban_message() {
|
||||
$message = $this->settings['ban_message'] ?? '<h1>Access Denied</h1>';
|
||||
$message = str_replace(
|
||||
['%IP%', '%DATE%', '%SITE%'],
|
||||
[$this->ip_address, current_time('mysql'), get_bloginfo('name')],
|
||||
$message
|
||||
);
|
||||
|
||||
wp_die($message, 'Access Denied', ['response' => 403]);
|
||||
}
|
||||
|
||||
private function show_browser_block_message($settings) {
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title><?php echo esc_html($settings['title']); ?></title>
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
||||
background: rgba(0,0,0,0.9);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 100vh;
|
||||
}
|
||||
.block-container {
|
||||
background: white;
|
||||
padding: 40px;
|
||||
border-radius: 10px;
|
||||
max-width: 500px;
|
||||
text-align: center;
|
||||
box-shadow: 0 10px 30px rgba(0,0,0,0.3);
|
||||
}
|
||||
h1 {
|
||||
margin: 0 0 20px 0;
|
||||
color: #333;
|
||||
}
|
||||
p {
|
||||
color: #666;
|
||||
line-height: 1.6;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
button {
|
||||
background: #2271b1;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 12px 30px;
|
||||
border-radius: 5px;
|
||||
font-size: 16px;
|
||||
cursor: pointer;
|
||||
transition: background 0.3s;
|
||||
}
|
||||
button:hover {
|
||||
background: #135e96;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="block-container">
|
||||
<h1><?php echo esc_html($settings['title']); ?></h1>
|
||||
<p><?php echo esc_html($settings['message']); ?></p>
|
||||
<button onclick="copyLink()">
|
||||
<?php echo esc_html($settings['button_text']); ?>
|
||||
</button>
|
||||
</div>
|
||||
<script>
|
||||
function copyLink() {
|
||||
navigator.clipboard.writeText(window.location.href).then(() => {
|
||||
alert('<?php _e('Link copied to clipboard!', 'wpban'); ?>');
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
<?php
|
||||
exit;
|
||||
}
|
||||
|
||||
private function get_cached_data($key, $callback, $expiration = 3600) {
|
||||
$value = wp_cache_get($key, $this->cache_group);
|
||||
|
||||
if ($value === false) {
|
||||
$value = $callback();
|
||||
wp_cache_set($key, $value, $this->cache_group, $expiration);
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
// Public methods for admin
|
||||
public function get_logs($filters = [], $page = 1, $per_page = 50) {
|
||||
global $wpdb;
|
||||
$table = $wpdb->prefix . 'wpban_logs';
|
||||
|
||||
$where = [];
|
||||
$where_values = [];
|
||||
|
||||
if (!empty($filters['date_from'])) {
|
||||
$where[] = "timestamp >= %s";
|
||||
$where_values[] = $filters['date_from'] . ' 00:00:00';
|
||||
}
|
||||
|
||||
if (!empty($filters['date_to'])) {
|
||||
$where[] = "timestamp <= %s";
|
||||
$where_values[] = $filters['date_to'] . ' 23:59:59';
|
||||
}
|
||||
|
||||
if (!empty($filters['action'])) {
|
||||
$where[] = "action = %s";
|
||||
$where_values[] = $filters['action'];
|
||||
}
|
||||
|
||||
if (!empty($filters['ip'])) {
|
||||
$where[] = "ip LIKE %s";
|
||||
$where_values[] = '%' . $wpdb->esc_like($filters['ip']) . '%';
|
||||
}
|
||||
|
||||
if (!empty($filters['country'])) {
|
||||
$where[] = "country_code = %s";
|
||||
$where_values[] = $filters['country'];
|
||||
}
|
||||
|
||||
$where_clause = $where ? 'WHERE ' . implode(' AND ', $where) : '';
|
||||
|
||||
// Get total count
|
||||
$count_query = "SELECT COUNT(*) FROM $table $where_clause";
|
||||
$total = $wpdb->get_var($wpdb->prepare($count_query, $where_values));
|
||||
|
||||
// Get logs with pagination
|
||||
$offset = ($page - 1) * $per_page;
|
||||
$query = "SELECT * FROM $table $where_clause ORDER BY timestamp DESC LIMIT %d OFFSET %d";
|
||||
$where_values[] = $per_page;
|
||||
$where_values[] = $offset;
|
||||
|
||||
$logs = $wpdb->get_results($wpdb->prepare($query, $where_values));
|
||||
|
||||
return [
|
||||
'logs' => $logs,
|
||||
'total' => $total,
|
||||
'pages' => ceil($total / $per_page),
|
||||
'current_page' => $page
|
||||
];
|
||||
}
|
||||
|
||||
public function get_stats() {
|
||||
global $wpdb;
|
||||
$prefix = $wpdb->prefix;
|
||||
|
||||
$stats = [
|
||||
'total_blocks' => $wpdb->get_var("SELECT COUNT(*) FROM {$prefix}wpban_logs"),
|
||||
'unique_ips' => $wpdb->get_var("SELECT COUNT(DISTINCT ip) FROM {$prefix}wpban_logs"),
|
||||
'today_blocks' => $wpdb->get_var($wpdb->prepare(
|
||||
"SELECT COUNT(*) FROM {$prefix}wpban_logs WHERE DATE(timestamp) = %s",
|
||||
current_time('Y-m-d')
|
||||
)),
|
||||
'active_rules' => $this->count_active_rules(),
|
||||
'top_countries' => $wpdb->get_results(
|
||||
"SELECT country_code, COUNT(*) as count
|
||||
FROM {$prefix}wpban_logs
|
||||
WHERE country_code IS NOT NULL
|
||||
GROUP BY country_code
|
||||
ORDER BY count DESC
|
||||
LIMIT 10"
|
||||
),
|
||||
'recent_blocks' => $wpdb->get_results(
|
||||
"SELECT * FROM {$prefix}wpban_logs
|
||||
ORDER BY timestamp DESC
|
||||
LIMIT 10"
|
||||
)
|
||||
];
|
||||
|
||||
return $stats;
|
||||
}
|
||||
|
||||
private function count_active_rules() {
|
||||
$count = 0;
|
||||
$rule_types = [
|
||||
'banned_ips', 'banned_ranges', 'banned_hosts',
|
||||
'banned_referers', 'banned_agents', 'blocked_crawlers'
|
||||
];
|
||||
|
||||
foreach ($rule_types as $type) {
|
||||
$count += count($this->settings[$type] ?? []);
|
||||
}
|
||||
|
||||
return $count;
|
||||
}
|
||||
|
||||
public function clear_cache() {
|
||||
wp_cache_delete_group($this->cache_group);
|
||||
}
|
||||
|
||||
public function get_templates() {
|
||||
return [
|
||||
'basic' => [
|
||||
'name' => __('Basic Protection', 'wpban'),
|
||||
'description' => __('Essential security for most WordPress sites', 'wpban'),
|
||||
'settings' => [
|
||||
'banned_agents' => ['*bot*', '*crawler*', '*spider*'],
|
||||
'enable_logging' => true,
|
||||
'rate_limits' => [
|
||||
'requests_per_minute' => 60,
|
||||
'login_per_hour' => 5
|
||||
]
|
||||
]
|
||||
],
|
||||
'strict' => [
|
||||
'name' => __('Strict Security', 'wpban'),
|
||||
'description' => __('Maximum protection with geo-blocking and rate limiting', 'wpban'),
|
||||
'settings' => [
|
||||
'banned_agents' => ['*bot*', '*crawler*', '*spider*', '*scraper*', 'curl*', 'wget*'],
|
||||
'blocked_crawlers' => ['GPTBot', 'ChatGPT-User', 'ClaudeBot', 'CCBot'],
|
||||
'rate_limits' => [
|
||||
'requests_per_minute' => 30,
|
||||
'login_per_hour' => 3,
|
||||
'api_per_minute' => 10
|
||||
],
|
||||
'geo_blocking' => [
|
||||
'enabled' => true,
|
||||
'blocked_countries' => []
|
||||
]
|
||||
]
|
||||
],
|
||||
'content' => [
|
||||
'name' => __('Content Protection', 'wpban'),
|
||||
'description' => __('Protect content from AI and scraping', 'wpban'),
|
||||
'settings' => [
|
||||
'blocked_crawlers' => array_keys(wpban_get_crawler_list()['ai']),
|
||||
'banned_agents' => ['*GPT*', '*Claude*', '*AI*Bot*'],
|
||||
'rate_limits' => [
|
||||
'requests_per_minute' => 30
|
||||
]
|
||||
]
|
||||
],
|
||||
'performance' => [
|
||||
'name' => __('Performance Mode', 'wpban'),
|
||||
'description' => __('Balanced security with minimal performance impact', 'wpban'),
|
||||
'settings' => [
|
||||
'enable_logging' => false,
|
||||
'rate_limits' => [
|
||||
'requests_per_minute' => 120,
|
||||
'login_per_hour' => 10
|
||||
]
|
||||
]
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
131
includes/class-wpban-templates.php
Normal file
131
includes/class-wpban-templates.php
Normal file
|
@ -0,0 +1,131 @@
|
|||
<?php
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
class WPBan_Templates {
|
||||
private $templates;
|
||||
|
||||
public function __construct() {
|
||||
$this->init_templates();
|
||||
}
|
||||
|
||||
private function init_templates() {
|
||||
$this->templates = [
|
||||
'basic' => [
|
||||
'name' => __('Basic Protection', 'wpban'),
|
||||
'description' => __('Essential security for most WordPress sites', 'wpban'),
|
||||
'settings' => [
|
||||
'banned_agents' => ['*bot*', '*crawler*', '*spider*'],
|
||||
'blocked_crawlers' => [],
|
||||
'enable_logging' => true,
|
||||
'enable_caching' => true,
|
||||
'ban_message' => '<h1>Access Denied</h1><p>Your access to this site has been restricted.</p>'
|
||||
]
|
||||
],
|
||||
'strict' => [
|
||||
'name' => __('Strict Security', 'wpban'),
|
||||
'description' => __('Maximum protection with login restrictions', 'wpban'),
|
||||
'settings' => [
|
||||
'banned_agents' => ['*bot*', '*crawler*', '*spider*', '*scraper*', 'curl*', 'wget*'],
|
||||
'blocked_crawlers' => ['GPTBot', 'ChatGPT-User', 'ClaudeBot', 'CCBot', 'PerplexityBot', 'Bytespider'],
|
||||
'browser_restrictions' => [
|
||||
'wechat_qq' => [
|
||||
'enabled' => true,
|
||||
'title' => __('Browser Not Supported', 'wpban'),
|
||||
'message' => __('Please use Chrome, Firefox, or Safari to access this site.', 'wpban'),
|
||||
'button_text' => __('Copy Link', 'wpban')
|
||||
]
|
||||
],
|
||||
'enable_logging' => true,
|
||||
'enable_caching' => true
|
||||
]
|
||||
],
|
||||
'content' => [
|
||||
'name' => __('Content Protection', 'wpban'),
|
||||
'description' => __('Protect original content from AI crawlers', 'wpban'),
|
||||
'settings' => [
|
||||
'blocked_crawlers' => [
|
||||
'GPTBot', 'ChatGPT-User', 'ClaudeBot', 'Claude-Web', 'anthropic-ai',
|
||||
'CCBot', 'PerplexityBot', 'Bytespider', 'FacebookBot', 'Meta-ExternalAgent',
|
||||
'cohere-ai', 'AI2Bot', 'Ai2Bot-Dolma', 'Google-Extended', 'ImagesiftBot'
|
||||
],
|
||||
'banned_agents' => ['*GPT*', '*Claude*', '*AI*Bot*'],
|
||||
'enable_logging' => true,
|
||||
'enable_caching' => true
|
||||
]
|
||||
],
|
||||
'membership' => [
|
||||
'name' => __('Membership Site', 'wpban'),
|
||||
'description' => __('Ideal for private or membership websites', 'wpban'),
|
||||
'settings' => [
|
||||
'banned_agents' => ['*bot*', '*crawler*', '*spider*', '*scraper*'],
|
||||
'blocked_crawlers' => array_merge(
|
||||
wpban_get_crawler_list()['ai'],
|
||||
wpban_get_crawler_list()['seo']
|
||||
),
|
||||
'banned_referers' => ['*google.*', '*bing.*', '*yahoo.*', '*baidu.*'],
|
||||
'enable_logging' => true,
|
||||
'enable_caching' => true
|
||||
]
|
||||
],
|
||||
'development' => [
|
||||
'name' => __('Development Mode', 'wpban'),
|
||||
'description' => __('Minimal restrictions for development sites', 'wpban'),
|
||||
'settings' => [
|
||||
'enable_logging' => true,
|
||||
'enable_caching' => false,
|
||||
'ban_message' => '<h1>Site Under Development</h1><p>This site is currently under development.</p>'
|
||||
]
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
public function get_all_templates() {
|
||||
return $this->templates;
|
||||
}
|
||||
|
||||
public function get_template($id) {
|
||||
return isset($this->templates[$id]) ? $this->templates[$id] : null;
|
||||
}
|
||||
|
||||
public function apply_template($template_id) {
|
||||
$template = $this->get_template($template_id);
|
||||
if (!$template) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get current settings
|
||||
$current_settings = get_option('wpban_settings', []);
|
||||
|
||||
// Merge template settings with defaults
|
||||
$new_settings = array_merge(
|
||||
[
|
||||
'banned_ips' => [],
|
||||
'banned_ranges' => [],
|
||||
'banned_hosts' => [],
|
||||
'banned_referers' => [],
|
||||
'banned_agents' => [],
|
||||
'whitelist_ips' => [],
|
||||
'login_allowed_ips' => [],
|
||||
'blocked_crawlers' => [],
|
||||
'browser_restrictions' => [],
|
||||
'ban_message' => '<h1>Access Denied</h1><p>Your access to this site has been restricted.</p>',
|
||||
'enable_logging' => true,
|
||||
'enable_caching' => true,
|
||||
'reverse_proxy' => false
|
||||
],
|
||||
$template['settings']
|
||||
);
|
||||
|
||||
// Preserve some user settings
|
||||
$preserve_fields = ['whitelist_ips', 'reverse_proxy'];
|
||||
foreach ($preserve_fields as $field) {
|
||||
if (isset($current_settings[$field])) {
|
||||
$new_settings[$field] = $current_settings[$field];
|
||||
}
|
||||
}
|
||||
|
||||
return $new_settings;
|
||||
}
|
||||
}
|
|
@ -1,44 +1,395 @@
|
|||
<?php
|
||||
namespace WPBan_Anything;
|
||||
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
function ban_anything_get_ip() {
|
||||
/**
|
||||
* Get client IP address with proxy support
|
||||
*/
|
||||
function wpban_get_ip($use_proxy = false) {
|
||||
$ip = $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0';
|
||||
$options = get_option('banned_options', ['reverse_proxy' => 0]);
|
||||
if ($options['reverse_proxy']) {
|
||||
if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
|
||||
$ip = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR'])[0];
|
||||
} elseif (!empty($_SERVER['HTTP_X_REAL_IP'])) {
|
||||
$ip = $_SERVER['HTTP_X_REAL_IP'];
|
||||
} elseif (!empty($_SERVER['HTTP_CLIENT_IP'])) {
|
||||
$ip = $_SERVER['HTTP_CLIENT_IP'];
|
||||
|
||||
if ($use_proxy) {
|
||||
$proxy_headers = [
|
||||
'HTTP_CF_CONNECTING_IP', // Cloudflare
|
||||
'HTTP_X_FORWARDED_FOR', // Standard proxy
|
||||
'HTTP_X_REAL_IP', // Nginx proxy
|
||||
'HTTP_CLIENT_IP', // Some proxies
|
||||
'HTTP_X_FORWARDED', // Some proxies
|
||||
'HTTP_X_CLUSTER_CLIENT_IP', // Some proxies
|
||||
'HTTP_FORWARDED_FOR', // Some proxies
|
||||
'HTTP_FORWARDED' // RFC 7239
|
||||
];
|
||||
|
||||
foreach ($proxy_headers as $header) {
|
||||
if (!empty($_SERVER[$header])) {
|
||||
$ips = explode(',', $_SERVER[$header]);
|
||||
$ip = trim($ips[0]);
|
||||
|
||||
// Validate IP
|
||||
if (filter_var($ip, FILTER_VALIDATE_IP)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return sanitize_text_field($ip);
|
||||
|
||||
// Final validation
|
||||
return filter_var($ip, FILTER_VALIDATE_IP) ? $ip : '0.0.0.0';
|
||||
}
|
||||
|
||||
function preg_match_wildcard($pattern, $subject) {
|
||||
$pattern = preg_quote($pattern, '#');
|
||||
$pattern = str_replace('\*', '.*', $pattern);
|
||||
return preg_match("#^$pattern$#i", $subject);
|
||||
/**
|
||||
* Match pattern with wildcard support
|
||||
*/
|
||||
function wpban_match_pattern($pattern, $string) {
|
||||
// Convert wildcard pattern to regex
|
||||
$pattern = str_replace(
|
||||
['*', '?', '[', ']', '(', ')', '{', '}', '^', '$', '+', '.', '\\'],
|
||||
['.*', '.', '\[', '\]', '\(', '\)', '\{', '\}', '\^', '\$', '\+', '\.', '\\\\'],
|
||||
$pattern
|
||||
);
|
||||
|
||||
return preg_match('/^' . $pattern . '$/i', $string);
|
||||
}
|
||||
|
||||
function ban_anything_ip_in_range($ip, $ranges) {
|
||||
foreach ($ranges as $range) {
|
||||
if (strpos($range, '/') !== false) {
|
||||
list($subnet, $bits) = explode('/', $range);
|
||||
$ip_long = ip2long($ip);
|
||||
$subnet_long = ip2long($subnet);
|
||||
$mask = -1 << (32 - (int)$bits);
|
||||
if (($ip_long & $mask) === ($subnet_long & $mask)) {
|
||||
return true;
|
||||
}
|
||||
} elseif ($ip === $range) {
|
||||
/**
|
||||
* Check if IP is in range (CIDR or range notation)
|
||||
*/
|
||||
function wpban_ip_in_range($ip, $range) {
|
||||
if (strpos($range, '/') !== false) {
|
||||
// CIDR notation
|
||||
list($subnet, $bits) = explode('/', $range);
|
||||
if ($bits < 0 || $bits > 32) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$ip_long = ip2long($ip);
|
||||
$subnet_long = ip2long($subnet);
|
||||
|
||||
if ($ip_long === false || $subnet_long === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$mask = -1 << (32 - $bits);
|
||||
return ($ip_long & $mask) == ($subnet_long & $mask);
|
||||
|
||||
} elseif (strpos($range, '-') !== false) {
|
||||
// Range notation (192.168.1.1-192.168.1.255)
|
||||
list($start, $end) = explode('-', $range);
|
||||
$ip_long = ip2long($ip);
|
||||
$start_long = ip2long(trim($start));
|
||||
$end_long = ip2long(trim($end));
|
||||
|
||||
if ($ip_long === false || $start_long === false || $end_long === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ($ip_long >= $start_long && $ip_long <= $end_long);
|
||||
}
|
||||
|
||||
// Check if it's a pattern
|
||||
return wpban_match_pattern($range, $ip);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get crawler list
|
||||
*/
|
||||
function wpban_get_crawler_list() {
|
||||
return [
|
||||
'ai' => [
|
||||
// OpenAI
|
||||
'GPTBot' => ['description' => __('OpenAI GPT Web Crawler', 'wpban')],
|
||||
'ChatGPT-User' => ['description' => __('ChatGPT Browser Tool', 'wpban')],
|
||||
'OAI-SearchBot' => ['description' => __('OpenAI Search Bot', 'wpban')],
|
||||
|
||||
// Anthropic
|
||||
'ClaudeBot' => ['description' => __('Anthropic Claude Bot', 'wpban')],
|
||||
'Claude-Web' => ['description' => __('Claude Web Browser', 'wpban')],
|
||||
'anthropic-ai' => ['description' => __('Anthropic AI Crawler', 'wpban')],
|
||||
|
||||
// Google
|
||||
'Google-Extended' => ['description' => __('Google Bard/Gemini Bot', 'wpban')],
|
||||
|
||||
// Microsoft
|
||||
'Bingbot' => ['description' => __('Microsoft Bing AI', 'wpban')],
|
||||
|
||||
// Meta
|
||||
'FacebookBot' => ['description' => __('Meta/Facebook Bot', 'wpban')],
|
||||
'Meta-ExternalAgent' => ['description' => __('Meta External Agent', 'wpban')],
|
||||
'Meta-ExternalFetcher' => ['description' => __('Meta External Fetcher', 'wpban')],
|
||||
|
||||
// Others
|
||||
'CCBot' => ['description' => __('Common Crawl Dataset', 'wpban')],
|
||||
'PerplexityBot' => ['description' => __('Perplexity AI Search', 'wpban')],
|
||||
'YouBot' => ['description' => __('You.com Search Bot', 'wpban')],
|
||||
'Bytespider' => ['description' => __('ByteDance Spider', 'wpban')],
|
||||
'cohere-ai' => ['description' => __('Cohere AI Bot', 'wpban')],
|
||||
'Diffbot' => ['description' => __('Diffbot Crawler', 'wpban')],
|
||||
'Amazonbot' => ['description' => __('Amazon Alexa Bot', 'wpban')],
|
||||
'Applebot-Extended' => ['description' => __('Apple AI Bot', 'wpban')],
|
||||
'AI2Bot' => ['description' => __('Allen Institute AI', 'wpban')],
|
||||
'Ai2Bot-Dolma' => ['description' => __('AI2 Dolma Dataset', 'wpban')],
|
||||
'Omgilibot' => ['description' => __('Webz.io Bot', 'wpban')],
|
||||
'webzio' => ['description' => __('Webz.io Crawler', 'wpban')],
|
||||
'ImagesiftBot' => ['description' => __('Image Analysis Bot', 'wpban')],
|
||||
'PetalBot' => ['description' => __('Huawei Petal Search', 'wpban')],
|
||||
],
|
||||
'seo' => [
|
||||
// Major Search Engines
|
||||
'Googlebot' => ['description' => __('Google Search (DO NOT BLOCK)', 'wpban')],
|
||||
'Bingbot' => ['description' => __('Bing Search (DO NOT BLOCK)', 'wpban')],
|
||||
'Slurp' => ['description' => __('Yahoo Search', 'wpban')],
|
||||
'DuckDuckBot' => ['description' => __('DuckDuckGo Search', 'wpban')],
|
||||
'Baiduspider' => ['description' => __('Baidu Search (China)', 'wpban')],
|
||||
'YandexBot' => ['description' => __('Yandex Search (Russia)', 'wpban')],
|
||||
|
||||
// SEO Tools
|
||||
'AhrefsBot' => ['description' => __('Ahrefs SEO Tool', 'wpban')],
|
||||
'SemrushBot' => ['description' => __('Semrush SEO Tool', 'wpban')],
|
||||
'MJ12bot' => ['description' => __('Majestic SEO Tool', 'wpban')],
|
||||
'DotBot' => ['description' => __('Moz SEO Tool', 'wpban')],
|
||||
'Screaming Frog' => ['description' => __('SEO Spider Tool', 'wpban')],
|
||||
|
||||
// Other
|
||||
'ia_archiver' => ['description' => __('Internet Archive', 'wpban')],
|
||||
'facebookexternalhit' => ['description' => __('Facebook Link Preview', 'wpban')],
|
||||
'LinkedInBot' => ['description' => __('LinkedIn Preview', 'wpban')],
|
||||
'WhatsApp' => ['description' => __('WhatsApp Link Preview', 'wpban')],
|
||||
'Twitterbot' => ['description' => __('Twitter Link Preview', 'wpban')],
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get country list
|
||||
*/
|
||||
function wpban_get_country_list() {
|
||||
return [
|
||||
'AF' => __('Afghanistan', 'wpban'),
|
||||
'AL' => __('Albania', 'wpban'),
|
||||
'DZ' => __('Algeria', 'wpban'),
|
||||
'AR' => __('Argentina', 'wpban'),
|
||||
'AM' => __('Armenia', 'wpban'),
|
||||
'AU' => __('Australia', 'wpban'),
|
||||
'AT' => __('Austria', 'wpban'),
|
||||
'AZ' => __('Azerbaijan', 'wpban'),
|
||||
'BD' => __('Bangladesh', 'wpban'),
|
||||
'BY' => __('Belarus', 'wpban'),
|
||||
'BE' => __('Belgium', 'wpban'),
|
||||
'BR' => __('Brazil', 'wpban'),
|
||||
'BG' => __('Bulgaria', 'wpban'),
|
||||
'CA' => __('Canada', 'wpban'),
|
||||
'CL' => __('Chile', 'wpban'),
|
||||
'CN' => __('China', 'wpban'),
|
||||
'CO' => __('Colombia', 'wpban'),
|
||||
'HR' => __('Croatia', 'wpban'),
|
||||
'CZ' => __('Czech Republic', 'wpban'),
|
||||
'DK' => __('Denmark', 'wpban'),
|
||||
'EG' => __('Egypt', 'wpban'),
|
||||
'EE' => __('Estonia', 'wpban'),
|
||||
'FI' => __('Finland', 'wpban'),
|
||||
'FR' => __('France', 'wpban'),
|
||||
'GE' => __('Georgia', 'wpban'),
|
||||
'DE' => __('Germany', 'wpban'),
|
||||
'GR' => __('Greece', 'wpban'),
|
||||
'HK' => __('Hong Kong', 'wpban'),
|
||||
'HU' => __('Hungary', 'wpban'),
|
||||
'IS' => __('Iceland', 'wpban'),
|
||||
'IN' => __('India', 'wpban'),
|
||||
'ID' => __('Indonesia', 'wpban'),
|
||||
'IR' => __('Iran', 'wpban'),
|
||||
'IQ' => __('Iraq', 'wpban'),
|
||||
'IE' => __('Ireland', 'wpban'),
|
||||
'IL' => __('Israel', 'wpban'),
|
||||
'IT' => __('Italy', 'wpban'),
|
||||
'JP' => __('Japan', 'wpban'),
|
||||
'KZ' => __('Kazakhstan', 'wpban'),
|
||||
'KE' => __('Kenya', 'wpban'),
|
||||
'KR' => __('South Korea', 'wpban'),
|
||||
'LV' => __('Latvia', 'wpban'),
|
||||
'LT' => __('Lithuania', 'wpban'),
|
||||
'MY' => __('Malaysia', 'wpban'),
|
||||
'MX' => __('Mexico', 'wpban'),
|
||||
'MA' => __('Morocco', 'wpban'),
|
||||
'NL' => __('Netherlands', 'wpban'),
|
||||
'NZ' => __('New Zealand', 'wpban'),
|
||||
'NG' => __('Nigeria', 'wpban'),
|
||||
'NO' => __('Norway', 'wpban'),
|
||||
'PK' => __('Pakistan', 'wpban'),
|
||||
'PE' => __('Peru', 'wpban'),
|
||||
'PH' => __('Philippines', 'wpban'),
|
||||
'PL' => __('Poland', 'wpban'),
|
||||
'PT' => __('Portugal', 'wpban'),
|
||||
'RO' => __('Romania', 'wpban'),
|
||||
'RU' => __('Russia', 'wpban'),
|
||||
'SA' => __('Saudi Arabia', 'wpban'),
|
||||
'RS' => __('Serbia', 'wpban'),
|
||||
'SG' => __('Singapore', 'wpban'),
|
||||
'SK' => __('Slovakia', 'wpban'),
|
||||
'SI' => __('Slovenia', 'wpban'),
|
||||
'ZA' => __('South Africa', 'wpban'),
|
||||
'ES' => __('Spain', 'wpban'),
|
||||
'SE' => __('Sweden', 'wpban'),
|
||||
'CH' => __('Switzerland', 'wpban'),
|
||||
'TW' => __('Taiwan', 'wpban'),
|
||||
'TH' => __('Thailand', 'wpban'),
|
||||
'TR' => __('Turkey', 'wpban'),
|
||||
'UA' => __('Ukraine', 'wpban'),
|
||||
'AE' => __('United Arab Emirates', 'wpban'),
|
||||
'GB' => __('United Kingdom', 'wpban'),
|
||||
'US' => __('United States', 'wpban'),
|
||||
'UZ' => __('Uzbekistan', 'wpban'),
|
||||
'VE' => __('Venezuela', 'wpban'),
|
||||
'VN' => __('Vietnam', 'wpban'),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize and validate IP address
|
||||
*/
|
||||
function wpban_sanitize_ip($ip) {
|
||||
// Remove any whitespace
|
||||
$ip = trim($ip);
|
||||
|
||||
// Check if it's a valid IP
|
||||
if (filter_var($ip, FILTER_VALIDATE_IP)) {
|
||||
return $ip;
|
||||
}
|
||||
|
||||
// Check if it's a pattern with wildcards
|
||||
if (strpos($ip, '*') !== false) {
|
||||
// Replace wildcards with 0 for validation
|
||||
$test_ip = str_replace('*', '0', $ip);
|
||||
if (filter_var($test_ip, FILTER_VALIDATE_IP)) {
|
||||
return $ip;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format bytes to human readable
|
||||
*/
|
||||
function wpban_format_bytes($bytes, $precision = 2) {
|
||||
$units = ['B', 'KB', 'MB', 'GB', 'TB'];
|
||||
|
||||
$bytes = max($bytes, 0);
|
||||
$pow = floor(($bytes ? log($bytes) : 0) / log(1024));
|
||||
$pow = min($pow, count($units) - 1);
|
||||
|
||||
$bytes /= (1 << (10 * $pow));
|
||||
|
||||
return round($bytes, $precision) . ' ' . $units[$pow];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user's country flag emoji
|
||||
*/
|
||||
function wpban_country_flag($country_code) {
|
||||
if (strlen($country_code) !== 2) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$country_code = strtoupper($country_code);
|
||||
$flag = '';
|
||||
|
||||
// Convert country code to flag emoji
|
||||
for ($i = 0; $i < strlen($country_code); $i++) {
|
||||
$flag .= mb_chr(ord($country_code[$i]) - ord('A') + 0x1F1E6, 'UTF-8');
|
||||
}
|
||||
|
||||
return $flag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Log security event (helper function)
|
||||
*/
|
||||
function wpban_log($action, $reason, $details = '') {
|
||||
if (isset($GLOBALS['wpban_security'])) {
|
||||
$GLOBALS['wpban_security']->log($action, $reason, $details);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if current user is whitelisted
|
||||
*/
|
||||
function wpban_is_current_user_whitelisted() {
|
||||
$settings = get_option('wpban_settings', []);
|
||||
$ip = wpban_get_ip($settings['reverse_proxy'] ?? false);
|
||||
|
||||
foreach ($settings['whitelist_ips'] ?? [] as $pattern) {
|
||||
if (wpban_match_pattern($pattern, $ip) || wpban_ip_in_range($ip, $pattern)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get attack type label
|
||||
*/
|
||||
function wpban_get_attack_type_label($type) {
|
||||
$labels = [
|
||||
'ip_ban' => __('IP Ban', 'wpban'),
|
||||
'rate_limit' => __('Rate Limit', 'wpban'),
|
||||
'geo_block' => __('Geo Block', 'wpban'),
|
||||
'crawler_block' => __('Crawler Block', 'wpban'),
|
||||
'failed_login' => __('Failed Login', 'wpban'),
|
||||
'brute_force' => __('Brute Force', 'wpban'),
|
||||
'banned' => __('Banned', 'wpban'),
|
||||
'blocked' => __('Blocked', 'wpban'),
|
||||
];
|
||||
|
||||
return $labels[$type] ?? $type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Export settings
|
||||
*/
|
||||
function wpban_export_settings() {
|
||||
$settings = get_option('wpban_settings', []);
|
||||
$bypass_path = get_option('wpban_bypass_path', '');
|
||||
|
||||
$export = [
|
||||
'version' => WPBAN_VERSION,
|
||||
'timestamp' => current_time('timestamp'),
|
||||
'site_url' => get_site_url(),
|
||||
'settings' => $settings,
|
||||
'bypass_path' => $bypass_path
|
||||
];
|
||||
|
||||
return json_encode($export, JSON_PRETTY_PRINT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Import settings
|
||||
*/
|
||||
function wpban_import_settings($json) {
|
||||
$data = json_decode($json, true);
|
||||
|
||||
if (!$data || !isset($data['settings'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Validate version compatibility
|
||||
if (version_compare($data['version'], '4.0', '<')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Import settings
|
||||
update_option('wpban_settings', $data['settings']);
|
||||
|
||||
// Optionally import bypass path
|
||||
if (!empty($data['bypass_path'])) {
|
||||
update_option('wpban_bypass_path', $data['bypass_path']);
|
||||
}
|
||||
|
||||
// Clear cache
|
||||
if (isset($GLOBALS['wpban_security'])) {
|
||||
$GLOBALS['wpban_security']->clear_cache();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
638
languages/wpban.pot
Normal file
638
languages/wpban.pot
Normal file
|
@ -0,0 +1,638 @@
|
|||
# wpban Pro Language Template
|
||||
# Copyright (C) 2024 WPBan
|
||||
# This file is distributed under the GPLv2 or later.
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: wpban Pro 5.0\n"
|
||||
"Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/wpban\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"POT-Creation-Date: 2024-01-01T00:00:00+00:00\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"X-Generator: WP-CLI 2.9.0\n"
|
||||
"X-Domain: wpban\n"
|
||||
|
||||
#. Plugin Name of the plugin
|
||||
msgid "wpban Pro"
|
||||
msgstr ""
|
||||
|
||||
#. Plugin URI of the plugin
|
||||
msgid "https://wpban.com/"
|
||||
msgstr ""
|
||||
|
||||
#. Description of the plugin
|
||||
msgid "Advanced WordPress security with geo-blocking, rate limiting, and intelligent threat detection."
|
||||
msgstr ""
|
||||
|
||||
#. Author of the plugin
|
||||
msgid "WPBan"
|
||||
msgstr ""
|
||||
|
||||
#. Author URI of the plugin
|
||||
msgid "https://wpban.com"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:30
|
||||
msgid "WPBan Security"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:38
|
||||
msgid "Settings"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:46
|
||||
msgid "Security Logs"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:47
|
||||
msgid "Logs"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:54
|
||||
msgid "Tools"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:80
|
||||
msgid "Are you sure you want to clear all logs?"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:81
|
||||
msgid "This will replace your current settings. Continue?"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:82
|
||||
msgid "Test email sent!"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:83
|
||||
msgid "An error occurred"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:95
|
||||
msgid "WPBan Security Dashboard"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:101
|
||||
msgid "Total Blocks"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:115
|
||||
msgid "Unique IPs"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:120
|
||||
msgid "Today's Blocks"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:125
|
||||
msgid "Active Rules"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:134
|
||||
msgid "Quick Actions"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:136
|
||||
msgid "Emergency Bypass URL:"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:142
|
||||
msgid "Copy"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:145
|
||||
msgid "Save this URL to access your site if you get locked out."
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:150
|
||||
msgid "Configure Settings"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:153
|
||||
msgid "View Logs"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:160
|
||||
msgid "Top Blocked Countries"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:187
|
||||
msgid "Security Templates"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:188
|
||||
msgid "Quickly apply pre-configured security settings."
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:195
|
||||
msgid "Apply"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:204
|
||||
msgid "Recent Activity"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:209 includes/class-wpban-admin.php:468
|
||||
msgid "Time"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:210 includes/class-wpban-admin.php:469
|
||||
msgid "IP"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:211 includes/class-wpban-admin.php:470
|
||||
msgid "Country"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:212 includes/class-wpban-admin.php:471
|
||||
msgid "Action"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:213 includes/class-wpban-admin.php:472
|
||||
msgid "Details"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:236
|
||||
msgid "WPBan Security Settings"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:243
|
||||
msgid "General"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:244
|
||||
msgid "IP Rules"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:245
|
||||
msgid "Advanced"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:246
|
||||
msgid "Rate Limiting"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:247
|
||||
msgid "Geo Blocking"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:248
|
||||
msgid "Crawlers"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:249
|
||||
msgid "Notifications"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:256
|
||||
msgid "Enable Logging"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:261
|
||||
msgid "Log all security events"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:266
|
||||
msgid "Reverse Proxy"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:271
|
||||
msgid "Server is behind a reverse proxy (Cloudflare, nginx, etc.)"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:276
|
||||
msgid "Ban Message"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:287
|
||||
msgid "Variables: %IP%, %DATE%, %SITE%"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:295
|
||||
msgid "Banned IPs"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:300
|
||||
msgid "One per line. Use * for wildcards (e.g., 192.168.*.*)"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:304
|
||||
msgid "IP Ranges"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:309
|
||||
msgid "CIDR: 192.168.0.0/24 or Range: 192.168.0.1-192.168.0.255"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:313
|
||||
msgid "Whitelist IPs"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:318
|
||||
msgid "These IPs will never be blocked"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:322
|
||||
msgid "Login Protection"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:327
|
||||
msgid "Only these IPs can access wp-login.php (leave empty to allow all)"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:336
|
||||
msgid "Banned Hosts"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:341
|
||||
msgid "e.g., *.badhost.com"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:345
|
||||
msgid "Banned Referers"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:352
|
||||
msgid "Banned User Agents"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:359
|
||||
msgid "Browser Restrictions"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:365
|
||||
msgid "Block WeChat/QQ Browsers"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:375
|
||||
msgid "Rate Limiting Settings"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:376
|
||||
msgid "Protect against floods and brute force attacks by limiting request rates."
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:380
|
||||
msgid "General Requests"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:385
|
||||
msgid "requests per minute"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:389
|
||||
msgid "Login Attempts"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:394
|
||||
msgid "attempts per hour"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:398
|
||||
msgid "API Requests"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:410
|
||||
msgid "Geographic Blocking"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:414
|
||||
msgid "Enable Geo Blocking"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:419
|
||||
msgid "Block or allow access based on country"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:424
|
||||
msgid "Block Mode"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:429
|
||||
msgid "Block selected countries"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:434
|
||||
msgid "Allow only selected countries"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:439
|
||||
msgid "Countries"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:454
|
||||
msgid "Hold Ctrl/Cmd to select multiple countries"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:463
|
||||
msgid "Select All AI"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:466
|
||||
msgid "Select All SEO"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:469
|
||||
msgid "Deselect All"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:479
|
||||
msgid "AI Crawlers"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:494
|
||||
msgid "SEO Crawlers"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:496
|
||||
msgid "⚠️ Blocking SEO crawlers may affect your search engine rankings!"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:516
|
||||
msgid "Enable Email Notifications"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:521
|
||||
msgid "Send email alerts for security events"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:526
|
||||
msgid "Recipient Email"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:532
|
||||
msgid "Send Test Email"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:537
|
||||
msgid "Alert Threshold"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:542
|
||||
msgid "Send alert after this many blocks from same IP"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:546
|
||||
msgid "Alert Events"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:549
|
||||
msgid "Rate limit exceeded"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:550
|
||||
msgid "Geographic block"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:551
|
||||
msgid "Brute force attempt"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:552
|
||||
msgid "Crawler blocked"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:571
|
||||
msgid "Save Settings"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:597
|
||||
msgid "From date"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:600
|
||||
msgid "To date"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:603
|
||||
msgid "All Actions"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:604
|
||||
msgid "Banned"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:605
|
||||
msgid "Blocked"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:606
|
||||
msgid "Failed Login"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:607
|
||||
msgid "Bypass Used"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:611
|
||||
msgid "IP Address"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:614
|
||||
msgid "Country Code"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:616
|
||||
msgid "Filter"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:617
|
||||
msgid "Reset"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:620
|
||||
msgid "Export CSV"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:624
|
||||
msgid "Clear Logs"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:638
|
||||
msgid "User Agent"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:639
|
||||
msgid "URI"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:645
|
||||
msgid "No logs found."
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:688
|
||||
msgid "WPBan Tools"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:694
|
||||
msgid "Import/Export Settings"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:695
|
||||
msgid "Backup your settings or migrate to another site."
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:697
|
||||
msgid "Export"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:700
|
||||
msgid "Download Settings"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:704
|
||||
msgid "Import"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:714
|
||||
msgid "Import Settings"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:722
|
||||
msgid "Database Maintenance"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:723
|
||||
msgid "Optimize your WPBan database tables."
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:736
|
||||
msgid "Total Log Entries"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:740
|
||||
msgid "Database Size"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:747
|
||||
msgid "Optimize Tables"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:751
|
||||
msgid "Clear Logs Older Than 30 Days"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:758
|
||||
msgid "System Information"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:829
|
||||
msgid "Settings saved successfully!"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:844
|
||||
msgid "Invalid template"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:853
|
||||
msgid "Template applied successfully!"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:928
|
||||
msgid "Logs cleared successfully!"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:949
|
||||
msgid "Test email sent successfully!"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-admin.php:951
|
||||
msgid "Failed to send test email. Please check your email settings."
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-security.php:109
|
||||
msgid "Too many requests. Please try again later."
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-security.php:144
|
||||
#: includes/class-wpban-security.php:152
|
||||
msgid "Access denied from your location."
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-security.php:415
|
||||
msgid "Basic Protection"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-security.php:416
|
||||
msgid "Essential security for most WordPress sites"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-security.php:427
|
||||
msgid "Strict Security"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-security.php:428
|
||||
msgid "Maximum protection with geo-blocking and rate limiting"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-security.php:443
|
||||
msgid "Content Protection"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-security.php:444
|
||||
msgid "Protect content from AI and scraping"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-security.php:454
|
||||
msgid "Performance Mode"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wpban-security.php:455
|
||||
msgid "Balanced security with minimal performance impact"
|
||||
msgstr ""
|
||||
|
||||
#: includes/functions.php:149
|
||||
msgid "OpenAI GPT Web Crawler"
|
||||
msgstr ""
|
||||
|
||||
#: includes/functions.php:150
|
||||
msgid "ChatGPT Browser Tool"
|
||||
msgstr ""
|
||||
|
||||
#: includes/functions.php:230
|
||||
msgid "Google Search (DO NOT BLOCK)"
|
||||
msgstr ""
|
||||
|
||||
#: includes/functions.php:231
|
||||
msgid "Bing Search (DO NOT BLOCK)"
|
||||
msgstr ""
|
||||
|
||||
#. Add all country names
|
||||
#: includes/functions.php:264
|
||||
msgid "Afghanistan"
|
||||
msgstr ""
|
||||
|
||||
#: includes/functions.php:265
|
||||
msgid "Albania"
|
||||
msgstr ""
|
||||
|
||||
#: includes/functions.php:327
|
||||
msgid "United States"
|
||||
msgstr ""
|
||||
|
||||
#: includes/functions.php:416
|
||||
msgid "IP Ban"
|
||||
msgstr ""
|
||||
|
||||
#: includes/functions.php:417
|
||||
msgid "Rate Limit"
|
||||
msgstr ""
|
||||
|
||||
#: includes/functions.php:418
|
||||
msgid "Geo Block"
|
||||
msgstr ""
|
||||
|
||||
#: includes/functions.php:419
|
||||
msgid "Crawler Block"
|
||||
msgstr ""
|
||||
|
||||
#: includes/functions.php:420
|
||||
msgid "Failed Login"
|
||||
msgstr ""
|
||||
|
||||
#: includes/functions.php:421
|
||||
msgid "Brute Force"
|
||||
msgstr ""
|
236
readme.txt
Normal file
236
readme.txt
Normal file
|
@ -0,0 +1,236 @@
|
|||
=== wpban Pro ===
|
||||
Contributors: wpban
|
||||
Tags: security, firewall, ban, geo-blocking, rate-limiting, crawler-blocking, brute-force, ip-blocking
|
||||
Requires at least: 6.7.2
|
||||
Tested up to: 6.7.2
|
||||
Stable tag: 5.0
|
||||
Requires PHP: 7.4
|
||||
License: GPLv2 or later
|
||||
License URI: https://www.gnu.org/licenses/gpl-2.0.html
|
||||
|
||||
Advanced WordPress security plugin with geo-blocking, rate limiting, AI crawler blocking, and intelligent threat detection.
|
||||
|
||||
== Description ==
|
||||
|
||||
wpban Pro is a comprehensive security solution for WordPress that protects your site from various threats including malicious bots, brute force attacks, content scrapers, and unauthorized access attempts.
|
||||
|
||||
= Key Features =
|
||||
|
||||
**🛡️ IP Management**
|
||||
* Ban IPs with wildcard support (e.g., 192.168.*.*)
|
||||
* IP range blocking (CIDR and range notation)
|
||||
* Whitelist trusted IPs
|
||||
* Automatic reverse proxy detection
|
||||
|
||||
**🌍 Geographic Blocking**
|
||||
* Block or allow specific countries
|
||||
* Real-time IP geolocation
|
||||
* Cached country lookups for performance
|
||||
* Whitelist/blacklist modes
|
||||
|
||||
**⚡ Rate Limiting**
|
||||
* Protect against DDoS and flood attacks
|
||||
* Separate limits for general requests, login attempts, and API calls
|
||||
* Automatic temporary bans for violators
|
||||
* Customizable thresholds
|
||||
|
||||
**🤖 Crawler Control**
|
||||
* Block 40+ AI crawlers (GPTBot, ClaudeBot, etc.)
|
||||
* Control SEO crawler access
|
||||
* Protect content from AI training datasets
|
||||
* robots.txt integration
|
||||
|
||||
**📊 Advanced Logging**
|
||||
* Detailed security event logs
|
||||
* Filter by date, action, IP, or country
|
||||
* Export logs to CSV
|
||||
* Automatic log rotation
|
||||
|
||||
**📧 Email Notifications**
|
||||
* Real-time security alerts
|
||||
* Customizable alert thresholds
|
||||
* Multiple event types
|
||||
* Test email functionality
|
||||
|
||||
**🚪 Login Protection**
|
||||
* Restrict wp-login.php access by IP
|
||||
* Brute force detection
|
||||
* Failed login tracking
|
||||
* Emergency bypass URL
|
||||
|
||||
**🎯 Security Templates**
|
||||
* Quick setup with pre-configured templates
|
||||
* Basic, Strict, Content Protection, and Performance modes
|
||||
* One-click application
|
||||
* Customizable settings
|
||||
|
||||
**🔧 Additional Features**
|
||||
* Browser restrictions (block WeChat/QQ)
|
||||
* User agent filtering
|
||||
* Referer blocking
|
||||
* Host-based banning
|
||||
* Import/export settings
|
||||
* Database optimization tools
|
||||
|
||||
= Performance Optimized =
|
||||
|
||||
* Smart caching system
|
||||
* Optimized database queries with indexes
|
||||
* Minimal performance impact
|
||||
* Lazy loading of features
|
||||
|
||||
= Emergency Access =
|
||||
|
||||
Never get locked out! WPBan provides an emergency bypass URL that allows you to access your site even if your IP gets banned accidentally.
|
||||
|
||||
== Installation ==
|
||||
|
||||
1. Upload the `wpban` folder to `/wp-content/plugins/`
|
||||
2. Activate the plugin through the 'Plugins' menu in WordPress
|
||||
3. Go to 'WPBan Security' in your admin menu
|
||||
4. Choose a security template or configure settings manually
|
||||
5. Save your emergency bypass URL in a safe place
|
||||
|
||||
= Minimum Requirements =
|
||||
|
||||
* WordPress 6.7.2 or higher
|
||||
* PHP 7.4 or higher
|
||||
* MySQL 5.7 or higher
|
||||
|
||||
== Frequently Asked Questions ==
|
||||
|
||||
= Will this plugin slow down my website? =
|
||||
|
||||
No. WPBan is designed with performance in mind. It uses intelligent caching, optimized database queries, and only loads features when needed.
|
||||
|
||||
= What happens if I accidentally ban myself? =
|
||||
|
||||
Use the emergency bypass URL provided in the dashboard. This special URL allows you to access your site and disable the ban. Always save this URL in a secure location.
|
||||
|
||||
= Can I block entire countries? =
|
||||
|
||||
Yes! WPBan includes geographic blocking that allows you to block or exclusively allow specific countries. The plugin uses free IP geolocation services for this feature.
|
||||
|
||||
= Will blocking SEO crawlers hurt my rankings? =
|
||||
|
||||
Yes, blocking major search engine crawlers (Googlebot, Bingbot) will negatively impact your SEO. The plugin shows warnings for these critical crawlers. Only block SEO crawlers if you have a specific reason.
|
||||
|
||||
= How do I protect against AI content scraping? =
|
||||
|
||||
Use the "Content Protection" template which blocks major AI crawlers, or manually select AI crawlers to block in the Crawlers settings. The plugin blocks access and adds robots.txt rules.
|
||||
|
||||
= Can I import/export settings? =
|
||||
|
||||
Yes! Go to Tools > Import/Export to backup your settings or migrate them to another site.
|
||||
|
||||
= How long are logs kept? =
|
||||
|
||||
Logs are automatically cleaned after 30 days to prevent database bloat. You can export logs before they're deleted or manually clear them at any time.
|
||||
|
||||
= Does it work with Cloudflare? =
|
||||
|
||||
Yes! Enable the "Reverse Proxy" option in General settings to properly detect visitor IPs when using Cloudflare or other proxy services.
|
||||
|
||||
== Screenshots ==
|
||||
|
||||
1. Dashboard - Overview of security statistics and quick actions
|
||||
2. Security Templates - One-click security configurations
|
||||
3. IP Rules - Manage banned IPs, ranges, and whitelists
|
||||
4. Rate Limiting - Configure request limits
|
||||
5. Geographic Blocking - Block or allow countries
|
||||
6. Crawler Management - Control bot access
|
||||
7. Security Logs - Detailed event tracking
|
||||
8. Email Notifications - Real-time alerts
|
||||
|
||||
== Changelog ==
|
||||
|
||||
= 5.0 =
|
||||
* Added geographic blocking with real-time IP geolocation
|
||||
* Implemented advanced rate limiting system
|
||||
* Added email notifications for security events
|
||||
* Improved logging with country tracking and pagination
|
||||
* Added import/export functionality
|
||||
* Optimized database performance with indexes
|
||||
* Added emergency bypass URL feature
|
||||
* Improved UI with WordPress native design
|
||||
* Added security templates for quick setup
|
||||
* Fixed array structure issue in templates
|
||||
|
||||
= 4.0 =
|
||||
* Complete rewrite with improved architecture
|
||||
* Added caching system
|
||||
* Enhanced performance
|
||||
* Better code organization
|
||||
|
||||
= 3.3 =
|
||||
* Initial public release
|
||||
* Basic IP blocking functionality
|
||||
* Crawler blocking
|
||||
* Simple logging
|
||||
|
||||
== Upgrade Notice ==
|
||||
|
||||
= 5.0 =
|
||||
Major update with geographic blocking, rate limiting, and email notifications. Backup your settings before upgrading.
|
||||
|
||||
== Advanced Usage ==
|
||||
|
||||
= Custom Templates =
|
||||
|
||||
You can create custom security templates by hooking into the `wpban_templates` filter:
|
||||
|
||||
`
|
||||
add_filter('wpban_templates', function($templates) {
|
||||
$templates['custom'] = [
|
||||
'name' => 'My Custom Template',
|
||||
'description' => 'Custom security configuration',
|
||||
'settings' => [
|
||||
'banned_ips' => ['1.2.3.4'],
|
||||
'rate_limits' => [
|
||||
'requests_per_minute' => 45
|
||||
]
|
||||
]
|
||||
];
|
||||
return $templates;
|
||||
});
|
||||
`
|
||||
|
||||
= Custom Country Detection =
|
||||
|
||||
Integrate with premium GeoIP services:
|
||||
|
||||
`
|
||||
add_filter('wpban_ip_country', function($country, $ip) {
|
||||
// Your custom country detection logic
|
||||
return $detected_country;
|
||||
}, 10, 2);
|
||||
`
|
||||
|
||||
= Whitelist Specific Pages =
|
||||
|
||||
Exclude certain pages from security checks:
|
||||
|
||||
`
|
||||
add_filter('wpban_skip_checks', function($skip) {
|
||||
if (is_page('special-page')) {
|
||||
return true;
|
||||
}
|
||||
return $skip;
|
||||
});
|
||||
`
|
||||
|
||||
== Support ==
|
||||
|
||||
For support, feature requests, or bug reports, please visit our [support forum](https://wordpress.org/support/plugin/wpban/) or [GitHub repository](https://github.com/wpban/wpban).
|
||||
|
||||
== Privacy Policy ==
|
||||
|
||||
This plugin stores:
|
||||
* Security logs containing IP addresses, user agents, and geographic data
|
||||
* Your security configuration settings
|
||||
|
||||
This plugin may connect to external services:
|
||||
* IP geolocation APIs (ip-api.com, ipinfo.io) for country detection
|
||||
* These services only receive IP addresses for lookup
|
||||
|
||||
All data is stored locally in your WordPress database and is not shared with third parties.
|
|
@ -1,51 +0,0 @@
|
|||
<?php
|
||||
/*
|
||||
Plugin Name: WPBan-Anything
|
||||
Plugin URI: https://wpban.com/
|
||||
Description: Ban users by IP, IP Range, host name, user agent, referer URL, restrict login page, block WeChat/QQ browsers, AI crawlers, and SEO crawlers.
|
||||
Version: 3.3
|
||||
Author: WPBan
|
||||
Author URI: https://wpban.com
|
||||
Text Domain: wpban-anything
|
||||
Requires at least: 6.7.2
|
||||
*/
|
||||
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
define('WPBAN_ANYTHING_VERSION', '3.3');
|
||||
define('WPBAN_ANYTHING_DIR', plugin_dir_path(__FILE__));
|
||||
define('WPBAN_ANYTHING_URL', plugin_dir_url(__FILE__));
|
||||
|
||||
require_once WPBAN_ANYTHING_DIR . 'includes/class-wpban-anything.php';
|
||||
require_once WPBAN_ANYTHING_DIR . 'includes/class-wpban-ai-crawlers.php';
|
||||
require_once WPBAN_ANYTHING_DIR . 'includes/class-wpban-seo-crawlers.php';
|
||||
require_once WPBAN_ANYTHING_DIR . 'includes/functions.php';
|
||||
|
||||
function wpban_anything_init() {
|
||||
load_plugin_textdomain('wpban-anything', false, dirname(plugin_basename(__FILE__)) . '/languages/');
|
||||
$wpban_anything = new WPBan_Anything\WPBan_Anything();
|
||||
$wpban_ai_crawlers = new WPBan_Anything\WPBan_AI_Crawlers();
|
||||
$wpban_seo_crawlers = new WPBan_Anything\WPBan_SEO_Crawlers();
|
||||
}
|
||||
add_action('plugins_loaded', 'wpban_anything_init');
|
||||
|
||||
register_uninstall_hook(__FILE__, 'wpban_anything_uninstall');
|
||||
function wpban_anything_uninstall() {
|
||||
$options = [
|
||||
'banned_ips', 'banned_hosts', 'banned_stats', 'banned_message',
|
||||
'banned_referers', 'banned_exclude_ips', 'banned_ips_range',
|
||||
'banned_user_agents', 'banned_options', 'login_restrictions',
|
||||
'wechat_qq_settings', 'ai_crawler_settings', 'seo_crawler_settings'
|
||||
];
|
||||
if (is_multisite()) {
|
||||
foreach (get_sites(['fields' => 'ids']) as $blog_id) {
|
||||
switch_to_blog($blog_id);
|
||||
array_walk($options, 'delete_option');
|
||||
restore_current_blog();
|
||||
}
|
||||
} else {
|
||||
array_walk($options, 'delete_option');
|
||||
}
|
||||
}
|
183
wpban.php
Normal file
183
wpban.php
Normal file
|
@ -0,0 +1,183 @@
|
|||
<?php
|
||||
/*
|
||||
Plugin Name: WPBan
|
||||
Plugin URI: https://wpban.com/
|
||||
Description: Advanced WordPress security with geo-blocking, rate limiting, and intelligent threat detection.
|
||||
Version: 1.5.0
|
||||
Author: WenPai.org
|
||||
Author URI: https://wpban.com
|
||||
Text Domain: wpban
|
||||
Requires at least: 6.7.2
|
||||
*/
|
||||
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
define('WPBAN_VERSION', '1.5.0');
|
||||
define('WPBAN_DIR', plugin_dir_path(__FILE__));
|
||||
define('WPBAN_URL', plugin_dir_url(__FILE__));
|
||||
define('WPBAN_BYPASS_KEY', 'wpban_bypass_' . substr(md5(AUTH_KEY), 0, 12));
|
||||
|
||||
// Create database tables on activation
|
||||
register_activation_hook(__FILE__, 'wpban_activate');
|
||||
function wpban_activate() {
|
||||
global $wpdb;
|
||||
|
||||
$charset_collate = $wpdb->get_charset_collate();
|
||||
|
||||
// Create logs table with improved indexes
|
||||
$table_logs = $wpdb->prefix . 'wpban_logs';
|
||||
$sql_logs = "CREATE TABLE $table_logs (
|
||||
id bigint(20) NOT NULL AUTO_INCREMENT,
|
||||
ip varchar(45) NOT NULL,
|
||||
country_code varchar(2) DEFAULT NULL,
|
||||
user_agent text,
|
||||
referer text,
|
||||
uri text,
|
||||
action varchar(50) NOT NULL,
|
||||
reason text,
|
||||
timestamp datetime DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (id),
|
||||
KEY ip_timestamp (ip, timestamp),
|
||||
KEY action_timestamp (action, timestamp),
|
||||
KEY country_timestamp (country_code, timestamp)
|
||||
) $charset_collate;";
|
||||
|
||||
// Create rate limit table
|
||||
$table_rate = $wpdb->prefix . 'wpban_rate_limits';
|
||||
$sql_rate = "CREATE TABLE $table_rate (
|
||||
ip varchar(45) NOT NULL,
|
||||
action varchar(50) NOT NULL,
|
||||
count int(11) DEFAULT 1,
|
||||
window_start datetime NOT NULL,
|
||||
last_attempt datetime DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (ip, action),
|
||||
KEY window_start (window_start)
|
||||
) $charset_collate;";
|
||||
|
||||
// Create geo cache table
|
||||
$table_geo = $wpdb->prefix . 'wpban_geo_cache';
|
||||
$sql_geo = "CREATE TABLE $table_geo (
|
||||
ip varchar(45) NOT NULL,
|
||||
country_code varchar(2) NOT NULL,
|
||||
country_name varchar(100),
|
||||
city varchar(100),
|
||||
cached_at datetime DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (ip),
|
||||
KEY cached_at (cached_at)
|
||||
) $charset_collate;";
|
||||
|
||||
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
|
||||
dbDelta($sql_logs);
|
||||
dbDelta($sql_rate);
|
||||
dbDelta($sql_geo);
|
||||
|
||||
// Set default options
|
||||
$default_settings = [
|
||||
'rate_limits' => [
|
||||
'requests_per_minute' => 60,
|
||||
'login_per_hour' => 5,
|
||||
'api_per_minute' => 30
|
||||
],
|
||||
'geo_blocking' => [
|
||||
'enabled' => false,
|
||||
'blocked_countries' => [],
|
||||
'allowed_countries' => []
|
||||
],
|
||||
'email_notifications' => [
|
||||
'enabled' => false,
|
||||
'recipient' => get_option('admin_email'),
|
||||
'threshold' => 10,
|
||||
'events' => ['rate_limit', 'geo_block', 'brute_force']
|
||||
]
|
||||
];
|
||||
|
||||
$existing = get_option('wpban_settings', []);
|
||||
update_option('wpban_settings', array_merge($default_settings, $existing));
|
||||
|
||||
// Generate bypass URL if not exists
|
||||
if (!get_option('wpban_bypass_path')) {
|
||||
update_option('wpban_bypass_path', wp_generate_password(16, false));
|
||||
}
|
||||
|
||||
// Schedule cron jobs
|
||||
if (!wp_next_scheduled('wpban_cleanup')) {
|
||||
wp_schedule_event(time(), 'daily', 'wpban_cleanup');
|
||||
}
|
||||
|
||||
if (!wp_next_scheduled('wpban_geo_update')) {
|
||||
wp_schedule_event(time(), 'weekly', 'wpban_geo_update');
|
||||
}
|
||||
}
|
||||
|
||||
// Load required files
|
||||
require_once WPBAN_DIR . 'includes/functions.php';
|
||||
require_once WPBAN_DIR . 'includes/class-wpban-security.php';
|
||||
require_once WPBAN_DIR . 'includes/class-wpban-admin.php';
|
||||
|
||||
// Initialize plugin
|
||||
add_action('plugins_loaded', 'wpban_init');
|
||||
function wpban_init() {
|
||||
load_plugin_textdomain('wpban', false, dirname(plugin_basename(__FILE__)) . '/languages/');
|
||||
|
||||
// Initialize security module
|
||||
$GLOBALS['wpban_security'] = new WPBan_Security();
|
||||
|
||||
if (is_admin()) {
|
||||
new WPBan_Admin();
|
||||
}
|
||||
}
|
||||
|
||||
// Cron jobs
|
||||
add_action('wpban_cleanup', 'wpban_cleanup_old_data');
|
||||
function wpban_cleanup_old_data() {
|
||||
global $wpdb;
|
||||
|
||||
// Clean old logs (keep 30 days)
|
||||
$wpdb->query($wpdb->prepare(
|
||||
"DELETE FROM {$wpdb->prefix}wpban_logs WHERE timestamp < %s",
|
||||
date('Y-m-d H:i:s', strtotime('-30 days'))
|
||||
));
|
||||
|
||||
// Clean old rate limit records
|
||||
$wpdb->query($wpdb->prepare(
|
||||
"DELETE FROM {$wpdb->prefix}wpban_rate_limits WHERE window_start < %s",
|
||||
date('Y-m-d H:i:s', strtotime('-24 hours'))
|
||||
));
|
||||
|
||||
// Clean old geo cache (keep 7 days)
|
||||
$wpdb->query($wpdb->prepare(
|
||||
"DELETE FROM {$wpdb->prefix}wpban_geo_cache WHERE cached_at < %s",
|
||||
date('Y-m-d H:i:s', strtotime('-7 days'))
|
||||
));
|
||||
}
|
||||
|
||||
// Cleanup on uninstall
|
||||
register_uninstall_hook(__FILE__, 'wpban_uninstall');
|
||||
function wpban_uninstall() {
|
||||
global $wpdb;
|
||||
|
||||
// Remove all options
|
||||
$options = [
|
||||
'wpban_settings', 'wpban_templates', 'wpban_bypass_path',
|
||||
'wpban_cache_data', 'wpban_stats', 'wpban_geo_db_version'
|
||||
];
|
||||
|
||||
foreach ($options as $option) {
|
||||
delete_option($option);
|
||||
}
|
||||
|
||||
// Remove database tables
|
||||
$tables = ['wpban_logs', 'wpban_rate_limits', 'wpban_geo_cache'];
|
||||
foreach ($tables as $table) {
|
||||
$wpdb->query("DROP TABLE IF EXISTS {$wpdb->prefix}{$table}");
|
||||
}
|
||||
|
||||
// Clear scheduled events
|
||||
wp_clear_scheduled_hook('wpban_cleanup');
|
||||
wp_clear_scheduled_hook('wpban_geo_update');
|
||||
|
||||
// Clear cache
|
||||
wp_cache_delete_group('wpban');
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue