mirror of https://github.com/halo-dev/halo
feat: add system setup support (halo-dev/console#632)
#### What type of PR is this? /kind feature /milestone 2.0 #### What this PR does / why we need it: 支持初始化默认数据。 #### Which issue(s) this PR fixes: Fixes https://github.com/halo-dev/halo/issues/2428 #### Special notes for your reviewer: /cc @halo-dev/sig-halo-console 测试方式: 1. 清空数据库。 2. 启动之后登录即可跳转到初始化界面。 3. 测试初始化的功能是否正常。 #### Does this PR introduce a user-facing change? ```release-note None ```pull/3445/head
parent
4b6096ce20
commit
0e81366754
|
@ -0,0 +1,21 @@
|
|||
import { useSystemStatesStore } from "@/stores/system-states";
|
||||
import type { Router } from "vue-router";
|
||||
|
||||
export function setupCheckStatesGuard(router: Router) {
|
||||
router.beforeEach(async (to, from, next) => {
|
||||
if (to.name === "Setup" || to.name === "Login") {
|
||||
next();
|
||||
}
|
||||
|
||||
const systemStateStore = useSystemStatesStore();
|
||||
|
||||
await systemStateStore.fetchSystemStates();
|
||||
|
||||
if (!systemStateStore.states.isSetup) {
|
||||
next({ name: "Setup" });
|
||||
return;
|
||||
}
|
||||
|
||||
next();
|
||||
});
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
import { createRouter, createWebHashHistory } from "vue-router";
|
||||
import routesConfig from "@/router/routes.config";
|
||||
import { setupPermissionGuard } from "./guards/permission";
|
||||
import { setupCheckStatesGuard } from "./guards/check-states";
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHashHistory(import.meta.env.BASE_URL),
|
||||
|
@ -9,5 +10,6 @@ const router = createRouter({
|
|||
});
|
||||
|
||||
setupPermissionGuard(router);
|
||||
setupCheckStatesGuard(router);
|
||||
|
||||
export default router;
|
||||
|
|
|
@ -2,6 +2,7 @@ import type { RouteRecordRaw } from "vue-router";
|
|||
import NotFound from "@/views/exceptions/NotFound.vue";
|
||||
import Forbidden from "@/views/exceptions/Forbidden.vue";
|
||||
import { BasicLayout } from "@halo-dev/admin-shared";
|
||||
import Setup from "@/views/system/Setup.vue";
|
||||
|
||||
export const routes: Array<RouteRecordRaw> = [
|
||||
{
|
||||
|
@ -20,6 +21,14 @@ export const routes: Array<RouteRecordRaw> = [
|
|||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: "/setup",
|
||||
name: "Setup",
|
||||
component: Setup,
|
||||
meta: {
|
||||
title: "系统初始化",
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export default routes;
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
import { apiClient } from "@/utils/api-client";
|
||||
import { defineStore } from "pinia";
|
||||
|
||||
interface SystemState {
|
||||
isSetup: boolean;
|
||||
}
|
||||
|
||||
interface SystemStatesState {
|
||||
states: SystemState;
|
||||
}
|
||||
|
||||
export const useSystemStatesStore = defineStore({
|
||||
id: "system-states",
|
||||
state: (): SystemStatesState => ({
|
||||
states: {
|
||||
isSetup: false,
|
||||
},
|
||||
}),
|
||||
actions: {
|
||||
async fetchSystemStates() {
|
||||
try {
|
||||
const { data } =
|
||||
await apiClient.extension.configMap.getv1alpha1ConfigMap({
|
||||
name: "system-states",
|
||||
});
|
||||
|
||||
if (data.data) {
|
||||
this.states = JSON.parse(data.data["states"]);
|
||||
return;
|
||||
}
|
||||
this.states.isSetup = false;
|
||||
} catch (error) {
|
||||
this.states.isSetup = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
|
@ -0,0 +1,148 @@
|
|||
<script lang="ts" setup>
|
||||
import logo from "@/assets/logo.svg";
|
||||
import { useSettingForm } from "@/composables/use-setting-form";
|
||||
import { useSystemStatesStore } from "@/stores/system-states";
|
||||
import { apiClient } from "@/utils/api-client";
|
||||
import { VButton } from "@halo-dev/components";
|
||||
import { onMounted, ref } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
import category from "./setup-data/category.json";
|
||||
import tag from "./setup-data/tag.json";
|
||||
import post from "./setup-data/post.json";
|
||||
import singlePage from "./setup-data/singlePage.json";
|
||||
import menu from "./setup-data/menu.json";
|
||||
import menuItems from "./setup-data/menu-items.json";
|
||||
import type {
|
||||
Category,
|
||||
PostRequest,
|
||||
SinglePageRequest,
|
||||
Tag,
|
||||
} from "@halo-dev/api-client";
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
const {
|
||||
configMapFormData,
|
||||
handleSaveConfigMap,
|
||||
handleFetchSettings,
|
||||
handleFetchConfigMap,
|
||||
} = useSettingForm(ref("system"), ref("system"));
|
||||
|
||||
const siteTitle = ref("");
|
||||
const loading = ref(false);
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
loading.value = true;
|
||||
|
||||
// Set site title
|
||||
if (configMapFormData.value) {
|
||||
configMapFormData.value["basic"].title = siteTitle.value;
|
||||
await handleSaveConfigMap();
|
||||
}
|
||||
|
||||
// Create category / tag / post
|
||||
await apiClient.extension.category.createcontentHaloRunV1alpha1Category({
|
||||
category: category as Category,
|
||||
});
|
||||
await apiClient.extension.tag.createcontentHaloRunV1alpha1Tag({
|
||||
tag: tag as Tag,
|
||||
});
|
||||
await apiClient.post.draftPost({ postRequest: post as PostRequest });
|
||||
|
||||
try {
|
||||
await apiClient.post.publishPost({ name: post.post.metadata.name });
|
||||
} catch (error) {
|
||||
console.error("Failed to publish post", error);
|
||||
}
|
||||
|
||||
// Create singlePage
|
||||
await apiClient.singlePage.draftSinglePage({
|
||||
singlePageRequest: singlePage as SinglePageRequest,
|
||||
});
|
||||
|
||||
try {
|
||||
await apiClient.singlePage.publishSinglePage({
|
||||
name: singlePage.page.metadata.name,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Failed to publish singlePage", error);
|
||||
}
|
||||
|
||||
// Create menu and menu items
|
||||
const menuItemPromises = menuItems.map((item) => {
|
||||
return apiClient.extension.menuItem.createv1alpha1MenuItem({
|
||||
menuItem: item,
|
||||
});
|
||||
});
|
||||
await Promise.all(menuItemPromises);
|
||||
await apiClient.extension.menu.createv1alpha1Menu({ menu: menu });
|
||||
|
||||
// Create system-states ConfigMap
|
||||
await apiClient.extension.configMap.createv1alpha1ConfigMap({
|
||||
configMap: {
|
||||
metadata: {
|
||||
name: "system-states",
|
||||
},
|
||||
kind: "ConfigMap",
|
||||
apiVersion: "v1alpha1",
|
||||
data: {
|
||||
states: JSON.stringify({ isSetup: true }),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
router.push({ name: "Dashboard" });
|
||||
} catch (error) {
|
||||
console.error("Failed to setup", error);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(async () => {
|
||||
const systemStatesStore = useSystemStatesStore();
|
||||
|
||||
if (systemStatesStore.states.isSetup) {
|
||||
router.push({ name: "Dashboard" });
|
||||
return;
|
||||
}
|
||||
|
||||
handleFetchSettings();
|
||||
handleFetchConfigMap();
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex h-screen flex-col items-center justify-center">
|
||||
<img :src="logo" alt="Logo" class="mb-8 w-20" />
|
||||
<div class="flex w-72 flex-col gap-4">
|
||||
<FormKit
|
||||
id="setup-form"
|
||||
name="setup-form"
|
||||
:actions="false"
|
||||
type="form"
|
||||
@submit="handleSubmit"
|
||||
@keyup.enter="$formkit.submit('setup-form')"
|
||||
>
|
||||
<FormKit
|
||||
v-model="siteTitle"
|
||||
:validation-messages="{
|
||||
required: '请输入站点名称',
|
||||
}"
|
||||
type="text"
|
||||
placeholder="站点名称"
|
||||
validation="required"
|
||||
></FormKit>
|
||||
</FormKit>
|
||||
<VButton
|
||||
block
|
||||
type="secondary"
|
||||
:loading="loading"
|
||||
@click="$formkit.submit('setup-form')"
|
||||
>
|
||||
初始化
|
||||
</VButton>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"spec": {
|
||||
"displayName": "默认分类",
|
||||
"slug": "default",
|
||||
"description": "这是你的默认分类,如不需要,删除即可。",
|
||||
"cover": "",
|
||||
"template": "",
|
||||
"priority": 0,
|
||||
"children": []
|
||||
},
|
||||
"apiVersion": "content.halo.run/v1alpha1",
|
||||
"kind": "Category",
|
||||
"metadata": {
|
||||
"name": "76514a40-6ef1-4ed9-b58a-e26945bde3ca"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
[
|
||||
{
|
||||
"spec": {
|
||||
"displayName": "首页",
|
||||
"href": "/",
|
||||
"children": [],
|
||||
"priority": 0
|
||||
},
|
||||
"apiVersion": "v1alpha1",
|
||||
"kind": "MenuItem",
|
||||
"metadata": { "name": "88c3f10b-321c-4092-86a8-70db00251b74" }
|
||||
},
|
||||
{
|
||||
"spec": {
|
||||
"children": [],
|
||||
"priority": 1,
|
||||
"categoryRef": {
|
||||
"version": "content.halo.run/v1alpha1",
|
||||
"kind": "Post",
|
||||
"name": "76514a40-6ef1-4ed9-b58a-e26945bde3ca"
|
||||
}
|
||||
},
|
||||
"apiVersion": "v1alpha1",
|
||||
"kind": "MenuItem",
|
||||
"metadata": { "name": "c4c814d1-0c2c-456b-8c96-4864965fee94" }
|
||||
},
|
||||
{
|
||||
"spec": {
|
||||
"displayName": "",
|
||||
"href": "",
|
||||
"children": [],
|
||||
"priority": 2,
|
||||
"tagRef": {
|
||||
"version": "content.halo.run/v1alpha1",
|
||||
"kind": "Tag",
|
||||
"name": "c33ceabb-d8f1-4711-8991-bb8f5c92ad7c"
|
||||
}
|
||||
},
|
||||
"apiVersion": "v1alpha1",
|
||||
"kind": "MenuItem",
|
||||
"metadata": { "name": "35869bd3-33b5-448b-91ee-cf6517a59644" }
|
||||
},
|
||||
{
|
||||
"spec": {
|
||||
"displayName": "",
|
||||
"href": "",
|
||||
"children": [],
|
||||
"priority": 3,
|
||||
"singlePageRef": {
|
||||
"version": "content.halo.run/v1alpha1",
|
||||
"kind": "SinglePage",
|
||||
"name": "373a5f79-f44f-441a-9df1-85a4f553ece8"
|
||||
}
|
||||
},
|
||||
"apiVersion": "v1alpha1",
|
||||
"kind": "MenuItem",
|
||||
"metadata": { "name": "b0d041fa-dc99-48f6-a193-8604003379cf" }
|
||||
}
|
||||
]
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"spec": {
|
||||
"displayName": "主菜单",
|
||||
"menuItems": [
|
||||
"88c3f10b-321c-4092-86a8-70db00251b74",
|
||||
"c4c814d1-0c2c-456b-8c96-4864965fee94",
|
||||
"35869bd3-33b5-448b-91ee-cf6517a59644",
|
||||
"b0d041fa-dc99-48f6-a193-8604003379cf"
|
||||
]
|
||||
},
|
||||
"apiVersion": "v1alpha1",
|
||||
"kind": "Menu",
|
||||
"metadata": { "name": "2f0ef354-6f8f-40b9-b41d-2163f3e51b4f" }
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
{
|
||||
"post": {
|
||||
"spec": {
|
||||
"title": "Hello Halo",
|
||||
"slug": "hello-halo",
|
||||
"template": "",
|
||||
"cover": "",
|
||||
"deleted": false,
|
||||
"published": false,
|
||||
"publishTime": "",
|
||||
"pinned": false,
|
||||
"allowComment": true,
|
||||
"visible": "PUBLIC",
|
||||
"version": 1,
|
||||
"priority": 0,
|
||||
"excerpt": {
|
||||
"autoGenerate": false,
|
||||
"raw": "如果你看到了这一篇文章,那么证明你已经安装成功了,感谢使用 Halo 进行创作,希望能够使用愉快。"
|
||||
},
|
||||
"categories": ["76514a40-6ef1-4ed9-b58a-e26945bde3ca"],
|
||||
"tags": ["c33ceabb-d8f1-4711-8991-bb8f5c92ad7c"],
|
||||
"htmlMetas": []
|
||||
},
|
||||
"apiVersion": "content.halo.run/v1alpha1",
|
||||
"kind": "Post",
|
||||
"metadata": {
|
||||
"name": "5152aea5-c2e8-4717-8bba-2263d46e19d5"
|
||||
}
|
||||
},
|
||||
"content": {
|
||||
"raw": "<h2 id=\"hello-halo\"><strong>Hello Halo</strong></h2><p>如果你看到了这一篇文章,那么证明你已经安装成功了,感谢使用 <a target=\"_blank\" rel=\"noopener noreferrer nofollow\" href=\"https://halo.run/\">Halo</a> 进行创作,希望能够使用愉快。</p><h2 id=\"%E7%9B%B8%E5%85%B3%E9%93%BE%E6%8E%A5\"><strong>相关链接</strong></h2><ul><li><p>官网:<a target=\"_blank\" rel=\"noopener noreferrer nofollow\" href=\"https://halo.run\">https://halo.run</a></p></li><li><p>文档:<a target=\"_blank\" rel=\"noopener noreferrer nofollow\" href=\"https://docs.halo.run\">https://docs.halo.run</a></p></li><li><p>社区:<a target=\"_blank\" rel=\"noopener noreferrer nofollow\" href=\"https://bbs.halo.run\">https://bbs.halo.run</a></p></li><li><p>主题仓库:<a target=\"_blank\" rel=\"noopener noreferrer nofollow\" href=\"https://halo.run/themes.html\">https://halo.run/themes.html</a></p></li><li><p>开源地址:<a target=\"_blank\" rel=\"noopener noreferrer nofollow\" href=\"https://github.com/halo-dev/halo\">https://github.com/halo-dev/halo</a></p></li></ul><p>在使用过程中,有任何问题都可以通过以上链接找寻答案,或者联系我们。</p><blockquote><p>这是一篇自动生成的文章,请删除这篇文章之后开始你的创作吧!</p></blockquote>",
|
||||
"content": "<h2 id=\"hello-halo\"><strong>Hello Halo</strong></h2><p>如果你看到了这一篇文章,那么证明你已经安装成功了,感谢使用 <a target=\"_blank\" rel=\"noopener noreferrer nofollow\" href=\"https://halo.run/\">Halo</a> 进行创作,希望能够使用愉快。</p><h2 id=\"%E7%9B%B8%E5%85%B3%E9%93%BE%E6%8E%A5\"><strong>相关链接</strong></h2><ul><li><p>官网:<a target=\"_blank\" rel=\"noopener noreferrer nofollow\" href=\"https://halo.run\">https://halo.run</a></p></li><li><p>文档:<a target=\"_blank\" rel=\"noopener noreferrer nofollow\" href=\"https://docs.halo.run\">https://docs.halo.run</a></p></li><li><p>社区:<a target=\"_blank\" rel=\"noopener noreferrer nofollow\" href=\"https://bbs.halo.run\">https://bbs.halo.run</a></p></li><li><p>主题仓库:<a target=\"_blank\" rel=\"noopener noreferrer nofollow\" href=\"https://halo.run/themes.html\">https://halo.run/themes.html</a></p></li><li><p>开源地址:<a target=\"_blank\" rel=\"noopener noreferrer nofollow\" href=\"https://github.com/halo-dev/halo\">https://github.com/halo-dev/halo</a></p></li></ul><p>在使用过程中,有任何问题都可以通过以上链接找寻答案,或者联系我们。</p><blockquote><p>这是一篇自动生成的文章,请删除这篇文章之后开始你的创作吧!</p></blockquote>",
|
||||
"rawType": "HTML"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
{
|
||||
"page": {
|
||||
"spec": {
|
||||
"title": "关于",
|
||||
"slug": "about",
|
||||
"template": "",
|
||||
"cover": "",
|
||||
"deleted": false,
|
||||
"published": false,
|
||||
"publishTime": "",
|
||||
"pinned": false,
|
||||
"allowComment": true,
|
||||
"visible": "PUBLIC",
|
||||
"version": 1,
|
||||
"priority": 0,
|
||||
"excerpt": {
|
||||
"autoGenerate": false,
|
||||
"raw": "这是一个自定义页面,你可以在后台的 页面 -> 自定义页面 找到它,你可以用于新建关于页面、联系我们页面等等。"
|
||||
},
|
||||
"htmlMetas": []
|
||||
},
|
||||
"apiVersion": "content.halo.run/v1alpha1",
|
||||
"kind": "SinglePage",
|
||||
"metadata": { "name": "373a5f79-f44f-441a-9df1-85a4f553ece8" }
|
||||
},
|
||||
"content": {
|
||||
"raw": "<h2><strong>关于页面</strong></h2><p>这是一个自定义页面,你可以在后台的 <code>页面</code> -> <code>自定义页面</code> 找到它,你可以用于新建关于页面、联系我们页面等等。</p><blockquote><p>这是一篇自动生成的页面,你可以在后台删除它。</p></blockquote>",
|
||||
"content": "<h2><strong>关于页面</strong></h2><p>这是一个自定义页面,你可以在后台的 <code>页面</code> -> <code>自定义页面</code> 找到它,你可以用于新建关于页面、联系我们页面等等。</p><blockquote><p>这是一篇自动生成的页面,你可以在后台删除它。</p></blockquote>",
|
||||
"rawType": "HTML"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"spec": {
|
||||
"displayName": "Halo",
|
||||
"slug": "halo",
|
||||
"color": "#ad95b2",
|
||||
"cover": ""
|
||||
},
|
||||
"apiVersion": "content.halo.run/v1alpha1",
|
||||
"kind": "Tag",
|
||||
"metadata": {
|
||||
"name": "c33ceabb-d8f1-4711-8991-bb8f5c92ad7c"
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue