feat: use xpier media library for uploads and markdown embeds

Store media:ID in post bodies instead of CDN URLs, and keep sablog attach tokens as a fallback.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
gouki
2026-09-16 19:03:52 +00:00
co-authored by Cursor
parent f6a9a8ea50
commit f81397a289
38 changed files with 5390 additions and 2059 deletions
+25 -25
View File
@@ -2,18 +2,18 @@
## 技术栈
| 层 | 技术 | 说明 |
|----|------|------|
| 框架 | Laravel 12.65 | PHP ^8.2,`declare(strict_types=1)` 全量启用 |
| 后台 | Filament 5.7 | admin panel,zh_CN,资源/页面/组件 |
| 前台交互 | Livewire 4 | Filament 依赖 |
| 数据库 | MySQL 8(生产)/ SQLite(测试 :memory:) | |
| 缓存/队列 | database / redis | 队列默认 database,可切 redis |
| Markdown | league/commonmark(GFM)+ html-to-markdown | 内容双格式 |
| 附件 | S3 兼容(R2/COS/OSS)+ spatie/laravel-medialibrary | 零落盘 |
| 异步 | Workerman 5 常驻(队列消费者 + WebSocket) | 复用 Laravel Queue Worker |
| 支付 | yansongda/pay(支付宝/微信) | 沙箱模式内置 |
| 权限 | spatie/laravel-permission | admin/editor/member |
| 层 | 技术 | 说明 |
| --------- | --------------------------------------------------------------------- | -------------------------------------------- |
| 框架 | Laravel 12.65 | PHP ^8.2,`declare(strict_types=1)` 全量启用 |
| 后台 | Filament 5.7 | admin panel,zh_CN,资源/页面/组件 |
| 前台交互 | Livewire 4 | Filament 依赖 |
| 数据库 | MySQL 8(生产)/ SQLite(测试 :memory:) | |
| 缓存/队列 | database / redis | 队列默认 database,可切 redis |
| Markdown | league/commonmark(GFM)+ html-to-markdown | 内容双格式 |
| 附件 | xpier media library(新上传)+ Spatie medialibrary(sablog 导入兜底) | 正文存 `media:ID` / `attach:ID` |
| 异步 | Workerman 5 常驻(队列消费者 + WebSocket) | 复用 Laravel Queue Worker |
| 支付 | yansongda/pay(支付宝/微信) | 沙箱模式内置 |
| 权限 | spatie/laravel-permission | admin/editor/member |
## 目录分层
@@ -37,7 +37,7 @@ plugins/ # 插件(plugin.json + ServiceProvider + 迁移/路由/
1. **内容双格式**:`posts.content_format`(markdown/html)。新文章默认 Markdown;老数据导入标记 html 原样保留,`content:convert` 可批量转 Markdown。渲染管线 `PostContentRenderer::render()` 统一输出 HTML,再经 `post.rendered` 过滤器(付费内容等)。
2. **附件短代码**:HTML 内容 `[attach=1]`/`[img=1]`;Markdown 内容用 `{{attach:1}}`/`{{img:1}}` 令牌(`[]` 是 Markdown 保留字符)。渲染时按**全局** legacy_attachmentid 解析,找不到媒体自动清理残留。
2. **附件短代码**:新内容 Markdown 用 `![alt](media:ID)` / `[name](media:ID)`,HTML 用 `[media=ID]`,渲染时换成媒体库 URL。老 sablog 仍用 `[attach=1]`/`[img=1]` 或 `{{attach:1}}`/`{{img:1}}`。不要把 CDN 地址写进正文。详见 [docs/media.md](media.md)。
3. **主题系统**:激活主题的 views 目录前置到全局视图路径(View Finder),主题覆盖同名视图、缺失回退默认视图。header 打开的容器由页面视图自闭合(避免 footer 错位)。资产开发期流式返回、生产 `theme:publish`。
@@ -56,21 +56,21 @@ plugins/ # 插件(plugin.json + ServiceProvider + 迁移/路由/
- **本地**:Herd 托管 `laralog.test`(PHP 8.2 + MySQL 8);无 S3 时附件落本地 public
- **测试**:PHPUnit,SQLite `:memory:`(`phpunit.xml`),`RefreshDatabase` + `seed()`;MySQL 专属 SQL(syncCounters)在 sqlite 兼容
- **常用命令**:
- `php artisan sablog:import --fresh --convert-markdown`(老库迁移)
- `php artisan workerman:serve start|stop`
- `php artisan test`
- `php artisan theme:publish` / `content:convert --all` / `attachments:sync-s3`
- `php artisan sablog:import --fresh --convert-markdown`(老库迁移)
- `php artisan workerman:serve start|stop`
- `php artisan test`
- `php artisan theme:publish` / `content:convert --all` / `attachments:sync-s3`
## 配置速查
| 配置 | 说明 |
|------|------|
| `config/blog.php` | 站点/每页数/附件磁盘 |
| `config/themes.php` | 主题目录/默认主题 |
| `config/plugins.php` | 插件目录/内置启用列表 |
| `config/workerman.php` | 消费者数/队列连接/队列名/重试 |
| `config/media.php` | S3 配置(R2/COS/OSS)与 Multipart 阈值 |
| `config/market.php` | 插件/主题市场远程源 |
| 配置 | 说明 |
| ---------------------- | -------------------------------------- |
| `config/blog.php` | 站点/每页数/附件磁盘 |
| `config/themes.php` | 主题目录/默认主题 |
| `config/plugins.php` | 插件目录/内置启用列表 |
| `config/workerman.php` | 消费者数/队列连接/队列名/重试 |
| `config/media.php` | S3 配置(R2/COS/OSS)与 Multipart 阈值 |
| `config/market.php` | 插件/主题市场远程源 |
## 已知边界