mirror of
https://github.com/zhang-pu/tblog.git
synced 2026-10-02 15:49:29 +08:00
Add files via upload
This commit is contained in:
commit
fdeba3dbbf
7 changed files with 711 additions and 0 deletions
46
CHANGELOG.md
Normal file
46
CHANGELOG.md
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
# 更新日志
|
||||||
|
|
||||||
|
所有版本更新记录均在此文件更新。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## [v1.2.0] - 2024-05-31
|
||||||
|
|
||||||
|
### 定时任务 + 远程备份
|
||||||
|
|
||||||
|
- ⏰ 完整的定时任务管理(创建/删除/启用/禁用/立即执行)
|
||||||
|
- ✅ Cron 表达式格式验证(分/时/日/月/周五段校验)
|
||||||
|
- 💾 远程 rsync 备份(SSH 主机/端口/用户/密钥)
|
||||||
|
- 🔗 远程连接测试工具(部署前验证连通性)
|
||||||
|
- 📊 备份统计面板(总数/本地大小/远程大小/磁盘占用)
|
||||||
|
- 📁 文件管理:浏览/上传/编辑/删除/新建目录/权限显示
|
||||||
|
- 🔐 SSL 证书列表(到期天数/状态指示灯)
|
||||||
|
- 🔄 SSL 单个续期 + 批量续期
|
||||||
|
- 🛡️ 自动漏洞修复(每日凌晨3点执行)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## [v1.0.0] - 2024-05-31
|
||||||
|
|
||||||
|
### 首发版本
|
||||||
|
|
||||||
|
- 🚀 完整的博客系统,包含文章发布、分类管理、评论系统
|
||||||
|
- 🎨 双主题支持:Flow(浅色)和 Magine(深色)
|
||||||
|
- 🔐 安全特性:CSRF 防护、XSS 过滤、bcrypt 密码哈希、评论速率限制
|
||||||
|
- 📱 响应式设计,支持移动端访问
|
||||||
|
- ✏️ 富文本编辑器,支持 Markdown 快捷键
|
||||||
|
- 🛡️ 垃圾评论过滤(蜜罐 + 关键词检测)
|
||||||
|
- ⚙️ 安装向导,开箱即用
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 待办
|
||||||
|
|
||||||
|
- [ ] 多 PHP 版本切换
|
||||||
|
- [ ] Node.js 支持
|
||||||
|
- [ ] nginx 日志查看器
|
||||||
|
- [ ] 插件系统
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
> 关注 GitHub 仓库获取最新更新:https://github.com/tblog-cn/tblog
|
||||||
21
LICENSE
Normal file
21
LICENSE
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2024 TBlog
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
118
README.md
Normal file
118
README.md
Normal file
|
|
@ -0,0 +1,118 @@
|
||||||
|
# TBlog
|
||||||
|
|
||||||
|
一个简洁、优雅的开源博客系统,基于 PHP + MySQL 开发。
|
||||||
|
|
||||||
|
🌐 网址:https://tblog.cn
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 特性
|
||||||
|
|
||||||
|
- ✏️ 富文本编辑器,写作体验流畅
|
||||||
|
- 🎨 两套主题可选(Flow 浅色 / Magine 深色)
|
||||||
|
- 💬 评论系统,支持垃圾评论过滤
|
||||||
|
- 📁 分类管理,文章归档
|
||||||
|
- 🔐 安全防护:CSRF 防护、XSS 过滤、密码 bcrypt 哈希、评论速率限制
|
||||||
|
- 📱 响应式布局,适配移动端
|
||||||
|
- 🛠 轻量高效,无需复杂依赖
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 环境要求
|
||||||
|
|
||||||
|
- PHP >= 7.0
|
||||||
|
- MySQL >= 5.6
|
||||||
|
- mysqli 扩展
|
||||||
|
- PDO 扩展(可选)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 安装
|
||||||
|
|
||||||
|
### 方式一:向导安装
|
||||||
|
|
||||||
|
1. 解压源码,上传至网站目录
|
||||||
|
2. 访问 `http://你的域名/install.php`
|
||||||
|
3. 按提示填写数据库信息,完成安装
|
||||||
|
4. 初始管理员账号:`admin` / `admin123`(安装后请立即修改)
|
||||||
|
|
||||||
|
### 方式二:手动配置
|
||||||
|
|
||||||
|
1. 创建数据库(例如 `tblog`)
|
||||||
|
2. 导入 `install.sql` 建表
|
||||||
|
3. 复制并重命名 `config.php.new` 为 `config.php`,填入数据库信息
|
||||||
|
4. 通过 SQL 设置管理员密码哈希:
|
||||||
|
|
||||||
|
```sql
|
||||||
|
INSERT INTO settings (`key`, value) VALUES ('admin_user', 'admin');
|
||||||
|
INSERT INTO settings (`key`, value) VALUES ('admin_hash', '$2y$10$YOUR_HASH_HERE');
|
||||||
|
```
|
||||||
|
|
||||||
|
生成哈希:PHP 执行 `echo password_hash('你的密码', PASSWORD_DEFAULT);`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 目录结构
|
||||||
|
|
||||||
|
```
|
||||||
|
tblog/
|
||||||
|
├── admin/ 管理后台
|
||||||
|
│ ├── assets/ 样式资源
|
||||||
|
│ ├── category.php 分类管理
|
||||||
|
│ ├── comment.php 评论管理
|
||||||
|
│ ├── index.php 管理概览
|
||||||
|
│ ├── login.php 登录页
|
||||||
|
│ ├── logout.php 退出
|
||||||
|
│ ├── post.php 文章管理
|
||||||
|
│ └── settings.php 系统设置
|
||||||
|
├── include/ 核心类库
|
||||||
|
│ ├── db.php 数据库连接
|
||||||
|
│ ├── router.php URL 路由
|
||||||
|
│ ├── function.common.php
|
||||||
|
│ ├── function.post.php
|
||||||
|
│ ├── function.category.php
|
||||||
|
│ ├── function.comment.php
|
||||||
|
│ └── HTMLPurifier.simple.php HTML 净化
|
||||||
|
├── static/ 静态资源
|
||||||
|
├── templates/ 主题模板
|
||||||
|
│ ├── flow/ Flow 主题(浅色)
|
||||||
|
│ └── magine/ Magine 主题(深色)
|
||||||
|
├── index.php 入口文件
|
||||||
|
├── config.php.new 配置文件模板
|
||||||
|
├── install.php 安装向导
|
||||||
|
└── install.sql 数据库建表脚本
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 管理后台
|
||||||
|
|
||||||
|
访问 `http://你的域名/admin/`,使用安装时设置的管理员账号登录。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 主题切换
|
||||||
|
|
||||||
|
文章可独立选择模板(Flow / Magine),全站默认模板可在后台「设置」中修改。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 开源协议
|
||||||
|
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 更新日志
|
||||||
|
|
||||||
|
详见 [CHANGELOG.md](./CHANGELOG.md)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 问题反馈
|
||||||
|
|
||||||
|
如有 Bug 或建议,欢迎提交 Issue。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**TBlog** — 简洁而不简单。
|
||||||
18
config.php.new
Normal file
18
config.php.new
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* TBlog - Configuration Template
|
||||||
|
* 复制此文件为 config.php 并填写数据库信息
|
||||||
|
*/
|
||||||
|
define('DB_HOST', 'localhost');
|
||||||
|
define('DB_PORT', 3306);
|
||||||
|
define('DB_USER', 'your_db_user');
|
||||||
|
define('DB_PASS', 'your_db_password');
|
||||||
|
define('DB_NAME', 'tblog');
|
||||||
|
define('DB_PREFIX', '');
|
||||||
|
|
||||||
|
// Path constants
|
||||||
|
define('ROOT_PATH', __DIR__ . '/');
|
||||||
|
define('INCLUDE_PATH', ROOT_PATH . 'include/');
|
||||||
|
define('TEMPLATE_PATH', ROOT_PATH . 'templates/');
|
||||||
|
|
||||||
|
session_start();
|
||||||
18
index.php
Normal file
18
index.php
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* ZhangPu Blog - Main Entry Point
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Load configuration
|
||||||
|
require_once __DIR__ . '/config.php';
|
||||||
|
|
||||||
|
// Load functions
|
||||||
|
require_once INCLUDE_PATH . 'function.common.php';
|
||||||
|
require_once INCLUDE_PATH . 'router.php';
|
||||||
|
|
||||||
|
// Get the route from URL
|
||||||
|
$route = isset($_GET['route']) ? $_GET['route'] : '';
|
||||||
|
$route = trim($route, '/');
|
||||||
|
|
||||||
|
// Route the request
|
||||||
|
route_request($route);
|
||||||
432
install.php
Normal file
432
install.php
Normal file
|
|
@ -0,0 +1,432 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* TBlog - Installation Wizard
|
||||||
|
*/
|
||||||
|
session_start();
|
||||||
|
|
||||||
|
// Load common functions
|
||||||
|
require_once __DIR__ . '/include/function.common.php';
|
||||||
|
|
||||||
|
$step = isset($_GET['step']) ? intval($_GET['step']) : 1;
|
||||||
|
$error = '';
|
||||||
|
$success = '';
|
||||||
|
|
||||||
|
// Handle form submission for step 2
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST' && $step === 2) {
|
||||||
|
$_SESSION['db_host'] = trim($_POST['db_host'] ?? 'localhost');
|
||||||
|
$_SESSION['db_port'] = intval($_POST['db_port'] ?? 3306);
|
||||||
|
$_SESSION['db_user'] = trim($_POST['db_user'] ?? '');
|
||||||
|
$_SESSION['db_pass'] = trim($_POST['db_pass'] ?? '');
|
||||||
|
$_SESSION['db_name'] = trim($_POST['db_name'] ?? '');
|
||||||
|
|
||||||
|
// Test connection
|
||||||
|
$mysqli = @new mysqli($_SESSION['db_host'], $_SESSION['db_user'], $_SESSION['db_pass'], '', $_SESSION['db_port']);
|
||||||
|
if ($mysqli->connect_error) {
|
||||||
|
$error = '数据库连接失败: ' . $mysqli->connect_error;
|
||||||
|
} else {
|
||||||
|
// Create database if not exists
|
||||||
|
$mysqli->query("CREATE DATABASE IF NOT EXISTS `{$_SESSION['db_name']}` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci");
|
||||||
|
$mysqli->select_db($_SESSION['db_name']);
|
||||||
|
|
||||||
|
// Read and execute install.sql
|
||||||
|
$sql_file = file_get_contents(__DIR__ . '/install.sql');
|
||||||
|
$statements = array_filter(array_map('trim', explode(';', $sql_file)));
|
||||||
|
|
||||||
|
foreach ($statements as $statement) {
|
||||||
|
if (!empty($statement)) {
|
||||||
|
if (!$mysqli->query($statement)) {
|
||||||
|
$error = '数据库初始化失败: ' . $mysqli->error;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$error) {
|
||||||
|
$success = '数据库连接成功,配置已保存';
|
||||||
|
header('Location: ?step=3');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
$mysqli->close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle step 3 - save config and set admin credentials
|
||||||
|
if ($step === 3) {
|
||||||
|
$config_content = "<?php
|
||||||
|
/**
|
||||||
|
* TBlog - Configuration
|
||||||
|
*/
|
||||||
|
define('DB_HOST', '{$_SESSION['db_host']}');
|
||||||
|
define('DB_PORT', {$_SESSION['db_port']});
|
||||||
|
define('DB_USER', '" . addslashes($_SESSION['db_user']) . "');
|
||||||
|
define('DB_PASS', '" . addslashes($_SESSION['db_pass']) . "');
|
||||||
|
define('DB_NAME', '{$_SESSION['db_name']}');
|
||||||
|
define('DB_PREFIX', '');
|
||||||
|
|
||||||
|
// Path constants
|
||||||
|
define('ROOT_PATH', __DIR__ . '/');
|
||||||
|
define('INCLUDE_PATH', ROOT_PATH . 'include/');
|
||||||
|
define('TEMPLATE_PATH', ROOT_PATH . 'templates/');
|
||||||
|
|
||||||
|
// Admin credentials stored in database (settings table)
|
||||||
|
// Initial setup below; change via admin panel after install
|
||||||
|
session_start();
|
||||||
|
";
|
||||||
|
|
||||||
|
if (!file_put_contents(__DIR__ . '/config.php', $config_content)) {
|
||||||
|
$error = '配置文件写入失败,请检查目录权限';
|
||||||
|
} else {
|
||||||
|
// Set initial admin credentials in database
|
||||||
|
$mysqli = @new mysqli($_SESSION['db_host'], $_SESSION['db_user'], $_SESSION['db_pass'], $_SESSION['db_name'], $_SESSION['db_port']);
|
||||||
|
if (!$mysqli->connect_error) {
|
||||||
|
$admin_user = 'admin';
|
||||||
|
$admin_hash = password_hash('admin123', PASSWORD_DEFAULT);
|
||||||
|
$mysqli->query("INSERT INTO settings (`key`, value) VALUES ('admin_user', 'admin') ON DUPLICATE KEY UPDATE value = 'admin'");
|
||||||
|
$mysqli->query("INSERT INTO settings (`key`, value) VALUES ('admin_hash', '$admin_hash') ON DUPLICATE KEY UPDATE value = '$admin_hash'");
|
||||||
|
$mysqli->close();
|
||||||
|
}
|
||||||
|
header('Location: ?step=4');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 5 - completion
|
||||||
|
if ($step === 5) {
|
||||||
|
session_destroy();
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>安装向导 - TBlog</title>
|
||||||
|
<style>
|
||||||
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||||
|
body {
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||||
|
background: linear-gradient(135deg, #22c55e 0%, #0ea5e9 100%);
|
||||||
|
min-height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
.installer {
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 16px;
|
||||||
|
box-shadow: 0 20px 60px rgba(0,0,0,0.3);
|
||||||
|
width: 100%;
|
||||||
|
max-width: 600px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.installer-header {
|
||||||
|
background: linear-gradient(135deg, #22c55e 0%, #16a34a 100%);
|
||||||
|
color: #fff;
|
||||||
|
padding: 30px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.installer-header h1 {
|
||||||
|
font-size: 28px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
.installer-header p {
|
||||||
|
opacity: 0.9;
|
||||||
|
}
|
||||||
|
.progress-bar {
|
||||||
|
display: flex;
|
||||||
|
padding: 0 30px;
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
.progress-step {
|
||||||
|
flex: 1;
|
||||||
|
height: 4px;
|
||||||
|
background: #e5e5e5;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.progress-step.completed {
|
||||||
|
background: #22c55e;
|
||||||
|
}
|
||||||
|
.progress-step.active {
|
||||||
|
background: #22c55e;
|
||||||
|
}
|
||||||
|
.progress-step::before {
|
||||||
|
content: attr(data-step);
|
||||||
|
position: absolute;
|
||||||
|
top: -30px;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
background: #e5e5e5;
|
||||||
|
border-radius: 50%;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 24px;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
.progress-step.completed::before,
|
||||||
|
.progress-step.active::before {
|
||||||
|
background: #22c55e;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
.installer-body {
|
||||||
|
padding: 40px 30px;
|
||||||
|
}
|
||||||
|
h2 {
|
||||||
|
color: #1a1a2e;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
font-size: 22px;
|
||||||
|
}
|
||||||
|
.form-group {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
label {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
color: #333;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
input[type="text"],
|
||||||
|
input[type="password"],
|
||||||
|
input[type="number"] {
|
||||||
|
width: 100%;
|
||||||
|
padding: 14px 16px;
|
||||||
|
border: 2px solid #e5e5e5;
|
||||||
|
border-radius: 8px;
|
||||||
|
font-size: 16px;
|
||||||
|
transition: border-color 0.3s;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
input:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: #22c55e;
|
||||||
|
}
|
||||||
|
.btn {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 14px 32px;
|
||||||
|
background: #22c55e;
|
||||||
|
color: #fff;
|
||||||
|
border: none;
|
||||||
|
border-radius: 8px;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background 0.3s;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
.btn:hover {
|
||||||
|
background: #16a34a;
|
||||||
|
}
|
||||||
|
.btn-secondary {
|
||||||
|
background: #e5e5e5;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
.btn-secondary:hover {
|
||||||
|
background: #d5d5d5;
|
||||||
|
}
|
||||||
|
.error {
|
||||||
|
background: #fee2e2;
|
||||||
|
color: #dc2626;
|
||||||
|
padding: 12px 16px;
|
||||||
|
border-radius: 8px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
.success {
|
||||||
|
background: #dcfce7;
|
||||||
|
color: #16a34a;
|
||||||
|
padding: 12px 16px;
|
||||||
|
border-radius: 8px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
.requirements {
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
.requirements li {
|
||||||
|
padding: 12px 0;
|
||||||
|
border-bottom: 1px solid #e5e5e5;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
.requirements li:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
.requirements .ok { color: #22c55e; }
|
||||||
|
.requirements .fail { color: #dc2626; }
|
||||||
|
.buttons {
|
||||||
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
|
margin-top: 30px;
|
||||||
|
}
|
||||||
|
.features {
|
||||||
|
background: #f9fafb;
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: 8px;
|
||||||
|
margin: 20px 0;
|
||||||
|
}
|
||||||
|
.features ul {
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
.features li {
|
||||||
|
padding: 8px 0;
|
||||||
|
padding-left: 24px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.features li::before {
|
||||||
|
content: '✓';
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
color: #22c55e;
|
||||||
|
}
|
||||||
|
.info-box {
|
||||||
|
background: #eff6ff;
|
||||||
|
border: 1px solid #bfdbfe;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 16px;
|
||||||
|
margin: 20px 0;
|
||||||
|
color: #1e40af;
|
||||||
|
}
|
||||||
|
.text-center { text-align: center; }
|
||||||
|
.mt-4 { margin-top: 24px; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="installer">
|
||||||
|
<div class="installer-header">
|
||||||
|
<h1>🌿 TBlog 安装向导</h1>
|
||||||
|
<p>一个简洁优雅的博客系统</p>
|
||||||
|
<div class="progress-bar">
|
||||||
|
<div class="progress-step <?php echo $step >= 1 ? 'completed' : ''; ?>" data-step="1"></div>
|
||||||
|
<div class="progress-step <?php echo $step >= 2 ? 'completed' : ''; ?> <?php echo $step == 2 ? 'active' : ''; ?>" data-step="2"></div>
|
||||||
|
<div class="progress-step <?php echo $step >= 3 ? 'completed' : ''; ?> <?php echo $step == 3 ? 'active' : ''; ?>" data-step="3"></div>
|
||||||
|
<div class="progress-step <?php echo $step >= 4 ? 'completed' : ''; ?> <?php echo $step == 4 ? 'active' : ''; ?>" data-step="4"></div>
|
||||||
|
<div class="progress-step <?php echo $step >= 5 ? 'completed' : ''; ?> <?php echo $step == 5 ? 'active' : ''; ?>" data-step="5"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="installer-body">
|
||||||
|
<?php if ($step === 1): ?>
|
||||||
|
<h2>第一步:环境检测</h2>
|
||||||
|
<ul class="requirements">
|
||||||
|
<li class="<?php echo PHP_VERSION_ID >= 70000 ? 'ok' : 'fail'; ?>">
|
||||||
|
PHP 版本 >= 7.0 (当前: <?php echo PHP_VERSION; ?>)
|
||||||
|
<?php echo PHP_VERSION_ID >= 70000 ? '✓' : '✗'; ?>
|
||||||
|
</li>
|
||||||
|
<li class="<?php echo extension_loaded('mysqli') ? 'ok' : 'fail'; ?>">
|
||||||
|
MySQLi 扩展 <?php echo extension_loaded('mysqli') ? '✓ 已启用' : '✗ 未启用'; ?>
|
||||||
|
</li>
|
||||||
|
<li class="<?php echo is_writable(__DIR__) ? 'ok' : 'fail'; ?>">
|
||||||
|
目录可写权限 <?php echo is_writable(__DIR__) ? '✓' : '✗'; ?>
|
||||||
|
</li>
|
||||||
|
<li class="<?php echo extension_loaded('pdo') ? 'ok' : 'fail'; ?>">
|
||||||
|
PDO 扩展 <?php echo extension_loaded('pdo') ? '✓ 已启用' : '✗ 未启用'; ?>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<?php
|
||||||
|
$requirements_ok = PHP_VERSION_ID >= 70000 && extension_loaded('mysqli') && is_writable(__DIR__);
|
||||||
|
?>
|
||||||
|
|
||||||
|
<?php if ($requirements_ok): ?>
|
||||||
|
<div class="success">环境检测通过,所有要求均已满足</div>
|
||||||
|
<?php else: ?>
|
||||||
|
<div class="error">环境检测未通过,请解决上述问题后继续</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<div class="buttons">
|
||||||
|
<?php if ($requirements_ok): ?>
|
||||||
|
<a href="?step=2" class="btn">下一步 →</a>
|
||||||
|
<?php else: ?>
|
||||||
|
<button class="btn" disabled>下一步 →</button>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php elseif ($step === 2): ?>
|
||||||
|
<h2>第二步:数据库配置</h2>
|
||||||
|
|
||||||
|
<?php if ($error): ?>
|
||||||
|
<div class="error"><?php echo e($error); ?></div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<form method="POST">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="db_host">数据库主机</label>
|
||||||
|
<input type="text" name="db_host" id="db_host" value="<?php echo e($_SESSION['db_host'] ?? 'localhost'); ?>" required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="db_port">数据库端口</label>
|
||||||
|
<input type="number" name="db_port" id="db_port" value="<?php echo e($_SESSION['db_port'] ?? 3306); ?>" required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="db_user">数据库用户名</label>
|
||||||
|
<input type="text" name="db_user" id="db_user" value="<?php echo e($_SESSION['db_user'] ?? 'root'); ?>" required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="db_pass">数据库密码</label>
|
||||||
|
<input type="password" name="db_pass" id="db_pass" value="<?php echo e($_SESSION['db_pass'] ?? ''); ?>">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="db_name">数据库名称</label>
|
||||||
|
<input type="text" name="db_name" id="db_name" value="<?php echo e($_SESSION['db_name'] ?? 'tblog'); ?>" required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="buttons">
|
||||||
|
<a href="?step=1" class="btn btn-secondary">← 上一步</a>
|
||||||
|
<button type="submit" class="btn">测试连接并继续 →</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<?php elseif ($step === 3): ?>
|
||||||
|
<h2>第三步:创建配置文件</h2>
|
||||||
|
|
||||||
|
<?php if ($error): ?>
|
||||||
|
<div class="error"><?php echo e($error); ?></div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<p>正在创建 <code>config.php</code> 配置文件...</p>
|
||||||
|
|
||||||
|
<div class="buttons">
|
||||||
|
<a href="?step=2" class="btn btn-secondary">← 上一步</a>
|
||||||
|
<a href="?step=4" class="btn">继续 →</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php elseif ($step === 4): ?>
|
||||||
|
<h2>第四步:安装完成</h2>
|
||||||
|
|
||||||
|
<div class="success">🎉 恭喜!TBlog 已安装成功!</div>
|
||||||
|
|
||||||
|
<div class="features">
|
||||||
|
<strong>默认管理员信息:</strong>
|
||||||
|
<ul>
|
||||||
|
<li>用户名:admin</li>
|
||||||
|
<li>密码:admin123</li>
|
||||||
|
<li style="color:#dc2626;">密码已使用 bcrypt 哈希加密存储</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="info-box">
|
||||||
|
<strong>⚠️ 安全提示:</strong>安装完成后请前往「设置」页面修改默认密码。
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="buttons">
|
||||||
|
<a href="?step=5" class="btn">完成安装</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php elseif ($step === 5): ?>
|
||||||
|
<div class="text-center">
|
||||||
|
<h2>🎉 安装完成!</h2>
|
||||||
|
<p class="mt-4">感谢使用 TBlog</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="buttons" style="justify-content: center; margin-top: 30px;">
|
||||||
|
<a href="index.php" class="btn">访问博客</a>
|
||||||
|
<a href="admin/" class="btn btn-secondary">管理后台</a>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
58
install.sql
Normal file
58
install.sql
Normal file
|
|
@ -0,0 +1,58 @@
|
||||||
|
CREATE TABLE IF NOT EXISTS `categories` (
|
||||||
|
`id` INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
`name` VARCHAR(100) NOT NULL COMMENT '分类名称',
|
||||||
|
`slug` VARCHAR(100) NOT NULL COMMENT 'URL别名',
|
||||||
|
`description` TEXT,
|
||||||
|
`parent_id` INT DEFAULT 0,
|
||||||
|
`order_num` INT DEFAULT 0,
|
||||||
|
`created_at` DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
UNIQUE KEY `slug` (`slug`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS `posts` (
|
||||||
|
`id` INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
`title` VARCHAR(255) NOT NULL COMMENT '标题',
|
||||||
|
`content` LONGTEXT NOT NULL COMMENT '正文',
|
||||||
|
`excerpt` TEXT COMMENT '摘要',
|
||||||
|
`slug` VARCHAR(200) NOT NULL COMMENT 'URL别名',
|
||||||
|
`category_id` INT DEFAULT 1,
|
||||||
|
`template` VARCHAR(50) DEFAULT 'flow',
|
||||||
|
`status` ENUM('draft','published') DEFAULT 'published',
|
||||||
|
`views` INT DEFAULT 0,
|
||||||
|
`created_at` DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
`updated_at` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||||
|
`deleted_at` DATETIME DEFAULT NULL,
|
||||||
|
KEY `slug` (`slug`),
|
||||||
|
KEY `category_id` (`category_id`),
|
||||||
|
KEY `status` (`status`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS `comments` (
|
||||||
|
`id` INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
`post_id` INT NOT NULL,
|
||||||
|
`author` VARCHAR(100) NOT NULL COMMENT '评论者',
|
||||||
|
`email` VARCHAR(200) DEFAULT '',
|
||||||
|
`url` VARCHAR(500) DEFAULT '',
|
||||||
|
`content` TEXT NOT NULL,
|
||||||
|
`ip` VARCHAR(50) DEFAULT '',
|
||||||
|
`status` ENUM('pending','approved','rejected') DEFAULT 'pending',
|
||||||
|
`created_at` DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
KEY `post_id` (`post_id`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS `settings` (
|
||||||
|
`key` VARCHAR(100) PRIMARY KEY,
|
||||||
|
`value` TEXT
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||||
|
|
||||||
|
-- Default data
|
||||||
|
INSERT INTO `categories` (`name`, `slug`, `description`, `order_num`) VALUES
|
||||||
|
('技术', 'tech', '技术文章', 1),
|
||||||
|
('生活', 'life', '生活随笔', 2),
|
||||||
|
('摄影', 'photo', '摄影作品', 3);
|
||||||
|
|
||||||
|
INSERT INTO `settings` (`key`, `value`) VALUES
|
||||||
|
('site_name', '张璞博客'),
|
||||||
|
('site_description', '一个简洁优雅的博客'),
|
||||||
|
('template', 'flow'),
|
||||||
|
('posts_per_page', '10');
|
||||||
Loading…
Reference in a new issue