From 944f240e89c162a4fe0f4db1bd846839394a00c3 Mon Sep 17 00:00:00 2001 From: Lain Iwakura Date: Thu, 24 Jul 2025 07:11:31 +0300 Subject: [PATCH] add admin --- .htaccess | 6 + README | 9 +- admin.php | 420 +++++++++++++++++++++++++++++++++++++++++++++ admin1337.php | 420 +++++++++++++++++++++++++++++++++++++++++++++ board.php | 4 + config.php | 6 +- config.php.example | 6 +- csrf.php | 12 ++ logger.php | 16 ++ styles.css | 4 +- 10 files changed, 898 insertions(+), 5 deletions(-) create mode 100644 .htaccess create mode 100644 admin.php create mode 100644 admin1337.php create mode 100644 csrf.php create mode 100644 logger.php diff --git a/.htaccess b/.htaccess new file mode 100644 index 0000000..8c346de --- /dev/null +++ b/.htaccess @@ -0,0 +1,6 @@ +RewriteEngine On + + + Order Allow,Deny + Deny from all + \ No newline at end of file diff --git a/README b/README index 01e661a..9ab4394 100644 --- a/README +++ b/README @@ -47,10 +47,17 @@ Docker установка: - Стиль 4chan/2ch - Ключ доступа для входа - Автообновление постов +- Админка для управления тредами Безопасность: - Валидация файлов - Ограничение размера - Защита от XSS - Rate limiting -- Безопасная загрузка файлов \ No newline at end of file +- Безопасная загрузка файлов + +Админка: +- Доступ по паролю из конфига +- Удаление тредов +- Просмотр статистики +- Путь: /admin1337.php \ No newline at end of file diff --git a/admin.php b/admin.php new file mode 100644 index 0000000..9bc93eb --- /dev/null +++ b/admin.php @@ -0,0 +1,420 @@ + + + + + + + mkach - Админка + + + +
+

mkach - Админка

+
+
+ +
+ +
+ +
+ +
+ + + setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + $db->exec('SET NAMES utf8'); +} catch (PDOException $e) { + die('Connection failed'); +} + +require_once 'logger.php'; + +if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['delete_thread'])) { + $threadId = $_POST['delete_thread']; + + try { + $db->beginTransaction(); + + $stmt = $db->prepare('DELETE FROM posts WHERE thread_id = ?'); + $stmt->execute([$threadId]); + + $stmt = $db->prepare('DELETE FROM threads WHERE thread_id = ?'); + $stmt->execute([$threadId]); + + $db->commit(); + logAdminAction('DELETE_THREAD', "Thread ID: $threadId"); + $success = 'Тред успешно удален'; + } catch (PDOException $e) { + $db->rollBack(); + $error = 'Ошибка при удалении треда'; + } +} + +if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['delete_post'])) { + $postId = $_POST['delete_post']; + + if (!preg_match('/^\d{6}$/', $postId)) { + $error = 'Неверный формат номера сообщения'; + } else { + try { + $stmt = $db->prepare('SELECT COUNT(*) FROM posts WHERE post_id = ?'); + $stmt->execute([$postId]); + $exists = $stmt->fetchColumn(); + + if ($exists) { + $stmt = $db->prepare('DELETE FROM posts WHERE post_id = ?'); + $stmt->execute([$postId]); + logAdminAction('DELETE_POST', "Post ID: $postId"); + $success = 'Сообщение №' . $postId . ' успешно удалено'; + } else { + $error = 'Сообщение №' . $postId . ' не найдено'; + } + } catch (PDOException $e) { + $error = 'Ошибка при удалении сообщения'; + } + } +} + +try { + $stmt = $db->prepare(' + SELECT t.*, b.name as board_name, COUNT(p.id) as post_count + FROM threads t + JOIN boards b ON t.board_id = b.board_id + LEFT JOIN posts p ON t.thread_id = p.thread_id + GROUP BY t.id + ORDER BY t.created_at DESC + '); + $stmt->execute(); + $threads = $stmt->fetchAll(PDO::FETCH_ASSOC); + + $stmt = $db->prepare(' + SELECT p.*, t.title as thread_title, b.board_id + FROM posts p + JOIN threads t ON p.thread_id = t.thread_id + JOIN boards b ON t.board_id = b.board_id + ORDER BY p.created_at DESC + LIMIT 50 + '); + $stmt->execute(); + $recentPosts = $stmt->fetchAll(PDO::FETCH_ASSOC); +} catch (PDOException $e) { + die('Database error'); +} +?> + + + + + + mkach - Админка + + + +
+
+

mkach - Админка

+ +
+ + +
+ + + +
+ + +

Управление тредами

+ + + + + + + + + + + + + + + + + + + + + + + +
IDДоскаНазваниеПостовСозданДействия
// +
+ + +
+
+ +

Удаление сообщений

+
+
+
+ + + +
+
+
+ +

Последние сообщения

+ + + + + + + + + + + + + + + + + + + + + +
ТредДоскаСообщениеВремя
// + 50 ? substr($message, 0, 50) . '...' : $message; + ?> +
+
+ + \ No newline at end of file diff --git a/admin1337.php b/admin1337.php new file mode 100644 index 0000000..9bc93eb --- /dev/null +++ b/admin1337.php @@ -0,0 +1,420 @@ + + + + + + + mkach - Админка + + + +
+

mkach - Админка

+
+
+ +
+ +
+ +
+ +
+ + + setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + $db->exec('SET NAMES utf8'); +} catch (PDOException $e) { + die('Connection failed'); +} + +require_once 'logger.php'; + +if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['delete_thread'])) { + $threadId = $_POST['delete_thread']; + + try { + $db->beginTransaction(); + + $stmt = $db->prepare('DELETE FROM posts WHERE thread_id = ?'); + $stmt->execute([$threadId]); + + $stmt = $db->prepare('DELETE FROM threads WHERE thread_id = ?'); + $stmt->execute([$threadId]); + + $db->commit(); + logAdminAction('DELETE_THREAD', "Thread ID: $threadId"); + $success = 'Тред успешно удален'; + } catch (PDOException $e) { + $db->rollBack(); + $error = 'Ошибка при удалении треда'; + } +} + +if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['delete_post'])) { + $postId = $_POST['delete_post']; + + if (!preg_match('/^\d{6}$/', $postId)) { + $error = 'Неверный формат номера сообщения'; + } else { + try { + $stmt = $db->prepare('SELECT COUNT(*) FROM posts WHERE post_id = ?'); + $stmt->execute([$postId]); + $exists = $stmt->fetchColumn(); + + if ($exists) { + $stmt = $db->prepare('DELETE FROM posts WHERE post_id = ?'); + $stmt->execute([$postId]); + logAdminAction('DELETE_POST', "Post ID: $postId"); + $success = 'Сообщение №' . $postId . ' успешно удалено'; + } else { + $error = 'Сообщение №' . $postId . ' не найдено'; + } + } catch (PDOException $e) { + $error = 'Ошибка при удалении сообщения'; + } + } +} + +try { + $stmt = $db->prepare(' + SELECT t.*, b.name as board_name, COUNT(p.id) as post_count + FROM threads t + JOIN boards b ON t.board_id = b.board_id + LEFT JOIN posts p ON t.thread_id = p.thread_id + GROUP BY t.id + ORDER BY t.created_at DESC + '); + $stmt->execute(); + $threads = $stmt->fetchAll(PDO::FETCH_ASSOC); + + $stmt = $db->prepare(' + SELECT p.*, t.title as thread_title, b.board_id + FROM posts p + JOIN threads t ON p.thread_id = t.thread_id + JOIN boards b ON t.board_id = b.board_id + ORDER BY p.created_at DESC + LIMIT 50 + '); + $stmt->execute(); + $recentPosts = $stmt->fetchAll(PDO::FETCH_ASSOC); +} catch (PDOException $e) { + die('Database error'); +} +?> + + + + + + mkach - Админка + + + +
+
+

mkach - Админка

+ +
+ + +
+ + + +
+ + +

Управление тредами

+ + + + + + + + + + + + + + + + + + + + + + + +
IDДоскаНазваниеПостовСозданДействия
// +
+ + +
+
+ +

Удаление сообщений

+
+
+
+ + + +
+
+
+ +

Последние сообщения

+ + + + + + + + + + + + + + + + + + + + + +
ТредДоскаСообщениеВремя
// + 50 ? substr($message, 0, 50) . '...' : $message; + ?> +
+
+ + \ No newline at end of file diff --git a/board.php b/board.php index b657e30..f03bdec 100644 --- a/board.php +++ b/board.php @@ -45,6 +45,10 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { $title = trim($_POST['title'] ?? ''); $description = trim($_POST['description'] ?? ''); + if (strlen($message) > $config['max_message_length']) { + $error = 'Сообщение слишком длинное'; + } + if ($message || $title || ($file && $file['error'] === UPLOAD_ERR_OK)) { $postId = sprintf('%06d', mt_rand(1, 999999)); diff --git a/config.php b/config.php index 0299e87..5ef07b8 100644 --- a/config.php +++ b/config.php @@ -10,7 +10,11 @@ return [ 'upload_path' => 'uploads/', 'max_file_size' => 26214400, 'allowed_types' => ['jpg', 'jpeg', 'png', 'gif', 'webp','JPG','JPEG','PNG','GIF','WEBP'], + 'max_message_length' => 10000, 'motd' => 'Добро пожаловать на mkach - анонимный имиджборд от МК', 'logo_enabled' => true, - 'logo_text' => 'mkach' + 'logo_text' => 'mkach', + 'admin' => [ + 'password' => 'admin1337' + ] ]; \ No newline at end of file diff --git a/config.php.example b/config.php.example index 59fcc91..36a98b2 100644 --- a/config.php.example +++ b/config.php.example @@ -10,7 +10,11 @@ return [ 'upload_path' => 'uploads/', 'max_file_size' => 26214400, 'allowed_types' => ['jpg', 'jpeg', 'png', 'gif', 'webp','JPG','JPEG','PNG','GIF','WEBP'], + 'max_message_length' => 10000, 'motd' => 'Добро пожаловать на mkach - анонимный имиджборд от МК', 'logo_enabled' => true, - 'logo_text' => 'mkach' + 'logo_text' => 'mkach', + 'admin' => [ + 'password' => 'admin1337' + ] ]; \ No newline at end of file diff --git a/csrf.php b/csrf.php new file mode 100644 index 0000000..d0886bb --- /dev/null +++ b/csrf.php @@ -0,0 +1,12 @@ + \ No newline at end of file diff --git a/logger.php b/logger.php new file mode 100644 index 0000000..e8239c2 --- /dev/null +++ b/logger.php @@ -0,0 +1,16 @@ + \ No newline at end of file diff --git a/styles.css b/styles.css index ef7b980..b1b03d6 100644 --- a/styles.css +++ b/styles.css @@ -43,7 +43,7 @@ body { color: #ff6b6b; } -.boards-btn, .logout-btn { +.boards-btn, .logout-btn, .admin-btn { background: #d6daf0; border: 1px solid #b7c5d9; padding: 5px 10px; @@ -53,7 +53,7 @@ body { margin-left: 5px; } -.boards-btn:hover, .logout-btn:hover { +.boards-btn:hover, .logout-btn:hover, .admin-btn:hover { background: #e5e9f0; }