Skip to content
This repository has been archived by the owner on Jan 21, 2024. It is now read-only.

feat: add actuator view page #832

Merged
merged 6 commits into from
Jan 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/components/src/icons/icons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ import IconSendPlaneFill from "~icons/ri/send-plane-fill";
import IconRocketLine from "~icons/ri/rocket-line";
import IconArrowUpCircleLine from "~icons/ri/arrow-up-circle-line";
import IconArrowDownCircleLine from "~icons/ri/arrow-down-circle-line";
import IconTerminalBoxLine from "~icons/ri/terminal-box-line";
import IconClipboardLine from "~icons/ri/clipboard-line";

export {
IconDashboard,
Expand Down Expand Up @@ -116,4 +118,6 @@ export {
IconRocketLine,
IconArrowUpCircleLine,
IconArrowDownCircleLine,
IconTerminalBoxLine,
IconClipboardLine,
};
2 changes: 2 additions & 0 deletions src/modules/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import pluginModule from "./system/plugins/module";
import userModule from "./system/users/module";
import roleModule from "./system/roles/module";
import settingModule from "./system/settings/module";
import actuatorModule from "./system/actuator/module";

// const coreModules = [
// dashboardModule,
Expand All @@ -28,6 +29,7 @@ const coreModules = [
postModule,
pluginModule,
settingModule,
actuatorModule,
dashboardModule,
menuModule,
commentModule,
Expand Down
241 changes: 241 additions & 0 deletions src/modules/system/actuator/Actuator.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,241 @@
<script lang="ts" setup>
import {
IconTerminalBoxLine,
IconClipboardLine,
VAlert,
VPageHeader,
VCard,
VButton,
Toast,
} from "@halo-dev/components";
import { computed, onMounted, ref } from "vue";
import type { Info, GlobalInfo, Startup } from "./types";
import axios from "axios";
import { formatDatetime } from "@/utils/date";
import { useClipboard } from "@vueuse/core";

const info = ref<Info>();
const globalInfo = ref<GlobalInfo>();
const startup = ref<Startup>();

const handleFetchActuatorInfo = async () => {
const { data } = await axios.get(
`${import.meta.env.VITE_API_URL}/actuator/info`,
{
withCredentials: true,
}
);
info.value = data;
};

const handleFetchActuatorGlobalInfo = async () => {
const { data } = await axios.get(
`${import.meta.env.VITE_API_URL}/actuator/globalinfo`,
{
withCredentials: true,
}
);
globalInfo.value = data;
};

const handleFetchActuatorStartup = async () => {
const { data } = await axios.get(
`${import.meta.env.VITE_API_URL}/actuator/startup`,
{
withCredentials: true,
}
);
startup.value = data;
};

const isExternalUrlValid = computed(() => {
if (!globalInfo.value?.externalUrl) {
return false;
}
const url = new URL(globalInfo.value.externalUrl);
const { host: currentHost, protocol: currentProtocol } = window.location;
return url.host === currentHost && url.protocol === currentProtocol;
});

onMounted(() => {
handleFetchActuatorInfo();
handleFetchActuatorGlobalInfo();
handleFetchActuatorStartup();
});

// copy system information to clipboard
const { copy, isSupported } = useClipboard();

const handleCopy = () => {
if (!isSupported.value) {
Toast.warning("当前浏览器不支持复制");
}

const text = `
- 外部访问地址:${globalInfo.value?.externalUrl}
- 启动时间:${formatDatetime(startup.value?.timeline.startTime)}
- Halo 版本:${info.value?.build?.version}
- 构建时间:${formatDatetime(info.value?.build?.time)}
- Git Commit:${info.value?.git?.commit.id}
- Java:${info.value?.java.runtime.name} / ${info.value?.java.runtime.version}
- 操作系统:${info.value?.os.name} / ${info.value?.os.version}
`;

copy(text);

Toast.success("复制成功");
};
</script>

<template>
<VPageHeader title="系统概览">
<template #icon>
<IconTerminalBoxLine class="mr-2 self-center" />
</template>
<template #actions>
<VButton size="sm" @click="handleCopy">
<template #icon>
<IconClipboardLine class="h-full w-full" />
</template>
复制
</VButton>
</template>
</VPageHeader>

<div class="m-0 flex flex-col gap-4 md:m-4">
<VCard :body-class="['!p-0']">
<div class="bg-white">
<div
class="flex items-center justify-between bg-white px-4 py-4 sm:px-6"
>
<div>
<h3 class="text-lg font-medium leading-6 text-gray-900">
基本信息
</h3>
</div>
</div>
<div class="border-t border-gray-200">
<dl class="divide-y divide-gray-100">
<div
class="bg-white px-4 py-5 hover:bg-gray-50 sm:grid sm:grid-cols-6 sm:gap-4 sm:px-6"
>
<dt class="text-sm font-medium text-gray-900">外部访问地址</dt>
<dd class="mt-1 text-sm text-gray-900 sm:col-span-2 sm:mt-0">
<span>
{{ globalInfo?.externalUrl }}
</span>
<VAlert
v-if="!isExternalUrlValid"
class="mt-3"
type="warning"
title="警告"
:closable="false"
>
<template #description>
检测到外部访问地址与当前访问地址不一致,可能会导致部分链接无法正常跳转,请检查外部访问地址设置。
</template>
</VAlert>
</dd>
</div>
<div
class="bg-white px-4 py-5 hover:bg-gray-50 sm:grid sm:grid-cols-6 sm:gap-4 sm:px-6"
>
<dt class="text-sm font-medium text-gray-900">启动时间</dt>
<dd class="mt-1 text-sm text-gray-900 sm:col-span-2 sm:mt-0">
{{ formatDatetime(startup?.timeline.startTime) }}
</dd>
</div>
<div
class="bg-white px-4 py-5 hover:bg-gray-50 sm:grid sm:grid-cols-6 sm:gap-4 sm:px-6"
>
<dt class="text-sm font-medium text-gray-900">时区</dt>
<dd class="mt-1 text-sm text-gray-900 sm:col-span-2 sm:mt-0">
{{ globalInfo?.timeZone }}
</dd>
</div>
<div
class="bg-white px-4 py-5 hover:bg-gray-50 sm:grid sm:grid-cols-6 sm:gap-4 sm:px-6"
>
<dt class="text-sm font-medium text-gray-900">语言</dt>
<dd class="mt-1 text-sm text-gray-900 sm:col-span-2 sm:mt-0">
{{ globalInfo?.locale }}
</dd>
</div>
</dl>
</div>
</div>
</VCard>
<VCard v-if="info" :body-class="['!p-0']">
<div class="bg-white">
<div
class="flex items-center justify-between bg-white px-4 py-4 sm:px-6"
>
<div>
<h3 class="text-lg font-medium leading-6 text-gray-900">
环境信息
</h3>
</div>
</div>
<div class="border-t border-gray-200">
<dl class="divide-y divide-gray-100">
<div
v-if="info.build"
class="bg-white px-4 py-5 hover:bg-gray-50 sm:grid sm:grid-cols-6 sm:gap-4 sm:px-6"
>
<dt class="text-sm font-medium text-gray-900">版本</dt>
<dd class="mt-1 text-sm text-gray-900 sm:col-span-2 sm:mt-0">
<a
:href="`https://github.com/halo-dev/halo/releases/tag/v${info.build.version}`"
class="hover:text-gray-600"
target="_blank"
>
{{ info.build.version }}
</a>
</dd>
</div>
<div
v-if="info.build"
class="bg-white px-4 py-5 hover:bg-gray-50 sm:grid sm:grid-cols-6 sm:gap-4 sm:px-6"
>
<dt class="text-sm font-medium text-gray-900">构建时间</dt>
<dd class="mt-1 text-sm text-gray-900 sm:col-span-2 sm:mt-0">
{{ formatDatetime(info.build.time) }}
</dd>
</div>
<div
v-if="info.git"
class="bg-white px-4 py-5 hover:bg-gray-50 sm:grid sm:grid-cols-6 sm:gap-4 sm:px-6"
>
<dt class="text-sm font-medium text-gray-900">Git Commit</dt>
<dd class="mt-1 text-sm text-gray-900 sm:col-span-2 sm:mt-0">
<a
:href="`https://github.com/halo-dev/halo/commit/${info.git.commit.id}`"
class="hover:text-gray-600"
target="_blank"
>
{{ info.git.commit.id }}
</a>
</dd>
</div>
<div
class="bg-white px-4 py-5 hover:bg-gray-50 sm:grid sm:grid-cols-6 sm:gap-4 sm:px-6"
>
<dt class="text-sm font-medium text-gray-900">Java</dt>
<dd class="mt-1 text-sm text-gray-900 sm:col-span-2 sm:mt-0">
{{ info.java.runtime.name }} / {{ info.java.runtime.version }}
</dd>
</div>
<div
class="bg-white px-4 py-5 hover:bg-gray-50 sm:grid sm:grid-cols-6 sm:gap-4 sm:px-6"
>
<dt class="text-sm font-medium text-gray-900">操作系统</dt>
<dd class="mt-1 text-sm text-gray-900 sm:col-span-2 sm:mt-0">
{{ info.os.name }} {{ info.os.version }} / {{ info.os.arch }}
</dd>
</div>
</dl>
</div>
</div>
</VCard>
</div>
</template>
32 changes: 32 additions & 0 deletions src/modules/system/actuator/module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { definePlugin } from "@halo-dev/console-shared";
import { IconTerminalBoxLine } from "@halo-dev/components";
import BasicLayout from "@/layouts/BasicLayout.vue";
import Actuator from "./Actuator.vue";
import { markRaw } from "vue";

export default definePlugin({
components: {},
routes: [
{
path: "/actuator",
component: BasicLayout,
children: [
{
path: "",
component: Actuator,
meta: {
title: "系统概览",
searchable: true,
menu: {
name: "概览",
group: "system",
icon: markRaw(IconTerminalBoxLine),
priority: 3,
mobile: true,
},
},
},
],
},
],
});
91 changes: 91 additions & 0 deletions src/modules/system/actuator/types/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
export interface GlobalInfo {
externalUrl: string;
timeZone: string;
locale: string;
allowComments: boolean;
allowAnonymousComments: boolean;
allowRegistration: boolean;
}

export interface Info {
git?: Git;
build?: Build;
java: Java;
os: Os;
}

export interface Commit {
id: string;
time: Date;
}

export interface Git {
branch: string;
commit: Commit;
}

export interface Build {
artifact: string;
name: string;
time: Date;
version: string;
group: string;
}

export interface Vendor {
name: string;
version: string;
}

export interface Runtime {
name: string;
version: string;
}

export interface Jvm {
name: string;
vendor: string;
version: string;
}

export interface Java {
version: string;
vendor: Vendor;
runtime: Runtime;
jvm: Jvm;
}

export interface Os {
name: string;
version: string;
arch: string;
}

export interface Tag {
key: string;
value: string;
}

export interface StartupStep {
name: string;
id: number;
tags: Tag[];
parentId?: number;
}

export interface Event {
endTime: Date;
duration: string;
startTime: Date;
startupStep: StartupStep;
}

export interface Timeline {
startTime: Date;
events: Event[];
}

export interface Startup {
springBootVersion: string;
timeline: Timeline;
}