diff --git a/.gitignore b/.gitignore index 7881d5b..5b293ca 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,8 @@ Homestead.yaml Thumbs.db /storage/media-library/temp/ + +# Workerman 进程文件(统一放 storage/framework 与 storage/logs,历史根目录文件一并排除) +/storage/framework/workerman.* +/workerman.*.pid +/workerman.*.status diff --git a/README.md b/README.md index 032a167..4cb1eb9 100644 --- a/README.md +++ b/README.md @@ -135,6 +135,9 @@ php artisan test # 84 个用例:迁移/老路由 301/主题/插件/会员 | 文档 | 内容 | |------|------| +| [docs/install.md](docs/install.md) | 安装指南(环境/配置/验收清单) | +| [docs/import.md](docs/import.md) | Sablog 老数据导入(命令/映射/附件/301 兼容) | +| [docs/deploy.md](docs/deploy.md) | 部署(nginx/systemd/workerman/cron/上线清单) | | [docs/development.md](docs/development.md) | 开发规范(代码/插件/主题/队列/测试/提交) | | [docs/tutorial-plugin.md](docs/tutorial-plugin.md) | 插件开发教程(10 分钟上手,配套 demo) | | [docs/tutorial-theme.md](docs/tutorial-theme.md) | 主题开发教程(10 分钟上手,配套 demo) | @@ -142,7 +145,6 @@ php artisan test # 84 个用例:迁移/老路由 301/主题/插件/会员 | [docs/themes.md](docs/themes.md) | 主题 API 详解(视图解析/注入点/变量) | | [docs/architecture.md](docs/architecture.md) | 架构与设计决策 | | [docs/api.md](docs/api.md) | 对外 API(小程序/第三方) | -| [docs/deploy.md](docs/deploy.md) | 部署(nginx/systemd/workerman/cron) | **上手示例**: - 插件:`plugins/demo.hello-world/`(短代码 / 钩子 / 路由 / 后台页 / 迁移 / 表单注入,全演示) diff --git a/app/Console/Commands/WorkermanServe.php b/app/Console/Commands/WorkermanServe.php index 431cb5c..b3b4cfc 100644 --- a/app/Console/Commands/WorkermanServe.php +++ b/app/Console/Commands/WorkermanServe.php @@ -22,7 +22,10 @@ class WorkermanServe extends Command public function handle(): int { + // 进程文件统一放 storage/framework/,不污染项目根目录 Worker::$pidFile = storage_path('framework/workerman.pid'); + Worker::$statusFile = storage_path('framework/workerman.status'); + Worker::$logFile = storage_path('logs/workerman.log'); $this->info('Workerman 启动中('.config('workerman.name').')...'); diff --git a/docs/deploy.md b/docs/deploy.md index f6a2002..3dc1b38 100644 --- a/docs/deploy.md +++ b/docs/deploy.md @@ -40,11 +40,27 @@ MARKET_TOKEN= ## 生产迁移顺序 -1. `php artisan migrate --seed` -2. 导入老数据:`php artisan sablog:import --database=sablog ... --attachments-dir=... --sync-attachments --convert-markdown` -3. 配置 S3 后同步附件:`php artisan attachments:sync-s3 --legacy-dir=...` -4. 生成主题资产(可选):`php artisan theme:publish` -5. 启动 Workerman + 队列 + 调度器 +1. `composer install --no-dev --optimize-autoloader` +2. `.env` 生产配置(见上;`APP_ENV=production`、`APP_DEBUG=false`、`APP_URL` 用正式域名) +3. `php artisan config:cache && php artisan route:cache && php artisan view:cache` +4. `php artisan migrate --seed` +5. 导入老数据(如需):见 `docs/import.md` +6. 配置 S3 后同步附件(如需):`php artisan attachments:sync-s3 --legacy-dir=...` +7. 生成主题资产:`php artisan theme:publish` +8. 启动队列消费者(workerman 或 queue:work,见下)+ 配置 cron 调度器 +9. 后台改默认管理员密码;确认内置插件启用状态(SEO/AI/支付/会员/外链中转) + +## 上线前检查清单 + +- [ ] `APP_ENV=production`、`APP_DEBUG=false`、`APP_URL` 正式域名 +- [ ] `php artisan config:cache` 已执行(改了 .env 后需重新执行) +- [ ] 队列消费者进程在跑(AI 审核依赖;systemd 配置见下) +- [ ] cron 已配置 `schedule:run`(每日备份依赖) +- [ ] 管理员密码已改(默认 `admin@laralog.test` / `password`) +- [ ] 支付:确认「支付设置」里沙箱开关符合预期(默认沙箱,真实渠道需公网回调) +- [ ] LLM 配置有效(不配则评论走人工审核流程) +- [ ] S3 已配置或确认回退本地磁盘(见 `docs/install.md`) +- [ ] `storage/`、`bootstrap/cache/` 目录可写 ## Nginx(前端由 Laravel 接管,老 URL 由应用层 301) @@ -115,7 +131,14 @@ systemctl enable --now laralog-workerman * * * * * cd /var/www/laralog && php artisan schedule:run >> /dev/null 2>&1 ``` -调度器内注册的内容:`spatie/laravel-backup` 备份、缓存清理等(按需在 `routes/console.php` 增删)。 +调度器内注册的内容(`routes/console.php`): + +- 每日 03:00:数据库备份(`backup:run --only-db`,保留 7 天,日志 `storage/logs/backup.log`) +- 每日 04:00:清理前台页面缓存 + +按需在 `routes/console.php` 增删。 + +> Workerman 进程文件(pid/status/log)统一写在 `storage/framework/` 与 `storage/logs/`,不会出现在项目根目录;已由 .gitignore 排除。 ## 队列消费:workerman 与 queue:work 等价 diff --git a/docs/import.md b/docs/import.md new file mode 100644 index 0000000..005fc52 --- /dev/null +++ b/docs/import.md @@ -0,0 +1,113 @@ +# Sablog 数据导入指南 + +把 SaBlog-X 1.6 老站数据完整迁入 LaraLog。**全部脚本化、可重复执行**,保留原始 ID,老 URL 自动 301 兼容(不产生 SEO 404)。 + +## 前置准备 + +1. 老库可访问:MySQL 8(或可导出的老 MySQL 5.x,先在 MySQL 8 建好导入目标) +2. 老附件目录:sablog 的 `attachments/`(在 Web 服务器上,含编号子目录) +3. 已按 `docs/install.md` 完成安装(`migrate --seed` 后导入) + +> 建议:先在**测试环境**用 `--dry-run` 风格(见下文幂等说明)完整跑一遍,确认数量后再上生产。 + +## 一键导入 + +```bash +php artisan sablog:import \ + --host=127.0.0.1 --port=3306 \ + --database=sablog --username=root --password=老库密码 \ + --prefix=sablog_ \ + --attachments-dir=/var/www/old/attachments \ + --sync-attachments \ + --convert-markdown +``` + +### 参数说明 + +| 参数 | 默认 | 说明 | +|------|------|------| +| `--driver` | mysql | 老库驱动(测试可用 `sqlite`) | +| `--host` / `--port` | 127.0.0.1 / 3306 | 老库地址 | +| `--database` | (必填) | 老库名 | +| `--username` / `--password` | root / 空 | 老库账号 | +| `--prefix` | `sablog_` | 老表前缀(老站可能自定义过) | +| `--attachments-dir` | — | 老附件目录,配合 `--sync-attachments` 用 | +| `--sync-attachments` | 关 | 同步附件文件到新存储(本地中转或直接上 S3) | +| `--convert-markdown` | 关 | 导入时把 HTML 正文转为 Markdown | +| `--fresh` | 关 | 清空目标表后重新导入 | + +## 数据映射 + +| 老表 | 新表 | 说明 | +|------|------|------| +| `users` | `users` | **ID 保留**;老 MD5 密码进 `legacy_md5`,登录时自动升级为 bcrypt | +| `articles` | `posts` | **articleid 保留为 id**;`[attach=xx]` 按内容格式处理(见下) | +| `categories` | `categories` | cid 保留;导入后自动重算文章数 | +| `comments` | `comments` | commentid 保留;visible 语义映射为状态 | +| `links` | `links` | linkid 保留 | +| `attachments` | medialibrary `media` | 文件 + `legacy_attachmentid` 映射(老 `[attach=xx]` 按此解析) | +| `settings` | `settings` | 键值映射为站点设置(可在后台「博客设置」继续改) | +| `tags` | spatie tags | 老 aids 文本解析为独立标签 + 关联 | + +**不迁移**:trackback/引用通告(垃圾来源,接口已关闭)、搜索缓存表、验证码(由 AI 审核 + 蜜罐 + 频控取代)。 + +## 附件短代码的处理 + +| 老内容 | 导入后 | +|--------|--------| +| HTML 正文 `[attach=12]` | 原样保留,**渲染时按 legacy id 解析**为附件链接/图片 | +| Markdown 正文(--convert-markdown) | 自动转成 `{{attach:12}}` 令牌(`[]` 是 Markdown 保留字符) | + +两种格式都能正常显示老附件,无需手工替换。 + +## 老 URL 兼容(301,无需 Nginx 配置) + +- 伪静态:`/show-12-1.html` → 新文章页;`/category-3-1.html` → 新分类页;`/archives-202008-1.html` → 新归档 +- 查询串:`/?action=show&id=12`、`/?action=search&keyword=x`、`/?action=tags&id=1` …全部映射 +- 稳定路径:`/rss.xml`、`/sitemap.xml`、`/robots.txt` 保持原样 +- 老入口:`attachment.php?id=` → 301 到 S3/本地媒体地址;`tburl.php`/`trackback.php` → 410 + +## 附件上 S3 + +```bash +# 导入后再把老附件批量上传(Multipart、幂等、可断点续传) +php artisan attachments:sync-s3 --legacy-dir=/var/www/old/attachments + +# 个别文件校验失败需要强制重传 +php artisan attachments:sync-s3 --legacy-dir=... --force +``` + +> 未配置 S3 时附件留在本地 public 磁盘,网站可正常展示;配好 S3 后再执行同步即可无缝切换。 + +## 内容转换(可选) + +```bash +php artisan content:convert --all # 全部 HTML 文章转 Markdown +php artisan content:convert 12 # 单篇 +php artisan content:convert --all --dry-run # 先预览 +``` + +## 幂等与重跑 + +- 导入按**原始 ID** `updateOrInsert`:重复执行不会产生重复数据,只会覆盖更新 +- 需要完全重来:`--fresh` 清空目标表后导入 +- `attachments:sync-s3` 自带去重:已上传的文件跳过(除非 `--force`) + +## 导入后检查清单 + +- [ ] 文章总数、评论总数与老站一致(后台仪表盘 / `SELECT COUNT(*)`) +- [ ] 抽查老 URL:`/show-12-1.html` 301 到新文章页 +- [ ] 抽查带 `[attach=xx]` 的文章:附件正常显示 +- [ ] 老账号用**原密码**登录成功(密码自动升级为 bcrypt) +- [ ] 分类文章数、文章评论数正确(导入脚本已自动重算) +- [ ] RSS/Sitemap 正常输出 +- [ ] 主题切换后前台布局正常 + +## 常见问题 + +| 现象 | 处理 | +|------|------| +| 报「找不到老表」 | 检查 `--prefix`(老站表前缀可能不是 `sablog_`)与老库名 | +| 附件显示不出来 | 确认 `--attachments-dir` 路径正确;看 `attachments:sync-s3` 输出报告 | +| 老密码登录失败 | 老库密码若是自定义加密(非 MD5),需要按 `User::verifyPassword` 扩展 | +| 中文乱码 | 老库导出时用 utf8mb4;`--driver=sqlite` 只用于测试 | diff --git a/docs/install.md b/docs/install.md new file mode 100644 index 0000000..a3c51a0 --- /dev/null +++ b/docs/install.md @@ -0,0 +1,94 @@ +# 安装指南 + +从零安装 LaraLog。已有 SaBlog-X 老数据的先看本页,数据迁移见 `docs/import.md`,上线见 `docs/deploy.md`。 + +## 环境要求 + +| 组件 | 要求 | +|------|------| +| PHP | **8.2+**(需 pcntl 扩展用于 Workerman;GD/Imagick 用于缩略图) | +| MySQL | **8.x** | +| Composer | 2.x | +| 可选 | Node.js(前台资源构建)、S3 兼容存储(R2/COS/OSS/MinIO)、OpenAI 兼容 LLM | + +## 1. 获取代码与依赖 + +```bash +git clone laralog +cd laralog +composer install --no-dev # 开发环境不加 --no-dev +cp .env.example .env +php artisan key:generate +``` + +`.env.example` 已包含全部配置键,关键项: + +```dotenv +APP_URL=http://laralog.test # 生产改正式域名(影响生成的全部链接) +DB_CONNECTION=mysql +DB_HOST=127.0.0.1 +DB_DATABASE=laralog +DB_USERNAME=root +DB_PASSWORD= + +# 以下为可选能力(不配也能跑,见注释): +# S3_* 附件存储(不配置回退本地 public) +# LLM_* AI 评论审核/润色 +# MARKET_* 插件/主题远程市场 +``` + +## 2. 建库与初始化 + +```bash +# 先在 MySQL 建库 +mysql -uroot -p -e "CREATE DATABASE laralog CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci" + +# 迁移 + 种子(创建管理员) +php artisan migrate --seed +``` + +**后台登录**:`http://{APP_URL}/admin`,默认账号 `admin@laralog.test` / `password`(**上线后第一时间改密码**)。 + +> 内置插件(SEO/AI/支付/会员/外链中转)随迁移自动建表并默认启用(config/plugins.php),后台「插件管理」可启停。 + +## 3. 开发环境运行 + +### Herd / Valet(推荐,macOS) + +```bash +# Herd:站点指向本目录即可,直接访问 http://laralog.test +``` + +### 内置服务器 + +```bash +php artisan serve # http://127.0.0.1:8000 +``` + +### 队列消费者(AI 审核/润色才需要) + +```bash +php artisan dev:watch # 开发期:监听代码变化自动重启 workerman(fswatch 或 PHP 轮询兜底) +# 或简单模式 +php artisan queue:work --queue=default,ai +``` + +## 4. 验收清单 + +- [ ] 前台首页/文章/分类/归档/标签/搜索/友链/登录注册正常 +- [ ] 后台 `/admin` 登录成功,文章/评论/设置/主题/插件页面可访问 +- [ ] 主题切换正常(后台「主题管理」,内置 sablog/modern/demo 三套) +- [ ] 发一条评论:评论区显示(若有 AI 配置,进审核队列) +- [ ] 支付:后台「支付设置」默认沙箱模式,会员套餐可走模拟支付闭环 +- [ ] 文章写 `[hello 世界]` 变问候语(demo 插件启用后,`/hello-world` 可访问) + +## 5. 常见问题 + +| 现象 | 处理 | +|------|------| +| 页面 500 / 空白 | `storage/`、`bootstrap/cache/` 目录需可写;`php artisan config:clear` | +| 中文乱码 | 数据库/连接字符集 utf8mb4 | +| 后台样式错乱 | `php artisan filament:assets`(重新发布后台资源) | +| 文章编辑框太小 | 已内置加大样式;仍不够可调整 `app/Providers/AppServiceProvider.php` 中 `.fi-fo-markdown-editor` 的 min-height | +| 附件上传失败 | 未配 S3 时走本地 public 磁盘;配 S3 后确认 `S3_*` 键与桶权限 | +| 插件停用后功能消失 | 属预期:付费列/外链中转等由插件注入,后台「插件管理」重新启用即可 |