From 36c4ffc5b45fa527755d938b72ae89e0936e2ab2 Mon Sep 17 00:00:00 2001 From: gouki Date: Fri, 18 Sep 2026 21:48:24 +0000 Subject: [PATCH] fix: restyle pager chips and restore theme ad slots Replace the loose page-count label with a SaBlog-style 6 / 316 chip, wire inject/stylevar HTML into list and post views, and flush page cache when ads are saved. Co-authored-by: Cursor --- CHANGELOG.md | 3 + Fetures.md | 9 +- README.md | 2 +- app/Blog/Support/ThemeInjector.php | 63 ++ app/Console/Commands/SablogImport.php | 31 +- app/Filament/Pages/ThemeInject.php | 1 + .../database/mysql-schema-20260918-214145.sql | 584 ++++++++++++++++++ docs/development.md | 1 + docs/import.md | 1 + docs/themes.md | 70 ++- public/css/blog.css | 25 +- public/themes/modern/style.css | 23 +- public/themes/sablog/style.css | 47 +- resources/views/archives.blade.php | 2 + resources/views/index.blade.php | 2 + resources/views/list.blade.php | 2 + resources/views/partials/footer.blade.php | 2 +- resources/views/partials/header.blade.php | 2 +- .../views/partials/pagination-links.blade.php | 2 +- resources/views/partials/sidebar.blade.php | 6 +- .../views/partials/theme-inject.blade.php | 6 + resources/views/search.blade.php | 2 + resources/views/show.blade.php | 3 +- resources/views/tag.blade.php | 2 + tests/Feature/HomeViewModeTest.php | 4 +- tests/Feature/SablogImportTest.php | 3 + tests/Feature/ThemeInjectTest.php | 21 +- themes/demo/views/partials/footer.blade.php | 2 +- themes/demo/views/partials/header.blade.php | 2 +- themes/modern/assets/style.css | 23 +- themes/modern/views/partials/footer.blade.php | 2 +- themes/modern/views/partials/header.blade.php | 2 +- themes/sablog/assets/style.css | 47 +- themes/sablog/views/index.blade.php | 2 + themes/sablog/views/list.blade.php | 2 + themes/sablog/views/partials/footer.blade.php | 2 +- themes/sablog/views/partials/header.blade.php | 2 +- .../sablog/views/partials/sidebar.blade.php | 4 +- themes/sablog/views/search.blade.php | 2 + themes/sablog/views/show.blade.php | 4 +- themes/sablog/views/tag.blade.php | 2 + 41 files changed, 936 insertions(+), 81 deletions(-) create mode 100644 docs/database/mysql-schema-20260918-214145.sql create mode 100644 resources/views/partials/theme-inject.blade.php diff --git a/CHANGELOG.md b/CHANGELOG.md index e39c017..b752d37 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ ## 2026-09-18 +- Pager chip is now `6 / 316` in the old SaBlog bar style, not a loose “第 x / y 页” label. +- Theme inject stacks cover list/content/footer; stylevars and leftover HTML ads render. Saving inject flushes page cache. `sablog:import` copies `stylevars`. +- Schema snapshot: `docs/database/mysql-schema-20260918-214145.sql`. - Homepage / lists have SaBlog-style **标准 | 列表** browse modes (`home_view_mode`, `?view=`, cookie). - Pagination is compact (`第 x / y 页` + fewer page chips) and the content column uses CSS grid so it no longer sits under the sidebar. - Imported SaBlog HTML no longer shows literal ` `; highlighter / stacked docker `
` blocks become wrapped `
`.
diff --git a/Fetures.md b/Fetures.md
index 2d93974..dd93cb7 100644
--- a/Fetures.md
+++ b/Fetures.md
@@ -61,11 +61,18 @@ See `README.md` and `docs/` for install, import, plugins, and themes.
 
 ## Pagination and imported HTML
 
-- Public pagers use `onEachSide(1)` plus “第 x / y 页”, and the main column is CSS grid `minmax(0, 1fr)` so page chips stay in the content column.
+- Public pagers use `onEachSide(1)` plus a compact `6 / 316` chip (old SaBlog `.p_bar` look), and the main column is CSS grid `minmax(0, 1fr)` so page chips stay in the content column.
 - `SablogHtmlNormalizer` strips leftover ` `, turns SyntaxHighlighter `codeText` / `dp-*` lists into `
`, and wraps stacked docker/shell `
` lines. - Render path always normalizes, so already-imported posts look right without rewriting the database. Persist with `php artisan sablog:fix-content --all`. - Code blocks wrap / scroll inside the column instead of stretching the page. +## Theme ads / inject + +- All list and post views include `content_top` / `content_bottom` (and post body) stacks. Saving **主题注入** flushes page cache. +- Theme variables (old stylevars) render automatically: `ad_header` → header, leftover HTML vars such as `huangjinlian` → footer. +- `sablog:import` now copies the old `stylevars` table into `theme_vars_{theme}`. +- Do not add a third half-finished skin. Tighten `sablog` to match the old site; a new theme is a later, complete job. + ## Media library (xpier) - Package: `xpier/filament-media-library` (admin Media Library + MediaPicker). diff --git a/README.md b/README.md index 00ac229..015f67f 100644 --- a/README.md +++ b/README.md @@ -67,7 +67,7 @@ php artisan sablog:fix-content --all S3_ACCESS_KEY=... S3_BUCKET=... S3_ENDPOINT=... php artisan attachments:sync-s3 --legacy-dir=/path/to/old/attachments ``` -> 不迁移的过时功能:trackback(引用通告,垃圾来源)、搜索缓存表、验证码(由 AI 审核 + 蜜罐 + 频控取代)、WAP、广告位变量。 +> 不迁移的过时功能:trackback(引用通告,垃圾来源)、搜索缓存表、验证码(由 AI 审核 + 蜜罐 + 频控取代)、WAP。老广告位 `stylevars` 会导入为当前主题变量并在对应注入点显示。 ## 主题 diff --git a/app/Blog/Support/ThemeInjector.php b/app/Blog/Support/ThemeInjector.php index 5d72139..4cdf460 100644 --- a/app/Blog/Support/ThemeInjector.php +++ b/app/Blog/Support/ThemeInjector.php @@ -34,6 +34,24 @@ class ThemeInjector 'body_end' => '页面末尾( 前)', ]; + /** + * Old SaBlog stylevar names → inject points. Unmapped HTML vars go to footer. + * + * @var array> + */ + public const STYLEVAR_POINTS = [ + 'head' => ['ad_head', 'headad'], + 'header' => ['headerad', 'header_ad', 'ad_header', 'topad', 'ad_top', 'sitead', 'site_ad'], + 'sidebar_top' => ['sidebarad', 'sidebar_ad', 'ad_sidebar', 'leftad', 'ad_left'], + 'sidebar_bottom' => ['ad_sidebar_bottom'], + 'content_top' => ['indexad', 'ad_index', 'contentad', 'ad_content'], + 'content_bottom' => ['ad_content_bottom'], + 'post_content_top' => ['ad_post_top', 'insad'], + 'post_content_bottom' => ['ad_post_bottom'], + 'footer' => ['footerad', 'footer_ad', 'ad_footer', 'huangjinlian', 'huangjinlian2', 'huangjinlian3'], + 'body_end' => ['ad_body_end'], + ]; + /** * 读取某一注入点的所有注入块(多条,已排序、已过滤停用), * 并把插件追加的 HTML 合并进来。 @@ -51,6 +69,10 @@ class ThemeInjector ->values() ->all(); + foreach (self::stylevarBlocks($point) as $html) { + $blocks[] = $html; + } + $extra = (string) app(PluginManager::class)->applyFilters("theme.inject.{$point}", ''); if ($extra !== '') { @@ -75,4 +97,45 @@ class ThemeInjector } }); } + + /** + * @return list + */ + private static function stylevarBlocks(string $point): array + { + $vars = app(ThemeManager::class)->variables(); + if ($vars === []) { + return []; + } + + $html = []; + foreach (self::STYLEVAR_POINTS[$point] ?? [] as $key) { + $value = trim((string) ($vars[$key] ?? '')); + if ($value !== '') { + $html[] = $value; + } + } + + if ($point !== 'footer') { + return $html; + } + + $mapped = []; + foreach (self::STYLEVAR_POINTS as $keys) { + foreach ($keys as $key) { + $mapped[$key] = true; + } + } + + foreach ($vars as $key => $value) { + $value = trim((string) $value); + if ($value === '' || isset($mapped[$key]) || ! str_contains($value, '<')) { + continue; + } + + $html[] = $value; + } + + return $html; + } } diff --git a/app/Console/Commands/SablogImport.php b/app/Console/Commands/SablogImport.php index c4ee7f2..32d3b10 100644 --- a/app/Console/Commands/SablogImport.php +++ b/app/Console/Commands/SablogImport.php @@ -59,6 +59,7 @@ class SablogImport extends Command } $this->importSettings(); + $this->importStylevars(); $this->importCategories(); $this->importUsers(); $this->importPosts(); @@ -144,7 +145,7 @@ class SablogImport extends Command * searchindex —— 搜索缓存,现代 SQL 全文检索足够 * sessions —— Laravel 原生会话 * statistics —— 实时统计 - * stylevars —— 老式广告位变量 + * stylevars —— 导入为当前主题的 theme_vars(广告位) * seccode / 验证码 —— 由 AI 审核 + 蜜罐 + 频控取代 */ private function importSettings(): void @@ -182,6 +183,34 @@ class SablogImport extends Command $this->report['settings'] = $count; } + private function importStylevars(): void + { + if (! $this->hasTable('stylevars')) { + return; + } + + $vars = []; + foreach ($this->rows('stylevars') as $row) { + $name = (string) ($row['title'] ?? $row['variable'] ?? $row['name'] ?? ''); + $value = (string) ($row['value'] ?? $row['content'] ?? ''); + if ($name === '' || $value === '') { + continue; + } + $vars[$name] = $value; + } + + if ($vars === []) { + return; + } + + $theme = (string) Setting::get('active_theme', 'sablog'); + $existing = Setting::get('theme_vars_'.$theme, []); + $existing = is_array($existing) ? $existing : (json_decode((string) $existing, true) ?: []); + + Setting::set('theme_vars_'.$theme, array_merge($existing, $vars)); + $this->report['stylevars'] = count($vars); + } + private function importCategories(): void { $count = 0; diff --git a/app/Filament/Pages/ThemeInject.php b/app/Filament/Pages/ThemeInject.php index 54e17ca..1c62ed0 100644 --- a/app/Filament/Pages/ThemeInject.php +++ b/app/Filament/Pages/ThemeInject.php @@ -119,6 +119,7 @@ class ThemeInject extends Page Setting::set('inject.blocks', $blocks); Setting::set('theme_vars_'.app(ThemeManager::class)->active(), $data['theme_vars'] ?? []); + \App\Blog\Support\PageCacheMiddleware::flush(); Notification::make()->title('注入内容已保存')->success()->send(); } diff --git a/docs/database/mysql-schema-20260918-214145.sql b/docs/database/mysql-schema-20260918-214145.sql new file mode 100644 index 0000000..3af0b28 --- /dev/null +++ b/docs/database/mysql-schema-20260918-214145.sql @@ -0,0 +1,584 @@ +/*!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', + `sticky_level` tinyint unsigned 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); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (26,'2026_09_18_210000_add_sticky_level_to_posts_table',3); diff --git a/docs/development.md b/docs/development.md index f75084b..3a18d73 100644 --- a/docs/development.md +++ b/docs/development.md @@ -83,6 +83,7 @@ routes/web.php # 前台规范路由;routes/legacy.php 老 URL 301(新增 - 前台 RSS / Sitemap / llms.txt 开新窗口。`/llms.txt` 和 `/posts/{slug}.md` 是核心路由,不要做成静态文件,新文章要马上出现 - 首页浏览模式:`home_view_mode` + `?view=list|full` + cookie `ll_home_view`。分页用 `blog_paginate()`(`onEachSide(1)`) - 老 sablog 正文清洗放 `SablogHtmlNormalizer`,导入 / 渲染 / `sablog:fix-content` 共用,不要各写一套 strip_tags +- 广告位用 `partials.theme-inject`。保存主题注入要清页面缓存。stylevars 跟 `theme_vars_{theme}` 走,不要再忽略这张表 ## 9. 常用命令速查 diff --git a/docs/import.md b/docs/import.md index 228a2be..0252a5d 100644 --- a/docs/import.md +++ b/docs/import.md @@ -47,6 +47,7 @@ php artisan sablog:import \ | `links` | `links` | linkid 保留 | | `attachments` | medialibrary `media` | 文件 + `legacy_attachmentid` 映射(老 `[attach=xx]` 按此解析) | | `settings` | `settings` | 键值映射为站点设置(可在后台「博客设置」继续改) | +| `stylevars` | `theme_vars_{theme}` | 老广告位变量,按位置打到主题注入点 | | `tags` | spatie tags | 老 aids 文本解析为独立标签 + 关联 | **不迁移**:trackback/引用通告(垃圾来源,接口已关闭)、搜索缓存表、验证码(由 AI 审核 + 蜜罐 + 频控取代)。 diff --git a/docs/themes.md b/docs/themes.md index d05adda..cf43e63 100644 --- a/docs/themes.md +++ b/docs/themes.md @@ -23,7 +23,18 @@ themes/{name}/ "description": "主题描述", "author": "作者", "screenshot": "screenshot.png", - "inject_points": ["head", "header", "sidebar_top", "sidebar_bottom", "content_top", "content_bottom", "post_content_top", "post_content_bottom", "footer", "body_end"] + "inject_points": [ + "head", + "header", + "sidebar_top", + "sidebar_bottom", + "content_top", + "content_bottom", + "post_content_top", + "post_content_bottom", + "footer", + "body_end" + ] } ``` @@ -69,21 +80,21 @@ themes/{name}/ ## 页面视图清单(控制器会传的数据) -| 视图 | 控制器 | 变量 | -|------|--------|------| -| `index` | HomeController | `$posts`(分页) | -| `show` | PostController | `$post, $comments, $contentHtml` | -| `list` | Category/Archive | `$posts, $category? / $archiveTitle?` | -| `archives` | ArchiveController | `$archives` | -| `tags` | TagController | `$tags` | -| `tag` | TagController | `$tag, $posts` | -| `search` | SearchController | `$posts, $keyword` | -| `links` | LinkController | `$links` | -| `comments` | CommentController | `$comments` | -| `login` / `register` / `profile` | AuthController | — | -| `membership.index` / `membership.mine` | 会员插件 | `$plans / $subscriptions` | -| `membership.paywall` / `membership.paid-teaser` | 会员插件(付费墙) | `$post` | -| `payments.sandbox` / `payments.result` | 支付插件 | `$payment` | +| 视图 | 控制器 | 变量 | +| ----------------------------------------------- | ------------------ | ------------------------------------- | +| `index` | HomeController | `$posts`(分页) | +| `show` | PostController | `$post, $comments, $contentHtml` | +| `list` | Category/Archive | `$posts, $category? / $archiveTitle?` | +| `archives` | ArchiveController | `$archives` | +| `tags` | TagController | `$tags` | +| `tag` | TagController | `$tag, $posts` | +| `search` | SearchController | `$posts, $keyword` | +| `links` | LinkController | `$links` | +| `comments` | CommentController | `$comments` | +| `login` / `register` / `profile` | AuthController | — | +| `membership.index` / `membership.mine` | 会员插件 | `$plans / $subscriptions` | +| `membership.paywall` / `membership.paid-teaser` | 会员插件(付费墙) | `$post` | +| `payments.sandbox` / `payments.result` | 支付插件 | `$payment` | ## 付费墙(paywall)覆盖 @@ -115,25 +126,26 @@ themes/{name}/ 主题视图内置 10 个注入点(Blade `@stack`),后台「主题注入」页管理: -| 注入点 | 位置 | 典型用途 | -|--------|------|----------| -| `theme:head` | `` 前 | 统计代码、广告、验证 | -| `theme:header` | 页头下方 | 顶部横幅/公告 | -| `theme:sidebar_top` | 侧边栏顶部 | 首屏广告 | -| `theme:sidebar_bottom` | 侧边栏底部 | 广告/临时链接 | -| `theme:content_top` | 内容区顶部 | 列表/文章上方横幅 | -| `theme:content_bottom` | 内容区底部 | 分页后广告 | -| `theme:post_content_top` | 文章正文前 | 正文顶部广告 | -| `theme:post_content_bottom` | 文章正文后 | 正文底部广告 | -| `theme:footer` | 页脚前 | 版权上方声明 | -| `theme:body_end` | `` 前 | 统计代码 | +| 注入点 | 位置 | 典型用途 | +| --------------------------- | ------------ | -------------------- | +| `theme:head` | `` 前 | 统计代码、广告、验证 | +| `theme:header` | 页头下方 | 顶部横幅/公告 | +| `theme:sidebar_top` | 侧边栏顶部 | 首屏广告 | +| `theme:sidebar_bottom` | 侧边栏底部 | 广告/临时链接 | +| `theme:content_top` | 内容区顶部 | 列表/文章上方横幅 | +| `theme:content_bottom` | 内容区底部 | 分页后广告 | +| `theme:post_content_top` | 文章正文前 | 正文顶部广告 | +| `theme:post_content_bottom` | 文章正文后 | 正文底部广告 | +| `theme:footer` | 页脚前 | 版权上方声明 | +| `theme:body_end` | `` 前 | 统计代码 | 特性: + - **同一位置可添加多条**注入块(名称 + HTML + 排序 + 启停),按排序渲染 - 插件可通过 filter `theme.inject.{point}` 追加 - 主题变量(stylevar 复活):后台「主题注入」→「主题变量」Tab,视图用 `{{ theme('var', 'key') }}` 引用任意 HTML -新主题须在对应 partial/视图放置 `@stack('theme:xxx')` 才能接收注入。 +新主题须在对应 partial/视图放置 `@include('partials.theme-inject', ['point' => 'xxx'])`(或 `@stack('theme:xxx')`)才能接收注入。主题变量里的 `ad_header` 等会自动打到对应位置,未映射且含 HTML 的变量落在页脚。保存「主题注入」会清前台整页缓存。 **声明注入点**:`theme.json` 的 `inject_points` 声明主题实现了哪些注入点(缺省视为全部支持),后台主题卡片会展示,切换主题时一目了然。 diff --git a/public/css/blog.css b/public/css/blog.css index 435442a..0eb9ff6 100644 --- a/public/css/blog.css +++ b/public/css/blog.css @@ -444,18 +444,22 @@ a:hover { max-width: 100%; } .pagination-info { - color: #888; + background: #f5f7fb; + border: 1px solid #d5dbe6; + border-radius: 6px; + color: #556; font-size: 13px; - margin-right: 6px; + padding: 6px 10px; + margin-right: 4px; white-space: nowrap; } .pagination-links a, .pagination-links .current, .pagination-links .disabled { - padding: 6px 12px; + padding: 6px 10px; border: 1px solid #ddd; border-radius: 6px; - font-size: 14px; + font-size: 13px; background: #fff; } .pagination-links .current { @@ -466,6 +470,19 @@ a:hover { .pagination-links .disabled { color: #bbb; } +.theme-inject { + margin: 10px 0; + max-width: 100%; + overflow: hidden; +} +.theme-inject:empty { + display: none; +} +.theme-inject img, +.theme-inject iframe { + max-width: 100%; + height: auto; +} .auth-layout { display: flex; diff --git a/public/themes/modern/style.css b/public/themes/modern/style.css index ce484ce..5be72f3 100644 --- a/public/themes/modern/style.css +++ b/public/themes/modern/style.css @@ -366,18 +366,22 @@ a:hover { max-width: 100%; } .pagination-info { + background: var(--bg); + border: 1px solid var(--border); + border-radius: 6px; color: var(--muted); font-size: 13px; - margin-right: 6px; + padding: 6px 10px; + margin-right: 4px; white-space: nowrap; } .pagination-links a, .pagination-links .current, .pagination-links .disabled { - padding: 6px 14px; + padding: 6px 10px; border: 1px solid var(--border); border-radius: 6px; - font-size: 14px; + font-size: 13px; background: var(--card); color: var(--text); } @@ -389,6 +393,19 @@ a:hover { .pagination-links .disabled { color: var(--muted); } +.theme-inject { + margin: 12px 0; + max-width: 100%; + overflow: hidden; +} +.theme-inject:empty { + display: none; +} +.theme-inject img, +.theme-inject iframe { + max-width: 100%; + height: auto; +} .m-footer { border-top: 1px solid var(--border); diff --git a/public/themes/sablog/style.css b/public/themes/sablog/style.css index ed1f3d9..80e10b9 100644 --- a/public/themes/sablog/style.css +++ b/public/themes/sablog/style.css @@ -370,34 +370,63 @@ a:hover { width: 100%; max-width: 100%; min-width: 0; - margin: 12px 0; + margin: 14px 0; + clear: both; } .pagination-links { display: flex; - gap: 4px; + gap: 1px; flex-wrap: wrap; align-items: center; max-width: 100%; - margin: 12px 0; + margin: 0; } .pagination-info { - color: #888; + background: #f5fbff; + border: 1px solid #86b9d6; + color: #336; font-size: 12px; - margin-right: 6px; + padding: 2px 6px; + margin-right: 4px; white-space: nowrap; } .pagination-links a, .pagination-links .current, .pagination-links .disabled { - padding: 2px 7px; - border: 1px solid #d8dfe6; + padding: 2px 6px; + border: 1px solid #dedeb8; background: #fff; font-size: 12px; line-height: 1.4; + min-width: 1.6em; + text-align: center; + text-decoration: none; +} +.pagination-links a:hover { + background: #f5fbff; + border-color: #86b9d6; } .pagination-links .current { - background: #1c62b0; - color: #fff; + background: #ffffd9; + color: #92a05a; + font-weight: 700; + border-color: #dedeb8; +} +.pagination-links .disabled { + color: #999; +} +.theme-inject { + margin: 8px 0; + max-width: 100%; + overflow: hidden; +} +.theme-inject:empty { + display: none; +} +.theme-inject img, +.theme-inject iframe { + max-width: 100%; + height: auto; } #footer { diff --git a/resources/views/archives.blade.php b/resources/views/archives.blade.php index ad89bb1..8f5a15c 100644 --- a/resources/views/archives.blade.php +++ b/resources/views/archives.blade.php @@ -5,6 +5,7 @@

文章归档

+ @include('partials.theme-inject', ['point' => 'content_top']) @forelse($archives as $year => $months)

{{ $year }} 年

@@ -24,6 +25,7 @@ @empty
暂无归档
@endforelse + @include('partials.theme-inject', ['point' => 'content_bottom'])
@include('partials.sidebar')
diff --git a/resources/views/index.blade.php b/resources/views/index.blade.php index 83e6eb3..efc71c7 100644 --- a/resources/views/index.blade.php +++ b/resources/views/index.blade.php @@ -12,6 +12,7 @@ @endif @include('partials.view-mode-switcher') + @include('partials.theme-inject', ['point' => 'content_top']) @forelse($posts as $post) @include('partials.post-card', ['post' => $post]) @@ -20,6 +21,7 @@ @endforelse @include('partials.pagination', ['paginator' => $posts]) + @include('partials.theme-inject', ['point' => 'content_bottom']) @include('partials.sidebar')
diff --git a/resources/views/list.blade.php b/resources/views/list.blade.php index da04505..5ca4a58 100644 --- a/resources/views/list.blade.php +++ b/resources/views/list.blade.php @@ -25,6 +25,7 @@ @include('partials.view-mode-switcher') + @include('partials.theme-inject', ['point' => 'content_top']) @forelse($posts as $post) @include('partials.post-card', ['post' => $post]) @@ -33,6 +34,7 @@ @endforelse @include('partials.pagination', ['paginator' => $posts]) + @include('partials.theme-inject', ['point' => 'content_bottom']) @include('partials.sidebar')
diff --git a/resources/views/partials/footer.blade.php b/resources/views/partials/footer.blade.php index 2d5f3d5..ccfd47d 100644 --- a/resources/views/partials/footer.blade.php +++ b/resources/views/partials/footer.blade.php @@ -1,4 +1,4 @@ -@stack('theme:footer') +@include('partials.theme-inject', ['point' => 'footer'])

diff --git a/resources/views/partials/header.blade.php b/resources/views/partials/header.blade.php index 8cfc1a2..d9a6ca0 100644 --- a/resources/views/partials/header.blade.php +++ b/resources/views/partials/header.blade.php @@ -29,4 +29,4 @@

-@stack('theme:header') +@include('partials.theme-inject', ['point' => 'header']) diff --git a/resources/views/partials/pagination-links.blade.php b/resources/views/partials/pagination-links.blade.php index 41f78db..9df4c1f 100644 --- a/resources/views/partials/pagination-links.blade.php +++ b/resources/views/partials/pagination-links.blade.php @@ -1,6 +1,6 @@ @if ($paginator->hasPages())
-@stack('theme:footer') +@include('partials.theme-inject', ['point' => 'footer']) @stack('theme:body_end') diff --git a/themes/demo/views/partials/header.blade.php b/themes/demo/views/partials/header.blade.php index 929aa1d..9611cfb 100644 --- a/themes/demo/views/partials/header.blade.php +++ b/themes/demo/views/partials/header.blade.php @@ -23,4 +23,4 @@ -@stack('theme:header') +@include('partials.theme-inject', ['point' => 'header']) diff --git a/themes/modern/assets/style.css b/themes/modern/assets/style.css index ce484ce..5be72f3 100644 --- a/themes/modern/assets/style.css +++ b/themes/modern/assets/style.css @@ -366,18 +366,22 @@ a:hover { max-width: 100%; } .pagination-info { + background: var(--bg); + border: 1px solid var(--border); + border-radius: 6px; color: var(--muted); font-size: 13px; - margin-right: 6px; + padding: 6px 10px; + margin-right: 4px; white-space: nowrap; } .pagination-links a, .pagination-links .current, .pagination-links .disabled { - padding: 6px 14px; + padding: 6px 10px; border: 1px solid var(--border); border-radius: 6px; - font-size: 14px; + font-size: 13px; background: var(--card); color: var(--text); } @@ -389,6 +393,19 @@ a:hover { .pagination-links .disabled { color: var(--muted); } +.theme-inject { + margin: 12px 0; + max-width: 100%; + overflow: hidden; +} +.theme-inject:empty { + display: none; +} +.theme-inject img, +.theme-inject iframe { + max-width: 100%; + height: auto; +} .m-footer { border-top: 1px solid var(--border); diff --git a/themes/modern/views/partials/footer.blade.php b/themes/modern/views/partials/footer.blade.php index 02ae69d..ea401cd 100644 --- a/themes/modern/views/partials/footer.blade.php +++ b/themes/modern/views/partials/footer.blade.php @@ -1,4 +1,4 @@ -@stack('theme:footer') +@include('partials.theme-inject', ['point' => 'footer'])