- 将数据库配置从硬编码改为从外部文件加载 - 支持MySQL 5.7+和SQLite两种数据库类型 - 改进安装向导流程,增加数据库类型选择 - 更新README文档说明数据库要求 - 替换CDN链接为国内镜像源 - 增加安装完成后的安全提醒 重构数据库类以支持更灵活的配置方式,包括端口设置和MySQL 5.7兼容性处理。安装向导现在可以正确配置SQLite或MySQL数据库,并生成相应的配置文件。同时优化了前端资源加载速度。
489 lines
16 KiB
PHP
489 lines
16 KiB
PHP
<?php
|
||
/**
|
||
* 安装脚本
|
||
* 用于初始化数据库和检查环境
|
||
*/
|
||
|
||
if (file_exists(__DIR__ . '/install.lock') && (!isset($_GET['step']) || $_GET['step'] != 4)) {
|
||
header('Location: index.php');
|
||
exit('系统已安装,请勿重复安装!');
|
||
}
|
||
|
||
$step = $_GET['step'] ?? 1;
|
||
$message = '';
|
||
$message_type = '';
|
||
|
||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||
if ($step == 2) {
|
||
// 数据库配置并测试连接
|
||
$db_type = $_POST['db_type'];
|
||
if ($db_type === 'sqlite') {
|
||
$sqlite_path = __DIR__ . '/data/submission.db';
|
||
if (!is_dir(__DIR__ . '/data')) mkdir(__DIR__ . '/data', 0777, true);
|
||
$config = [
|
||
'type' => 'sqlite',
|
||
'sqlite_path' => $sqlite_path,
|
||
'host' => '',
|
||
'port' => '',
|
||
'db_name' => '',
|
||
'username' => '',
|
||
'password' => ''
|
||
];
|
||
} else {
|
||
$config = [
|
||
'type' => 'mysql',
|
||
'host' => $_POST['db_host'],
|
||
'port' => $_POST['db_port'],
|
||
'db_name' => $_POST['db_name'],
|
||
'username' => $_POST['db_user'],
|
||
'password' => $_POST['db_pass'],
|
||
'sqlite_path' => ''
|
||
];
|
||
}
|
||
require_once __DIR__ . '/config/database.php';
|
||
try {
|
||
file_put_contents(__DIR__ . '/config/db_config.php', "<?php\nreturn " . var_export($config, true) . ";\n");
|
||
$db = (new Database())->getConnection();
|
||
$message = '数据库连接成功!';
|
||
$message_type = 'success';
|
||
header('Location: install.php?step=3');
|
||
exit;
|
||
} catch (Exception $e) {
|
||
if (file_exists(__DIR__ . '/config/db_config.php')) {
|
||
unlink(__DIR__ . '/config/db_config.php');
|
||
}
|
||
$message = $e->getMessage();
|
||
$message_type = 'error';
|
||
}
|
||
} elseif ($step == 3) {
|
||
// 初始化数据库表结构(略,可复用你原有的表结构SQL)
|
||
// ...
|
||
file_put_contents(__DIR__ . '/install.lock', 'installed');
|
||
header('Location: install.php?step=4');
|
||
exit;
|
||
}
|
||
}
|
||
|
||
// 环境检查
|
||
function checkEnvironment() {
|
||
$checks = [
|
||
'PHP版本 >= 7.4' => version_compare(PHP_VERSION, '7.4.0', '>='),
|
||
'PDO扩展' => extension_loaded('pdo'),
|
||
'PDO MySQL扩展' => extension_loaded('pdo_mysql'),
|
||
'PDO SQLite扩展' => extension_loaded('pdo_sqlite'),
|
||
'GD扩展' => extension_loaded('gd'),
|
||
'cURL扩展' => extension_loaded('curl'),
|
||
'config目录可写' => is_writable(__DIR__ . '/config'),
|
||
'data目录可写' => is_writable(__DIR__ . '/data') || mkdir(__DIR__ . '/data', 0755, true)
|
||
];
|
||
|
||
return $checks;
|
||
}
|
||
|
||
$env_checks = checkEnvironment();
|
||
?>
|
||
|
||
<!DOCTYPE html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>安装向导 - 内容投稿系统</title>
|
||
<link href="https://cdn.bootcdn.net/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
|
||
<style>
|
||
* {
|
||
margin: 0;
|
||
padding: 0;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
body {
|
||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||
min-height: 100vh;
|
||
padding: 20px;
|
||
}
|
||
|
||
.container {
|
||
max-width: 600px;
|
||
margin: 0 auto;
|
||
background: rgba(255, 255, 255, 0.95);
|
||
border-radius: 20px;
|
||
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
|
||
overflow: hidden;
|
||
backdrop-filter: blur(10px);
|
||
}
|
||
|
||
.header {
|
||
background: linear-gradient(135deg, #4f46e5 0%, #7c3aed 100%);
|
||
color: white;
|
||
padding: 40px;
|
||
text-align: center;
|
||
}
|
||
|
||
.header h1 {
|
||
font-size: 2rem;
|
||
font-weight: 700;
|
||
margin-bottom: 10px;
|
||
}
|
||
|
||
.step-indicator {
|
||
display: flex;
|
||
justify-content: center;
|
||
gap: 10px;
|
||
margin-top: 20px;
|
||
}
|
||
|
||
.step {
|
||
width: 30px;
|
||
height: 30px;
|
||
border-radius: 50%;
|
||
background: rgba(255, 255, 255, 0.3);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.step.active {
|
||
background: white;
|
||
color: #4f46e5;
|
||
}
|
||
|
||
.step.completed {
|
||
background: #10b981;
|
||
color: white;
|
||
}
|
||
|
||
.content {
|
||
padding: 40px;
|
||
}
|
||
|
||
.message {
|
||
padding: 15px;
|
||
border-radius: 8px;
|
||
margin-bottom: 20px;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.message.success {
|
||
background: #d1fae5;
|
||
color: #065f46;
|
||
border: 1px solid #10b981;
|
||
}
|
||
|
||
.message.error {
|
||
background: #fee2e2;
|
||
color: #991b1b;
|
||
border: 1px solid #ef4444;
|
||
}
|
||
|
||
.check-list {
|
||
list-style: none;
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
.check-item {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 10px;
|
||
padding: 10px 0;
|
||
border-bottom: 1px solid #e5e7eb;
|
||
}
|
||
|
||
.check-item:last-child {
|
||
border-bottom: none;
|
||
}
|
||
|
||
.check-icon {
|
||
width: 20px;
|
||
text-align: center;
|
||
}
|
||
|
||
.check-icon.success {
|
||
color: #10b981;
|
||
}
|
||
|
||
.check-icon.error {
|
||
color: #ef4444;
|
||
}
|
||
|
||
.form-group {
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
.form-group label {
|
||
display: block;
|
||
margin-bottom: 8px;
|
||
font-weight: 600;
|
||
color: #374151;
|
||
}
|
||
|
||
.form-group input,
|
||
.form-group select {
|
||
width: 100%;
|
||
padding: 12px 16px;
|
||
border: 2px solid #e5e7eb;
|
||
border-radius: 8px;
|
||
font-size: 16px;
|
||
}
|
||
|
||
.form-group input:focus,
|
||
.form-group select:focus {
|
||
outline: none;
|
||
border-color: #4f46e5;
|
||
}
|
||
|
||
.btn {
|
||
padding: 12px 24px;
|
||
border: none;
|
||
border-radius: 8px;
|
||
font-size: 16px;
|
||
font-weight: 600;
|
||
cursor: pointer;
|
||
transition: all 0.3s ease;
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
}
|
||
|
||
.btn-primary {
|
||
background: linear-gradient(135deg, #4f46e5 0%, #7c3aed 100%);
|
||
color: white;
|
||
}
|
||
|
||
.btn-primary:hover {
|
||
transform: translateY(-2px);
|
||
box-shadow: 0 10px 20px rgba(79, 70, 229, 0.3);
|
||
}
|
||
|
||
.btn-success {
|
||
background: #10b981;
|
||
color: white;
|
||
}
|
||
|
||
.btn-success:hover {
|
||
background: #059669;
|
||
}
|
||
|
||
.actions {
|
||
display: flex;
|
||
gap: 15px;
|
||
justify-content: center;
|
||
margin-top: 30px;
|
||
}
|
||
|
||
.db-option {
|
||
padding: 20px;
|
||
border: 2px solid #e5e7eb;
|
||
border-radius: 8px;
|
||
margin-bottom: 15px;
|
||
cursor: pointer;
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
.db-option:hover {
|
||
border-color: #4f46e5;
|
||
}
|
||
|
||
.db-option.selected {
|
||
border-color: #4f46e5;
|
||
background: #f0f9ff;
|
||
}
|
||
|
||
.db-config {
|
||
display: none;
|
||
margin-top: 20px;
|
||
padding: 20px;
|
||
background: #f8fafc;
|
||
border-radius: 8px;
|
||
}
|
||
|
||
.db-config.active {
|
||
display: block;
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="container">
|
||
<div class="header">
|
||
<h1><i class="fas fa-cogs"></i> 安装向导</h1>
|
||
<p>内容投稿系统安装配置</p>
|
||
<div class="step-indicator">
|
||
<div class="step <?php echo $step >= 1 ? ($step > 1 ? 'completed' : 'active') : ''; ?>">1</div>
|
||
<div class="step <?php echo $step >= 2 ? ($step > 2 ? 'completed' : 'active') : ''; ?>">2</div>
|
||
<div class="step <?php echo $step >= 3 ? ($step > 3 ? 'completed' : 'active') : ''; ?>">3</div>
|
||
<div class="step <?php echo $step >= 4 ? 'active' : ''; ?>">4</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="content">
|
||
<?php if ($message): ?>
|
||
<div class="message <?php echo $message_type; ?>">
|
||
<?php echo htmlspecialchars($message); ?>
|
||
</div>
|
||
<?php endif; ?>
|
||
|
||
<?php if ($step == 1): ?>
|
||
<!-- 步骤1: 环境检查 -->
|
||
<h2>环境检查</h2>
|
||
<p>正在检查服务器环境是否满足运行要求...</p>
|
||
|
||
<ul class="check-list">
|
||
<?php foreach ($env_checks as $name => $status): ?>
|
||
<li class="check-item">
|
||
<div class="check-icon <?php echo $status ? 'success' : 'error'; ?>">
|
||
<i class="fas <?php echo $status ? 'fa-check' : 'fa-times'; ?>"></i>
|
||
</div>
|
||
<span><?php echo $name; ?></span>
|
||
</li>
|
||
<?php endforeach; ?>
|
||
</ul>
|
||
|
||
<?php if (array_product($env_checks)): ?>
|
||
<div class="actions">
|
||
<a href="?step=2" class="btn btn-primary">
|
||
<i class="fas fa-arrow-right"></i> 下一步
|
||
</a>
|
||
</div>
|
||
<?php else: ?>
|
||
<div class="message error">
|
||
<i class="fas fa-exclamation-triangle"></i>
|
||
环境检查未通过,请先解决上述问题后再继续安装。
|
||
</div>
|
||
<?php endif; ?>
|
||
|
||
<?php elseif ($step == 2): ?>
|
||
<!-- 步骤2: 数据库配置 -->
|
||
<h2>数据库配置</h2>
|
||
<p>请选择数据库类型并配置连接信息。</p>
|
||
|
||
<form method="POST">
|
||
<div class="db-option" onclick="selectDatabase('mysql')">
|
||
<h3><i class="fas fa-database"></i> MySQL数据库</h3>
|
||
<p>适合生产环境,性能更好,支持并发访问</p>
|
||
</div>
|
||
|
||
<div class="db-option" onclick="selectDatabase('sqlite')">
|
||
<h3><i class="fas fa-file-alt"></i> SQLite数据库</h3>
|
||
<p>适合小型站点,无需额外配置,开箱即用</p>
|
||
</div>
|
||
|
||
<input type="hidden" name="db_type" id="db_type" value="mysql">
|
||
|
||
<div class="db-config" id="mysql-config">
|
||
<h4>MySQL配置</h4>
|
||
<div class="form-group">
|
||
<label for="db_host">数据库主机</label>
|
||
<input type="text" name="db_host" id="db_host" value="localhost" required>
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="db_port">端口</label>
|
||
<input type="text" name="db_port" id="db_port" value="3306" required>
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="db_name">数据库名称</label>
|
||
<input type="text" name="db_name" id="db_name" value="submission_system" required>
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="db_user">用户名</label>
|
||
<input type="text" name="db_user" id="db_user" value="root" required>
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="db_pass">密码</label>
|
||
<input type="password" name="db_pass" id="db_pass">
|
||
</div>
|
||
</div>
|
||
|
||
<div class="db-config" id="sqlite-config">
|
||
<h4>SQLite配置</h4>
|
||
<p>SQLite数据库将自动创建在 data/submission.db 文件中,无需额外配置。</p>
|
||
</div>
|
||
|
||
<div class="actions">
|
||
<button type="submit" class="btn btn-primary">
|
||
<i class="fas fa-check"></i> 测试连接
|
||
</button>
|
||
</div>
|
||
</form>
|
||
|
||
<?php elseif ($step == 3): ?>
|
||
<!-- 步骤3: 初始化数据库 -->
|
||
<h2>初始化数据库</h2>
|
||
<p>数据库连接成功!现在将创建必要的数据表。</p>
|
||
|
||
<form method="POST">
|
||
<div class="actions">
|
||
<button type="submit" class="btn btn-primary">
|
||
<i class="fas fa-database"></i> 初始化数据库
|
||
</button>
|
||
</div>
|
||
</form>
|
||
|
||
<?php elseif ($step == 4): ?>
|
||
<!-- 步骤4: 安装完成 -->
|
||
<h2>安装完成</h2>
|
||
<div class="message success">
|
||
<i class="fas fa-check-circle"></i>
|
||
恭喜!内容投稿系统安装成功!
|
||
</div>
|
||
<h3>安全提醒</h3>
|
||
<div class="message error">
|
||
<i class="fas fa-exclamation-triangle"></i>
|
||
为了系统安全,请立即删除或重命名 install.php 文件!
|
||
</div>
|
||
|
||
<h3>默认管理员账户</h3>
|
||
<p><strong>用户名:</strong>admin</p>
|
||
<p><strong>密码:</strong>admin</p>
|
||
<p style="color: #ef4444; margin-top: 10px;">⚠️ 请登录后台后立即修改默认密码!</p>
|
||
|
||
<h3>下一步操作</h3>
|
||
<ul style="margin: 20px 0; padding-left: 20px;">
|
||
<li>删除或重命名 install.php 文件以确保安全</li>
|
||
<li>配置Web服务器(如Apache、Nginx)</li>
|
||
<li>设置适当的文件权限</li>
|
||
<li>登录管理后台修改默认密码</li>
|
||
</ul>
|
||
|
||
<div class="actions">
|
||
<a href="index.php" class="btn btn-primary">
|
||
<i class="fas fa-home"></i> 访问前台
|
||
</a>
|
||
<a href="admin/login.php" class="btn btn-success">
|
||
<i class="fas fa-shield-alt"></i> 管理后台
|
||
</a>
|
||
</div>
|
||
<?php endif; ?>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
function selectDatabase(type) {
|
||
// 移除所有选中状态
|
||
document.querySelectorAll('.db-option').forEach(option => {
|
||
option.classList.remove('selected');
|
||
});
|
||
|
||
// 隐藏所有配置
|
||
document.querySelectorAll('.db-config').forEach(config => {
|
||
config.classList.remove('active');
|
||
});
|
||
|
||
// 选中当前选项
|
||
event.currentTarget.classList.add('selected');
|
||
|
||
// 显示对应配置
|
||
document.getElementById(type + '-config').classList.add('active');
|
||
|
||
// 设置表单值
|
||
document.getElementById('db_type').value = type;
|
||
}
|
||
|
||
// 默认选择MySQL
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
document.querySelector('.db-option').click();
|
||
});
|
||
</script>
|
||
</body>
|
||
</html>
|