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
+4
View File
@@ -79,6 +79,10 @@ WORKERMAN_TIMEOUT=60
# ---- 附件 S3 兼容存储(R2 / COS / OSS / MinIO),不配置则回退本地 public ----
UPLOAD_DISK=uploads
# New uploads/covers use xpier media library (keep sablog Spatie attachments as fallback)
MEDIA_DISK=public
MEDIA_DIRECTORY=media
MEDIA_VISIBILITY=public
S3_ACCESS_KEY=
S3_SECRET_KEY=
S3_REGION=auto
+1
View File
@@ -0,0 +1 @@
24
+12
View File
@@ -0,0 +1,12 @@
# Changelog
## 2026-09-16
- Switched new attachments/covers to `xpier/filament-media-library`.
- Markdown body now inserts `![alt](media:ID)` (files: `[name](media:ID)`); URLs are resolved at render time. Legacy sablog attach tokens are unchanged.
- Schema snapshot: `docs/database/mysql-schema-20260916-131822.sql`.
## 2026-09-07
- Synced this repository from Gitea to local Herd for development (`http://laralog.test`).
- Local schema snapshot: `docs/database/schema-20260907-223917.sql`.
+21
View File
@@ -0,0 +1,21 @@
# LaraLog Features
Modern blog migrated from SaBlog-X: Filament admin, themes, plugins, sablog import, S3 attachments, Workerman AI queue.
## Local development
- Site: http://laralog.test
- Admin: http://laralog.test/admin
- Account: `admin@laralog.test` / `password`
- Database: MySQL `laralog` (Herd, root / empty password)
- Remote: `git@gitea.neatcn.com:gouki/laralog.git`
See `README.md` and `docs/` for install, import, plugins, and themes.
## Media library (xpier)
- Package: `xpier/filament-media-library` (admin Media Library + MediaPicker).
- **Markdown body must store `![alt](media:ID)` or `[name](media:ID)`**, never a CDN/object-storage URL. HTML uses `[media=ID]`.
- Insert flow: pick a file → **Insert into body**. Cover uses `cover_media_id`.
- Legacy sablog `[attach=ID]` / `{{attach:ID}}` still render from Spatie media.
- Details: `docs/media.md`.
+32 -29
View File
@@ -10,17 +10,17 @@
## 技术栈
| 组件 | 版本 | 说明 |
|------|------|------|
| Laravel | ^12.0 | 应用框架 |
| Filament | ^5.7 | 后台面板(zh_CN) |
| Livewire | ^4.4 | 前台交互 |
| Workerman | ^5.2 | 常驻进程:队列消费者 + WebSocket 进度推送 |
| yansongda/pay | ^3.7 | 支付宝 / 微信支付 |
| spatie 库 | — | permission / medialibrary / sitemap / robots / tags / backup |
| league/commonmark | ^2.9 | Markdown 渲染(GFM) |
| league/html-to-markdown | ^5.1 | 老 HTML 内容转换 |
| MySQL | 8.x | 数据库 |
| 组件 | 版本 | 说明 |
| ----------------------- | ----- | ------------------------------------------------------------ |
| Laravel | ^12.0 | 应用框架 |
| Filament | ^5.7 | 后台面板(zh_CN) |
| Livewire | ^4.4 | 前台交互 |
| Workerman | ^5.2 | 常驻进程:队列消费者 + WebSocket 进度推送 |
| yansongda/pay | ^3.7 | 支付宝 / 微信支付 |
| spatie 库 | — | permission / medialibrary / sitemap / robots / tags / backup |
| league/commonmark | ^2.9 | Markdown 渲染(GFM) |
| league/html-to-markdown | ^5.1 | 老 HTML 内容转换 |
| MySQL | 8.x | 数据库 |
## 快速开始
@@ -74,8 +74,8 @@ S3_ACCESS_KEY=... S3_BUCKET=... S3_ENDPOINT=... php artisan attachments:sync-s3
- 目录:`plugins/{vendor}.{name}/`,含 `plugin.json` + `src/ServiceProvider.php`(继承 `App\Blog\Support\PluginServiceProvider`)+ 可选 `database/migrations`、`routes/web.php`、`views/`
- 钩子:`addAction(hook, cb)` / `addFilter(hook, cb, priority)`,核心钩子:
- `comment.created`(AI 审核)、`payment.paid`(按 payable 分发订阅激活/解锁)、`post.rendered`(付费内容过滤/paywall)、`seo.structured_data`
- `filament.post_form` / `filament.post_table`(后台文章表单/表格注入)
- `comment.created`(AI 审核)、`payment.paid`(按 payable 分发订阅激活/解锁)、`post.rendered`(付费内容过滤/paywall)、`seo.structured_data`
- `filament.post_form` / `filament.post_table`(后台文章表单/表格注入)
- 插件依赖 `requires` 强制校验:启用需依赖已启用,停用/卸载会被依赖者阻止;启动时依赖不满足的插件跳过
- 订单商品化:`Payment` 多态 `payable` 关联购买实体,实体实现 `payableLabel()` / `payableUrl()` 接入订单展示(详见 docs/plugins.md)
- 后台「插件管理」:启停 / 上传 ZIP 安装 / 卸载 / 市场拉取(配置 `MARKET_URL` 后可用)
@@ -85,7 +85,7 @@ S3_ACCESS_KEY=... S3_BUCKET=... S3_ENDPOINT=... php artisan attachments:sync-s3
- 每篇文章有 `content_format`(markdown / html),新文章默认 Markdown
- 老数据导入标记为 html 原样保留;`content:convert` 批量转 Markdown
- 附件短代码:HTML 内容用 `[attach=1]` / `[img=1]`,Markdown 内容用 `{{attach:1}}` / `{{img:1}}`(`[]` 是 Markdown 保留字符),渲染时统一解析
- 附件短代码:新图 `![alt](media:ID)` / `[name](media:ID)`;HTML `[media=ID]`。老附件 HTML `[attach=1]` / `[img=1]`,Markdown `{{attach:1}}` / `{{img:1}}`。不要把 CDN 地址写进正文(见 [docs/media.md](docs/media.md))。
- 付费内容:`[paid]...[/paid]` 短代码,非会员/未解锁访客隐藏(会员插件)
## 异步处理(Workerman)
@@ -98,8 +98,8 @@ php artisan workerman:serve stop
- 消费者复用 Laravel `Queue\Worker`,**database / redis 队列统一支持**,常驻进程内框架只启动一次(省去每个任务的启动开销)
- 任务统一实现 `App\Blog\Jobs\AiJob` 接口(`handle(LlmClient)`),依赖由容器自动注入
- 队列选择:
- database(默认):`WORKERMAN_QUEUE_CONNECTION=database`
- redis:`WORKERMAN_QUEUE_CONNECTION=redis` 并确保 `QUEUE_CONNECTION=redis`、`REDIS_*` 配置正确;`redis` 连接的 `block_for` 建议设为 `0`(配合 1s 轮询,避免阻塞 Workerman event loop)
- database(默认):`WORKERMAN_QUEUE_CONNECTION=database`
- redis:`WORKERMAN_QUEUE_CONNECTION=redis` 并确保 `QUEUE_CONNECTION=redis`、`REDIS_*` 配置正确;`redis` 连接的 `block_for` 建议设为 `0`(配合 1s 轮询,避免阻塞 Workerman event loop)
- 消费队列:`WORKERMAN_QUEUES=default,ai`(ai 队列为 AI 审核/润色任务)
- 失败重试:`WORKERMAN_MAX_TRIES`(默认 3),超时 `WORKERMAN_TIMEOUT`(默认 60s),重试耗尽进 `failed_jobs` 表
- 降级路径:`php artisan queue:work` 照常可用(同一队列)
@@ -107,6 +107,7 @@ php artisan workerman:serve stop
## 外链中转(/go,内置插件)
内置插件 `neatstudio.link-tracker`(外链中转):
- 正文外链、评论正文 URL、评论作者网址、友链统一改写为 `/go/{hash}`:302 跳转 + **点击统计**、可**停用拦截**(返回 410)、隐藏真实链接、为未来广告位预留
- 站内链接/相对路径/mailto/tel/锚点不中转;同一 URL 复用同一短码
- 后台「管理 → 外链中转」可查看点击量、启停、删除
@@ -128,25 +129,27 @@ php artisan workerman:serve stop
## 测试
```bash
php artisan test # 84 个用例:迁移/老路由 301/主题/插件/会员支付流/AI 审核/商城购买
php artisan test # Feature + Unit: import, legacy URLs, themes, plugins, membership, media:ID render
```
## 开发者文档
| 文档 | 内容 |
|------|------|
| [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) |
| [docs/plugins.md](docs/plugins.md) | 插件 API 详解(钩子/依赖/商品化/市场契约) |
| [docs/themes.md](docs/themes.md) | 主题 API 详解(视图解析/注入点/变量) |
| [docs/architecture.md](docs/architecture.md) | 架构与设计决策 |
| [docs/api.md](docs/api.md) | 对外 API(小程序/第三方) |
| 文档 | 内容 |
| -------------------------------------------------- | --------------------------------------------- |
| [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) |
| [docs/plugins.md](docs/plugins.md) | 插件 API 详解(钩子/依赖/商品化/市场契约) |
| [docs/themes.md](docs/themes.md) | 主题 API 详解(视图解析/注入点/变量) |
| [docs/architecture.md](docs/architecture.md) | 架构与设计决策 |
| [docs/media.md](docs/media.md) | Media library + Markdown `media:ID` |
| [docs/api.md](docs/api.md) | 对外 API(小程序/第三方) |
**上手示例**:
- 插件:`plugins/demo.hello-world/`(短代码 / 钩子 / 路由 / 后台页 / 迁移 / 表单注入,全演示)
- 主题:`themes/demo/`(最小可运行主题,绿色系,覆盖 partials 即可换肤)
+7 -2
View File
@@ -5,6 +5,7 @@ declare(strict_types=1);
namespace App\Blog\Services;
use App\Blog\Support\MarkdownMediaEmbed;
use App\Models\Post;
use Illuminate\Support\Facades\Cache;
use League\CommonMark\Environment\Environment;
@@ -36,11 +37,15 @@ class PostContentRenderer
*/
public function render(Post $post): string
{
$embed = app(MarkdownMediaEmbed::class);
$raw = (string) $post->content;
$content = $post->content_format === 'markdown'
? $this->toHtml($post->content)
: $post->content;
? $this->toHtml($embed->expandMarkdown($raw))
: $raw;
$html = $this->renderShortcodes($content, $post);
$html = $embed->hydrateHtml($html);
// 插件钩子:付费内容过滤、AI 处理等
$html = app(\App\Blog\Support\PluginManager::class)->applyFilters('post.rendered', $html, $post);
+131
View File
@@ -0,0 +1,131 @@
<?php
declare(strict_types=1);
namespace App\Blog\Support;
use Xpier\FilamentMediaLibrary\Models\MediaLibrary;
/**
* Markdown/HTML body must store library references, never CDN URLs.
*
* Markdown: ![alt](media:12) or [file.pdf](media:12)
* HTML: [media=12]
*/
class MarkdownMediaEmbed
{
public const PROTOCOL = 'media:';
public function snippet(MediaLibrary $media, string $format = 'markdown'): string
{
$id = (int) $media->getKey();
$isImage = $this->isImage($media);
if ($format === 'html') {
return '[media='.$id.']';
}
$label = $this->escapeMdLabel($this->label($media, $isImage));
return $isImage
? '!['.$label.']('.self::PROTOCOL.$id.')'
: '['.$label.']('.self::PROTOCOL.$id.')';
}
public function expandMarkdown(string $markdown): string
{
return preg_replace_callback(
'/\]\(media:(\d+)\)/i',
function (array $matches): string {
$url = $this->publicUrl((int) $matches[1]);
return $url !== null ? ']('.$url.')' : $matches[0];
},
$markdown
) ?? $markdown;
}
public function hydrateHtml(string $html): string
{
$html = preg_replace_callback(
'/\b(src|href)=([\'"])media:(\d+)\2/i',
function (array $matches): string {
$url = $this->publicUrl((int) $matches[3]);
if ($url === null) {
return $matches[0];
}
return $matches[1].'='.$matches[2].$url.$matches[2];
},
$html
) ?? $html;
return preg_replace_callback(
'/\[media=(\d+)\]/i',
function (array $matches): string {
$id = (int) $matches[1];
$media = $this->find($id);
$url = $this->publicUrl($id);
if ($url === null) {
return '';
}
if ($media && $this->isImage($media)) {
return sprintf(
'<img src="%s" alt="%s" loading="lazy" />',
e($url),
e($this->label($media, true))
);
}
$label = $media ? $this->label($media, false) : 'file-'.$id;
return sprintf('<a href="%s">%s</a>', e($url), e($label));
},
$html
) ?? $html;
}
public function find(int $id): ?MediaLibrary
{
try {
return MediaLibrary::query()->find($id);
} catch (\Throwable) {
return null;
}
}
public function publicUrl(int $id): ?string
{
$media = $this->find($id);
$url = $media?->url;
return filled($url) ? (string) $url : null;
}
public function isImage(MediaLibrary $media): bool
{
if ($media->type === MediaLibrary::TYPE_IMAGE) {
return true;
}
return is_string($media->mime_type) && str_starts_with($media->mime_type, 'image/');
}
protected function label(MediaLibrary $media, bool $isImage): string
{
if (filled($media->alt_text)) {
return (string) $media->alt_text;
}
$name = (string) ($media->original_name ?: basename((string) $media->path) ?: 'media-'.$media->getKey());
return $isImage ? (pathinfo($name, PATHINFO_FILENAME) ?: $name) : $name;
}
protected function escapeMdLabel(string $label): string
{
return str_replace(['[', ']', '!'], ['\\[', '\\]', '\\!'], $label);
}
}
@@ -4,12 +4,11 @@ declare(strict_types=1);
namespace App\Filament\Resources\Categories\Schemas;
use App\Support\MediaDisk;
use Filament\Forms\Components\SpatieMediaLibraryFileUpload;
use Filament\Forms\Components\Textarea;
use Filament\Forms\Components\TextInput;
use Filament\Schemas\Components\Section;
use Filament\Schemas\Schema;
use Xpier\FilamentMediaLibrary\Components\MediaPicker;
class CategoryForm
{
@@ -43,12 +42,11 @@ class CategoryForm
]),
Section::make('封面')
->schema([
SpatieMediaLibraryFileUpload::make('cover')
MediaPicker::make('cover_media_id')
->label('封面图')
->collection('cover')
->image()
->imageEditor()
->disk(MediaDisk::name()),
->module('articles')
->mediaType('image')
->relationship('coverMedia'),
]),
]);
}
@@ -27,6 +27,11 @@ class MediaResource extends Resource
protected static ?string $pluralModelLabel = '媒体';
public static function shouldRegisterNavigation(): bool
{
return false;
}
protected static ?string $model = Media::class;
protected static string|BackedEnum|null $navigationIcon = Heroicon::OutlinedRectangleStack;
@@ -2,18 +2,23 @@
declare(strict_types=1);
namespace App\Filament\Resources\Posts\Schemas;
use App\Support\MediaDisk;
use App\Blog\Support\MarkdownMediaEmbed;
use Filament\Actions\Action;
use Filament\Forms\Components\DateTimePicker;
use Filament\Forms\Components\MarkdownEditor;
use Filament\Forms\Components\Select;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Components\Textarea;
use Filament\Forms\Components\Toggle;
use Filament\Notifications\Notification;
use Filament\Schemas\Components\Actions;
use Filament\Schemas\Components\Utilities\Get;
use Filament\Schemas\Components\Utilities\Set;
use Filament\Schemas\Schema;
use Filament\Forms\Components\SpatieMediaLibraryFileUpload;
use Xpier\FilamentMediaLibrary\Components\MediaPicker;
use Xpier\FilamentMediaLibrary\Models\MediaLibrary;
class PostForm
{
@@ -39,7 +44,8 @@ class PostForm
'markdown' => 'Markdown',
'html' => 'HTML',
])
->default('markdown'),
->default('markdown')
->live(),
Select::make('status')
->label('状态')
->options([
@@ -51,17 +57,49 @@ class PostForm
MarkdownEditor::make('content')
->label('正文')
->required()
->helperText('Markdown 插入图片用 ![说明](media:ID),文件用 [文件名](media:ID)。不要把 CDN / 对象存储地址写进正文。')
->fileAttachments(false)
->columnSpanFull(),
MediaPicker::make('insert_media_id')
->label('从媒体库插入正文')
->module('articles')
->mediaType(null)
->dehydrated(false)
->helperText('选好媒体后点「插入到正文」。Markdown 写入 media:ID 引用,渲染时再换成真实地址。')
->columnSpanFull(),
Actions::make([
Action::make('insertSelectedMedia')
->label('插入到正文')
->action(function (Get $get, Set $set): void {
$mediaId = $get('insert_media_id');
$media = is_numeric($mediaId)
? MediaLibrary::query()->find((int) $mediaId)
: null;
if (! $media instanceof MediaLibrary) {
Notification::make()->title('请先从媒体库选择文件')->warning()->send();
return;
}
$snippet = app(MarkdownMediaEmbed::class)->snippet(
$media,
(string) ($get('content_format') ?: 'markdown')
);
$set('content', rtrim((string) $get('content'))."\n\n".$snippet."\n");
$set('insert_media_id', null);
Notification::make()->title('已写入正文引用')->success()->send();
}),
])->columnSpanFull(),
]),
\Filament\Schemas\Components\Section::make('封面与摘要')
->columns(2)
->schema([
SpatieMediaLibraryFileUpload::make('cover')
MediaPicker::make('cover_media_id')
->label('封面图')
->collection('cover')
->image()
->imageEditor()
->disk(MediaDisk::name())
->module('articles')
->mediaType('image')
->relationship('coverMedia')
->columnSpanFull(),
Textarea::make('excerpt')
->label('摘要(留空自动截取)')
+18
View File
@@ -6,8 +6,10 @@ namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Spatie\MediaLibrary\HasMedia;
use Xpier\FilamentMediaLibrary\Models\MediaLibrary;
use Spatie\MediaLibrary\InteractsWithMedia;
class Category extends Model implements HasMedia
@@ -21,6 +23,7 @@ class Category extends Model implements HasMedia
'display_order',
'post_count',
'description',
'cover_media_id',
];
public function posts(): HasMany
@@ -28,6 +31,21 @@ class Category extends Model implements HasMedia
return $this->hasMany(Post::class);
}
public function coverMedia(): BelongsTo
{
return $this->belongsTo(MediaLibrary::class, 'cover_media_id');
}
public function coverImageUrl(): ?string
{
$libraryUrl = $this->coverMedia?->url;
if (filled($libraryUrl)) {
return (string) $libraryUrl;
}
return $this->getFirstMedia('cover')?->getUrl();
}
public function getUrlAttribute(): string
{
return route('category.show', $this->slug ?? $this->id);
+26 -2
View File
@@ -11,6 +11,7 @@ use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Spatie\MediaLibrary\HasMedia;
use Xpier\FilamentMediaLibrary\Models\MediaLibrary;
use Spatie\MediaLibrary\InteractsWithMedia;
use Spatie\MediaLibrary\MediaCollections\Models\Media;
use Spatie\Tags\HasTags;
@@ -30,6 +31,7 @@ class Post extends Model implements HasMedia
'content',
'content_format',
'keywords',
'cover_media_id',
'status',
'is_sticky',
'close_comment',
@@ -57,6 +59,26 @@ class Post extends Model implements HasMedia
return $this->belongsTo(User::class, 'user_id');
}
public function coverMedia(): BelongsTo
{
return $this->belongsTo(MediaLibrary::class, 'cover_media_id');
}
public function coverImageUrl(?string $conversion = 'card'): ?string
{
$libraryUrl = $this->coverMedia?->url;
if (filled($libraryUrl)) {
return (string) $libraryUrl;
}
$cover = $this->getFirstMedia('cover');
if ($cover === null) {
return null;
}
return $cover->getUrl($conversion) ?: $cover->getUrl();
}
public function comments(): HasMany
{
return $this->hasMany(Comment::class);
@@ -133,8 +155,10 @@ class Post extends Model implements HasMedia
$text = strip_tags($this->content);
// 付费块不进入摘要,避免付费内容泄漏到列表页/SEO meta
$text = preg_replace('/\[paid\].*?\[\/paid\]/s', '', $text);
$text = preg_replace('/\[[^\]]*\]/', '', $text);
$text = preg_replace('/\[paid\].*?\[\/paid\]/s', '', $text) ?? $text;
$text = preg_replace('/!\[([^\]]*)\]\((?:media|attach):\d+\)/i', '$1', $text) ?? $text;
$text = preg_replace('/\[([^\]]*)\]\((?:media|attach):\d+\)/i', '$1', $text) ?? $text;
$text = preg_replace('/\[[^\]]*\]/', '', $text) ?? $text;
return mb_substr($text, 0, 200);
}
+4
View File
@@ -41,6 +41,10 @@ class AppServiceProvider extends ServiceProvider
*/
public function boot(): void
{
config([
'filament-media-library.navigation_group' => '内容',
]);
// 自动加载所有插件的迁移(插件目录下 database/migrations)
foreach (glob(base_path('plugins/*/database/migrations')) ?: [] as $path) {
$this->loadMigrationsFrom($path);
@@ -28,6 +28,7 @@ use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken;
use Illuminate\Routing\Middleware\SubstituteBindings;
use Illuminate\Session\Middleware\StartSession;
use Illuminate\View\Middleware\ShareErrorsFromSession;
use Xpier\FilamentMediaLibrary\Filament\MediaLibraryPlugin;
class AdminPanelProvider extends PanelProvider
{
@@ -98,6 +99,10 @@ class AdminPanelProvider extends PanelProvider
->widgets([
AccountWidget::class,
])
->plugins([
MediaLibraryPlugin::make()
->navigationGroup((string) config('filament-media-library.navigation_group', '内容')),
])
->middleware([
EncryptCookies::class,
AddQueuedCookiesToResponse::class,
+12 -2
View File
@@ -3,7 +3,10 @@
"name": "laravel/laravel",
"type": "project",
"description": "The skeleton application for the Laravel framework.",
"keywords": ["laravel", "framework"],
"keywords": [
"laravel",
"framework"
],
"license": "MIT",
"require": {
"php": "^8.2",
@@ -23,6 +26,7 @@
"spatie/laravel-sitemap": "^8.0",
"spatie/laravel-tags": "^4.0",
"workerman/workerman": "^5.0",
"xpier/filament-media-library": "^1.5",
"yansongda/pay": "^3.7"
},
"require-dev": {
@@ -104,5 +108,11 @@
}
},
"minimum-stability": "stable",
"prefer-stable": true
"prefer-stable": true,
"repositories": [
{
"type": "vcs",
"url": "https://github.com/ffqa/xpier-filament-media-library.git"
}
]
}
Generated
+408 -1919
View File
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,40 @@
<?php
declare(strict_types=1);
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
if (! Schema::hasColumn('posts', 'cover_media_id')) {
Schema::table('posts', function (Blueprint $table): void {
$table->unsignedBigInteger('cover_media_id')->nullable()->after('keywords');
});
}
if (! Schema::hasColumn('categories', 'cover_media_id')) {
Schema::table('categories', function (Blueprint $table): void {
$table->unsignedBigInteger('cover_media_id')->nullable()->after('description');
});
}
}
public function down(): void
{
if (Schema::hasColumn('posts', 'cover_media_id')) {
Schema::table('posts', function (Blueprint $table): void {
$table->dropColumn('cover_media_id');
});
}
if (Schema::hasColumn('categories', 'cover_media_id')) {
Schema::table('categories', function (Blueprint $table): void {
$table->dropColumn('cover_media_id');
});
}
}
};
@@ -0,0 +1,53 @@
<?php
declare(strict_types=1);
use Illuminate\Database\Migrations\Migration;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
if (! Schema::hasTable('media_folders')) {
return;
}
$now = now();
$folders = [
['code' => 'general', 'name' => 'General', 'sort' => 0],
['code' => 'articles', 'name' => 'Articles', 'sort' => 1],
['code' => 'banners', 'name' => 'Banners', 'sort' => 2],
];
foreach ($folders as $folder) {
$exists = DB::table('media_folders')->where('code', $folder['code'])->exists();
if ($exists) {
continue;
}
DB::table('media_folders')->insert([
'parent_id' => null,
'code' => $folder['code'],
'name' => $folder['name'],
'sort' => $folder['sort'],
'is_active' => true,
'created_at' => $now,
'updated_at' => $now,
]);
}
}
public function down(): void
{
if (! Schema::hasTable('media_folders')) {
return;
}
DB::table('media_folders')
->whereIn('code', ['general', 'articles', 'banners'])
->whereNull('parent_id')
->delete();
}
};
+582
View File
@@ -0,0 +1,582 @@
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
DROP TABLE IF EXISTS `cache`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `cache` (
`key` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`value` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL,
`expiration` int NOT NULL,
PRIMARY KEY (`key`),
KEY `cache_expiration_index` (`expiration`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `cache_locks`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `cache_locks` (
`key` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`owner` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`expiration` int NOT NULL,
PRIMARY KEY (`key`),
KEY `cache_locks_expiration_index` (`expiration`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `categories`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `categories` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL,
`slug` varchar(120) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`display_order` tinyint NOT NULL DEFAULT '0',
`description` text COLLATE utf8mb4_unicode_ci,
`cover_media_id` bigint unsigned DEFAULT NULL,
`post_count` int unsigned NOT NULL DEFAULT '0',
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `categories_slug_unique` (`slug`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `comments`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `comments` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`post_id` bigint unsigned NOT NULL,
`user_id` bigint unsigned DEFAULT NULL,
`author_name` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL,
`author_email` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`author_url` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`content` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL,
`ip` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`status` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'pending',
`ai_review` json DEFAULT NULL,
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `comments_user_id_foreign` (`user_id`),
KEY `comments_post_id_status_index` (`post_id`,`status`),
KEY `comments_status_index` (`status`),
CONSTRAINT `comments_post_id_foreign` FOREIGN KEY (`post_id`) REFERENCES `posts` (`id`) ON DELETE CASCADE,
CONSTRAINT `comments_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `failed_jobs`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `failed_jobs` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`uuid` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`connection` text COLLATE utf8mb4_unicode_ci NOT NULL,
`queue` text COLLATE utf8mb4_unicode_ci NOT NULL,
`payload` longtext COLLATE utf8mb4_unicode_ci NOT NULL,
`exception` longtext COLLATE utf8mb4_unicode_ci NOT NULL,
`failed_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `failed_jobs_uuid_unique` (`uuid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `hello_world_logs`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `hello_world_logs` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`comment_id` bigint unsigned DEFAULT NULL,
`note` varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `hello_world_logs_comment_id_foreign` (`comment_id`),
CONSTRAINT `hello_world_logs_comment_id_foreign` FOREIGN KEY (`comment_id`) REFERENCES `comments` (`id`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `job_batches`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `job_batches` (
`id` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`total_jobs` int NOT NULL,
`pending_jobs` int NOT NULL,
`failed_jobs` int NOT NULL,
`failed_job_ids` longtext COLLATE utf8mb4_unicode_ci NOT NULL,
`options` mediumtext COLLATE utf8mb4_unicode_ci,
`cancelled_at` int DEFAULT NULL,
`created_at` int NOT NULL,
`finished_at` int DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `jobs`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `jobs` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`queue` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`payload` longtext COLLATE utf8mb4_unicode_ci NOT NULL,
`attempts` tinyint unsigned NOT NULL,
`reserved_at` int unsigned DEFAULT NULL,
`available_at` int unsigned NOT NULL,
`created_at` int unsigned NOT NULL,
PRIMARY KEY (`id`),
KEY `jobs_queue_index` (`queue`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `links`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `links` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL,
`url` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`note` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`display_order` tinyint NOT NULL DEFAULT '0',
`visible` tinyint(1) NOT NULL DEFAULT '1',
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `market_packages`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `market_packages` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`item_key` varchar(190) COLLATE utf8mb4_unicode_ci NOT NULL,
`item_type` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL,
`title` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`price` bigint unsigned NOT NULL,
`status` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'pending',
`payment_id` bigint unsigned DEFAULT NULL,
`user_id` bigint unsigned DEFAULT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `market_packages_item_key_item_type_unique` (`item_key`,`item_type`),
KEY `market_packages_payment_id_foreign` (`payment_id`),
KEY `market_packages_user_id_foreign` (`user_id`),
CONSTRAINT `market_packages_payment_id_foreign` FOREIGN KEY (`payment_id`) REFERENCES `payments` (`id`) ON DELETE SET NULL,
CONSTRAINT `market_packages_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `media`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `media` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`model_type` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`model_id` bigint unsigned NOT NULL,
`uuid` char(36) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`collection_name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`file_name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`mime_type` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`disk` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`conversions_disk` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`size` bigint unsigned NOT NULL,
`manipulations` json NOT NULL,
`custom_properties` json NOT NULL,
`generated_conversions` json NOT NULL,
`responsive_images` json NOT NULL,
`order_column` int unsigned DEFAULT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `media_uuid_unique` (`uuid`),
KEY `media_model_type_model_id_index` (`model_type`,`model_id`),
KEY `media_order_column_index` (`order_column`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `media_folders`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `media_folders` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`parent_id` bigint unsigned DEFAULT NULL,
`code` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`sort` int NOT NULL DEFAULT '0',
`is_active` tinyint(1) NOT NULL DEFAULT '1',
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `media_folders_code_unique` (`code`),
KEY `media_folders_parent_id_foreign` (`parent_id`),
CONSTRAINT `media_folders_parent_id_foreign` FOREIGN KEY (`parent_id`) REFERENCES `media_folders` (`id`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `media_library`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `media_library` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`user_id` bigint unsigned DEFAULT NULL,
`disk` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'public',
`path` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`original_name` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`extension` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`mime_type` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`size` bigint unsigned NOT NULL DEFAULT '0',
`type` varchar(30) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'image',
`source` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'admin',
`folder` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`alt_text` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`custom_properties` json DEFAULT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
`deleted_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `media_library_path_unique` (`path`),
KEY `media_library_user_id_foreign` (`user_id`),
KEY `media_library_type_index` (`type`),
KEY `media_library_folder_index` (`folder`),
KEY `media_library_source_index` (`source`),
CONSTRAINT `media_library_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `membership_plans`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `membership_plans` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(60) COLLATE utf8mb4_unicode_ci NOT NULL,
`slug` varchar(60) COLLATE utf8mb4_unicode_ci NOT NULL,
`description` text COLLATE utf8mb4_unicode_ci,
`price` bigint unsigned NOT NULL,
`duration_days` int unsigned NOT NULL DEFAULT '30',
`permissions` json DEFAULT NULL,
`active` tinyint(1) NOT NULL DEFAULT '1',
`sort` int unsigned NOT NULL DEFAULT '0',
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `membership_plans_slug_unique` (`slug`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `migrations`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `migrations` (
`id` int unsigned NOT NULL AUTO_INCREMENT,
`migration` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`batch` int NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `model_has_permissions`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `model_has_permissions` (
`permission_id` bigint unsigned NOT NULL,
`model_type` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`model_id` bigint unsigned NOT NULL,
PRIMARY KEY (`permission_id`,`model_id`,`model_type`),
KEY `model_has_permissions_model_id_model_type_index` (`model_id`,`model_type`),
CONSTRAINT `model_has_permissions_permission_id_foreign` FOREIGN KEY (`permission_id`) REFERENCES `permissions` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `model_has_roles`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `model_has_roles` (
`role_id` bigint unsigned NOT NULL,
`model_type` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`model_id` bigint unsigned NOT NULL,
PRIMARY KEY (`role_id`,`model_id`,`model_type`),
KEY `model_has_roles_model_id_model_type_index` (`model_id`,`model_type`),
CONSTRAINT `model_has_roles_role_id_foreign` FOREIGN KEY (`role_id`) REFERENCES `roles` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `password_reset_tokens`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `password_reset_tokens` (
`email` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`token` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`created_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`email`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `payments`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `payments` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`order_no` varchar(40) COLLATE utf8mb4_unicode_ci NOT NULL,
`user_id` bigint unsigned DEFAULT NULL,
`subject` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`description` varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`amount` bigint unsigned NOT NULL,
`channel` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'alipay',
`status` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'pending',
`gateway_trade_no` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`payload` json DEFAULT NULL,
`paid_at` timestamp NULL DEFAULT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
`payable_type` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`payable_id` bigint unsigned DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `payments_order_no_unique` (`order_no`),
KEY `payments_user_id_status_index` (`user_id`,`status`),
KEY `payments_payable_type_payable_id_index` (`payable_type`,`payable_id`),
CONSTRAINT `payments_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `permissions`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `permissions` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`guard_name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `permissions_name_guard_name_unique` (`name`,`guard_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `personal_access_tokens`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `personal_access_tokens` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`tokenable_type` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`tokenable_id` bigint unsigned NOT NULL,
`name` text COLLATE utf8mb4_unicode_ci NOT NULL,
`token` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL,
`abilities` text COLLATE utf8mb4_unicode_ci,
`last_used_at` timestamp NULL DEFAULT NULL,
`expires_at` timestamp NULL DEFAULT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `personal_access_tokens_token_unique` (`token`),
KEY `personal_access_tokens_tokenable_type_tokenable_id_index` (`tokenable_type`,`tokenable_id`),
KEY `personal_access_tokens_expires_at_index` (`expires_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `plugin_records`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `plugin_records` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`vendor` varchar(60) COLLATE utf8mb4_unicode_ci NOT NULL,
`name` varchar(60) COLLATE utf8mb4_unicode_ci NOT NULL,
`version` varchar(40) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`enabled` tinyint(1) NOT NULL DEFAULT '1',
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `plugin_records_vendor_name_unique` (`vendor`,`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `posts`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `posts` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`category_id` bigint unsigned DEFAULT NULL,
`user_id` bigint unsigned DEFAULT NULL,
`title` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`slug` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`excerpt` text COLLATE utf8mb4_unicode_ci,
`content` longtext COLLATE utf8mb4_unicode_ci NOT NULL,
`content_format` varchar(10) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'markdown',
`keywords` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`cover_media_id` bigint unsigned DEFAULT NULL,
`status` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'published',
`is_sticky` tinyint(1) NOT NULL DEFAULT '0',
`close_comment` tinyint(1) NOT NULL DEFAULT '0',
`read_password` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`views` bigint unsigned NOT NULL DEFAULT '0',
`comment_count` int unsigned NOT NULL DEFAULT '0',
`meta` json DEFAULT NULL,
`published_at` timestamp NULL DEFAULT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `posts_slug_unique` (`slug`),
KEY `posts_category_id_foreign` (`category_id`),
KEY `posts_user_id_foreign` (`user_id`),
KEY `posts_status_published_at_index` (`status`,`published_at`),
KEY `posts_status_index` (`status`),
KEY `posts_published_at_index` (`published_at`),
CONSTRAINT `posts_category_id_foreign` FOREIGN KEY (`category_id`) REFERENCES `categories` (`id`) ON DELETE SET NULL,
CONSTRAINT `posts_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `redirects`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `redirects` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`hash` varchar(16) COLLATE utf8mb4_unicode_ci NOT NULL,
`target_url` text COLLATE utf8mb4_unicode_ci NOT NULL,
`clicks` bigint unsigned NOT NULL DEFAULT '0',
`status` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'active',
`last_clicked_at` timestamp NULL DEFAULT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `redirects_hash_unique` (`hash`),
KEY `redirects_target_url_index` (`target_url`(191))
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `role_has_permissions`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `role_has_permissions` (
`permission_id` bigint unsigned NOT NULL,
`role_id` bigint unsigned NOT NULL,
PRIMARY KEY (`permission_id`,`role_id`),
KEY `role_has_permissions_role_id_foreign` (`role_id`),
CONSTRAINT `role_has_permissions_permission_id_foreign` FOREIGN KEY (`permission_id`) REFERENCES `permissions` (`id`) ON DELETE CASCADE,
CONSTRAINT `role_has_permissions_role_id_foreign` FOREIGN KEY (`role_id`) REFERENCES `roles` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `roles`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `roles` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`guard_name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `roles_name_guard_name_unique` (`name`,`guard_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `sessions`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `sessions` (
`id` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`user_id` bigint unsigned DEFAULT NULL,
`ip_address` varchar(45) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`user_agent` text COLLATE utf8mb4_unicode_ci,
`payload` longtext COLLATE utf8mb4_unicode_ci NOT NULL,
`last_activity` int NOT NULL,
PRIMARY KEY (`id`),
KEY `sessions_user_id_index` (`user_id`),
KEY `sessions_last_activity_index` (`last_activity`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `settings`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `settings` (
`key` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`value` text COLLATE utf8mb4_unicode_ci NOT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`key`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `subscriptions`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `subscriptions` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`user_id` bigint unsigned NOT NULL,
`membership_plan_id` bigint unsigned DEFAULT NULL,
`status` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'active',
`starts_at` timestamp NULL DEFAULT NULL,
`ends_at` timestamp NULL DEFAULT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `subscriptions_membership_plan_id_foreign` (`membership_plan_id`),
KEY `subscriptions_user_id_status_index` (`user_id`,`status`),
CONSTRAINT `subscriptions_membership_plan_id_foreign` FOREIGN KEY (`membership_plan_id`) REFERENCES `membership_plans` (`id`) ON DELETE SET NULL,
CONSTRAINT `subscriptions_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `taggables`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `taggables` (
`tag_id` bigint unsigned NOT NULL,
`taggable_type` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`taggable_id` bigint unsigned NOT NULL,
UNIQUE KEY `taggables_tag_id_taggable_id_taggable_type_unique` (`tag_id`,`taggable_id`,`taggable_type`),
KEY `taggables_taggable_type_taggable_id_index` (`taggable_type`,`taggable_id`),
CONSTRAINT `taggables_tag_id_foreign` FOREIGN KEY (`tag_id`) REFERENCES `tags` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `tags`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `tags` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`name` json NOT NULL,
`slug` json NOT NULL,
`type` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`order_column` int DEFAULT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `users`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `users` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`email` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`email_verified_at` timestamp NULL DEFAULT NULL,
`password` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`remember_token` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
`legacy_md5` varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`url` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`logincount` int unsigned NOT NULL DEFAULT '0',
`loginip` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`regip` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`logintime` timestamp NULL DEFAULT NULL,
`lastpost_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `users_email_unique` (`email`),
KEY `users_legacy_md5_index` (`legacy_md5`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (1,'0001_01_01_000000_create_users_table',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (2,'0001_01_01_000001_create_cache_table',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (3,'0001_01_01_000002_create_jobs_table',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (4,'2026_08_11_092446_create_permission_tables',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (5,'2026_08_11_092448_create_media_table',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (6,'2026_08_11_092449_create_tag_tables',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (7,'2026_08_11_100000_create_membership_tables',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (8,'2026_08_11_100000_create_payments_table',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (9,'2026_08_11_100001_create_categories_and_posts_table',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (10,'2026_08_11_100002_create_comments_and_trackbacks_table',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (11,'2026_08_11_100003_create_links_and_settings_table',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (12,'2026_08_11_100004_add_blog_columns_to_users_table',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (13,'2026_08_11_100005_add_content_format_to_posts_table',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (14,'2026_08_11_113537_create_personal_access_tokens_table',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (15,'2026_08_12_000000_create_hello_world_logs_table',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (16,'2026_08_12_100000_add_payable_to_payments_table',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (17,'2026_08_12_200000_create_market_packages_table',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (18,'2026_08_12_300000_add_description_to_categories_table',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (19,'2026_08_12_400000_create_redirects_table',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (20,'2026_08_15_000001_create_media_library_table',2);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (21,'2026_08_15_000002_create_media_folders_table',2);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (22,'2026_08_15_000003_add_soft_deletes_to_media_library_table',2);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (23,'2026_08_15_000004_add_custom_properties_to_media_library_table',2);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (24,'2026_09_16_000001_add_cover_media_id_to_posts_and_categories',2);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (25,'2026_09_16_000002_seed_default_media_folders',2);
+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` | 插件/主题市场远程源 |
## 已知边界
@@ -0,0 +1,527 @@
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
DROP TABLE IF EXISTS `cache`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `cache` (
`key` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`value` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL,
`expiration` int NOT NULL,
PRIMARY KEY (`key`),
KEY `cache_expiration_index` (`expiration`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `cache_locks`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `cache_locks` (
`key` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`owner` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`expiration` int NOT NULL,
PRIMARY KEY (`key`),
KEY `cache_locks_expiration_index` (`expiration`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `categories`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `categories` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL,
`slug` varchar(120) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`display_order` tinyint NOT NULL DEFAULT '0',
`description` text COLLATE utf8mb4_unicode_ci,
`post_count` int unsigned NOT NULL DEFAULT '0',
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `categories_slug_unique` (`slug`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `comments`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `comments` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`post_id` bigint unsigned NOT NULL,
`user_id` bigint unsigned DEFAULT NULL,
`author_name` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL,
`author_email` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`author_url` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`content` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL,
`ip` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`status` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'pending',
`ai_review` json DEFAULT NULL,
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `comments_user_id_foreign` (`user_id`),
KEY `comments_post_id_status_index` (`post_id`,`status`),
KEY `comments_status_index` (`status`),
CONSTRAINT `comments_post_id_foreign` FOREIGN KEY (`post_id`) REFERENCES `posts` (`id`) ON DELETE CASCADE,
CONSTRAINT `comments_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `failed_jobs`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `failed_jobs` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`uuid` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`connection` text COLLATE utf8mb4_unicode_ci NOT NULL,
`queue` text COLLATE utf8mb4_unicode_ci NOT NULL,
`payload` longtext COLLATE utf8mb4_unicode_ci NOT NULL,
`exception` longtext COLLATE utf8mb4_unicode_ci NOT NULL,
`failed_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `failed_jobs_uuid_unique` (`uuid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `hello_world_logs`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `hello_world_logs` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`comment_id` bigint unsigned DEFAULT NULL,
`note` varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `hello_world_logs_comment_id_foreign` (`comment_id`),
CONSTRAINT `hello_world_logs_comment_id_foreign` FOREIGN KEY (`comment_id`) REFERENCES `comments` (`id`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `job_batches`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `job_batches` (
`id` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`total_jobs` int NOT NULL,
`pending_jobs` int NOT NULL,
`failed_jobs` int NOT NULL,
`failed_job_ids` longtext COLLATE utf8mb4_unicode_ci NOT NULL,
`options` mediumtext COLLATE utf8mb4_unicode_ci,
`cancelled_at` int DEFAULT NULL,
`created_at` int NOT NULL,
`finished_at` int DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `jobs`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `jobs` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`queue` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`payload` longtext COLLATE utf8mb4_unicode_ci NOT NULL,
`attempts` tinyint unsigned NOT NULL,
`reserved_at` int unsigned DEFAULT NULL,
`available_at` int unsigned NOT NULL,
`created_at` int unsigned NOT NULL,
PRIMARY KEY (`id`),
KEY `jobs_queue_index` (`queue`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `links`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `links` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL,
`url` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`note` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`display_order` tinyint NOT NULL DEFAULT '0',
`visible` tinyint(1) NOT NULL DEFAULT '1',
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `market_packages`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `market_packages` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`item_key` varchar(190) COLLATE utf8mb4_unicode_ci NOT NULL,
`item_type` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL,
`title` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`price` bigint unsigned NOT NULL,
`status` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'pending',
`payment_id` bigint unsigned DEFAULT NULL,
`user_id` bigint unsigned DEFAULT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `market_packages_item_key_item_type_unique` (`item_key`,`item_type`),
KEY `market_packages_payment_id_foreign` (`payment_id`),
KEY `market_packages_user_id_foreign` (`user_id`),
CONSTRAINT `market_packages_payment_id_foreign` FOREIGN KEY (`payment_id`) REFERENCES `payments` (`id`) ON DELETE SET NULL,
CONSTRAINT `market_packages_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `media`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `media` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`model_type` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`model_id` bigint unsigned NOT NULL,
`uuid` char(36) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`collection_name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`file_name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`mime_type` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`disk` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`conversions_disk` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`size` bigint unsigned NOT NULL,
`manipulations` json NOT NULL,
`custom_properties` json NOT NULL,
`generated_conversions` json NOT NULL,
`responsive_images` json NOT NULL,
`order_column` int unsigned DEFAULT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `media_uuid_unique` (`uuid`),
KEY `media_model_type_model_id_index` (`model_type`,`model_id`),
KEY `media_order_column_index` (`order_column`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `membership_plans`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `membership_plans` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(60) COLLATE utf8mb4_unicode_ci NOT NULL,
`slug` varchar(60) COLLATE utf8mb4_unicode_ci NOT NULL,
`description` text COLLATE utf8mb4_unicode_ci,
`price` bigint unsigned NOT NULL,
`duration_days` int unsigned NOT NULL DEFAULT '30',
`permissions` json DEFAULT NULL,
`active` tinyint(1) NOT NULL DEFAULT '1',
`sort` int unsigned NOT NULL DEFAULT '0',
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `membership_plans_slug_unique` (`slug`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `migrations`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `migrations` (
`id` int unsigned NOT NULL AUTO_INCREMENT,
`migration` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`batch` int NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `model_has_permissions`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `model_has_permissions` (
`permission_id` bigint unsigned NOT NULL,
`model_type` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`model_id` bigint unsigned NOT NULL,
PRIMARY KEY (`permission_id`,`model_id`,`model_type`),
KEY `model_has_permissions_model_id_model_type_index` (`model_id`,`model_type`),
CONSTRAINT `model_has_permissions_permission_id_foreign` FOREIGN KEY (`permission_id`) REFERENCES `permissions` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `model_has_roles`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `model_has_roles` (
`role_id` bigint unsigned NOT NULL,
`model_type` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`model_id` bigint unsigned NOT NULL,
PRIMARY KEY (`role_id`,`model_id`,`model_type`),
KEY `model_has_roles_model_id_model_type_index` (`model_id`,`model_type`),
CONSTRAINT `model_has_roles_role_id_foreign` FOREIGN KEY (`role_id`) REFERENCES `roles` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `password_reset_tokens`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `password_reset_tokens` (
`email` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`token` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`created_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`email`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `payments`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `payments` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`order_no` varchar(40) COLLATE utf8mb4_unicode_ci NOT NULL,
`user_id` bigint unsigned DEFAULT NULL,
`subject` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`description` varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`amount` bigint unsigned NOT NULL,
`channel` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'alipay',
`status` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'pending',
`gateway_trade_no` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`payload` json DEFAULT NULL,
`paid_at` timestamp NULL DEFAULT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
`payable_type` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`payable_id` bigint unsigned DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `payments_order_no_unique` (`order_no`),
KEY `payments_user_id_status_index` (`user_id`,`status`),
KEY `payments_payable_type_payable_id_index` (`payable_type`,`payable_id`),
CONSTRAINT `payments_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `permissions`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `permissions` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`guard_name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `permissions_name_guard_name_unique` (`name`,`guard_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `personal_access_tokens`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `personal_access_tokens` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`tokenable_type` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`tokenable_id` bigint unsigned NOT NULL,
`name` text COLLATE utf8mb4_unicode_ci NOT NULL,
`token` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL,
`abilities` text COLLATE utf8mb4_unicode_ci,
`last_used_at` timestamp NULL DEFAULT NULL,
`expires_at` timestamp NULL DEFAULT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `personal_access_tokens_token_unique` (`token`),
KEY `personal_access_tokens_tokenable_type_tokenable_id_index` (`tokenable_type`,`tokenable_id`),
KEY `personal_access_tokens_expires_at_index` (`expires_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `plugin_records`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `plugin_records` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`vendor` varchar(60) COLLATE utf8mb4_unicode_ci NOT NULL,
`name` varchar(60) COLLATE utf8mb4_unicode_ci NOT NULL,
`version` varchar(40) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`enabled` tinyint(1) NOT NULL DEFAULT '1',
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `plugin_records_vendor_name_unique` (`vendor`,`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `posts`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `posts` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`category_id` bigint unsigned DEFAULT NULL,
`user_id` bigint unsigned DEFAULT NULL,
`title` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`slug` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`excerpt` text COLLATE utf8mb4_unicode_ci,
`content` longtext COLLATE utf8mb4_unicode_ci NOT NULL,
`content_format` varchar(10) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'markdown',
`keywords` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`status` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'published',
`is_sticky` tinyint(1) NOT NULL DEFAULT '0',
`close_comment` tinyint(1) NOT NULL DEFAULT '0',
`read_password` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`views` bigint unsigned NOT NULL DEFAULT '0',
`comment_count` int unsigned NOT NULL DEFAULT '0',
`meta` json DEFAULT NULL,
`published_at` timestamp NULL DEFAULT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `posts_slug_unique` (`slug`),
KEY `posts_category_id_foreign` (`category_id`),
KEY `posts_user_id_foreign` (`user_id`),
KEY `posts_status_published_at_index` (`status`,`published_at`),
KEY `posts_status_index` (`status`),
KEY `posts_published_at_index` (`published_at`),
CONSTRAINT `posts_category_id_foreign` FOREIGN KEY (`category_id`) REFERENCES `categories` (`id`) ON DELETE SET NULL,
CONSTRAINT `posts_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `redirects`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `redirects` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`hash` varchar(16) COLLATE utf8mb4_unicode_ci NOT NULL,
`target_url` text COLLATE utf8mb4_unicode_ci NOT NULL,
`clicks` bigint unsigned NOT NULL DEFAULT '0',
`status` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'active',
`last_clicked_at` timestamp NULL DEFAULT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `redirects_hash_unique` (`hash`),
KEY `redirects_target_url_index` (`target_url`(191))
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `role_has_permissions`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `role_has_permissions` (
`permission_id` bigint unsigned NOT NULL,
`role_id` bigint unsigned NOT NULL,
PRIMARY KEY (`permission_id`,`role_id`),
KEY `role_has_permissions_role_id_foreign` (`role_id`),
CONSTRAINT `role_has_permissions_permission_id_foreign` FOREIGN KEY (`permission_id`) REFERENCES `permissions` (`id`) ON DELETE CASCADE,
CONSTRAINT `role_has_permissions_role_id_foreign` FOREIGN KEY (`role_id`) REFERENCES `roles` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `roles`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `roles` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`guard_name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `roles_name_guard_name_unique` (`name`,`guard_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `sessions`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `sessions` (
`id` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`user_id` bigint unsigned DEFAULT NULL,
`ip_address` varchar(45) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`user_agent` text COLLATE utf8mb4_unicode_ci,
`payload` longtext COLLATE utf8mb4_unicode_ci NOT NULL,
`last_activity` int NOT NULL,
PRIMARY KEY (`id`),
KEY `sessions_user_id_index` (`user_id`),
KEY `sessions_last_activity_index` (`last_activity`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `settings`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `settings` (
`key` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`value` text COLLATE utf8mb4_unicode_ci NOT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`key`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `subscriptions`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `subscriptions` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`user_id` bigint unsigned NOT NULL,
`membership_plan_id` bigint unsigned DEFAULT NULL,
`status` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'active',
`starts_at` timestamp NULL DEFAULT NULL,
`ends_at` timestamp NULL DEFAULT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `subscriptions_membership_plan_id_foreign` (`membership_plan_id`),
KEY `subscriptions_user_id_status_index` (`user_id`,`status`),
CONSTRAINT `subscriptions_membership_plan_id_foreign` FOREIGN KEY (`membership_plan_id`) REFERENCES `membership_plans` (`id`) ON DELETE SET NULL,
CONSTRAINT `subscriptions_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `taggables`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `taggables` (
`tag_id` bigint unsigned NOT NULL,
`taggable_type` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`taggable_id` bigint unsigned NOT NULL,
UNIQUE KEY `taggables_tag_id_taggable_id_taggable_type_unique` (`tag_id`,`taggable_id`,`taggable_type`),
KEY `taggables_taggable_type_taggable_id_index` (`taggable_type`,`taggable_id`),
CONSTRAINT `taggables_tag_id_foreign` FOREIGN KEY (`tag_id`) REFERENCES `tags` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `tags`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `tags` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`name` json NOT NULL,
`slug` json NOT NULL,
`type` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`order_column` int DEFAULT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `users`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `users` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`email` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`email_verified_at` timestamp NULL DEFAULT NULL,
`password` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`remember_token` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
`legacy_md5` varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`url` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`logincount` int unsigned NOT NULL DEFAULT '0',
`loginip` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`regip` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`logintime` timestamp NULL DEFAULT NULL,
`lastpost_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `users_email_unique` (`email`),
KEY `users_legacy_md5_index` (`legacy_md5`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (1,'0001_01_01_000000_create_users_table',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (2,'0001_01_01_000001_create_cache_table',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (3,'0001_01_01_000002_create_jobs_table',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (4,'2026_08_11_092446_create_permission_tables',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (5,'2026_08_11_092448_create_media_table',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (6,'2026_08_11_092449_create_tag_tables',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (7,'2026_08_11_100000_create_membership_tables',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (8,'2026_08_11_100000_create_payments_table',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (9,'2026_08_11_100001_create_categories_and_posts_table',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (10,'2026_08_11_100002_create_comments_and_trackbacks_table',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (11,'2026_08_11_100003_create_links_and_settings_table',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (12,'2026_08_11_100004_add_blog_columns_to_users_table',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (13,'2026_08_11_100005_add_content_format_to_posts_table',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (14,'2026_08_11_113537_create_personal_access_tokens_table',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (15,'2026_08_12_000000_create_hello_world_logs_table',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (16,'2026_08_12_100000_add_payable_to_payments_table',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (17,'2026_08_12_200000_create_market_packages_table',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (18,'2026_08_12_300000_add_description_to_categories_table',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (19,'2026_08_12_400000_create_redirects_table',1);
@@ -0,0 +1,582 @@
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
DROP TABLE IF EXISTS `cache`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `cache` (
`key` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`value` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL,
`expiration` int NOT NULL,
PRIMARY KEY (`key`),
KEY `cache_expiration_index` (`expiration`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `cache_locks`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `cache_locks` (
`key` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`owner` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`expiration` int NOT NULL,
PRIMARY KEY (`key`),
KEY `cache_locks_expiration_index` (`expiration`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `categories`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `categories` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL,
`slug` varchar(120) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`display_order` tinyint NOT NULL DEFAULT '0',
`description` text COLLATE utf8mb4_unicode_ci,
`cover_media_id` bigint unsigned DEFAULT NULL,
`post_count` int unsigned NOT NULL DEFAULT '0',
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `categories_slug_unique` (`slug`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `comments`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `comments` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`post_id` bigint unsigned NOT NULL,
`user_id` bigint unsigned DEFAULT NULL,
`author_name` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL,
`author_email` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`author_url` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`content` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL,
`ip` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`status` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'pending',
`ai_review` json DEFAULT NULL,
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `comments_user_id_foreign` (`user_id`),
KEY `comments_post_id_status_index` (`post_id`,`status`),
KEY `comments_status_index` (`status`),
CONSTRAINT `comments_post_id_foreign` FOREIGN KEY (`post_id`) REFERENCES `posts` (`id`) ON DELETE CASCADE,
CONSTRAINT `comments_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `failed_jobs`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `failed_jobs` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`uuid` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`connection` text COLLATE utf8mb4_unicode_ci NOT NULL,
`queue` text COLLATE utf8mb4_unicode_ci NOT NULL,
`payload` longtext COLLATE utf8mb4_unicode_ci NOT NULL,
`exception` longtext COLLATE utf8mb4_unicode_ci NOT NULL,
`failed_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `failed_jobs_uuid_unique` (`uuid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `hello_world_logs`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `hello_world_logs` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`comment_id` bigint unsigned DEFAULT NULL,
`note` varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `hello_world_logs_comment_id_foreign` (`comment_id`),
CONSTRAINT `hello_world_logs_comment_id_foreign` FOREIGN KEY (`comment_id`) REFERENCES `comments` (`id`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `job_batches`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `job_batches` (
`id` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`total_jobs` int NOT NULL,
`pending_jobs` int NOT NULL,
`failed_jobs` int NOT NULL,
`failed_job_ids` longtext COLLATE utf8mb4_unicode_ci NOT NULL,
`options` mediumtext COLLATE utf8mb4_unicode_ci,
`cancelled_at` int DEFAULT NULL,
`created_at` int NOT NULL,
`finished_at` int DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `jobs`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `jobs` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`queue` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`payload` longtext COLLATE utf8mb4_unicode_ci NOT NULL,
`attempts` tinyint unsigned NOT NULL,
`reserved_at` int unsigned DEFAULT NULL,
`available_at` int unsigned NOT NULL,
`created_at` int unsigned NOT NULL,
PRIMARY KEY (`id`),
KEY `jobs_queue_index` (`queue`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `links`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `links` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL,
`url` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`note` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`display_order` tinyint NOT NULL DEFAULT '0',
`visible` tinyint(1) NOT NULL DEFAULT '1',
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `market_packages`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `market_packages` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`item_key` varchar(190) COLLATE utf8mb4_unicode_ci NOT NULL,
`item_type` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL,
`title` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`price` bigint unsigned NOT NULL,
`status` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'pending',
`payment_id` bigint unsigned DEFAULT NULL,
`user_id` bigint unsigned DEFAULT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `market_packages_item_key_item_type_unique` (`item_key`,`item_type`),
KEY `market_packages_payment_id_foreign` (`payment_id`),
KEY `market_packages_user_id_foreign` (`user_id`),
CONSTRAINT `market_packages_payment_id_foreign` FOREIGN KEY (`payment_id`) REFERENCES `payments` (`id`) ON DELETE SET NULL,
CONSTRAINT `market_packages_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `media`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `media` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`model_type` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`model_id` bigint unsigned NOT NULL,
`uuid` char(36) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`collection_name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`file_name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`mime_type` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`disk` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`conversions_disk` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`size` bigint unsigned NOT NULL,
`manipulations` json NOT NULL,
`custom_properties` json NOT NULL,
`generated_conversions` json NOT NULL,
`responsive_images` json NOT NULL,
`order_column` int unsigned DEFAULT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `media_uuid_unique` (`uuid`),
KEY `media_model_type_model_id_index` (`model_type`,`model_id`),
KEY `media_order_column_index` (`order_column`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `media_folders`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `media_folders` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`parent_id` bigint unsigned DEFAULT NULL,
`code` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`sort` int NOT NULL DEFAULT '0',
`is_active` tinyint(1) NOT NULL DEFAULT '1',
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `media_folders_code_unique` (`code`),
KEY `media_folders_parent_id_foreign` (`parent_id`),
CONSTRAINT `media_folders_parent_id_foreign` FOREIGN KEY (`parent_id`) REFERENCES `media_folders` (`id`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `media_library`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `media_library` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`user_id` bigint unsigned DEFAULT NULL,
`disk` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'public',
`path` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`original_name` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`extension` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`mime_type` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`size` bigint unsigned NOT NULL DEFAULT '0',
`type` varchar(30) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'image',
`source` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'admin',
`folder` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`alt_text` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`custom_properties` json DEFAULT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
`deleted_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `media_library_path_unique` (`path`),
KEY `media_library_user_id_foreign` (`user_id`),
KEY `media_library_type_index` (`type`),
KEY `media_library_folder_index` (`folder`),
KEY `media_library_source_index` (`source`),
CONSTRAINT `media_library_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `membership_plans`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `membership_plans` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(60) COLLATE utf8mb4_unicode_ci NOT NULL,
`slug` varchar(60) COLLATE utf8mb4_unicode_ci NOT NULL,
`description` text COLLATE utf8mb4_unicode_ci,
`price` bigint unsigned NOT NULL,
`duration_days` int unsigned NOT NULL DEFAULT '30',
`permissions` json DEFAULT NULL,
`active` tinyint(1) NOT NULL DEFAULT '1',
`sort` int unsigned NOT NULL DEFAULT '0',
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `membership_plans_slug_unique` (`slug`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `migrations`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `migrations` (
`id` int unsigned NOT NULL AUTO_INCREMENT,
`migration` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`batch` int NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `model_has_permissions`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `model_has_permissions` (
`permission_id` bigint unsigned NOT NULL,
`model_type` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`model_id` bigint unsigned NOT NULL,
PRIMARY KEY (`permission_id`,`model_id`,`model_type`),
KEY `model_has_permissions_model_id_model_type_index` (`model_id`,`model_type`),
CONSTRAINT `model_has_permissions_permission_id_foreign` FOREIGN KEY (`permission_id`) REFERENCES `permissions` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `model_has_roles`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `model_has_roles` (
`role_id` bigint unsigned NOT NULL,
`model_type` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`model_id` bigint unsigned NOT NULL,
PRIMARY KEY (`role_id`,`model_id`,`model_type`),
KEY `model_has_roles_model_id_model_type_index` (`model_id`,`model_type`),
CONSTRAINT `model_has_roles_role_id_foreign` FOREIGN KEY (`role_id`) REFERENCES `roles` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `password_reset_tokens`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `password_reset_tokens` (
`email` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`token` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`created_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`email`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `payments`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `payments` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`order_no` varchar(40) COLLATE utf8mb4_unicode_ci NOT NULL,
`user_id` bigint unsigned DEFAULT NULL,
`subject` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`description` varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`amount` bigint unsigned NOT NULL,
`channel` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'alipay',
`status` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'pending',
`gateway_trade_no` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`payload` json DEFAULT NULL,
`paid_at` timestamp NULL DEFAULT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
`payable_type` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`payable_id` bigint unsigned DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `payments_order_no_unique` (`order_no`),
KEY `payments_user_id_status_index` (`user_id`,`status`),
KEY `payments_payable_type_payable_id_index` (`payable_type`,`payable_id`),
CONSTRAINT `payments_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `permissions`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `permissions` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`guard_name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `permissions_name_guard_name_unique` (`name`,`guard_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `personal_access_tokens`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `personal_access_tokens` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`tokenable_type` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`tokenable_id` bigint unsigned NOT NULL,
`name` text COLLATE utf8mb4_unicode_ci NOT NULL,
`token` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL,
`abilities` text COLLATE utf8mb4_unicode_ci,
`last_used_at` timestamp NULL DEFAULT NULL,
`expires_at` timestamp NULL DEFAULT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `personal_access_tokens_token_unique` (`token`),
KEY `personal_access_tokens_tokenable_type_tokenable_id_index` (`tokenable_type`,`tokenable_id`),
KEY `personal_access_tokens_expires_at_index` (`expires_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `plugin_records`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `plugin_records` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`vendor` varchar(60) COLLATE utf8mb4_unicode_ci NOT NULL,
`name` varchar(60) COLLATE utf8mb4_unicode_ci NOT NULL,
`version` varchar(40) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`enabled` tinyint(1) NOT NULL DEFAULT '1',
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `plugin_records_vendor_name_unique` (`vendor`,`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `posts`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `posts` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`category_id` bigint unsigned DEFAULT NULL,
`user_id` bigint unsigned DEFAULT NULL,
`title` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`slug` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`excerpt` text COLLATE utf8mb4_unicode_ci,
`content` longtext COLLATE utf8mb4_unicode_ci NOT NULL,
`content_format` varchar(10) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'markdown',
`keywords` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`cover_media_id` bigint unsigned DEFAULT NULL,
`status` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'published',
`is_sticky` tinyint(1) NOT NULL DEFAULT '0',
`close_comment` tinyint(1) NOT NULL DEFAULT '0',
`read_password` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`views` bigint unsigned NOT NULL DEFAULT '0',
`comment_count` int unsigned NOT NULL DEFAULT '0',
`meta` json DEFAULT NULL,
`published_at` timestamp NULL DEFAULT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `posts_slug_unique` (`slug`),
KEY `posts_category_id_foreign` (`category_id`),
KEY `posts_user_id_foreign` (`user_id`),
KEY `posts_status_published_at_index` (`status`,`published_at`),
KEY `posts_status_index` (`status`),
KEY `posts_published_at_index` (`published_at`),
CONSTRAINT `posts_category_id_foreign` FOREIGN KEY (`category_id`) REFERENCES `categories` (`id`) ON DELETE SET NULL,
CONSTRAINT `posts_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `redirects`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `redirects` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`hash` varchar(16) COLLATE utf8mb4_unicode_ci NOT NULL,
`target_url` text COLLATE utf8mb4_unicode_ci NOT NULL,
`clicks` bigint unsigned NOT NULL DEFAULT '0',
`status` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'active',
`last_clicked_at` timestamp NULL DEFAULT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `redirects_hash_unique` (`hash`),
KEY `redirects_target_url_index` (`target_url`(191))
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `role_has_permissions`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `role_has_permissions` (
`permission_id` bigint unsigned NOT NULL,
`role_id` bigint unsigned NOT NULL,
PRIMARY KEY (`permission_id`,`role_id`),
KEY `role_has_permissions_role_id_foreign` (`role_id`),
CONSTRAINT `role_has_permissions_permission_id_foreign` FOREIGN KEY (`permission_id`) REFERENCES `permissions` (`id`) ON DELETE CASCADE,
CONSTRAINT `role_has_permissions_role_id_foreign` FOREIGN KEY (`role_id`) REFERENCES `roles` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `roles`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `roles` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`guard_name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `roles_name_guard_name_unique` (`name`,`guard_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `sessions`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `sessions` (
`id` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`user_id` bigint unsigned DEFAULT NULL,
`ip_address` varchar(45) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`user_agent` text COLLATE utf8mb4_unicode_ci,
`payload` longtext COLLATE utf8mb4_unicode_ci NOT NULL,
`last_activity` int NOT NULL,
PRIMARY KEY (`id`),
KEY `sessions_user_id_index` (`user_id`),
KEY `sessions_last_activity_index` (`last_activity`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `settings`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `settings` (
`key` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`value` text COLLATE utf8mb4_unicode_ci NOT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`key`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `subscriptions`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `subscriptions` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`user_id` bigint unsigned NOT NULL,
`membership_plan_id` bigint unsigned DEFAULT NULL,
`status` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'active',
`starts_at` timestamp NULL DEFAULT NULL,
`ends_at` timestamp NULL DEFAULT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `subscriptions_membership_plan_id_foreign` (`membership_plan_id`),
KEY `subscriptions_user_id_status_index` (`user_id`,`status`),
CONSTRAINT `subscriptions_membership_plan_id_foreign` FOREIGN KEY (`membership_plan_id`) REFERENCES `membership_plans` (`id`) ON DELETE SET NULL,
CONSTRAINT `subscriptions_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `taggables`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `taggables` (
`tag_id` bigint unsigned NOT NULL,
`taggable_type` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`taggable_id` bigint unsigned NOT NULL,
UNIQUE KEY `taggables_tag_id_taggable_id_taggable_type_unique` (`tag_id`,`taggable_id`,`taggable_type`),
KEY `taggables_taggable_type_taggable_id_index` (`taggable_type`,`taggable_id`),
CONSTRAINT `taggables_tag_id_foreign` FOREIGN KEY (`tag_id`) REFERENCES `tags` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `tags`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `tags` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`name` json NOT NULL,
`slug` json NOT NULL,
`type` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`order_column` int DEFAULT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `users`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `users` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`email` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`email_verified_at` timestamp NULL DEFAULT NULL,
`password` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`remember_token` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
`legacy_md5` varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`url` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`logincount` int unsigned NOT NULL DEFAULT '0',
`loginip` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`regip` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`logintime` timestamp NULL DEFAULT NULL,
`lastpost_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `users_email_unique` (`email`),
KEY `users_legacy_md5_index` (`legacy_md5`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (1,'0001_01_01_000000_create_users_table',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (2,'0001_01_01_000001_create_cache_table',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (3,'0001_01_01_000002_create_jobs_table',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (4,'2026_08_11_092446_create_permission_tables',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (5,'2026_08_11_092448_create_media_table',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (6,'2026_08_11_092449_create_tag_tables',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (7,'2026_08_11_100000_create_membership_tables',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (8,'2026_08_11_100000_create_payments_table',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (9,'2026_08_11_100001_create_categories_and_posts_table',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (10,'2026_08_11_100002_create_comments_and_trackbacks_table',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (11,'2026_08_11_100003_create_links_and_settings_table',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (12,'2026_08_11_100004_add_blog_columns_to_users_table',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (13,'2026_08_11_100005_add_content_format_to_posts_table',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (14,'2026_08_11_113537_create_personal_access_tokens_table',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (15,'2026_08_12_000000_create_hello_world_logs_table',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (16,'2026_08_12_100000_add_payable_to_payments_table',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (17,'2026_08_12_200000_create_market_packages_table',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (18,'2026_08_12_300000_add_description_to_categories_table',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (19,'2026_08_12_400000_create_redirects_table',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (20,'2026_08_15_000001_create_media_library_table',2);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (21,'2026_08_15_000002_create_media_folders_table',2);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (22,'2026_08_15_000003_add_soft_deletes_to_media_library_table',2);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (23,'2026_08_15_000004_add_custom_properties_to_media_library_table',2);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (24,'2026_09_16_000001_add_cover_media_id_to_posts_and_categories',2);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (25,'2026_09_16_000002_seed_default_media_folders',2);
+32 -31
View File
@@ -24,39 +24,40 @@ php artisan sablog:import \
### 参数说明
| 参数 | 默认 | 说明 |
|------|------|------|
| `--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` | 关 | 清空目标表后重新导入 |
| 参数 | 默认 | 说明 |
| --------------------------- | ---------------- | ------------------------------------------- |
| `--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 文本解析为独立标签 + 关联 |
| 老表 | 新表 | 说明 |
| ------------- | -------------------- | ---------------------------------------------------------------- |
| `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 保留字符) |
| 老内容 | 导入后 |
| ----------------------------------- | --------------------------------------------------------------------------------------------- |
| HTML 正文 `[attach=12]` | 原样保留,**渲染时按 legacy id 解析**为附件链接/图片 |
| Markdown 正文(--convert-markdown) | 自动转成 `{{attach:12}}` 令牌(`[]` 是 Markdown 保留字符) |
| 新写作(媒体库) | 写入 `![alt](media:ID)` / `[name](media:ID)`,渲染时换成媒体库 URL(见 [media.md](media.md)) |
两种格式都能正常显示老附件,无需手工替换。
@@ -105,9 +106,9 @@ php artisan content:convert --all --dry-run # 先预览
## 常见问题
| 现象 | 处理 |
|------|------|
| 报「找不到老表」 | 检查 `--prefix`(老站表前缀可能不是 `sablog_`)与老库名 |
| 附件显示不出来 | 确认 `--attachments-dir` 路径正确;看 `attachments:sync-s3` 输出报告 |
| 老密码登录失败 | 老库密码若是自定义加密(非 MD5),需要按 `User::verifyPassword` 扩展 |
| 中文乱码 | 老库导出时用 utf8mb4;`--driver=sqlite` 只用于测试 |
| 现象 | 处理 |
| ---------------- | -------------------------------------------------------------------- |
| 报「找不到老表」 | 检查 `--prefix`(老站表前缀可能不是 `sablog_`)与老库名 |
| 附件显示不出来 | 确认 `--attachments-dir` 路径正确;看 `attachments:sync-s3` 输出报告 |
| 老密码登录失败 | 老库密码若是自定义加密(非 MD5),需要按 `User::verifyPassword` 扩展 |
| 中文乱码 | 老库导出时用 utf8mb4;`--driver=sqlite` 只用于测试 |
+15 -14
View File
@@ -4,12 +4,12 @@
## 环境要求
| 组件 | 要求 |
|------|------|
| PHP | **8.2+**(需 pcntl 扩展用于 Workerman;GD/Imagick 用于缩略图) |
| MySQL | **8.x** |
| Composer | 2.x |
| 可选 | Node.js(前台资源构建)、S3 兼容存储(R2/COS/OSS/MinIO)、OpenAI 兼容 LLM |
| 组件 | 要求 |
| -------- | ------------------------------------------------------------------------- |
| PHP | **8.2+**(需 pcntl 扩展用于 Workerman;GD/Imagick 用于缩略图) |
| MySQL | **8.x** |
| Composer | 2.x |
| 可选 | Node.js(前台资源构建)、S3 兼容存储(R2/COS/OSS/MinIO)、OpenAI 兼容 LLM |
## 1. 获取代码与依赖
@@ -33,6 +33,7 @@ DB_PASSWORD=
# 以下为可选能力(不配也能跑,见注释):
# S3_* 附件存储(不配置回退本地 public)
# MEDIA_DISK=public 新媒体库磁盘(正文写 media:ID,见 docs/media.md)
# LLM_* AI 评论审核/润色
# MARKET_* 插件/主题远程市场
```
@@ -84,11 +85,11 @@ php artisan queue:work --queue=default,ai
## 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_*` 键与桶权限 |
| 插件停用后功能消失 | 属预期:付费列/外链中转等由插件注入,后台「插件管理」重新启用即可 |
| 现象 | 处理 |
| ------------------ | ------------------------------------------------------------------------------------------------------------- |
| 页面 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_*` 键与桶权限 |
| 插件停用后功能消失 | 属预期:付费列/外链中转等由插件注入,后台「插件管理」重新启用即可 |
+32
View File
@@ -0,0 +1,32 @@
# Media library (xpier)
New uploads, covers, and in-body images/files use `xpier/filament-media-library`. Sablog Spatie media / `attachments` rows stay for import and legacy shortcodes.
## Markdown body (do not paste CDN URLs)
The editor writes a **stable reference**. The public URL is resolved only when the page is rendered.
| Format | Image | File |
| -------- | ------------------ | ----------------------- |
| Markdown | `![alt](media:12)` | `[notes.pdf](media:12)` |
| HTML | `[media=12]` | `[media=12]` |
Admin flow: pick a file in **Insert from media library**, then click **Insert into body**. That appends the snippet above. Do not copy object-storage / CDN addresses into the article.
Legacy sablog tokens are unchanged: `[attach=ID]`, `{{attach:ID}}`, `![alt](attach:ID)`.
## Admin
- Library: `/admin/media-library`
- Folders: `/admin/media-folders` (defaults: `general`, `articles`, `banners`)
- Cover: `MediaPicker` bound to `coverMedia` / `cover_media_id`
## Config
```env
MEDIA_DISK=public
MEDIA_DIRECTORY=media
MEDIA_VISIBILITY=public
```
Run `php artisan storage:link` when using the `public` disk locally.
+2561
View File
File diff suppressed because it is too large Load Diff
+1
View File
@@ -32,5 +32,6 @@
<env name="PULSE_ENABLED" value="false"/>
<env name="TELESCOPE_ENABLED" value="false"/>
<env name="NIGHTWATCH_ENABLED" value="false"/>
<env name="MEDIA_DISK" value="public"/>
</php>
</phpunit>
@@ -84,8 +84,8 @@ class SeoMiddleware
'mainEntityOfPage' => route('posts.show', $post->slug ?: $post->id),
];
if ($cover = $post->getFirstMedia('cover')) {
$data['image'] = [$cover->getUrl('card') ?: $cover->getUrl()];
if ($cover = $post->coverImageUrl()) {
$data['image'] = [$cover];
}
return $data;
@@ -24,10 +24,14 @@ return new class extends Migration
$table->string('status', 20)->default('active'); // active / disabled
$table->timestamp('last_clicked_at')->nullable();
$table->timestamps();
// MySQL 5.7 不支持对 TEXT 建全值索引,用前缀索引
$table->index([DB::raw('target_url(191)')], 'redirects_target_url_index');
});
// MySQL 5.7 cannot index TEXT fully; SQLite does not support prefix indexes.
if (Schema::getConnection()->getDriverName() === 'mysql') {
Schema::table('redirects', function (Blueprint $table): void {
$table->index([DB::raw('target_url(191)')], 'redirects_target_url_index');
});
}
}
public function down(): void
+3 -3
View File
@@ -9,10 +9,10 @@
<main class="content">
<h1 class="list-title">{{ $archiveTitle ?? ($category->name ?? '文章列表') }}</h1>
@if(isset($category) && ($category->description || $category->getFirstMedia('cover')))
@if(isset($category) && ($category->description || $category->coverImageUrl()))
<div class="category-intro">
@if($cover = $category->getFirstMedia('cover'))
<img src="{{ $cover->getUrl() }}" alt="{{ $category->name }}" class="category-cover">
@if($url = $category->coverImageUrl())
<img src="{{ $url }}" alt="{{ $category->name }}" class="category-cover">
@endif
<div>
@if($category->description)
+2 -2
View File
@@ -7,9 +7,9 @@
@endphp
<div class="paywall" role="note" aria-label="付费内容">
@if($cover = $post->getFirstMedia('cover'))
@if($url = $post->coverImageUrl())
<div class="paywall-cover">
<img src="{{ $cover->getUrl('card') ?: $cover->getUrl() }}" alt="{{ $post->title }}">
<img src="{{ $url }}" alt="{{ $post->title }}">
</div>
@endif
+2 -2
View File
@@ -1,7 +1,7 @@
<article class="post-card {{ $post->is_sticky ? 'sticky' : '' }}">
@if($cover = $post->getFirstMedia('cover'))
@if($url = $post->coverImageUrl())
<a href="{{ route('posts.show', $post->slug ?: $post->id) }}" class="post-cover">
<img src="{{ $cover->getUrl('card') ?: $cover->getUrl() }}" alt="{{ $post->title }}" loading="lazy">
<img src="{{ $url }}" alt="{{ $post->title }}" loading="lazy">
</a>
@endif
<h2 class="post-title">
+2 -2
View File
@@ -30,9 +30,9 @@
</details>
</nav>
@endif
@if($cover = $post->getFirstMedia('cover'))
@if($url = $post->coverImageUrl())
<div class="post-cover">
<img src="{{ $cover->getUrl('card') ?: $cover->getUrl() }}" alt="{{ $post->title }}">
<img src="{{ $url }}" alt="{{ $post->title }}">
</div>
@endif
<h1 class="post-title">{{ $post->title }}</h1>
+9
View File
@@ -50,6 +50,15 @@ class AdminPagesTest extends TestCase
$this->actingAs($this->admin)->get('/admin/media')->assertOk();
}
public function test_media_library_resource_loads(): void
{
$this->actingAs($this->admin)->get('/admin/media-library')->assertOk();
$this->actingAs($this->admin)->get('/admin/media-folders')->assertOk();
$this->actingAs($this->admin)->get('/admin/posts/create')
->assertOk()
->assertSee('media:ID', false);
}
public function test_plugin_and_payment_pages_load(): void
{
$this->actingAs($this->admin)->get('/admin/plugins')->assertOk();
+113
View File
@@ -0,0 +1,113 @@
<?php
declare(strict_types=1);
namespace Tests\Feature;
use App\Blog\Services\PostContentRenderer;
use App\Models\Post;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
use Xpier\FilamentMediaLibrary\Models\MediaLibrary;
class MarkdownMediaLibraryTest extends TestCase
{
use RefreshDatabase;
public function test_markdown_body_renders_media_protocol_to_image_without_storing_url(): void
{
$media = $this->createLibraryImage(12, 'Hero shot');
$post = Post::query()->create([
'user_id' => User::factory()->create()->id,
'title' => 'Media embed post',
'slug' => 'media-embed-post',
'content' => "Intro\n\n![Hero shot](media:{$media->id})\n",
'content_format' => 'markdown',
'status' => 'published',
'published_at' => now()->subMinute(),
]);
$html = app(PostContentRenderer::class)->render($post);
$this->assertStringContainsString((string) $media->url, $html);
$this->assertStringContainsString('<img', $html);
$this->assertStringNotContainsString('media:'.$media->id, $html);
$this->assertStringContainsString("![Hero shot](media:{$media->id})", (string) $post->fresh()->content);
}
public function test_html_body_hydrates_media_shortcode(): void
{
$media = $this->createLibraryImage(21, 'Inline');
$post = Post::query()->create([
'title' => 'HTML media post',
'content' => '<p>see [media='.$media->id.']</p>',
'content_format' => 'html',
'status' => 'published',
'published_at' => now()->subMinute(),
]);
$html = app(PostContentRenderer::class)->render($post);
$this->assertStringContainsString((string) $media->url, $html);
$this->assertStringNotContainsString('[media='.$media->id.']', $html);
}
public function test_published_post_page_shows_library_image(): void
{
$media = $this->createLibraryImage(33, 'Cover art');
$post = Post::query()->create([
'title' => 'Visible media post',
'slug' => 'visible-media-post',
'content' => "![Cover art](media:{$media->id})",
'content_format' => 'markdown',
'status' => 'published',
'published_at' => now()->subMinute(),
'cover_media_id' => $media->id,
]);
$this->get('/posts/'.$post->slug.'.shtml')
->assertOk()
->assertSee((string) $media->url, false)
->assertDontSee('media:'.$media->id, false);
$this->assertSame((string) $media->url, $post->fresh()->coverImageUrl());
}
public function test_missing_media_reference_does_not_crash_render(): void
{
$post = Post::query()->create([
'title' => 'Broken media post',
'content' => '![gone](media:99999)',
'content_format' => 'markdown',
'status' => 'published',
'published_at' => now()->subMinute(),
]);
$html = app(PostContentRenderer::class)->render($post);
$this->assertIsString($html);
$this->assertStringContainsString('media:99999', $html);
}
private function createLibraryImage(int $id, string $alt): MediaLibrary
{
$media = new MediaLibrary;
$media->forceFill([
'id' => $id,
'disk' => 'public',
'path' => 'media/library/articles/image/test-'.$id.'.jpg',
'original_name' => 'test-'.$id.'.jpg',
'extension' => 'jpg',
'mime_type' => 'image/jpeg',
'size' => 128,
'type' => MediaLibrary::TYPE_IMAGE,
'source' => MediaLibrary::SOURCE_ADMIN,
'folder' => 'articles',
'alt_text' => $alt,
]);
$media->save();
return $media->fresh();
}
}
+57
View File
@@ -0,0 +1,57 @@
<?php
declare(strict_types=1);
namespace Tests\Unit;
use App\Blog\Support\MarkdownMediaEmbed;
use Tests\TestCase;
use Xpier\FilamentMediaLibrary\Models\MediaLibrary;
class MarkdownMediaEmbedTest extends TestCase
{
public function test_markdown_image_snippet_uses_media_protocol_not_url(): void
{
$media = new MediaLibrary([
'alt_text' => 'Hero shot',
'type' => MediaLibrary::TYPE_IMAGE,
'mime_type' => 'image/jpeg',
'original_name' => 'hero.jpg',
'path' => 'media/library/articles/hero.jpg',
]);
$media->id = 12;
$snippet = app(MarkdownMediaEmbed::class)->snippet($media, 'markdown');
$this->assertSame('![Hero shot](media:12)', $snippet);
$this->assertStringNotContainsString('http', $snippet);
$this->assertStringNotContainsString('/storage/', $snippet);
}
public function test_markdown_file_snippet_is_a_link_reference(): void
{
$media = new MediaLibrary([
'alt_text' => '',
'type' => MediaLibrary::TYPE_FILE,
'mime_type' => 'application/pdf',
'original_name' => 'notes.pdf',
'path' => 'media/library/articles/notes.pdf',
]);
$media->id = 8;
$this->assertSame('[notes.pdf](media:8)', app(MarkdownMediaEmbed::class)->snippet($media, 'markdown'));
}
public function test_html_snippet_uses_shortcode(): void
{
$media = new MediaLibrary([
'type' => MediaLibrary::TYPE_IMAGE,
'mime_type' => 'image/png',
'original_name' => 'pic.png',
'path' => 'pic.png',
]);
$media->id = 3;
$this->assertSame('[media=3]', app(MarkdownMediaEmbed::class)->snippet($media, 'html'));
}
}
+3 -3
View File
@@ -9,10 +9,10 @@
<main id="content">
<h1 class="list-title">{{ $archiveTitle ?? ($category->name ?? '文章列表') }}</h1>
@if(isset($category) && ($category->description || $category->getFirstMedia('cover')))
@if(isset($category) && ($category->description || $category->coverImageUrl()))
<div class="category-intro">
@if($cover = $category->getFirstMedia('cover'))
<img src="{{ $cover->getUrl() }}" alt="{{ $category->name }}" class="category-cover">
@if($url = $category->coverImageUrl())
<img src="{{ $url }}" alt="{{ $category->name }}" class="category-cover">
@endif
<div>
@if($category->description)
+2 -2
View File
@@ -30,8 +30,8 @@
</details>
</nav>
@endif
@if($cover = $post->getFirstMedia('cover'))
<div class="post-cover"><img src="{{ $cover->getUrl('card') ?: $cover->getUrl() }}" alt="{{ $post->title }}"></div>
@if($url = $post->coverImageUrl())
<div class="post-cover"><img src="{{ $url }}" alt="{{ $post->title }}"></div>
@endif
<h1 class="post-title">{{ $post->title }}</h1>
<div class="post-meta">