-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathinit.php
More file actions
342 lines (299 loc) · 13.8 KB
/
Copy pathinit.php
File metadata and controls
342 lines (299 loc) · 13.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
<?php
/**
* Плагін блокування ботів
*
* Цей плагін блокує автоматичні запити від ботів та сканерів,
* дозволяючи налаштувати список дозволених ботів.
*
* @package BotBlocker
* @version 1.0.0
* @author Flowaxy Team
*/
declare(strict_types=1);
$rootDir = defined('ROOT_DIR') ? ROOT_DIR : dirname(__DIR__, 2);
require_once $rootDir . '/engine/core/support/base/BasePlugin.php';
require_once $rootDir . '/engine/core/support/helpers/UrlHelper.php';
if (! function_exists('addHook')) {
require_once $rootDir . '/engine/includes/functions.php';
}
// Завантажуємо ClassAutoloader для реєстрації класів
if (file_exists($rootDir . '/engine/core/system/ClassAutoloader.php')) {
require_once $rootDir . '/engine/core/system/ClassAutoloader.php';
}
// Підключаємо SecurityHelper для перевірки авторизації
if (!class_exists('SecurityHelper') && file_exists($rootDir . '/engine/core/support/helpers/SecurityHelper.php')) {
require_once $rootDir . '/engine/core/support/helpers/SecurityHelper.php';
}
// Підключаємо Session для перевірки сесії
if (!class_exists('Session') && file_exists($rootDir . '/engine/infrastructure/security/Session.php')) {
require_once $rootDir . '/engine/infrastructure/security/Session.php';
}
// Завантажуємо сервіс для блокування ботів
$botBlockerServiceFile = dirname(__FILE__) . '/src/Services/BotBlockerService.php';
if (file_exists($botBlockerServiceFile)) {
require_once $botBlockerServiceFile;
}
/**
* Клас плагіна блокування ботів
*
* Відповідає за ініціалізацію плагіна, реєстрацію маршрутів,
* меню та обробку запитів для блокування ботів.
*/
class BotBlockerPlugin extends BasePlugin
{
private string $pluginDir;
/** @var bool Захист від повторної перевірки запиту */
private static bool $requestChecked = false;
/**
* Конструктор плагіна
*/
public function __construct()
{
parent::__construct();
$reflection = new ReflectionClass($this);
$this->pluginDir = dirname($reflection->getFileName());
}
/**
* Ініціалізація плагіна
*
* Реєструє маршрути адмін-панелі, пункти меню та хуки для блокування ботів.
*/
public function init(): void
{
// Реєстрація маршруту для налаштувань
addHook('admin_register_routes', [$this, 'registerAdminRoute'], 10, 1);
// Реєстрація пункту меню
addFilter('admin_menu', [$this, 'registerAdminMenu'], 20);
// Реєстрація в категоріях налаштувань
addFilter('settings_categories', [$this, 'registerSettingsCategory'], 10);
// Блокування ботів (ранній хук з високим пріоритетом)
addHook('handle_early_request', [$this, 'blockBots'], 1);
}
/**
* Реєстрація адмін-маршруту
*
* @param mixed $router Роутер для реєстрації маршрутів
* @return void
*/
public function registerAdminRoute($router): void
{
$pageFile = $this->pluginDir . '/src/admin/pages/BotBlockerAdminPage.php';
if (file_exists($pageFile)) {
// Реєструємо клас в автозавантажувачі
if (isset($GLOBALS['engineAutoloader'])) {
$autoloader = $GLOBALS['engineAutoloader'];
if ($autoloader instanceof ClassAutoloader ||
(is_object($autoloader) && method_exists($autoloader, 'addClassMap'))) {
$autoloader->addClassMap([
'BotBlockerAdminPage' => $pageFile
]);
}
}
require_once $pageFile;
if (class_exists('BotBlockerAdminPage')) {
$router->add(['GET', 'POST'], 'bot-blocker', 'BotBlockerAdminPage');
}
}
}
/**
* Реєстрація пункту меню в адмін-панелі
*
* Додає пункт "Блокування ботів" в меню "Система"
*
* @param array<int, array<string, mixed>> $menu Поточне меню адмін-панелі
* @return array<int, array<string, mixed>> Оновлене меню
*/
public function registerAdminMenu(array $menu): array
{
// Додаємо пункт в меню "Система"
$found = false;
foreach ($menu as &$item) {
if (isset($item['page']) && $item['page'] === 'system') {
if (!isset($item['submenu'])) {
$item['submenu'] = [];
}
$item['submenu'][] = [
'text' => 'Блокування ботів',
'icon' => 'fas fa-shield-alt',
'href' => UrlHelper::admin('bot-blocker'),
'page' => 'bot-blocker',
'order' => 10,
'permission' => null,
];
$found = true;
break;
}
}
// Якщо меню "Система" не знайдено, створюємо його
if (! $found) {
$menu[] = [
'text' => 'Система',
'icon' => 'fas fa-server',
'href' => '#',
'page' => 'system',
'order' => 60,
'permission' => null,
'submenu' => [
[
'text' => 'Блокування ботів',
'icon' => 'fas fa-shield-alt',
'href' => UrlHelper::admin('bot-blocker'),
'page' => 'bot-blocker',
'order' => 10,
'permission' => null,
],
],
];
}
return $menu;
}
/**
* Реєстрація в категоріях налаштувань
*
* Додає плагін до категорії "Система" на сторінці /admin/settings
*
* @param array<string, mixed> $categories Поточні категорії
* @return array<string, mixed> Оновлені категорії
*/
public function registerSettingsCategory(array $categories): array
{
// Перевіряємо, чи плагін активний
$pluginManager = function_exists('pluginManager') ? pluginManager() : null;
if (!$pluginManager || !method_exists($pluginManager, 'isPluginActive') || !$pluginManager->isPluginActive('bot-blocker')) {
return $categories;
}
// Додаємо до категорії "Система"
if (isset($categories['system'])) {
$categories['system']['items'][] = [
'title' => 'Блокування ботів',
'description' => 'Управління блокуванням ботів',
'url' => UrlHelper::admin('bot-blocker'),
'icon' => 'fas fa-shield-alt',
'permission' => null,
];
}
return $categories;
}
/**
* Блокування ботів
*
* Перевіряє запит на наявність бота та блокує його, якщо необхідно.
* Ігнорує запити до адмін-панелі, API та від авторизованих користувачів.
*
* Цей метод використовується як фільтр для handle_early_request хука.
* Приймає початкове значення (зазвичай false) і повертає true, якщо запит заблоковано.
*
* Підтримує два формати виклику:
* - Через HookManager: blockBots($handled, $context)
* - Через PluginManager: blockBots([$handled, $context])
*
* @param mixed $handled Початкове значення (false = запит не оброблено) або масив [$handled, $context]
* @param array<string, mixed>|null $context Контекст хука (якщо передається окремо)
* @return bool true - якщо бот заблоковано, false - якщо продовжити обробку
*/
public function blockBots(mixed $handled = false, ?array $context = null): bool
{
// Обробка випадку, коли передається масив [$handled, $context]
if (is_array($handled) && count($handled) >= 1) {
$context = $handled[1] ?? null;
$handled = $handled[0] ?? false;
}
// Нормалізуємо значення
$handled = (bool)$handled;
// Захист від повторної перевірки
if (self::$requestChecked) {
return $handled;
}
try {
$path = $_SERVER['REQUEST_URI'] ?? '/';
// Ігноруємо запити до адмін-панелі, API та статичних файлів
if (str_starts_with($path, '/admin') ||
str_starts_with($path, '/api') ||
$path === '/favicon.ico' ||
str_starts_with($path, '/robots.txt') ||
str_starts_with($path, '/sitemap') ||
preg_match('/\.(ico|png|jpg|jpeg|gif|css|js|woff|woff2|ttf|svg)$/i', $path)) {
self::$requestChecked = true;
return $handled;
}
// Перевіряємо авторизацію
$adminUserId = 0;
if (function_exists('sessionManager')) {
$session = sessionManager();
$adminUserId = (int)($session->get('admin_user_id') ?? 0);
}
// Ігноруємо запити від авторизованих користувачів
if ($adminUserId > 0) {
self::$requestChecked = true;
return $handled;
}
// Додаткова перевірка через SecurityHelper
if (function_exists('SecurityHelper') && class_exists('SecurityHelper')) {
if (method_exists('SecurityHelper', 'isAdminLoggedIn') && SecurityHelper::isAdminLoggedIn()) {
self::$requestChecked = true;
return $handled;
}
}
// Отримуємо User-Agent та IP
$userAgent = $_SERVER['HTTP_USER_AGENT'] ?? '';
$ip = $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0';
// Перевіряємо, чи це бот
if (class_exists('BotBlockerService')) {
$botBlocker = new BotBlockerService($this->getSlug());
$isBotResult = $botBlocker->isBot($userAgent, $ip);
if ($isBotResult) {
self::$requestChecked = true;
$botBlocker->blockBot($userAgent, $ip, $path);
return true;
}
}
} catch (\Throwable $e) {
logger()->logException($e, ['plugin' => 'bot-blocker', 'method' => 'blockBots']);
}
self::$requestChecked = true;
return $handled; // Продовжуємо обробку запиту
}
/**
* Встановлення плагіна (створення таблиць)
*
* Створює необхідні таблиці в базі даних та встановлює налаштування за замовчуванням.
*/
public function install(): void
{
try {
$db = DatabaseHelper::getInstance();
if (!$db || !$db->isAvailable()) {
return;
}
// Перевіряємо існування таблиці
if (DatabaseHelper::tableExists('bot_blocker_logs')) {
return; // Таблиця вже існує
}
// Таблиця логів блокувань
$db->execute('
CREATE TABLE IF NOT EXISTS bot_blocker_logs (
id INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
ip_address VARCHAR(45) NOT NULL,
user_agent TEXT,
url VARCHAR(500),
blocked_at DATETIME NOT NULL,
created_at DATETIME NOT NULL,
PRIMARY KEY (id),
KEY idx_blocked_at (blocked_at),
KEY idx_ip_address (ip_address)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
');
// Встановлюємо налаштування за замовчуванням
$defaultSettings = [
'block_enabled' => '1',
'allowed_bots' => json_encode(['googlebot', 'bingbot', 'yandexbot', 'baiduspider'], JSON_UNESCAPED_UNICODE),
];
foreach ($defaultSettings as $key => $value) {
$this->setSetting($key, $value);
}
} catch (\Exception $e) {
logger()->logException($e, ['plugin' => 'bot-blocker', 'action' => 'install']);
}
}
}
return new BotBlockerPlugin();