447 lines
18 KiB
PHP
Executable File
447 lines
18 KiB
PHP
Executable File
<?php
|
||
// install.php - установщик приложения
|
||
|
||
// Проверяем, не установлено ли приложение уже
|
||
if (file_exists('config/config.php')) {
|
||
die('Приложение уже установлено. Для переустановки удалите файл config/config.php');
|
||
}
|
||
|
||
// SQL для создания таблиц
|
||
$database_sql = <<<SQL
|
||
SET FOREIGN_KEY_CHECKS=0;
|
||
|
||
-- Удаляем существующие таблицы в правильном порядке (сначала дочерние, потом родительские)
|
||
DROP TABLE IF EXISTS `user_sessions`;
|
||
DROP TABLE IF EXISTS `chapters`;
|
||
DROP TABLE IF EXISTS `books`;
|
||
DROP TABLE IF EXISTS `series`;
|
||
DROP TABLE IF EXISTS `users`;
|
||
|
||
-- Таблица пользователей
|
||
CREATE TABLE `users` (
|
||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||
`username` varchar(255) NOT NULL,
|
||
`display_name` varchar(255) DEFAULT NULL,
|
||
`password_hash` varchar(255) NOT NULL,
|
||
`email` varchar(255) DEFAULT NULL,
|
||
`avatar` varchar(500) DEFAULT NULL,
|
||
`bio` text DEFAULT NULL,
|
||
`created_at` timestamp NOT NULL DEFAULT current_timestamp(),
|
||
`last_login` timestamp NULL DEFAULT NULL,
|
||
`is_active` tinyint(1) DEFAULT 0,
|
||
PRIMARY KEY (`id`),
|
||
UNIQUE KEY `username` (`username`)
|
||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
|
||
|
||
-- Таблица серий
|
||
CREATE TABLE `series` (
|
||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||
`title` varchar(255) NOT NULL,
|
||
`description` text DEFAULT NULL,
|
||
`user_id` int(11) NOT NULL,
|
||
`created_at` timestamp NOT NULL DEFAULT current_timestamp(),
|
||
`updated_at` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
|
||
PRIMARY KEY (`id`),
|
||
KEY `user_id` (`user_id`),
|
||
KEY `idx_series_user_id` (`user_id`),
|
||
CONSTRAINT `series_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE
|
||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
|
||
|
||
-- Таблица книг
|
||
CREATE TABLE `books` (
|
||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||
`title` varchar(255) NOT NULL,
|
||
`description` text DEFAULT NULL,
|
||
`genre` varchar(100) DEFAULT NULL,
|
||
`cover_image` varchar(500) DEFAULT NULL,
|
||
`user_id` int(11) NOT NULL,
|
||
`series_id` int(11) DEFAULT NULL,
|
||
`sort_order_in_series` int(11) DEFAULT NULL,
|
||
`created_at` timestamp NOT NULL DEFAULT current_timestamp(),
|
||
`updated_at` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
|
||
`share_token` varchar(32) DEFAULT NULL,
|
||
`published` tinyint(1) NOT NULL DEFAULT 0,
|
||
PRIMARY KEY (`id`),
|
||
UNIQUE KEY `share_token` (`share_token`),
|
||
KEY `user_id` (`user_id`),
|
||
KEY `series_id` (`series_id`),
|
||
KEY `idx_sort_order_in_series` (`sort_order_in_series`),
|
||
KEY `idx_books_series_id` (`series_id`),
|
||
KEY `idx_books_sort_order` (`sort_order_in_series`),
|
||
CONSTRAINT `books_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE,
|
||
CONSTRAINT `books_ibfk_2` FOREIGN KEY (`series_id`) REFERENCES `series` (`id`) ON DELETE SET NULL
|
||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
|
||
|
||
-- Таблица глав
|
||
CREATE TABLE `chapters` (
|
||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||
`book_id` int(11) NOT NULL,
|
||
`title` varchar(255) NOT NULL,
|
||
`content` text NOT NULL,
|
||
`word_count` int(11) DEFAULT 0,
|
||
`sort_order` int(11) NOT NULL DEFAULT 0,
|
||
`status` enum('draft','published') DEFAULT 'draft',
|
||
`created_at` timestamp NOT NULL DEFAULT current_timestamp(),
|
||
`updated_at` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
|
||
PRIMARY KEY (`id`),
|
||
KEY `book_id` (`book_id`),
|
||
CONSTRAINT `chapters_ibfk_1` FOREIGN KEY (`book_id`) REFERENCES `books` (`id`) ON DELETE CASCADE
|
||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
|
||
|
||
-- Таблица сессий пользователей
|
||
CREATE TABLE `user_sessions` (
|
||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||
`user_id` int(11) NOT NULL,
|
||
`session_token` varchar(64) NOT NULL,
|
||
`created_at` timestamp NOT NULL DEFAULT current_timestamp(),
|
||
`expires_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
|
||
PRIMARY KEY (`id`),
|
||
KEY `user_id` (`user_id`),
|
||
CONSTRAINT `user_sessions_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE
|
||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
|
||
|
||
SET FOREIGN_KEY_CHECKS=1;
|
||
SQL;
|
||
|
||
$step = $_GET['step'] ?? '1';
|
||
$error = '';
|
||
$success = '';
|
||
|
||
// Обработка формы
|
||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||
if ($step === '1') {
|
||
// Шаг 1: Проверка подключения к БД
|
||
$db_host = $_POST['db_host'] ?? 'localhost';
|
||
$db_name = $_POST['db_name'] ?? 'writer_app';
|
||
$db_user = $_POST['db_user'] ?? '';
|
||
$db_pass = $_POST['db_pass'] ?? '';
|
||
|
||
try {
|
||
// Пытаемся подключиться к MySQL
|
||
$pdo = new PDO("mysql:host=$db_host", $db_user, $db_pass);
|
||
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||
|
||
// Пытаемся создать базу данных если не существует
|
||
$pdo->exec("CREATE DATABASE IF NOT EXISTS `$db_name` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci");
|
||
$pdo->exec("USE `$db_name`");
|
||
|
||
// Сохраняем данные в сессии для следующего шага
|
||
session_start();
|
||
$_SESSION['install_data'] = [
|
||
'db_host' => $db_host,
|
||
'db_name' => $db_name,
|
||
'db_user' => $db_user,
|
||
'db_pass' => $db_pass
|
||
];
|
||
|
||
header('Location: install.php?step=2');
|
||
exit;
|
||
|
||
} catch (PDOException $e) {
|
||
$error = "Ошибка подключения к базе данных: " . $e->getMessage();
|
||
}
|
||
|
||
} elseif ($step === '2') {
|
||
// Шаг 2: Создание администратора
|
||
session_start();
|
||
if (!isset($_SESSION['install_data'])) {
|
||
header('Location: install.php?step=1');
|
||
exit;
|
||
}
|
||
|
||
$admin_username = $_POST['admin_username'] ?? '';
|
||
$admin_password = $_POST['admin_password'] ?? '';
|
||
$admin_email = $_POST['admin_email'] ?? '';
|
||
$admin_display_name = $_POST['admin_display_name'] ?? $admin_username;
|
||
|
||
if (empty($admin_username) || empty($admin_password)) {
|
||
$error = 'Имя пользователя и пароль администратора обязательны';
|
||
} else {
|
||
try {
|
||
$db = $_SESSION['install_data'];
|
||
$pdo = new PDO("mysql:host={$db['db_host']};dbname={$db['db_name']}", $db['db_user'], $db['db_pass']);
|
||
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||
|
||
// Создаем таблицы
|
||
$pdo->exec($database_sql);
|
||
|
||
// Создаем администратора
|
||
$password_hash = password_hash($admin_password, PASSWORD_DEFAULT);
|
||
$stmt = $pdo->prepare("
|
||
INSERT INTO users (username, display_name, password_hash, email, is_active, created_at)
|
||
VALUES (?, ?, ?, ?, 1, NOW())
|
||
");
|
||
$stmt->execute([$admin_username, $admin_display_name, $password_hash, $admin_email]);
|
||
|
||
// Создаем config.php
|
||
$config_content = generate_config($db);
|
||
if (file_put_contents('config/config.php', $config_content)) {
|
||
// Создаем папки для загрузок
|
||
if (!file_exists('uploads/covers')) {
|
||
mkdir('uploads/covers', 0755, true);
|
||
}
|
||
if (!file_exists('uploads/avatars')) {
|
||
mkdir('uploads/avatars', 0755, true);
|
||
}
|
||
|
||
$success = 'Установка завершена успешно!';
|
||
session_destroy();
|
||
} else {
|
||
$error = 'Не удалось создать файл config.php. Проверьте права доступа к папке config/';
|
||
}
|
||
|
||
} catch (PDOException $e) {
|
||
$error = "Ошибка при установке: " . $e->getMessage();
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
function generate_config($db) {
|
||
$site_url = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? "https" : "http") . "://$_SERVER[HTTP_HOST]";
|
||
$base_path = str_replace('/install.php', '', $_SERVER['PHP_SELF']);
|
||
$site_url .= $base_path;
|
||
|
||
return <<<EOT
|
||
<?php
|
||
// config/config.php - автоматически сгенерирован установщиком
|
||
// Подключаем функции
|
||
require_once __DIR__ . '/../includes/functions.php';
|
||
session_start();
|
||
|
||
// Настройки базы данных
|
||
define('DB_HOST', '{$db['db_host']}');
|
||
define('DB_USER', '{$db['db_user']}');
|
||
define('DB_PASS', '{$db['db_pass']}');
|
||
define('DB_NAME', '{$db['db_name']}');
|
||
define('SITE_URL', '{$site_url}');
|
||
|
||
// Настройки приложения
|
||
define('APP_NAME', 'Web Writer');
|
||
|
||
define('CONTROLLERS_PATH', __DIR__ . '/../controllers/');
|
||
define('VIEWS_PATH', __DIR__ . '/../views/');
|
||
define('LAYOUTS_PATH', VIEWS_PATH . 'layouts/');
|
||
|
||
define('UPLOAD_PATH', __DIR__ . '/../uploads/');
|
||
define('COVERS_PATH', UPLOAD_PATH . 'covers/');
|
||
define('COVERS_URL', SITE_URL . '/uploads/covers/');
|
||
define('AVATARS_PATH', UPLOAD_PATH . 'avatars/');
|
||
define('AVATARS_URL', SITE_URL . '/uploads/avatars/');
|
||
|
||
// Создаем папку для загрузок, если ее нет
|
||
if (!file_exists(COVERS_PATH)) {
|
||
mkdir(COVERS_PATH, 0755, true);
|
||
}
|
||
if (!file_exists(AVATARS_PATH)) {
|
||
mkdir(AVATARS_PATH, 0755, true);
|
||
}
|
||
|
||
// Подключение к базе данных
|
||
try {
|
||
\$pdo = new PDO("mysql:host=" . DB_HOST . ";dbname=" . DB_NAME . ";charset=utf8mb4", DB_USER, DB_PASS);
|
||
\$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||
} catch(PDOException \$e) {
|
||
error_log("DB Error: " . \$e->getMessage());
|
||
die("Ошибка подключения к базе данных");
|
||
}
|
||
|
||
|
||
|
||
// Автозагрузка моделей
|
||
spl_autoload_register(function (\$class_name) {
|
||
\$model_file = __DIR__ . '/../models/' . \$class_name . '.php';
|
||
if (file_exists(\$model_file)) {
|
||
require_once \$model_file;
|
||
}
|
||
});
|
||
?>
|
||
EOT;
|
||
}
|
||
?>
|
||
<!DOCTYPE html>
|
||
<html lang="ru">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>Установка Web Writer</title>
|
||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@picocss/pico@1.5.10/css/pico.min.css">
|
||
<style>
|
||
.installation-steps {
|
||
display: flex;
|
||
justify-content: center;
|
||
margin-bottom: 2rem;
|
||
}
|
||
.step {
|
||
padding: 10px 20px;
|
||
margin: 0 10px;
|
||
border-radius: 20px;
|
||
background: #f0f0f0;
|
||
color: #666;
|
||
}
|
||
.step.active {
|
||
background: #007bff;
|
||
color: white;
|
||
}
|
||
.install-container {
|
||
max-width: 600px;
|
||
margin: 2rem auto;
|
||
padding: 2rem;
|
||
}
|
||
.alert {
|
||
padding: 1rem;
|
||
margin: 1rem 0;
|
||
border-radius: 5px;
|
||
}
|
||
.alert-error {
|
||
background: #ffebee;
|
||
color: #c62828;
|
||
border: 1px solid #ffcdd2;
|
||
}
|
||
.alert-success {
|
||
background: #e8f5e8;
|
||
color: #2e7d32;
|
||
border: 1px solid #c8e6c9;
|
||
}
|
||
.button-group {
|
||
display: flex;
|
||
gap: 10px;
|
||
margin-top: 1rem;
|
||
}
|
||
.button-group a,
|
||
.button-group button {
|
||
flex: 1;
|
||
text-align: center;
|
||
padding: 0.75rem;
|
||
text-decoration: none;
|
||
border: 1px solid;
|
||
border-radius: 4px;
|
||
font-size: 1rem;
|
||
cursor: pointer;
|
||
box-sizing: border-box;
|
||
height: 50px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
.button-group a {
|
||
background: var(--secondary);
|
||
border-color: var(--secondary);
|
||
color: var(--secondary-inverse);
|
||
}
|
||
.button-group button {
|
||
background: var(--primary);
|
||
border-color: var(--primary);
|
||
color: var(--primary-inverse);
|
||
}
|
||
.button-group a:hover,
|
||
.button-group button:hover {
|
||
opacity: 0.9;
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<main class="container">
|
||
<div class="install-container">
|
||
<h1 style="text-align: center;">Установка Web Writer</h1>
|
||
|
||
<!-- Шаги установки -->
|
||
<div class="installation-steps">
|
||
<div class="step <?= $step === '1' ? 'active' : '' ?>">1. База данных</div>
|
||
<div class="step <?= $step === '2' ? 'active' : '' ?>">2. Администратор</div>
|
||
<div class="step <?= $step === '3' ? 'active' : '' ?>">3. Завершение</div>
|
||
</div>
|
||
|
||
<?php if ($error): ?>
|
||
<div class="alert alert-error">
|
||
<?= htmlspecialchars($error) ?>
|
||
</div>
|
||
<?php endif; ?>
|
||
|
||
<?php if ($success): ?>
|
||
<div class="alert alert-success">
|
||
<?= htmlspecialchars($success) ?>
|
||
<div style="margin-top: 1rem;">
|
||
<a href="index.php" role="button" class="contrast">Перейти к приложению</a>
|
||
</div>
|
||
</div>
|
||
<?php endif; ?>
|
||
|
||
<?php if (!$success): ?>
|
||
<?php if ($step === '1'): ?>
|
||
<!-- Шаг 1: Настройки базы данных -->
|
||
<form method="post">
|
||
<h3>Настройки базы данных</h3>
|
||
|
||
<label for="db_host">
|
||
Хост БД
|
||
<input type="text" id="db_host" name="db_host" value="<?= htmlspecialchars($_POST['db_host'] ?? 'localhost') ?>" required>
|
||
</label>
|
||
|
||
<label for="db_name">
|
||
Имя базы данных
|
||
<input type="text" id="db_name" name="db_name" value="<?= htmlspecialchars($_POST['db_name'] ?? 'writer_app') ?>" required>
|
||
</label>
|
||
|
||
<label for="db_user">
|
||
Пользователь БД
|
||
<input type="text" id="db_user" name="db_user" value="<?= htmlspecialchars($_POST['db_user'] ?? '') ?>" required>
|
||
</label>
|
||
|
||
<label for="db_pass">
|
||
Пароль БД
|
||
<input type="password" id="db_pass" name="db_pass" value="<?= htmlspecialchars($_POST['db_pass'] ?? '') ?>">
|
||
</label>
|
||
|
||
<button type="submit" class="contrast" style="width: 100%;">Продолжить</button>
|
||
</form>
|
||
|
||
<?php elseif ($step === '2'): ?>
|
||
<!-- Шаг 2: Создание администратора -->
|
||
<form method="post">
|
||
<h3>Создание администратора</h3>
|
||
<p>Создайте учетную запись администратора для управления приложением.</p>
|
||
|
||
<label for="admin_username">
|
||
Имя пользователя *
|
||
<input type="text" id="admin_username" name="admin_username" value="<?= htmlspecialchars($_POST['admin_username'] ?? '') ?>" required>
|
||
</label>
|
||
|
||
<label for="admin_password">
|
||
Пароль *
|
||
<input type="password" id="admin_password" name="admin_password" required>
|
||
</label>
|
||
|
||
<label for="admin_display_name">
|
||
Отображаемое имя
|
||
<input type="text" id="admin_display_name" name="admin_display_name" value="<?= htmlspecialchars($_POST['admin_display_name'] ?? '') ?>">
|
||
</label>
|
||
|
||
<label for="admin_email">
|
||
Email
|
||
<input type="email" id="admin_email" name="admin_email" value="<?= htmlspecialchars($_POST['admin_email'] ?? '') ?>">
|
||
</label>
|
||
|
||
<div class="button-group">
|
||
<a href="install.php?step=1">Назад</a>
|
||
<button type="submit">Завершить установку</button>
|
||
</div>
|
||
</form>
|
||
<?php endif; ?>
|
||
<?php endif; ?>
|
||
|
||
<?php if ($step === '1' && !$success): ?>
|
||
<div style="margin-top: 2rem; padding: 1rem; background: #f8f9fa; border-radius: 5px;">
|
||
<h4>Перед установкой убедитесь, что:</h4>
|
||
<ul>
|
||
<li>Сервер MySQL запущен и доступен</li>
|
||
<li>У вас есть данные для подключения к БД (хост, пользователь, пароль)</li>
|
||
<li>Папка <code>config/</code> доступна для записи</li>
|
||
<li>Папка <code>uploads/</code> доступна для записи</li>
|
||
</ul>
|
||
</div>
|
||
<?php endif; ?>
|
||
</div>
|
||
</main>
|
||
</body>
|
||
</html>
|