186 lines
7.7 KiB
PHP
186 lines
7.7 KiB
PHP
<?php
|
||
session_start();
|
||
header('Content-Type: text/html; charset=utf-8');
|
||
header('X-Content-Type-Options: nosniff');
|
||
header('X-Frame-Options: DENY');
|
||
header('X-XSS-Protection: 1; mode=block');
|
||
|
||
if (!isset($_SESSION['authenticated'])) {
|
||
header('Location: index.php');
|
||
exit;
|
||
}
|
||
|
||
$config = require 'config.php';
|
||
$boardId = isset($_GET['board']) ? urldecode($_GET['board']) : 'b';
|
||
|
||
try {
|
||
$db = new PDO(
|
||
"mysql:host={$config['db']['host']};dbname={$config['db']['name']}",
|
||
$config['db']['user'],
|
||
$config['db']['pass']
|
||
);
|
||
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||
} catch (PDOException $e) {
|
||
die('Connection failed');
|
||
}
|
||
|
||
require_once 'RateLimiter.php';
|
||
require_once 'AnonymousID.php';
|
||
$rateLimiter = new RateLimiter($db);
|
||
$rateLimiter->cleanup();
|
||
|
||
$ip = $_SERVER['REMOTE_ADDR'];
|
||
$anonymousID = new AnonymousID($db, $ip, $boardId);
|
||
|
||
try {
|
||
$db->exec('SET NAMES utf8');
|
||
$stmt = $db->prepare('SELECT * FROM boards WHERE board_id = ?');
|
||
$stmt->execute([$boardId]);
|
||
$board = $stmt->fetch(PDO::FETCH_ASSOC);
|
||
|
||
if (!$board) {
|
||
header('Location: index.php');
|
||
exit;
|
||
}
|
||
} catch (PDOException $e) {
|
||
die('Database error');
|
||
}
|
||
|
||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||
if (!$rateLimiter->isAllowed($ip)) {
|
||
$error = 'Слишком много запросов';
|
||
} else {
|
||
$title = trim($_POST['title'] ?? '');
|
||
$description = trim($_POST['description'] ?? '');
|
||
$message = trim($_POST['message'] ?? '');
|
||
$file = $_FILES['file'] ?? null;
|
||
|
||
if ($title && ($description || $message || ($file && $file['error'] === UPLOAD_ERR_OK))) {
|
||
$threadId = sprintf('%06d', mt_rand(1, 999999));
|
||
$postId = sprintf('%06d', mt_rand(1, 999999));
|
||
$anonymousId = $anonymousID->getIDForThread();
|
||
|
||
$fileName = null;
|
||
$fileSize = null;
|
||
$fileType = null;
|
||
|
||
if ($file && $file['error'] === UPLOAD_ERR_OK) {
|
||
$fileExt = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
|
||
|
||
if (!in_array($fileExt, $config['allowed_types'])) {
|
||
$error = 'Неподдерживаемый тип файла';
|
||
} elseif ($file['size'] > $config['max_file_size']) {
|
||
$error = 'Файл слишком большой';
|
||
} else {
|
||
$uploadDir = $config['upload_path'];
|
||
if (!is_dir($uploadDir)) {
|
||
mkdir($uploadDir, 0755, true);
|
||
}
|
||
|
||
$fileName = $threadId . '.' . $fileExt;
|
||
$filePath = $uploadDir . $fileName;
|
||
|
||
if (move_uploaded_file($file['tmp_name'], $filePath)) {
|
||
$fileSize = $file['size'];
|
||
$fileType = $fileExt;
|
||
} else {
|
||
$error = 'Ошибка загрузки файла';
|
||
}
|
||
}
|
||
}
|
||
|
||
if (empty($error)) {
|
||
$stmt = $db->prepare('
|
||
INSERT INTO threads (thread_id, board_id, title, description, file_name, file_size, file_type, ip_address, anonymous_id)
|
||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||
');
|
||
$stmt->execute([$threadId, $boardId, $title, $description, $fileName, $fileSize, $fileType, $ip, $anonymousId]);
|
||
|
||
if ($message) {
|
||
$stmt = $db->prepare('
|
||
INSERT INTO posts (post_id, thread_id, board_id, message, ip_address, anonymous_id)
|
||
VALUES (?, ?, ?, ?, ?, ?)
|
||
');
|
||
$stmt->execute([$postId, $threadId, $boardId, $message, $ip, $anonymousId]);
|
||
}
|
||
|
||
header('Location: board.php?board=' . $boardId . '&thread=' . $threadId);
|
||
exit;
|
||
}
|
||
} else {
|
||
$error = 'Введите название треда и хотя бы описание, сообщение или файл';
|
||
}
|
||
}
|
||
}
|
||
?>
|
||
<!DOCTYPE html>
|
||
<html>
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>mkach - Создать тред в /<?= htmlspecialchars($boardId) ?>/</title>
|
||
<link rel="stylesheet" href="styles.css">
|
||
</head>
|
||
<body>
|
||
<div class="container">
|
||
<div class="header">
|
||
<h1><a href="index.php" class="home-link">mkach</a> - Создать тред в /<?= htmlspecialchars($boardId) ?>/</h1>
|
||
<div class="header-buttons">
|
||
<a href="board.php?board=<?= urlencode($boardId) ?>" class="boards-btn">← Назад</a>
|
||
<a href="index.php" class="boards-btn">Доски</a>
|
||
<a href="?logout=1" class="logout-btn">Выход</a>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="new-thread-container">
|
||
<?php if (!empty($error)): ?>
|
||
<div class="error"><?= htmlspecialchars($error) ?></div>
|
||
<?php endif; ?>
|
||
|
||
<div class="thread-form">
|
||
<h2>Создать новый тред</h2>
|
||
<form method="post" enctype="multipart/form-data">
|
||
<div class="form-row">
|
||
<label>Название треда:</label>
|
||
<input type="text" name="title" placeholder="Введите название треда" class="title-input" required>
|
||
</div>
|
||
|
||
<div class="form-row">
|
||
<label>Описание:</label>
|
||
<textarea name="description" placeholder="Описание треда (поддерживает Markdown)" class="description-input"></textarea>
|
||
</div>
|
||
|
||
<div class="form-row">
|
||
<label>Первое сообщение (необязательно):</label>
|
||
<textarea name="message" placeholder="Первое сообщение в треде (поддерживает Markdown)" class="message-input"></textarea>
|
||
</div>
|
||
|
||
<div class="form-row">
|
||
<label>Файл (необязательно):</label>
|
||
<input type="file" name="file" accept=".jpg,.jpeg,.png,.gif,.webp" class="file-input">
|
||
</div>
|
||
|
||
<div class="form-row">
|
||
<button type="submit" class="send-btn">Создать тред</button>
|
||
<a href="board.php?board=<?= urlencode($boardId) ?>" class="cancel-btn">Отмена</a>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
|
||
<div class="markdown-help">
|
||
<h3>Поддерживаемые теги Markdown:</h3>
|
||
<ul>
|
||
<li><code>**жирный**</code> - жирный текст</li>
|
||
<li><code>*курсив*</code> - курсив</li>
|
||
<li><code>_подчеркнутый_</code> - подчеркнутый</li>
|
||
<li><code>~~зачеркнутый~~</code> - зачеркнутый</li>
|
||
<li><code>`код`</code> - код</li>
|
||
<li><code>>>123456</code> - ссылка на пост</li>
|
||
<li><code>[текст](url)</code> - ссылка</li>
|
||
<li><code>* элемент</code> - список</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</body>
|
||
</html>
|